summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/CODEOWNERS6
-rw-r--r--.gitlab/ci/frontend.gitlab-ci.yml43
-rw-r--r--.gitlab/ci/rails.gitlab-ci.yml1
-rw-r--r--.gitlab/ci/review.gitlab-ci.yml33
-rw-r--r--.gitlab/ci/test-metadata.gitlab-ci.yml1
-rw-r--r--.gitlab/merge_request_templates/Database changes.md13
-rw-r--r--.haml-lint.yml4
-rw-r--r--.rubocop.yml4
-rw-r--r--CHANGELOG.md271
-rw-r--r--Gemfile1
-rw-r--r--Gemfile.lock6
-rw-r--r--VERSION2
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_gfm.js4
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_metrics.js24
-rw-r--r--app/assets/javascripts/boards/components/board_blank_state.vue11
-rw-r--r--app/assets/javascripts/boards/components/board_form.vue216
-rw-r--r--app/assets/javascripts/boards/components/boards_selector.vue334
-rw-r--r--app/assets/javascripts/boards/components/modal/footer.vue3
-rw-r--r--app/assets/javascripts/boards/components/project_select.vue8
-rw-r--r--app/assets/javascripts/boards/ee_functions.js7
-rw-r--r--app/assets/javascripts/boards/index.js19
-rw-r--r--app/assets/javascripts/boards/mixins/modal_footer.js1
-rw-r--r--app/assets/javascripts/boards/mount_multiple_boards_switcher.js37
-rw-r--r--app/assets/javascripts/commons/polyfills.js1
-rw-r--r--app/assets/javascripts/groups/components/group_item.vue2
-rw-r--r--app/assets/javascripts/ide/services/index.js8
-rw-r--r--app/assets/javascripts/ide/stores/mutations/file.js12
-rw-r--r--app/assets/javascripts/ide/stores/utils.js4
-rw-r--r--app/assets/javascripts/labels_select.js3
-rw-r--r--app/assets/javascripts/monitoring/components/charts/area.vue96
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue7
-rw-r--r--app/assets/javascripts/monitoring/components/embed.vue97
-rw-r--r--app/assets/javascripts/monitoring/constants.js2
-rw-r--r--app/assets/javascripts/monitoring/stores/actions.js12
-rw-r--r--app/assets/javascripts/monitoring/stores/mutation_types.js1
-rw-r--r--app/assets/javascripts/monitoring/stores/mutations.js3
-rw-r--r--app/assets/javascripts/monitoring/stores/state.js1
-rw-r--r--app/assets/javascripts/notes/components/discussion_actions.vue25
-rw-r--r--app/assets/javascripts/reports/components/report_item.vue2
-rw-r--r--app/assets/javascripts/reports/components/summary_row.vue4
-rw-r--r--app/assets/javascripts/repository/components/table/index.vue1
-rw-r--r--app/assets/javascripts/repository/components/table/row.vue7
-rw-r--r--app/assets/javascripts/repository/queries/getFiles.query.graphql2
-rw-r--r--app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue32
-rw-r--r--app/assets/stylesheets/_ee/application_ee.scss5
-rw-r--r--app/assets/stylesheets/application.scss4
-rw-r--r--app/assets/stylesheets/framework/common.scss1
-rw-r--r--app/assets/stylesheets/pages/groups.scss4
-rw-r--r--app/assets/stylesheets/pages/issuable.scss20
-rw-r--r--app/assets/stylesheets/pages/labels.scss4
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss5
-rw-r--r--app/assets/stylesheets/pages/prometheus.scss5
-rw-r--r--app/controllers/admin/groups_controller.rb3
-rw-r--r--app/controllers/admin/requests_profiles_controller.rb12
-rw-r--r--app/controllers/chaos_controller.rb54
-rw-r--r--app/controllers/groups_controller.rb3
-rw-r--r--app/controllers/ide_controller.rb1
-rw-r--r--app/controllers/projects/cycle_analytics/events_controller.rb10
-rw-r--r--app/controllers/projects/merge_requests/creations_controller.rb8
-rw-r--r--app/controllers/projects/snippets_controller.rb3
-rw-r--r--app/controllers/projects/wikis_controller.rb3
-rw-r--r--app/controllers/projects_controller.rb8
-rw-r--r--app/controllers/registrations_controller.rb3
-rw-r--r--app/controllers/sessions_controller.rb7
-rw-r--r--app/controllers/snippets_controller.rb3
-rw-r--r--app/finders/autocomplete/move_to_project_finder.rb9
-rw-r--r--app/graphql/types/tree/submodule_type.rb3
-rw-r--r--app/graphql/types/tree/tree_type.rb4
-rw-r--r--app/helpers/submodule_helper.rb22
-rw-r--r--app/models/active_session.rb12
-rw-r--r--app/models/clusters/applications/runner.rb2
-rw-r--r--app/models/concerns/deployment_platform.rb23
-rw-r--r--app/models/concerns/issuable.rb7
-rw-r--r--app/models/concerns/project_api_compatibility.rb6
-rw-r--r--app/models/concerns/taskable.rb3
-rw-r--r--app/models/cycle_analytics/group_level.rb31
-rw-r--r--app/models/cycle_analytics/level_base.rb (renamed from app/models/cycle_analytics/base.rb)6
-rw-r--r--app/models/cycle_analytics/project_level.rb5
-rw-r--r--app/models/dashboard_group_milestone.rb3
-rw-r--r--app/models/environment.rb47
-rw-r--r--app/models/group.rb4
-rw-r--r--app/models/pages_domain.rb2
-rw-r--r--app/models/personal_access_token.rb1
-rw-r--r--app/models/project.rb19
-rw-r--r--app/models/project_auto_devops.rb2
-rw-r--r--app/models/project_feature.rb2
-rw-r--r--app/models/project_services/jira_service.rb8
-rw-r--r--app/policies/group_policy.rb5
-rw-r--r--app/serializers/analytics_issue_entity.rb6
-rw-r--r--app/serializers/analytics_merge_request_entity.rb2
-rw-r--r--app/serializers/analytics_stage_entity.rb4
-rw-r--r--app/serializers/diff_file_base_entity.rb11
-rw-r--r--app/serializers/diffs_entity.rb5
-rw-r--r--app/serializers/discussion_serializer.rb14
-rw-r--r--app/serializers/group_analytics_stage_entity.rb16
-rw-r--r--app/serializers/group_analytics_stage_serializer.rb5
-rw-r--r--app/serializers/submodule_entity.rb25
-rw-r--r--app/services/commits/create_service.rb18
-rw-r--r--app/services/files/multi_service.rb1
-rw-r--r--app/services/issuable/clone/attributes_rewriter.rb2
-rw-r--r--app/services/merge_requests/push_options_handler_service.rb42
-rw-r--r--app/services/wiki_pages/base_service.rb6
-rw-r--r--app/views/admin/groups/show.html.haml5
-rw-r--r--app/views/admin/projects/show.html.haml5
-rw-r--r--app/views/admin/requests_profiles/index.html.haml3
-rw-r--r--app/views/admin/users/index.html.haml2
-rw-r--r--app/views/devise/passwords/edit.html.haml6
-rw-r--r--app/views/devise/sessions/_new_ldap.html.haml6
-rw-r--r--app/views/devise/shared/_tabs_ldap.html.haml6
-rw-r--r--app/views/devise/shared/_tabs_normal.html.haml4
-rw-r--r--app/views/groups/_group_admin_settings.html.haml6
-rw-r--r--app/views/groups/settings/_permissions.html.haml1
-rw-r--r--app/views/groups/settings/_subgroup_creation_level.html.haml3
-rw-r--r--app/views/layouts/header/_current_user_dropdown.html.haml4
-rw-r--r--app/views/projects/merge_requests/creations/_new_compare.html.haml2
-rw-r--r--app/views/projects/merge_requests/creations/_new_submit.html.haml3
-rw-r--r--app/views/projects/pages_domains/_form.html.haml2
-rw-r--r--app/views/projects/pipelines/charts.html.haml7
-rw-r--r--app/views/projects/pipelines/charts/_overall.haml21
-rw-r--r--app/views/projects/pipelines/charts/_pipeline_statistics.haml14
-rw-r--r--app/views/projects/pipelines/charts/_pipelines.haml2
-rw-r--r--app/views/shared/_issuable_meta_data.html.haml4
-rw-r--r--app/views/shared/boards/_switcher.html.haml16
-rw-r--r--app/views/shared/issuable/_form.html.haml2
-rw-r--r--app/views/shared/issuable/_search_bar.html.haml2
-rw-r--r--app/views/shared/projects/_project.html.haml4
-rw-r--r--app/workers/all_queues.yml6
-rw-r--r--app/workers/chaos/cpu_spin_worker.rb12
-rw-r--r--app/workers/chaos/db_spin_worker.rb12
-rw-r--r--app/workers/chaos/kill_worker.rb12
-rw-r--r--app/workers/chaos/leak_mem_worker.rb12
-rw-r--r--app/workers/chaos/sleep_worker.rb12
-rw-r--r--app/workers/concerns/chaos_queue.rb9
-rw-r--r--app/workers/gitlab_usage_ping_worker.rb6
-rw-r--r--changelogs/unreleased/11039-moved-code-difference-from-EE-to-CE.yml5
-rw-r--r--changelogs/unreleased/11448-fix-cs-with-k8s-runners.yml5
-rw-r--r--changelogs/unreleased/11888-regression-deploy-correlation-markers-on-monitoring-graphs-not-clickable.yml5
-rw-r--r--changelogs/unreleased/12533-shared-runners-warning.yml5
-rw-r--r--changelogs/unreleased/12550-fullscrean.yml5
-rw-r--r--changelogs/unreleased/12553-preferences.yml5
-rw-r--r--changelogs/unreleased/17690-Protect-TeamCity-builds-for-triggering-when-a-branch-is-deleted-And-add-MR-option.yml5
-rw-r--r--changelogs/unreleased/29775-fix-nested-lists-unnecessary-margin.yml5
-rw-r--r--changelogs/unreleased/30355-use-hours-only-for-time-tracking.yml5
-rw-r--r--changelogs/unreleased/30974-issue-search-by-number.yml (renamed from changelogs/unreleased/-30974-issue-search-by-number.yml)2
-rw-r--r--changelogs/unreleased/32452-multiple-discussions.yml5
-rw-r--r--changelogs/unreleased/35757-move-issues-in-boards-pderichs.yml5
-rw-r--r--changelogs/unreleased/38105-pre-release-tag.yml5
-rw-r--r--changelogs/unreleased/40379-CJK-search-min-chars.yml5
-rw-r--r--changelogs/unreleased/42399-registry-confirm-deletion.yml5
-rw-r--r--changelogs/unreleased/44106-include-subgroups-in-group-activity.yml5
-rw-r--r--changelogs/unreleased/44949-do-not-update-updated_at-on-an-issue-when-reordering-it.yml5
-rw-r--r--changelogs/unreleased/45104-special-characters-in-project-name-path-prevent-users-from-using-the-container-registry.yml5
-rw-r--r--changelogs/unreleased/45120-fix-ide-editor-to-update-size-on-show-change.yml5
-rw-r--r--changelogs/unreleased/49814-display-in-admin-area-if-emails-are-verified-or-not.yml5
-rw-r--r--changelogs/unreleased/50228-deploy-tokens-custom-username.yml5
-rw-r--r--changelogs/unreleased/50834-change-http-status-code-when-repository-disabled.yml5
-rw-r--r--changelogs/unreleased/51794-add-ordering-to-runner-jobs-api.yml5
-rw-r--r--changelogs/unreleased/51952-forking-via-webide.yml5
-rw-r--r--changelogs/unreleased/51952-redirect-to-webide-in-fork.yml5
-rw-r--r--changelogs/unreleased/52366-improved-group-lists-ui.yml5
-rw-r--r--changelogs/unreleased/52442-minimal-remove-mysql-support.yml5
-rw-r--r--changelogs/unreleased/52954-allow-developers-to-delete-tags.yml5
-rw-r--r--changelogs/unreleased/53357-fix-plus-in-upload-file-names.yml5
-rw-r--r--changelogs/unreleased/53811-issue-boards-to-core-projects-backend-ce.yml5
-rw-r--r--changelogs/unreleased/54117-transactional-rebase.yml5
-rw-r--r--changelogs/unreleased/54595-incorrect-reaction-emoji-placement-in-discussion.yml5
-rw-r--r--changelogs/unreleased/55487-enable-group-terminals-button.yml5
-rw-r--r--changelogs/unreleased/55564-remove-if-in-before-after-action.yml5
-rw-r--r--changelogs/unreleased/55623-group-cluster-apis.yml5
-rw-r--r--changelogs/unreleased/55902-disable-creation-of-non-rbac-kubernetes-clusters.yml5
-rw-r--r--changelogs/unreleased/55953-renamed-discussion-to-thread.yml5
-rw-r--r--changelogs/unreleased/57538-normalize-users-private-profile-field.yml5
-rw-r--r--changelogs/unreleased/57793-fix-line-age.yml5
-rw-r--r--changelogs/unreleased/57815.yml5
-rw-r--r--changelogs/unreleased/57918-encrypt-feature-flags-tokens-changelog.yml5
-rw-r--r--changelogs/unreleased/57973-errors-in-application-settings-panel-shows-wrong-panel.yml5
-rw-r--r--changelogs/unreleased/58065-uniform-html-txt-email.yml5
-rw-r--r--changelogs/unreleased/58689-regroup-jump-button-in-discussion.yml6
-rw-r--r--changelogs/unreleased/58802-rename-webide.yml5
-rw-r--r--changelogs/unreleased/58808-fix-image-diff-on-text.yml5
-rw-r--r--changelogs/unreleased/59028-fix-extra-plus-in-diffs.yml5
-rw-r--r--changelogs/unreleased/59257-find-new-branches-harder.yml5
-rw-r--r--changelogs/unreleased/60617-enable-project-cluster-jit.yml5
-rw-r--r--changelogs/unreleased/60666-kubernetes-applications-uninstall-runner.yml5
-rw-r--r--changelogs/unreleased/60856-deleting-binary-file.yml5
-rw-r--r--changelogs/unreleased/60859-upload-after-delete.yml5
-rw-r--r--changelogs/unreleased/60860-keep-empty-folders-in-tree.yml5
-rw-r--r--changelogs/unreleased/60879-fix-reports-timing-out.yml5
-rw-r--r--changelogs/unreleased/60948-display-groupid-on-group-admin-page.yml5
-rw-r--r--changelogs/unreleased/60949-display-projectid-on-project-admin-page.yml5
-rw-r--r--changelogs/unreleased/61005-grafanaInAdminSettingsMonitoringMenu.yml5
-rw-r--r--changelogs/unreleased/61156-instance-level-cluster-pod-terminal-access.yml5
-rw-r--r--changelogs/unreleased/61201-pass-identities-to-external-authorization.yml5
-rw-r--r--changelogs/unreleased/61207-adjusted-hoverable-area-in-sidebar.yml5
-rw-r--r--changelogs/unreleased/61284-frontend-follow-up-from-add-packages_size-to-projectstatistics.yml5
-rw-r--r--changelogs/unreleased/61342-commit-search-result-doesn-t-pass-wcag-color-audit.yml5
-rw-r--r--changelogs/unreleased/61613-spacing-mr-widgets.yml5
-rw-r--r--changelogs/unreleased/62088-search-back.yml5
-rw-r--r--changelogs/unreleased/62124-new-threaded-discussion-design.yml5
-rw-r--r--changelogs/unreleased/62183-update-response-code-for-bulk-delete-api-for-container-registry.yml5
-rw-r--r--changelogs/unreleased/62772-disable-kubernetes-credential-passthrough.yml5
-rw-r--r--changelogs/unreleased/62772-migrate-managed-clusters-to-unmanaged.yml6
-rw-r--r--changelogs/unreleased/62826-graphql-emoji-mutations.yml5
-rw-r--r--changelogs/unreleased/62826-graphql-note-mutations.yml5
-rw-r--r--changelogs/unreleased/62938-wcag-aa-edited-text-color.yml5
-rw-r--r--changelogs/unreleased/62968-environment-details-header-border-misaligned.yml5
-rw-r--r--changelogs/unreleased/62980-username-availability-checker-breaks-inline-validation.yml5
-rw-r--r--changelogs/unreleased/63079-migrate-clusters-with-no-token-to-unmanaged.yml6
-rw-r--r--changelogs/unreleased/63200-reply-button-broken.yml5
-rw-r--r--changelogs/unreleased/63227-fix-double-border.yml5
-rw-r--r--changelogs/unreleased/63247-add-conf-toast-and-link.yml5
-rw-r--r--changelogs/unreleased/63261-the-graphql-query-for-the-mr-popover-failes-on-the-frontend.yml6
-rw-r--r--changelogs/unreleased/63298-prevent-excessive-sanitization-asciidoc.yml5
-rw-r--r--changelogs/unreleased/63438-oauth2-support-with-gitlab-personal-access-token.yml5
-rw-r--r--changelogs/unreleased/63475-fix-n-1.yml5
-rw-r--r--changelogs/unreleased/63479-jira-capitalization.yml5
-rw-r--r--changelogs/unreleased/63507-fix-race-condition-fetching-token.yml5
-rw-r--r--changelogs/unreleased/63559-remove-avatar-from-sign-in.yml5
-rw-r--r--changelogs/unreleased/63590-pipeline-actions-cause-full-refresh.yml5
-rw-r--r--changelogs/unreleased/63656-runner-tags-search-dropdown-is-empty.yml5
-rw-r--r--changelogs/unreleased/63667-hashed-storage-migration-count-correctly.yml5
-rw-r--r--changelogs/unreleased/63691-fix-doc-link.yml5
-rw-r--r--changelogs/unreleased/63833-fix-jira-issues-url.yml5
-rw-r--r--changelogs/unreleased/63873-process-start-time.yml6
-rw-r--r--changelogs/unreleased/63945-update-mixin-deep-to-1-3-2.yml5
-rw-r--r--changelogs/unreleased/63971-remove-istanbul.yml5
-rw-r--r--changelogs/unreleased/64066-fix-uneven-click-areas.yml5
-rw-r--r--changelogs/unreleased/64070-asciidoctor-enable-section-anchors.yml5
-rw-r--r--changelogs/unreleased/64091-fix-broken-terminal.yml5
-rw-r--r--changelogs/unreleased/64091-fix-sprockets-paths.yml5
-rw-r--r--changelogs/unreleased/64160-fix-duplicate-buttons.yml5
-rw-r--r--changelogs/unreleased/64161-gitlab-fqdn.yml5
-rw-r--r--changelogs/unreleased/64176-fix-error-handling.yml5
-rw-r--r--changelogs/unreleased/64249-align-container-registry-empty-state-with-design-guidelines.yml5
-rw-r--r--changelogs/unreleased/64265-center-loading-icon.yml5
-rw-r--r--changelogs/unreleased/64295-predictable-environment-slugs.yml5
-rw-r--r--changelogs/unreleased/64314-ci-icon.yml5
-rw-r--r--changelogs/unreleased/64321-wrong-url-when-creating-milestones-from-instance-milestones-dashboard.yml5
-rw-r--r--changelogs/unreleased/64331-Assignee-field-in-a-new-issue-has-an-incorrect-line-wrap.yml5
-rw-r--r--changelogs/unreleased/64407-vfazio-quirk-omniauth-strategies-openidconnect.yml5
-rw-r--r--changelogs/unreleased/64416-lodash-4-6-2-for-prototype-pollution.yml5
-rw-r--r--changelogs/unreleased/64697-markdown-issues-checkbox-inside-blockquote-status-won-t-be-saved.yml5
-rw-r--r--changelogs/unreleased/64700-fix-the-color-of-the-visibility-icon-on-project-lists.yml5
-rw-r--r--changelogs/unreleased/64731-fix-project-auto-devops-api.yml5
-rw-r--r--changelogs/unreleased/64870-can-t-save-pages-domain-form-with-let-s-encrypt-enabled-if-current-certificate-is-outdated.yml6
-rw-r--r--changelogs/unreleased/FixLocaleEN.yml5
-rw-r--r--changelogs/unreleased/Remove-unresolved-class-in-discussion-header.yml5
-rw-r--r--changelogs/unreleased/add-clusters-to-deployment.yml5
-rw-r--r--changelogs/unreleased/add-metrics-dashboard-permission-check.yml5
-rw-r--r--changelogs/unreleased/add-salesforce-logo.yml5
-rw-r--r--changelogs/unreleased/add-strategies-column-to-scopes-table.yml5
-rw-r--r--changelogs/unreleased/add-support-for-start-sha-to-commits-api.yml5
-rw-r--r--changelogs/unreleased/allow-reactive-caching-of-nil.yml5
-rw-r--r--changelogs/unreleased/always-allow-prometheus-access-in-dev.yml5
-rw-r--r--changelogs/unreleased/always-display-environment-selector.yml5
-rw-r--r--changelogs/unreleased/an-sidekiq-chaos.yml5
-rw-r--r--changelogs/unreleased/api-doc-negative-commit-message-push-rule.yml5
-rw-r--r--changelogs/unreleased/asciidoc-enable-syntax-highlighting.yml5
-rw-r--r--changelogs/unreleased/asciidoctor-upgrade.yml5
-rw-r--r--changelogs/unreleased/backstage-gb-improve-performance-environment-statuses-endpoint.yml5
-rw-r--r--changelogs/unreleased/bjk-64064_cache_metrics.yml5
-rw-r--r--changelogs/unreleased/bjk-fix_prom_example.yml5
-rw-r--r--changelogs/unreleased/bjk-usage_ping.yml5
-rw-r--r--changelogs/unreleased/bvl-markdown-graphql.yml5
-rw-r--r--changelogs/unreleased/bvl-rename-routes-after-user-rename.yml5
-rw-r--r--changelogs/unreleased/caneldem-master-patch-77839.yml5
-rw-r--r--changelogs/unreleased/ce-11098-update-merge-request-settings-description-text.yml5
-rw-r--r--changelogs/unreleased/centralize-markdownlint-config.yml5
-rw-r--r--changelogs/unreleased/check-min-schema-migrate.yml5
-rw-r--r--changelogs/unreleased/clusters-group-cte.yml5
-rw-r--r--changelogs/unreleased/create-merge-train-ref-ce.yml5
-rw-r--r--changelogs/unreleased/db-update-geo-nodes-primary.yml5
-rw-r--r--changelogs/unreleased/dm-submodule-helper-routing.yml5
-rw-r--r--changelogs/unreleased/dohtaset.yml5
-rw-r--r--changelogs/unreleased/dz-remove-deprecated-group-routes.yml5
-rw-r--r--changelogs/unreleased/dz-remove-deprecated-user-routes.yml5
-rw-r--r--changelogs/unreleased/embedded-metrics-be-2.yml5
-rw-r--r--changelogs/unreleased/expose-saml-provider-id-to-users-api.yml5
-rw-r--r--changelogs/unreleased/fe-delete-old-boardservice-on-component-modal-footer.yml6
-rw-r--r--changelogs/unreleased/fe-delete-old-boardservice.yml6
-rw-r--r--changelogs/unreleased/fe-issue-reorder.yml5
-rw-r--r--changelogs/unreleased/feature-gb-serverless-app-deployment-template.yml5
-rw-r--r--changelogs/unreleased/feature-uninstall_cluster_ingress.yml5
-rw-r--r--changelogs/unreleased/feature-uninstall_jupyter_hub_app.yml5
-rw-r--r--changelogs/unreleased/fix-alignment-on-security-reports.yml5
-rw-r--r--changelogs/unreleased/fix-broken-vue-i18n-strings.yml5
-rw-r--r--changelogs/unreleased/fix-comment-race-condition.yml5
-rw-r--r--changelogs/unreleased/fix-facivon-url-if-uploads-object-store-enabled.yml5
-rw-r--r--changelogs/unreleased/fix-i18n-updated-projects.yml5
-rw-r--r--changelogs/unreleased/fix-jupyter-git-v3.yml5
-rw-r--r--changelogs/unreleased/fix-median-counting-for-cycle-analytics.yml5
-rw-r--r--changelogs/unreleased/fix-pipeline-schedule-edge-case.yml6
-rw-r--r--changelogs/unreleased/fix-sidekiq-transaction-check-race.yml5
-rw-r--r--changelogs/unreleased/fix-unicorn-sampler-workers-count.yml5
-rw-r--r--changelogs/unreleased/fj-avoid-incresaing-usage-ping-when-not-enabled.yml5
-rw-r--r--changelogs/unreleased/fj-count-web-ide-merge-requests.yml5
-rw-r--r--changelogs/unreleased/fj-fix-subgroup-search-url.yml5
-rw-r--r--changelogs/unreleased/georgekoltsov-63955-fix-import-with-source-branch-deleted.yml5
-rw-r--r--changelogs/unreleased/georgekoltsov-64311-set-visibility-private-if-internal-restricted.yml5
-rw-r--r--changelogs/unreleased/gitaly-version-v1-53-0.yml5
-rw-r--r--changelogs/unreleased/gitaly-version-v1.49.0.yml5
-rw-r--r--changelogs/unreleased/gitaly-version-v1.51.0.yml5
-rw-r--r--changelogs/unreleased/gitaly-version-v1.52.0.yml5
-rw-r--r--changelogs/unreleased/graphql-tree-last-commit.yml5
-rw-r--r--changelogs/unreleased/group-milestones-dashboard-blunceford.yml5
-rw-r--r--changelogs/unreleased/gt-remove-tooltip-directive-on-project-avatar-image-component.yml5
-rw-r--r--changelogs/unreleased/hfy-apply-knative-cluster-role-on-service-account-creation.yml5
-rw-r--r--changelogs/unreleased/hide-restricted-visibility-radio.yml5
-rw-r--r--changelogs/unreleased/id-clean-up-mr-assignees.yml5
-rw-r--r--changelogs/unreleased/id-extract-widget-into-different-request.yml5
-rw-r--r--changelogs/unreleased/id-stale-branches.yml5
-rw-r--r--changelogs/unreleased/issue-63222.yml5
-rw-r--r--changelogs/unreleased/issue-zoom-url.yml5
-rw-r--r--changelogs/unreleased/issue_64021.yml5
-rw-r--r--changelogs/unreleased/jc-detect-nfs-for-rugged.yml5
-rw-r--r--changelogs/unreleased/jc-remove-catfile-flag.yml5
-rw-r--r--changelogs/unreleased/jprovazn-project-search.yml5
-rw-r--r--changelogs/unreleased/jramsay-enable-object-dedupe-by-default.yml5
-rw-r--r--changelogs/unreleased/knative-0-6.yml5
-rw-r--r--changelogs/unreleased/limit-amount-of-tests-returned.yml5
-rw-r--r--changelogs/unreleased/maintainers-can-create-subgroup.yml5
-rw-r--r--changelogs/unreleased/mh-board-tooltips.yml5
-rw-r--r--changelogs/unreleased/mh-boards-filter-bar.yml5
-rw-r--r--changelogs/unreleased/mh-collapsible-boards.yml5
-rw-r--r--changelogs/unreleased/mh-colon-autocomplete.yml5
-rw-r--r--changelogs/unreleased/mh-mermaid-linebreaks.yml5
-rw-r--r--changelogs/unreleased/move-all-configs-to-global.yml5
-rw-r--r--changelogs/unreleased/mw-project-list-color-fix.yml5
-rw-r--r--changelogs/unreleased/osw-persist-tmp-snippet-uploads.yml5
-rw-r--r--changelogs/unreleased/osw-sync-merge-ref-upon-mergeability-check.yml5
-rw-r--r--changelogs/unreleased/paginate-license-management.yml5
-rw-r--r--changelogs/unreleased/patch-29.yml5
-rw-r--r--changelogs/unreleased/po-raw-changes-encoding.yml5
-rw-r--r--changelogs/unreleased/pre-releases-38105a.yml5
-rw-r--r--changelogs/unreleased/prepare-cycle-analytics-for-group-level.yml5
-rw-r--r--changelogs/unreleased/project_api.yml5
-rw-r--r--changelogs/unreleased/refactor-sentry.yml5
-rw-r--r--changelogs/unreleased/registry-fix-multi-delete-modal.yml5
-rw-r--r--changelogs/unreleased/remove-auto-ssl-ff.yml6
-rw-r--r--changelogs/unreleased/remove-kubernetes-service-deployment-platform.yml5
-rw-r--r--changelogs/unreleased/remove-support-for-legacy-pipeline-triggers.yml5
-rw-r--r--changelogs/unreleased/remove_group_and_instance_clusters_feature_flag.yml5
-rw-r--r--changelogs/unreleased/require-pipeline-when-enabling-only-allow-merge-if-pipeline-succeeds.yml5
-rw-r--r--changelogs/unreleased/rj-fix-manual-order.yml5
-rw-r--r--changelogs/unreleased/rm-src-branch.yml5
-rw-r--r--changelogs/unreleased/sanitize_rake_ldap_check_output.yml5
-rw-r--r--changelogs/unreleased/search-blob-basenames.yml5
-rw-r--r--changelogs/unreleased/security-2858-fix-color-validation.yml5
-rw-r--r--changelogs/unreleased/security-59581-related-merge-requests-count.yml5
-rw-r--r--changelogs/unreleased/security-DOS_issue_comments_banzai.yml5
-rw-r--r--changelogs/unreleased/security-bvl-enforce-graphql-type-authorization.yml5
-rw-r--r--changelogs/unreleased/security-fp-prevent-billion-laughs-attack.yml5
-rw-r--r--changelogs/unreleased/security-mr-head-pipeline-leak.yml5
-rw-r--r--changelogs/unreleased/security-notes-in-private-snippets.yml5
-rw-r--r--changelogs/unreleased/security-prevent-detection-of-merge-request-template-name.yml5
-rw-r--r--changelogs/unreleased/set-higher-ttl-for-trace-write.yml5
-rw-r--r--changelogs/unreleased/sh-add-force-random-password-user-api.yml5
-rw-r--r--changelogs/unreleased/sh-add-gitaly-ref-caching-search-controller.yml5
-rw-r--r--changelogs/unreleased/sh-add-rugged-logs.yml5
-rw-r--r--changelogs/unreleased/sh-add-thread-memory-cache.yml5
-rw-r--r--changelogs/unreleased/sh-audit-event-json-log-format-from-and-to.yml5
-rw-r--r--changelogs/unreleased/sh-avoid-loading-pipeline-status.yml5
-rw-r--r--changelogs/unreleased/sh-bump-fog-aws.yml5
-rw-r--r--changelogs/unreleased/sh-cache-feature-flag-names.yml5
-rw-r--r--changelogs/unreleased/sh-cache-flipper-checks-in-memory.yml5
-rw-r--r--changelogs/unreleased/sh-cache-flipper-names-memory-cache.yml5
-rw-r--r--changelogs/unreleased/sh-cache-negative-entries-find-commit.yml5
-rw-r--r--changelogs/unreleased/sh-clean-up-bitbucket-import-errors.yml5
-rw-r--r--changelogs/unreleased/sh-disable-reactive-caching-automatic-retries.yml5
-rw-r--r--changelogs/unreleased/sh-enable-ref-name-caching-discussions.yml5
-rw-r--r--changelogs/unreleased/sh-fix-httpclient-ssl.yml5
-rw-r--r--changelogs/unreleased/sh-fix-issue-63349.yml5
-rw-r--r--changelogs/unreleased/sh-fix-issue-63910.yml5
-rw-r--r--changelogs/unreleased/sh-handle-nil-replication-lag.yml5
-rw-r--r--changelogs/unreleased/sh-improve-redis-peek.yml5
-rw-r--r--changelogs/unreleased/sh-make-githost-json.yml5
-rw-r--r--changelogs/unreleased/sh-optimize-todos-controller.yml5
-rw-r--r--changelogs/unreleased/sh-remove-import-columns-from-projects.yml5
-rw-r--r--changelogs/unreleased/sh-service-template-bug.yml5
-rw-r--r--changelogs/unreleased/sh-strong-memoize-appearances.yml5
-rw-r--r--changelogs/unreleased/sh-support-subnets-ip-rate-limiter.yml5
-rw-r--r--changelogs/unreleased/sh-update-mermaid.yml5
-rw-r--r--changelogs/unreleased/sh-upgrade-rouge-3-5-1.yml5
-rw-r--r--changelogs/unreleased/slugify.yml5
-rw-r--r--changelogs/unreleased/small-s-in-elasticsearch-in-code.yml5
-rw-r--r--changelogs/unreleased/small-s-in-elasticsearch.yml5
-rw-r--r--changelogs/unreleased/support-jsonb-default-value.yml5
-rw-r--r--changelogs/unreleased/tc-rake-orphan-artifacts.yml5
-rw-r--r--changelogs/unreleased/transaction-metrics.yml5
-rw-r--r--changelogs/unreleased/tz-update-mr-count-over-tabs.yml6
-rw-r--r--changelogs/unreleased/unicorn-sampler-fix.yml5
-rw-r--r--changelogs/unreleased/update-clair-version.yml6
-rw-r--r--changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-6-0.yml5
-rw-r--r--changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-7-0.yml5
-rw-r--r--changelogs/unreleased/update-pages-to-1-7-0.yml5
-rw-r--r--changelogs/unreleased/update-pagination-texts.yml5
-rw-r--r--changelogs/unreleased/update-tar-to-2-2-2.yml5
-rw-r--r--changelogs/unreleased/update-todo-in-ui.yml5
-rw-r--r--changelogs/unreleased/use-pg-9-6-11-on-ci.yml5
-rw-r--r--changelogs/unreleased/wiki-usage-pings.yml5
-rw-r--r--changelogs/unreleased/winh-jest-markdown-header.yml5
-rw-r--r--changelogs/unreleased/winh-notes-service-applySuggestion.yml5
-rw-r--r--changelogs/unreleased/winh-notes-service-deleteNote.yml5
-rw-r--r--changelogs/unreleased/winh-notes-service-toggleAward.yml5
-rw-r--r--changelogs/unreleased/winh-updateResolvableDiscussionsCounts-typo.yml5
-rw-r--r--changelogs/unreleased/z-index-fix-for-diff-file-dropdown.yml5
-rw-r--r--changelogs/unreleased/z-index-tools.yml5
-rw-r--r--changelogs/unreleased/zj-gitaly-usage-data.yml5
-rw-r--r--config/application.rb11
-rw-r--r--config/initializers/1_settings.rb1
-rw-r--r--config/initializers/7_prometheus_metrics.rb3
-rw-r--r--config/initializers/lograge.rb7
-rw-r--r--config/initializers/peek.rb2
-rw-r--r--config/initializers/zz_metrics.rb31
-rw-r--r--config/karma.config.js10
-rw-r--r--config/locales/en.yml6
-rw-r--r--config/routes.rb2
-rw-r--r--config/routes/admin.rb2
-rw-r--r--config/settings.rb14
-rw-r--r--config/sidekiq_queues.yml1
-rw-r--r--danger/database/Dangerfile42
-rw-r--r--danger/metadata/Dangerfile12
-rw-r--r--danger/roulette/Dangerfile15
-rw-r--r--db/fixtures/development/14_pipelines.rb18
-rw-r--r--db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb18
-rw-r--r--db/migrate/20190709204413_add_rule_type_to_approval_project_rules.rb17
-rw-r--r--db/migrate/20190710151229_add_index_to_approval_project_rules_rule_type.rb17
-rw-r--r--db/post_migrate/20190703185326_fix_wrong_pages_access_level.rb28
-rw-r--r--db/post_migrate/20190715114644_drop_project_features_pages_access_level_default.rb12
-rw-r--r--db/schema.rb7
-rw-r--r--doc/administration/auth/ldap-ee.md2
-rw-r--r--doc/administration/gitaly/index.md670
-rw-r--r--doc/administration/job_artifacts.md1
-rw-r--r--doc/administration/logs.md14
-rw-r--r--doc/administration/merge_request_diffs.md1
-rw-r--r--doc/administration/monitoring/performance/img/request_profile_result.pngbin3216 -> 33920 bytes
-rw-r--r--doc/administration/monitoring/performance/request_profiling.md4
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md3
-rw-r--r--doc/administration/pages/img/lets_encrypt_integration_v12_1.pngbin0 -> 98409 bytes
-rw-r--r--doc/administration/pages/index.md17
-rw-r--r--doc/administration/repository_checks.md4
-rw-r--r--doc/administration/repository_storage_types.md4
-rw-r--r--doc/administration/uploads.md17
-rw-r--r--doc/api/README.md6
-rw-r--r--doc/api/commits.md9
-rw-r--r--doc/api/container_registry.md4
-rw-r--r--doc/api/lint.md2
-rw-r--r--doc/api/projects.md4
-rw-r--r--doc/api/users.md3
-rw-r--r--doc/ci/caching/index.md2
-rw-r--r--doc/ci/examples/end_to_end_testing_webdriverio/index.md2
-rw-r--r--doc/ci/introduction/index.md36
-rw-r--r--doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md9
-rw-r--r--doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/img/merge_train_position_v12_0.pngbin0 -> 18121 bytes
-rw-r--r--doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md39
-rw-r--r--doc/development/README.md2
-rw-r--r--doc/development/chaos_endpoints.md31
-rw-r--r--doc/development/code_review.md36
-rw-r--r--doc/development/contributing/issue_workflow.md59
-rw-r--r--doc/development/contributing/merge_request_workflow.md3
-rw-r--r--doc/development/database_debugging.md34
-rw-r--r--doc/development/database_review.md101
-rw-r--r--doc/development/diffs.md2
-rw-r--r--doc/development/documentation/feature-change-workflow.md30
-rw-r--r--doc/development/documentation/improvement-workflow.md6
-rw-r--r--doc/development/documentation/index.md43
-rw-r--r--doc/development/documentation/styleguide.md26
-rw-r--r--doc/development/fe_guide/frontend_faq.md6
-rw-r--r--doc/development/fe_guide/icons.md16
-rw-r--r--doc/development/fe_guide/style_guide_js.md4
-rw-r--r--doc/development/fe_guide/style_guide_scss.md2
-rw-r--r--doc/development/file_storage.md4
-rw-r--r--doc/development/geo.md26
-rw-r--r--doc/development/git_object_deduplication.md110
-rw-r--r--doc/development/go_guide/index.md37
-rw-r--r--doc/development/gotchas.md2
-rw-r--r--doc/development/import_export.md4
-rw-r--r--doc/development/interacting_components.md29
-rw-r--r--doc/development/licensed_feature_availability.md6
-rw-r--r--doc/development/new_fe_guide/style/javascript.md5
-rw-r--r--doc/development/performance.md8
-rw-r--r--doc/development/policies.md4
-rw-r--r--doc/development/testing_guide/end_to_end/page_objects.md40
-rw-r--r--doc/development/testing_guide/end_to_end/quick_start_guide.md57
-rw-r--r--doc/development/testing_guide/frontend_testing.md47
-rw-r--r--doc/development/testing_guide/review_apps.md8
-rw-r--r--doc/gitlab-basics/create-project.md32
-rw-r--r--doc/integration/elasticsearch.md6
-rw-r--r--doc/integration/jenkins_deprecated.md2
-rw-r--r--doc/integration/jira_development_panel.md6
-rw-r--r--doc/security/reset_root_password.md2
-rw-r--r--doc/subscriptions/index.md8
-rw-r--r--doc/topics/autodevops/index.md4
-rw-r--r--doc/university/training/topics/getting_started.md19
-rw-r--r--doc/user/admin_area/settings/img/email_confirmation.pngbin0 -> 14260 bytes
-rw-r--r--doc/user/admin_area/settings/sign_up_restrictions.md23
-rw-r--r--doc/user/application_security/dast/index.md22
-rw-r--r--doc/user/application_security/index.md6
-rw-r--r--doc/user/application_security/sast/analyzers.md143
-rw-r--r--doc/user/application_security/sast/index.md52
-rw-r--r--doc/user/application_security/security_dashboard/index.md4
-rw-r--r--doc/user/clusters/applications.md12
-rw-r--r--doc/user/group/bulk_editing/img/bulk-editing.pngbin0 -> 100007 bytes
-rw-r--r--doc/user/group/bulk_editing/index.md22
-rw-r--r--doc/user/group/index.md4
-rw-r--r--doc/user/group/saml_sso/img/group_saml_settings.pngbin89399 -> 140408 bytes
-rw-r--r--doc/user/group/saml_sso/index.md2
-rw-r--r--doc/user/group/subgroups/index.md64
-rw-r--r--doc/user/markdown.md14
-rw-r--r--doc/user/permissions.md5
-rw-r--r--doc/user/project/badges.md16
-rw-r--r--doc/user/project/bulk_editing.md23
-rw-r--r--doc/user/project/clusters/serverless/index.md37
-rw-r--r--doc/user/project/cycle_analytics.md14
-rw-r--r--[-rwxr-xr-x]doc/user/project/img/autocomplete_characters_example1_v12_0.pngbin17510 -> 17510 bytes
-rw-r--r--[-rwxr-xr-x]doc/user/project/img/autocomplete_characters_example2_v12_0.pngbin14623 -> 14623 bytes
-rw-r--r--doc/user/project/import/svn.md10
-rw-r--r--doc/user/project/index.md18
-rw-r--r--doc/user/project/integrations/img/prometheus_dashboard_area_panel_type.pngbin0 -> 65101 bytes
-rw-r--r--doc/user/project/integrations/img/prometheus_dashboard_single_stat_panel_type.pngbin0 -> 26781 bytes
-rw-r--r--doc/user/project/integrations/mock_ci.md4
-rw-r--r--doc/user/project/integrations/prometheus.md61
-rw-r--r--doc/user/project/integrations/webhooks.md14
-rw-r--r--doc/user/project/issue_board.md12
-rw-r--r--doc/user/project/issues/confidential_issues.md46
-rw-r--r--doc/user/project/issues/img/confidential_mr_branch_dropdown_v12_1.pngbin0 -> 121644 bytes
-rw-r--r--doc/user/project/issues/img/confidential_mr_dropdown_v12_1.pngbin0 -> 128156 bytes
-rw-r--r--doc/user/project/issues/index.md5
-rw-r--r--doc/user/project/issues/managing_issues.md4
-rw-r--r--doc/user/project/issues/sorting_issue_lists.md30
-rw-r--r--doc/user/project/merge_requests/blocking_merge_requests.md133
-rw-r--r--doc/user/project/merge_requests/img/edit_blocking_merge_requests.pngbin0 -> 9926 bytes
-rw-r--r--doc/user/project/merge_requests/img/edit_blocking_merge_requests_inaccessible.pngbin0 -> 10867 bytes
-rw-r--r--doc/user/project/merge_requests/img/show_blocking_merge_requests_in_mr_widget.pngbin0 -> 27089 bytes
-rw-r--r--doc/user/project/merge_requests/index.md46
-rw-r--r--doc/user/project/packages/npm_registry.md23
-rw-r--r--doc/user/project/pages/custom_domains_ssl_tls_certification/img/lets_encrypt_integration_v12_1.pngbin0 -> 35040 bytes
-rw-r--r--doc/user/project/pages/custom_domains_ssl_tls_certification/index.md11
-rw-r--r--doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md87
-rw-r--r--doc/user/project/pages/index.md2
-rw-r--r--doc/user/project/pages/introduction.md26
-rw-r--r--doc/user/project/pages/lets_encrypt_for_gitlab_pages.md11
-rw-r--r--doc/user/project/quick_actions.md6
-rw-r--r--doc/user/project/settings/import_export.md5
-rw-r--r--doc/user/search/advanced_search_syntax.md6
-rw-r--r--doc/user/search/index.md10
-rw-r--r--doc/workflow/forking_workflow.md34
-rw-r--r--doc/workflow/lfs/lfs_administration.md1
-rw-r--r--doc/workflow/repository_mirroring.md12
-rw-r--r--lib/api/commits.rb16
-rw-r--r--lib/api/helpers/projects_helpers.rb1
-rw-r--r--lib/banzai/filter/ascii_doc_sanitization_filter.rb33
-rw-r--r--lib/gitlab/access.rb11
-rw-r--r--lib/gitlab/auth/user_auth_finders.rb17
-rw-r--r--lib/gitlab/background_migration/fix_pages_access_level.rb128
-rw-r--r--lib/gitlab/chaos.rb49
-rw-r--r--lib/gitlab/ci/templates/Serverless.gitlab-ci.yml13
-rw-r--r--lib/gitlab/cycle_analytics/base_event_fetcher.rb21
-rw-r--r--lib/gitlab/cycle_analytics/base_query.rb14
-rw-r--r--lib/gitlab/cycle_analytics/base_stage.rb24
-rw-r--r--lib/gitlab/cycle_analytics/code_event_fetcher.rb6
-rw-r--r--lib/gitlab/cycle_analytics/group_stage_summary.rb24
-rw-r--r--lib/gitlab/cycle_analytics/issue_event_fetcher.rb6
-rw-r--r--lib/gitlab/cycle_analytics/issue_helper.rb3
-rw-r--r--lib/gitlab/cycle_analytics/metrics_tables.rb8
-rw-r--r--lib/gitlab/cycle_analytics/permissions.rb2
-rw-r--r--lib/gitlab/cycle_analytics/plan_event_fetcher.rb6
-rw-r--r--lib/gitlab/cycle_analytics/plan_helper.rb13
-rw-r--r--lib/gitlab/cycle_analytics/production_event_fetcher.rb6
-rw-r--r--lib/gitlab/cycle_analytics/review_event_fetcher.rb6
-rw-r--r--lib/gitlab/cycle_analytics/summary/group/base.rb24
-rw-r--r--lib/gitlab/cycle_analytics/summary/group/deploy.rb29
-rw-r--r--lib/gitlab/cycle_analytics/summary/group/issue.rb25
-rw-r--r--lib/gitlab/danger/helper.rb26
-rw-r--r--lib/gitlab/database/migration_helpers.rb71
-rw-r--r--lib/gitlab/git.rb5
-rw-r--r--lib/gitlab/git/repository.rb26
-rw-r--r--lib/gitlab/git/rugged_impl/blob.rb2
-rw-r--r--lib/gitlab/git/rugged_impl/commit.rb6
-rw-r--r--lib/gitlab/git/rugged_impl/repository.rb2
-rw-r--r--lib/gitlab/git/rugged_impl/tree.rb2
-rw-r--r--lib/gitlab/git/rugged_impl/use_rugged.rb17
-rw-r--r--lib/gitlab/git_logger.rb6
-rw-r--r--lib/gitlab/gitaly_client.rb25
-rw-r--r--lib/gitlab/gitaly_client/operation_service.rb9
-rw-r--r--lib/gitlab/grape_logging/loggers/perf_logger.rb19
-rw-r--r--lib/gitlab/graphql/representation/submodule_tree_entry.rb34
-rw-r--r--lib/gitlab/import_export/project_tree_restorer.rb1
-rw-r--r--lib/gitlab/metrics/samplers/ruby_sampler.rb25
-rw-r--r--lib/gitlab/metrics/subscribers/rails_cache.rb15
-rw-r--r--lib/gitlab/push_options.rb7
-rw-r--r--lib/gitlab/request_profiler.rb15
-rw-r--r--lib/gitlab/request_profiler/middleware.rb62
-rw-r--r--lib/gitlab/request_profiler/profile.rb42
-rw-r--r--lib/gitlab/rugged_instrumentation.rb30
-rw-r--r--lib/gitlab/slug/environment.rb58
-rw-r--r--lib/gitlab/submodule_links.rb26
-rw-r--r--lib/gitlab/usage_data.rb15
-rw-r--r--lib/gitlab/usage_data_counters/redis_counter.rb10
-rw-r--r--lib/gitlab/usage_data_counters/web_ide_commits_counter.rb13
-rw-r--r--lib/gitlab/usage_data_counters/web_ide_counter.rb47
-rw-r--r--lib/gitlab/usage_data_counters/wiki_page_counter.rb32
-rw-r--r--lib/peek/views/redis_detailed.rb (renamed from lib/peek/views/redis.rb)48
-rw-r--r--lib/prometheus/pid_provider.rb42
-rw-r--r--lib/tasks/frontend.rake21
-rw-r--r--lib/tasks/karma.rake11
-rw-r--r--locale/gitlab.pot33
-rw-r--r--package.json14
-rw-r--r--qa/Gemfile1
-rw-r--r--qa/Gemfile.lock3
-rw-r--r--qa/qa/page/base.rb4
-rw-r--r--qa/qa/page/main/login.rb10
-rw-r--r--qa/qa/page/main/menu.rb2
-rw-r--r--qa/qa/page/validator.rb6
-rw-r--r--qa/qa/scenario/test/sanity/selectors.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb12
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb28
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb41
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb18
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb33
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb30
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb14
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/mattermost/create_group_with_mattermost_team_spec.rb4
-rw-r--r--qa/spec/specs/runner_spec.rb4
-rwxr-xr-xscripts/lint-rugged7
-rwxr-xr-xscripts/merge-html-reports84
-rw-r--r--spec/controllers/admin/groups_controller_spec.rb8
-rw-r--r--spec/controllers/admin/requests_profiles_controller_spec.rb65
-rw-r--r--spec/controllers/autocomplete_controller_spec.rb22
-rw-r--r--spec/controllers/chaos_controller_spec.rb127
-rw-r--r--spec/controllers/dashboard/milestones_controller_spec.rb15
-rw-r--r--spec/controllers/ide_controller_spec.rb17
-rw-r--r--spec/controllers/projects/cycle_analytics/events_controller_spec.rb64
-rw-r--r--spec/controllers/projects/merge_requests/creations_controller_spec.rb42
-rw-r--r--spec/factories/clusters/clusters.rb2
-rw-r--r--spec/factories/groups.rb4
-rw-r--r--spec/factories/pages_domains.rb84
-rw-r--r--spec/factories/projects.rb4
-rw-r--r--spec/features/admin/admin_groups_spec.rb9
-rw-r--r--spec/features/admin/admin_projects_spec.rb1
-rw-r--r--spec/features/admin/admin_requests_profiles_spec.rb115
-rw-r--r--spec/features/groups/group_settings_spec.rb8
-rw-r--r--spec/features/groups/show_spec.rb99
-rw-r--r--spec/features/issues/bulk_assignment_labels_spec.rb2
-rw-r--r--spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb6
-rw-r--r--spec/features/projects/tree/create_directory_spec.rb4
-rw-r--r--spec/features/projects/tree/create_file_spec.rb4
-rw-r--r--spec/finders/autocomplete/move_to_project_finder_spec.rb40
-rw-r--r--spec/fixtures/security-reports/deprecated/gl-sast-report.json20
-rw-r--r--spec/fixtures/security-reports/master/gl-sast-report.json20
-rw-r--r--spec/frontend/behaviors/markdown/render_metrics_spec.js37
-rw-r--r--spec/frontend/environment.js8
-rw-r--r--spec/frontend/fixtures/abuse_reports.rb (renamed from spec/javascripts/fixtures/abuse_reports.rb)2
-rw-r--r--spec/frontend/fixtures/admin_users.rb (renamed from spec/javascripts/fixtures/admin_users.rb)2
-rw-r--r--spec/frontend/fixtures/application_settings.rb (renamed from spec/javascripts/fixtures/application_settings.rb)2
-rw-r--r--spec/frontend/fixtures/autocomplete_sources.rb (renamed from spec/javascripts/fixtures/autocomplete_sources.rb)2
-rw-r--r--spec/frontend/fixtures/blob.rb (renamed from spec/javascripts/fixtures/blob.rb)2
-rw-r--r--spec/frontend/fixtures/boards.rb (renamed from spec/javascripts/fixtures/boards.rb)2
-rw-r--r--spec/frontend/fixtures/branches.rb (renamed from spec/javascripts/fixtures/branches.rb)2
-rw-r--r--spec/frontend/fixtures/clusters.rb (renamed from spec/javascripts/fixtures/clusters.rb)2
-rw-r--r--spec/frontend/fixtures/commit.rb (renamed from spec/javascripts/fixtures/commit.rb)2
-rw-r--r--spec/frontend/fixtures/deploy_keys.rb (renamed from spec/javascripts/fixtures/deploy_keys.rb)2
-rw-r--r--spec/frontend/fixtures/groups.rb (renamed from spec/javascripts/fixtures/groups.rb)4
-rw-r--r--spec/frontend/fixtures/issues.rb (renamed from spec/javascripts/fixtures/issues.rb)6
-rw-r--r--spec/frontend/fixtures/jobs.rb (renamed from spec/javascripts/fixtures/jobs.rb)4
-rw-r--r--spec/frontend/fixtures/labels.rb (renamed from spec/javascripts/fixtures/labels.rb)4
-rw-r--r--spec/frontend/fixtures/merge_requests.rb (renamed from spec/javascripts/fixtures/merge_requests.rb)2
-rw-r--r--spec/frontend/fixtures/merge_requests_diffs.rb (renamed from spec/javascripts/fixtures/merge_requests_diffs.rb)3
-rw-r--r--spec/frontend/fixtures/pipeline_schedules.rb (renamed from spec/javascripts/fixtures/pipeline_schedules.rb)4
-rw-r--r--spec/frontend/fixtures/pipelines.rb (renamed from spec/javascripts/fixtures/pipelines.rb)2
-rw-r--r--spec/frontend/fixtures/projects.rb (renamed from spec/javascripts/fixtures/projects.rb)12
-rw-r--r--spec/frontend/fixtures/prometheus_service.rb (renamed from spec/javascripts/fixtures/prometheus_service.rb)2
-rw-r--r--spec/frontend/fixtures/raw.rb (renamed from spec/javascripts/fixtures/raw.rb)0
-rw-r--r--spec/frontend/fixtures/search.rb (renamed from spec/javascripts/fixtures/search.rb)2
-rw-r--r--spec/frontend/fixtures/services.rb (renamed from spec/javascripts/fixtures/services.rb)2
-rw-r--r--spec/frontend/fixtures/sessions.rb (renamed from spec/javascripts/fixtures/sessions.rb)2
-rw-r--r--spec/frontend/fixtures/snippet.rb (renamed from spec/javascripts/fixtures/snippet.rb)2
-rw-r--r--spec/frontend/fixtures/static/README.md3
-rw-r--r--spec/frontend/fixtures/static/ajax_loading_spinner.html (renamed from spec/javascripts/fixtures/static/ajax_loading_spinner.html)0
-rw-r--r--spec/frontend/fixtures/static/balsamiq_viewer.html (renamed from spec/javascripts/fixtures/static/balsamiq_viewer.html)0
-rw-r--r--spec/frontend/fixtures/static/create_item_dropdown.html (renamed from spec/javascripts/fixtures/static/create_item_dropdown.html)0
-rw-r--r--spec/frontend/fixtures/static/environments/table.html (renamed from spec/javascripts/fixtures/static/environments/table.html)0
-rw-r--r--spec/frontend/fixtures/static/environments_logs.html29
-rw-r--r--spec/frontend/fixtures/static/event_filter.html (renamed from spec/javascripts/fixtures/static/event_filter.html)0
-rw-r--r--spec/frontend/fixtures/static/gl_dropdown.html (renamed from spec/javascripts/fixtures/static/gl_dropdown.html)0
-rw-r--r--spec/frontend/fixtures/static/gl_field_errors.html (renamed from spec/javascripts/fixtures/static/gl_field_errors.html)0
-rw-r--r--spec/frontend/fixtures/static/images/green_box.png (renamed from spec/javascripts/fixtures/static/images/green_box.png)bin1306 -> 1306 bytes
-rw-r--r--spec/frontend/fixtures/static/images/one_white_pixel.png (renamed from spec/javascripts/fixtures/static/images/one_white_pixel.png)bin68 -> 68 bytes
-rw-r--r--spec/frontend/fixtures/static/images/red_box.png (renamed from spec/javascripts/fixtures/static/images/red_box.png)bin1305 -> 1305 bytes
-rw-r--r--spec/frontend/fixtures/static/issuable_filter.html (renamed from spec/javascripts/fixtures/static/issuable_filter.html)0
-rw-r--r--spec/frontend/fixtures/static/issue_sidebar_label.html (renamed from spec/javascripts/fixtures/static/issue_sidebar_label.html)0
-rw-r--r--spec/frontend/fixtures/static/line_highlighter.html (renamed from spec/javascripts/fixtures/static/line_highlighter.html)0
-rw-r--r--spec/frontend/fixtures/static/linked_tabs.html (renamed from spec/javascripts/fixtures/static/linked_tabs.html)0
-rw-r--r--spec/frontend/fixtures/static/merge_requests_show.html (renamed from spec/javascripts/fixtures/static/merge_requests_show.html)0
-rw-r--r--spec/frontend/fixtures/static/mini_dropdown_graph.html (renamed from spec/javascripts/fixtures/static/mini_dropdown_graph.html)0
-rw-r--r--spec/frontend/fixtures/static/notebook_viewer.html (renamed from spec/javascripts/fixtures/static/notebook_viewer.html)0
-rw-r--r--spec/frontend/fixtures/static/oauth_remember_me.html (renamed from spec/javascripts/fixtures/static/oauth_remember_me.html)0
-rw-r--r--spec/frontend/fixtures/static/pdf_viewer.html (renamed from spec/javascripts/fixtures/static/pdf_viewer.html)0
-rw-r--r--spec/frontend/fixtures/static/pipeline_graph.html (renamed from spec/javascripts/fixtures/static/pipeline_graph.html)0
-rw-r--r--spec/frontend/fixtures/static/pipelines.html (renamed from spec/javascripts/fixtures/static/pipelines.html)0
-rw-r--r--spec/frontend/fixtures/static/project_select_combo_button.html (renamed from spec/javascripts/fixtures/static/project_select_combo_button.html)0
-rw-r--r--spec/frontend/fixtures/static/projects.json (renamed from spec/javascripts/fixtures/static/projects.json)0
-rw-r--r--spec/frontend/fixtures/static/search_autocomplete.html (renamed from spec/javascripts/fixtures/static/search_autocomplete.html)0
-rw-r--r--spec/frontend/fixtures/static/signin_tabs.html (renamed from spec/javascripts/fixtures/static/signin_tabs.html)0
-rw-r--r--spec/frontend/fixtures/static/sketch_viewer.html (renamed from spec/javascripts/fixtures/static/sketch_viewer.html)0
-rw-r--r--spec/frontend/fixtures/todos.rb (renamed from spec/javascripts/fixtures/todos.rb)4
-rw-r--r--spec/frontend/fixtures/u2f.rb (renamed from spec/javascripts/fixtures/u2f.rb)4
-rw-r--r--spec/frontend/helpers/fixtures.js7
-rw-r--r--spec/frontend/ide/services/index_spec.js32
-rw-r--r--spec/frontend/mocks/ce/lib/utils/axios_utils.js15
-rw-r--r--spec/frontend/mocks/mocks_helper.js60
-rw-r--r--spec/frontend/mocks/mocks_helper_spec.js147
-rw-r--r--spec/frontend/mocks/node/jquery.js13
-rw-r--r--spec/frontend/mocks_spec.js13
-rw-r--r--spec/frontend/monitoring/embed/embed_spec.js78
-rw-r--r--spec/frontend/monitoring/embed/mock_data.js87
-rw-r--r--spec/frontend/notes/components/discussion_jump_to_next_button_spec.js5
-rw-r--r--spec/frontend/operation_settings/components/external_dashboard_spec.js5
-rw-r--r--spec/frontend/repository/components/table/row_spec.js16
-rw-r--r--spec/frontend/test_setup.js22
-rw-r--r--spec/frontend/vue_shared/components/markdown/suggestion_diff_header_spec.js5
-rw-r--r--spec/graphql/types/tree/submodule_type_spec.rb2
-rw-r--r--spec/helpers/submodule_helper_spec.rb81
-rw-r--r--spec/javascripts/boards/board_blank_state_spec.js19
-rw-r--r--spec/javascripts/boards/components/board_form_spec.js56
-rw-r--r--spec/javascripts/boards/components/boards_selector_spec.js205
-rw-r--r--spec/javascripts/fixtures/.gitignore7
-rw-r--r--spec/javascripts/fixtures/static/README.md3
-rw-r--r--spec/javascripts/helpers/vue_test_utils_helper.js18
-rw-r--r--spec/javascripts/ide/stores/modules/commit/actions_spec.js4
-rw-r--r--spec/javascripts/ide/stores/mutations/file_spec.js37
-rw-r--r--spec/javascripts/notes/components/note_actions/reply_button_spec.js5
-rw-r--r--spec/javascripts/test_constants.js4
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb50
-rw-r--r--spec/lib/gitlab/auth/user_auth_finders_spec.rb14
-rw-r--r--spec/lib/gitlab/auth_spec.rb64
-rw-r--r--spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb1
-rw-r--r--spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb6
-rw-r--r--spec/lib/gitlab/cycle_analytics/code_stage_spec.rb98
-rw-r--r--spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb88
-rw-r--r--spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb73
-rw-r--r--spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb78
-rw-r--r--spec/lib/gitlab/cycle_analytics/review_stage_spec.rb68
-rw-r--r--spec/lib/gitlab/cycle_analytics/shared_event_spec.rb2
-rw-r--r--spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb4
-rw-r--r--spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb70
-rw-r--r--spec/lib/gitlab/cycle_analytics/test_stage_spec.rb4
-rw-r--r--spec/lib/gitlab/cycle_analytics/usage_data_spec.rb2
-rw-r--r--spec/lib/gitlab/danger/helper_spec.rb30
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb77
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb16
-rw-r--r--spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb3
-rw-r--r--spec/lib/gitlab/git_spec.rb20
-rw-r--r--spec/lib/gitlab/gitaly_client_spec.rb13
-rw-r--r--spec/lib/gitlab/graphql/representation/submodule_tree_entry_spec.rb30
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/import_export/project.json3
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb20
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb7
-rw-r--r--spec/lib/gitlab/import_export/relation_rename_service_spec.rb1
-rw-r--r--spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb10
-rw-r--r--spec/lib/gitlab/request_profiler/profile_spec.rb59
-rw-r--r--spec/lib/gitlab/request_profiler_spec.rb41
-rw-r--r--spec/lib/gitlab/rugged_instrumentation_spec.rb27
-rw-r--r--spec/lib/gitlab/slug/environment_spec.rb38
-rw-r--r--spec/lib/gitlab/usage_data_counters/redis_counter_spec.rb55
-rw-r--r--spec/lib/gitlab/usage_data_counters/web_ide_counter_spec.rb56
-rw-r--r--spec/lib/gitlab/usage_data_counters/wiki_page_counter_spec.rb69
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb32
-rw-r--r--spec/lib/peek/views/redis_detailed_spec.rb25
-rw-r--r--spec/lib/prometheus/pid_provider_spec.rb79
-rw-r--r--spec/migrations/fix_wrong_pages_access_level_spec.rb97
-rw-r--r--spec/models/active_session_spec.rb13
-rw-r--r--spec/models/concerns/deployment_platform_spec.rb28
-rw-r--r--spec/models/concerns/issuable_spec.rb21
-rw-r--r--spec/models/concerns/project_api_compatibility_spec.rb51
-rw-r--r--spec/models/cycle_analytics/code_spec.rb4
-rw-r--r--spec/models/cycle_analytics/group_level_spec.rb46
-rw-r--r--spec/models/cycle_analytics/issue_spec.rb2
-rw-r--r--spec/models/cycle_analytics/plan_spec.rb2
-rw-r--r--spec/models/cycle_analytics/production_spec.rb4
-rw-r--r--spec/models/cycle_analytics/project_level_spec.rb2
-rw-r--r--spec/models/cycle_analytics/review_spec.rb2
-rw-r--r--spec/models/cycle_analytics/staging_spec.rb4
-rw-r--r--spec/models/cycle_analytics/test_spec.rb8
-rw-r--r--spec/models/environment_spec.rb26
-rw-r--r--spec/models/group_spec.rb7
-rw-r--r--spec/models/pages_domain_spec.rb24
-rw-r--r--spec/models/project_feature_spec.rb28
-rw-r--r--spec/models/project_services/jira_service_spec.rb24
-rw-r--r--spec/models/project_spec.rb20
-rw-r--r--spec/policies/group_policy_spec.rb127
-rw-r--r--spec/requests/api/commits_spec.rb205
-rw-r--r--spec/requests/api/groups_spec.rb4
-rw-r--r--spec/requests/request_profiler_spec.rb20
-rw-r--r--spec/serializers/analytics_issue_entity_spec.rb6
-rw-r--r--spec/serializers/analytics_issue_serializer_spec.rb6
-rw-r--r--spec/serializers/analytics_merge_request_serializer_spec.rb6
-rw-r--r--spec/serializers/analytics_stage_serializer_spec.rb10
-rw-r--r--spec/serializers/diff_file_base_entity_spec.rb26
-rw-r--r--spec/services/groups/create_service_spec.rb8
-rw-r--r--spec/services/merge_requests/push_options_handler_service_spec.rb76
-rw-r--r--spec/services/projects/update_service_spec.rb4
-rw-r--r--spec/services/submodules/update_service_spec.rb2
-rw-r--r--spec/services/task_list_toggle_service_spec.rb17
-rw-r--r--spec/services/wiki_pages/base_service_spec.rb27
-rw-r--r--spec/services/wiki_pages/create_service_spec.rb25
-rw-r--r--spec/services/wiki_pages/destroy_service_spec.rb12
-rw-r--r--spec/services/wiki_pages/update_service_spec.rb25
-rw-r--r--spec/spec_helper.rb3
-rw-r--r--spec/support/capybara.rb1
-rw-r--r--spec/support/cycle_analytics_helpers/test_generation.rb12
-rw-r--r--spec/support/helpers/expect_request_with_status.rb11
-rw-r--r--spec/support/helpers/javascript_fixtures_helpers.rb2
-rw-r--r--spec/support/helpers/stub_configuration.rb4
-rw-r--r--spec/support/helpers/test_env.rb16
-rw-r--r--spec/support/shared_contexts/policies/group_policy_shared_context.rb2
-rw-r--r--spec/support/shared_examples/taskable_shared_examples.rb21
-rw-r--r--yarn.lock579
830 files changed, 9378 insertions, 3732 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index b865b212ac0..13c8b4a8458 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -9,8 +9,12 @@
app/assets/ @ClemMakesApps @fatihacet @filipa @iamphill @mikegreiling @timzallmann @kushalpandya @pslaughter
*.scss @annabeldunstone @ClemMakesApps @fatihacet @filipa @iamphill @mikegreiling @timzallmann @kushalpandya @pslaughter
-# Someone from the database team should review changes in `db/`
+# Maintainers from the Database team should review changes in `db/`
db/ @abrandl @NikolayS
+lib/gitlab/background_migration/ @abrandl @NikolayS
+lib/gitlab/database/ @abrandl @NikolayS
+lib/gitlab/sql/ @abrandl @NikolayS
+/ee/db/ @abrandl @NikolayS
# Feature specific owners
/ee/lib/gitlab/code_owners/ @reprazent
diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml
index 45a6a177943..5d4bbc06e93 100644
--- a/.gitlab/ci/frontend.gitlab-ci.yml
+++ b/.gitlab/ci/frontend.gitlab-ci.yml
@@ -113,37 +113,6 @@ compile-assets pull-cache:
- master@gitlab-org/gitlab-ee
- /(^docs[\/-].*|.*-docs$)/
-gitlab:ui:visual:
- extends: .dedicated-runner
- before_script: []
- allow_failure: true
- dependencies:
- - compile-assets
- - compile-assets pull-cache
- script:
- # Remove node modules from GitLab that may conflict with gitlab-ui
- - rm -r node_modules
- - git clone https://gitlab.com/gitlab-org/gitlab-ui.git
- - cp public/assets/application-*.css gitlab-ui/styles/application.css
- - cd gitlab-ui
- - yarn install
- - CSS_URL=./application.css yarn test
- only:
- changes:
- - app/assets/stylesheets/*.scss
- - app/assets/stylesheets/**/*.scss
- - app/assets/stylesheets/**/**/*.scss
- except:
- refs:
- - /(^docs[\/-].*|.*-docs$)/
- - master
- variables:
- - $CI_COMMIT_MESSAGE =~ /\[skip visual\]/i
- artifacts:
- paths:
- - gitlab-ui/tests/__image_snapshots__/
- when: always
-
karma:
extends: .dedicated-no-docs-pull-cache-job
<<: *use-pg
@@ -168,6 +137,8 @@ karma:
paths:
- chrome_debug.log
- coverage-javascript/
+ - tmp/tests/frontend/
+# see https://gitlab.com/gitlab-org/gitlab-ce/issues/64756
# reports:
# junit: junit_karma.xml
@@ -181,7 +152,7 @@ jest:
script:
- scripts/gitaly-test-spawn
- date
- - bundle exec rake karma:fixtures
+ - bundle exec rake frontend:fixtures
- date
- yarn jest --ci --coverage
artifacts:
@@ -191,6 +162,8 @@ jest:
paths:
- coverage-frontend/
- junit_jest.xml
+ - tmp/tests/frontend/
+# see https://gitlab.com/gitlab-org/gitlab-ce/issues/64756
# reports:
# junit: junit_jest.xml
cache:
@@ -237,15 +210,15 @@ qa:selectors:
qa-frontend-node:8:
<<: *qa-frontend-node
- image: node:8-alpine
+ image: node:carbon
qa-frontend-node:10:
<<: *qa-frontend-node
- image: node:10-alpine
+ image: node:dubnium
qa-frontend-node:latest:
<<: *qa-frontend-node
- image: node:alpine
+ image: node:latest
allow_failure: true
lint:javascript:report:
diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml
index c7a51beeee5..d0b1f1ab98f 100644
--- a/.gitlab/ci/rails.gitlab-ci.yml
+++ b/.gitlab/ci/rails.gitlab-ci.yml
@@ -80,6 +80,7 @@
- rspec_profiling/
- tmp/capybara/
- tmp/memory_test/
+# see https://gitlab.com/gitlab-org/gitlab-ce/issues/64756
# reports:
# junit: junit_rspec.xml
diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml
index ce019de213b..41d52c4e095 100644
--- a/.gitlab/ci/review.gitlab-ci.yml
+++ b/.gitlab/ci/review.gitlab-ci.yml
@@ -174,7 +174,38 @@ review-qa-all:
script:
- export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/review-qa-all_master_report.json
- export KNAPSACK_TEST_FILE_PATTERN=qa/specs/features/**/*_spec.rb
- - gitlab-qa Test::Instance::Any "${QA_IMAGE}" "${CI_ENVIRONMENT_URL}"
+ - gitlab-qa Test::Instance::Any "${QA_IMAGE}" "${CI_ENVIRONMENT_URL}" -- --format RspecJunitFormatter --out tmp/rspec-${CI_JOB_ID}.xml --format html --out tmp/rspec.htm --color --format documentation
+
+parallel-spec-reports:
+ extends: .dedicated-runner
+ dependencies:
+ - review-qa-all
+ image: ruby:2.6-alpine
+ services: []
+ before_script: []
+ variables:
+ SETUP_DB: "false"
+ NEW_PARALLEL_SPECS_REPORT: qa/report-new.html
+ BASE_ARTIFACT_URL: "${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/file/qa/"
+ stage: post-test
+ allow_failure: true
+ when: manual
+ retry: 0
+ artifacts:
+ when: always
+ paths:
+ - qa/report-new.html
+ - qa/gitlab-qa-run-*
+ reports:
+ junit: qa/gitlab-qa-run-*/**/rspec-*.xml
+ script:
+ - apk add --update build-base libxml2-dev libxslt-dev && rm -rf /var/cache/apk/*
+ - gem install nokogiri
+ - cd qa/gitlab-qa-run-*/gitlab-*
+ - ARTIFACT_DIRS=$(pwd |rev| awk -F / '{print $1,$2}' | rev | sed s_\ _/_)
+ - cd ../../..
+ - '[[ -f $NEW_PARALLEL_SPECS_REPORT ]] || echo "{}" > ${NEW_PARALLEL_SPECS_REPORT}'
+ - scripts/merge-html-reports ${NEW_PARALLEL_SPECS_REPORT} ${BASE_ARTIFACT_URL}${ARTIFACT_DIRS} qa/gitlab-qa-run-*/**/rspec.htm
.review-performance-base: &review-performance-base
<<: *review-qa-base
diff --git a/.gitlab/ci/test-metadata.gitlab-ci.yml b/.gitlab/ci/test-metadata.gitlab-ci.yml
index 2454ea85652..3a5735a2be9 100644
--- a/.gitlab/ci/test-metadata.gitlab-ci.yml
+++ b/.gitlab/ci/test-metadata.gitlab-ci.yml
@@ -39,6 +39,7 @@ update-tests-metadata:
policy: push
script:
- retry gem install fog-aws mime-types activesupport rspec_profiling postgres-copy --no-document
+ - echo "{}" > ${KNAPSACK_RSPEC_SUITE_REPORT_PATH}
- scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec_*_pg_node_*.json
- '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $KNAPSACK_RSPEC_SUITE_REPORT_PATH'
- rm -f knapsack/${CI_PROJECT_NAME}/*_node_*.json
diff --git a/.gitlab/merge_request_templates/Database changes.md b/.gitlab/merge_request_templates/Database changes.md
index 3f457174492..2077997a0cb 100644
--- a/.gitlab/merge_request_templates/Database changes.md
+++ b/.gitlab/merge_request_templates/Database changes.md
@@ -39,20 +39,11 @@ When adding tables:
- [ ] Ordered columns based on the [Ordering Table Columns](https://docs.gitlab.com/ee/development/ordering_table_columns.html#ordering-table-columns) guidelines
- [ ] Added foreign keys to any columns pointing to data in other tables
-- [ ] Added indexes for fields that are used in statements such as WHERE, ORDER BY, GROUP BY, and JOINs
+- [ ] Added indexes for fields that are used in statements such as `WHERE`, `ORDER BY`, `GROUP BY`, and `JOIN`s
When removing columns, tables, indexes or other structures:
- [ ] Removed these in a post-deployment migration
- [ ] Made sure the application no longer uses (or ignores) these structures
-## General checklist
-
-- [ ] [Changelog entry](https://docs.gitlab.com/ee/development/changelog.html) added, if necessary
-- [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/documentation/)
-- [ ] [Tests added for this feature/bug](https://docs.gitlab.com/ee/development/testing_guide/index.html)
-- [ ] Conforms to the [code review guidelines](https://docs.gitlab.com/ee/development/code_review.html)
-- [ ] Conforms to the [merge request performance guidelines](https://docs.gitlab.com/ee/development/merge_request_performance_guidelines.html)
-- [ ] Conforms to the [style guides](https://docs.gitlab.com/ee/development/contributing/style_guides.html)
-
-/label ~database
+/label ~database ~"database::review pending"
diff --git a/.haml-lint.yml b/.haml-lint.yml
index 0412b24a48c..399fa9656a0 100644
--- a/.haml-lint.yml
+++ b/.haml-lint.yml
@@ -118,7 +118,6 @@ linters:
- Lint/ParenthesesAsGroupedExpression
- Lint/RedundantWithIndex
- Lint/SafeNavigationConsistency
- - Lint/Syntax
- Metrics/BlockNesting
- Naming/VariableName
- Performance/RedundantMatch
@@ -155,6 +154,9 @@ linters:
enabled: true
style: space
+ Syntax:
+ enabled: true
+
Indentation:
enabled: true
character: space # or tab
diff --git a/.rubocop.yml b/.rubocop.yml
index e5fe527e611..79e06439ac2 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -60,8 +60,8 @@ Style/FrozenStringLiteralComment:
RSpec/FilePath:
Exclude:
- 'qa/**/*'
- - 'spec/javascripts/fixtures/*'
- - 'ee/spec/javascripts/fixtures/*'
+ - 'spec/frontend/fixtures/*'
+ - 'ee/spec/frontend/fixtures/*'
- 'spec/requests/api/v3/*'
Naming/FileName:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b73585722f..34ace58bb14 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,277 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 12.1.0
+
+### Security (11 changes, 2 of them are from the community)
+
+- Update tar to 2.2.2. !29949 (Takuya Noguchi)
+- Update lodash to 4.7.14 and lodash.mergewith to 4.6.2. !30602 (Takuya Noguchi)
+- Correctly check permissions when creating snippet notes.
+- Gate MR head_pipeline behind read_pipeline ability.
+- Prevent Billion Laughs attack.
+- Add missing authorizations in GraphQL.
+- Fix Denial of Service for comments when rendering issues/MR comments.
+- Expose merge requests count based on user access.
+- Fix DoS vulnerability in color validation regex.
+- Prevent the detection of merge request templates by unauthorized users.
+- Persist tmp snippet uploads at users.
+
+### Removed (7 changes)
+
+- Disable Kubernetes credential passthrough for managed project-level clusters. !29262
+- Remove deprecated group routes. !29351
+- Remove support for creating non-RBAC kubernetes clusters. !29614
+- Remove Kubernetes service integration and Kubernetes service template from available deployment platforms. !29786
+- Remove MySQL support. !29790
+- Remove depreated /u/:username routing. !30044
+- Remove support for legacy pipeline triggers. !30133
+
+### Fixed (84 changes, 14 of them are from the community)
+
+- Update a user's routes after updating their name. !23272
+- Show poper panel when validation error occurs in admin settings panels. !25434
+- Expect bytes from Gitaly RPC GetRawChanges. !28164
+- Sanitize LDAP output in Rake tasks. !28427
+- Left align mr widget icons and text. !28561
+- Keep the empty folders in the tree. !29196
+- Fix incorrect emoji placement in commit diff discussion. !29445
+- Fix favicon path with uploads of object store. !29482 (Roger Meier)
+- Remove duplicate trailing +/- char in merge request discussions. !29518
+- Fix the signup form's username validation messages not displaying. !29678 (Jiaan Louw)
+- Fix broken environment selector and always display it on monitoring dashboard. !29705
+- Fix Container Scanning job timeout when using the kubernetes executor. !29706
+- Look for new branches more carefully. !29761
+- Fix nested lists unnecessary margin. !29775 (Kuba Kopeć)
+- Fix reports jobs timing out because of cache. !29780
+- Fix Double Border in Profile Page. !29784 (Yoginth <@yo>)
+- Remove minimum character limits for fuzzy searches when using a CTE. !29810
+- Set default sort method for dashboard projects list. !29830 (David Palubin)
+- Protect TeamCity builds from triggering when a branch has been deleted. And a MR-option. !29836 (Nikolay Novikov, Raphael Tweitmann)
+- Fix pipeline schedule does not run correctly when it's scheduled at the same time with the cron worker. !29848
+- Always shows author of created issue/started discussion/comment in HTML body and text of email. !29886 (Frank van Rest)
+- Build correct basenames for title search results. !29898
+- Resolve "500 error when forking via the web IDE button". !29909
+- Turn commit sha in monitor charts popover to link. !29914
+- Fix broken URLs for uploads with a plus in the filename. !29915
+- Retry fetching Kubernetes Secret#token (#63507). !29922
+- Enforce presence of pipeline when "Pipeline must succeed" project setting is enabled. !29926
+- Fix unresponsive reply button in discussions. !29936
+- Allow asynchronous rebase operations to be monitored. !29940
+- Resolve Avatar in Please sign in pattern too large. !29944
+- Persist the cluster a deployment was deployed to. !29960
+- Fix runner tags search dropdown being empty when there are tags. !29985
+- Display the correct amount of projects being migrated/rolled-back to Hashed Storage when specifying ranges. !29996
+- Resolve Environment details header border misaligned. !30011
+- Correct link to docs for External Dashboard. !30019
+- Fix Jupyter-Git integration. !30020 (Amit Rathi)
+- Update Mermaid to 8.1.0. !30036
+- Fix background migrations failing with unused replication slot. !30042
+- Disable Rails SQL query cache when applying service templates. !30060
+- Set higher TTL for write lock of trace to prevent concurrent archiving. !30064
+- Fix charts on Cluster health page. !30073
+- Display boards filter bar on mobile. !30120
+- Fix IDE editor not showing when switching back from preview. !30135
+- Support note position tracing on an image. !30158
+- Replace slugifyWithHyphens with improved slugify function. !30172 (Luke Ward)
+- 'Open' and 'Closed' issue board lists no longer display a redundant tooltip. !30187
+- Fix pipelines table to update without refreshing after action. !30190
+- Change ruby_process_start_time_seconds metric to unix timestamp instead of seconds from boot. !30195
+- Fix attachments using the wrong URLs in e-mails. !30197
+- Make sure UnicornSampler is started only in master process. !30215
+- Don't show image diff note on text file. !30221
+- Fix median counting for cycle analytics. !30229
+- In WebIDE allow adding new entries of the same name as deleted entry. !30239
+- Don't let logged out user do manual order. !30264
+- Skip spam check for task list updates. !30279
+- Make Housekeeping button do a full garbage collection. !30289
+- Removing an image should not output binary data. !30314
+- Fix spacing issues for toasts. !30345
+- Fix race in forbid_sidekiq_in_transactions.rb. !30359
+- Fixed back navigation for projects filter. !30373
+- Fix environments broken terminal. !30401
+- Fix invalid SSL certificate errors on Drone CI service. !30422
+- Fix subgroup url in search drop down. !30457
+- Make unicorn_workers to return meaningful results. !30506
+- Fix wrong URL when creating milestones from instance milestones dashboard. !30512
+- Fixed incorrect line wrap for assignee label in issues. !30523 (Marc Schwede)
+- Improves section header whitespace on the CI/CD Charts page. !30531
+- Prevent multiple confirmation modals from opening when deleting a repository. !30532
+- Aligns CI icon in Merge Request dashboard. !30558
+- Add text-secondary to controls in project list. !30567
+- Review Tools: Add large z-index to toolbar. !30583
+- Hide restricted and disallowed visibility radios. !30590
+- Resolve Label picker: Line break on long label titles. !30610
+- Fix a bug that prevented projects containing merge request diff comments from being imported. !30630
+- I fixed z index bug in diff page. !30657 (Faruk Can)
+- Allow client authentication method to be configured for OpenID Connect. !30683 (Vincent Fazio)
+- Fix commenting before discussions are loaded. !30724
+- Fix linebreak rendering in Mermaid flowcharts. !30730
+- Make httpclient respect system SSL configuration. !30749
+- Bump fog-aws to v3.5.2. !30803
+- API: Allow changing only ci_default_git_depth. !30888 (Mathieu Parent)
+- Search issuables by iids. (Riccardo Padovani)
+- Fix broken warnings while Editing Issues and Edit File on MR.
+- Make sure we are receiving the proper information on the MR Popover by updating the IID in the graphql query.
+
+### Changed (39 changes, 8 of them are from the community)
+
+- Improve group list UI. !26542
+- Backport and Docs for Paginate license management and add license search. !27602
+- Update merge requests section description text on project settings page. !27838
+- Knative version bump 0.5 -> 0.6. !28798 (Chris Baumbauer)
+- Add salesforce logo for salesforce SSO. !28857
+- Enforced requirements for UltraAuth users. !28941 (Kartikey Tanna)
+- Return 400 when deleting tags more often than once per hour. !29448
+- Add identity information to external authorization requests. !29461
+- Enable just-in-time Kubernetes resource creation for project-level clusters. !29515
+- renamed discussion to thread in merge-request and issue timeline. !29553 (Michel Engelen)
+- Changed HTTP Status Code for disabled repository on /branches and /commits to 404. !29585 (Sam Battalio)
+- Enable Git object pools. !29595 (jramsay)
+- Updated container registry to display error message when special characters in path. Documentation has also been updated. !29616
+- Allow developers to delete tags. !29668
+- Will not update issue timestamps when changing positions in a list. !29677
+- Include a link back to the MR for Visual Review feedback form. !29719
+- Improve discussion reply buttons layout and how jump to next discussion button appears. !29779
+- Renders a pre-release tag for releases. !29797
+- Migrate NULL values for users.private_profile column and update users API to reject null value for private_profile. !29888
+- Re-name files in Web IDE in a more natural way. !29948
+- Include events from subgroups in group's activity. !29953 (Fabian Schneider @fabsrc)
+- Upgrade to Gitaly v1.49.0. !29990
+- Remove group and instance clusters feature flag. !30124
+- Add support for creating random passwords in user creation API. !30138
+- Support CIDR notation in IP rate limiter. !30146
+- Add Redis call details in Peek performance bar. !30191
+- Create Knative role and binding with service account. !30235
+- Add cleanup migration for MR's multiple assignees. !30261
+- Updates PHP template to php:latest to ensure always targeting latest stable. !30319 (Paul Giberson)
+- Format `from` and `to` fields in JSON audit log. !30333
+- Upgrade to Gitaly v1.51.0. !30353
+- Modify cycle analytics on project level. !30356
+- Extract clair version as CLAIR_EXECUTABLE_VERSION variable and update clair executable from v8 to v11. !30396
+- Upgrade Rouge to 3.5.1. !30431
+- Move multiple issue boards to core. !30503
+- Upgrade to Gitaly v1.52.0. !30568
+- Upgrade to Gitaly v1.53.0. !30614
+- Open WebIDE in fork when user doesn't have access. !30642
+- Propagate python version variable. (Can Eldem)
+
+### Performance (25 changes, 1 of them is from the community)
+
+- Remove tooltip directive on project avatar image component. !29631 (George Tsiolis)
+- Use Rugged if we detect storage is NFS and we can access the disk. !29725
+- Add endpoint for fetching diverging commit counts. !29802
+- Cache feature flag names in Redis for a minute. !29816
+- Avoid storing backtraces from Bitbucket Cloud imports in the database. !29862
+- Remove import columns from projects table. !29863
+- Enable Gitaly ref name caching for discussions.json. !29951
+- Allow caching of negative FindCommit matches. !29952
+- Eliminate N+1 queries in Dashboard::TodosController. !29954
+- Memoize non-existent custom appearances. !29957
+- Add a separate endpoint for fetching MRs serialized as widgets. !29979
+- Use CTE to fetch clusters hierarchy in single query. !30063
+- Enable Gitaly ref caching for SearchController. !30105
+- Avoid loading pipeline status in search results. !30111
+- Improve performance of MergeRequestsController#ci_environment_status endpoint. !30224
+- Add a memory cache local to the thread to reduce Redis load. !30233
+- Cache Flipper persisted names directly to local memory storage. !30265
+- Limit amount of JUnit tests returned. !30274
+- Cache Flipper feature flags in L1 and L2 caches. !30276
+- Prevent amplification of ReactiveCachingWorker jobs upon failures. !30432
+- Allow ReactiveCaching to support nil value. !30456
+- Improve performance of fetching environments statuses. !30560
+- Do Redis lookup in batches in ActiveSession.sessions_from_ids. !30561
+- Remove catfile cache feature flag. !30750
+- Fix Gitaly auto-detection caching. !30954
+
+### Added (46 changes, 12 of them are from the community)
+
+- Document the negative commit message push rule for the API. !14004 (Maikel Vlasman)
+- Expose saml_provider_id in the users API. !14045
+- Improve Project API. !28327 (Mathieu Parent)
+- Remove Sentry from application settings. !28447 (Roger Meier)
+- Implement borderless discussion design with new reply field. !28580
+- Enable terminals for instance and group clusters. !28613
+- Resolve Multiple discussions per line in merge request diffs. !28748
+- Adds link to Grafana in Admin > Monitoring settings when grafana is enabled in config. !28937 (Romain Maneschi)
+- Bring Manual Ordering on Issue List. !29410
+- Added commit type to tree GraphQL response. !29412
+- New API for User Counts, updates on success of an MR the count on top and in other tabs. !29441
+- Add option to limit time tracking units to hours. !29469 (Jon Kolb)
+- Add confirmation for registry image deletion. !29505
+- Sync merge ref upon mergeability check. !29569
+- Show an Upcoming Status for Releases. !29577
+- Add order_by and sort params to list runner jobs api. !29629 (Sujay Patel)
+- Allow custom username for deploy tokens. !29639
+- Add a verified pill next to email addresses under the admin users section. !29669
+- Add rake task to clean orphan artifact files. !29681
+- Render GFM in GraphQL. !29700
+- Upgrade asciidoctor version to 2.0.10. !29741 (Rajendra Kadam)
+- Allow auto-completing scoped labels. !29749
+- Enable syntax highlighting for AsciiDoc. !29835 (Guillaume Grossetie)
+- Expose placeholder element for metrics charts in GFM. !29861
+- Added a min schema version check to db:migrate. !29882
+- Extract zoom link from issue and pass to frontend. !29910 (raju249)
+- GraphQL mutations for add, remove and toggle emoji. !29919
+- Labeled issue boards can now collapse. !29955
+- Allow Ingress to be uninstalled from the UI. !29977
+- Add permission check to metrics dashboards endpoint. !30017
+- Allow JupyterHub to be uninstalled from the UI. !30097
+- Allow GitLab Runner to be uninstalled from the UI. !30176
+- GraphQL mutations for managing Notes. !30210
+- Add API for CRUD group clusters. !30213
+- Add endpoint to move multiple issues in boards. !30216
+- Enable terminals button for group clusters. !30255
+- Prevent excessive sanitization of AsciiDoc ouptut. !30290 (Guillaume Grossetie)
+- Extend `MergeToRefService` to create merge ref from an arbitrary ref. !30361
+- Add CI variable to provide GitLab HOST. !30417
+- Add migration for adding rule_type to approval_project_rules. !30575
+- Enable section anchors in Asciidoctor. !30666 (Guillaume Grossetie)
+- Preserve footnote link ids in Asciidoctor. !30790 (Guillaume Grossetie)
+- Add support for generating SSL certificates for custon pages domains through Let's Encrypt.
+- Introduce default: for gitlab-ci.yml.
+- Move Multiple Issue Boards for Projects to Core.
+- Add Gitaly data to the usage ping.
+
+### Other (35 changes, 15 of them are from the community)
+
+- Remove unresolved class and fixed height in discussion header. !28440 (David Palubin)
+- Moved EE/CE code differences for file `app/views/search/_category.html.haml` into CE. !28755 (Michel Engelen)
+- Changes "Todo" to "To Do" in the UI for clarity. !28844
+- Migrate GitLab managed project-level clusters to unmanaged if a Kubernetes namespace was unable to be created. !29251
+- Migrate GitLab managed project-level clusters to unmanaged if they are missing a Kubernetes service account token. !29648
+- Add strategies column to operations_feature_flag_scopes table. !29808
+- Disallow `NULL` values for `geo_nodes.primary` column. !29818 (Arun Kumar Mohan)
+- Replace 'JIRA' with 'Jira'. !29849 (Takuya Noguchi)
+- Support jsonb default in add_column_with_default migration helper. !29871
+- Update pagination prev and next texts. !29911
+- Adds metrics to measure cost of expensive operations. !29928
+- Always allow access to health endpoints from localhost in dev. !29930
+- Update GitLab Runner Helm Chart to 0.6.0. !29982
+- Use darker gray color for system note metadata and edited text. !30054
+- Fix typo in docs about Elasticsearch. !30162 (Takuya Noguchi)
+- Fix typo in code comments about Elasticsearch. !30163 (Takuya Noguchi)
+- Update mixin-deep to 1.3.2. !30223 (Takuya Noguchi)
+- Migrate markdown header_spec.js to Jest. !30228 (Martin Hobert)
+- Remove istanbul JavaScript package. !30232 (Takuya Noguchi)
+- Centralize markdownlint configuration. !30263
+- Use PostgreSQL 9.6.11 in CI tests. !30270 (Takuya Noguchi)
+- Fix typo in updateResolvableDiscussionsCounts action. !30278 (Frank van Rest)
+- Change color for namespace in commit search. !30312
+- Remove applySuggestion from notes service. !30399 (Frank van Rest)
+- Improved readability of storage statistics in group / project admin area. !30406
+- Alignign empty container registry message with design guidelines. !30502
+- Remove toggleAward from notes service. !30536 (Frank van Rest)
+- Remove deleteNote from notes service. !30537 (Frank van Rest)
+- change the use of boardService in favor of boardsStore on footer for the board component. !30616 (eduarmreyes)
+- Update example Prometheus scrape config. !30739
+- Update GitLab Pages to v1.7.0.
+- Add token_encrypted column to operations_feature_flags_clients table.
+- Removes EE diff for app/views/profiles/preferences/show.html.haml.
+- Removes EE differences for app/views/layouts/fullscreen.html.haml.
+- Removes EE differences for app/views/admin/users/show.html.haml.
+
+
## 12.0.3 (2019-06-27)
- No changes.
diff --git a/Gemfile b/Gemfile
index 8bffc2a973d..de1f44642f2 100644
--- a/Gemfile
+++ b/Gemfile
@@ -299,7 +299,6 @@ gem 'peek-gc', '~> 0.0.2'
gem 'peek-mysql2', '~> 1.2.0', group: :mysql
gem 'peek-pg', '~> 1.3.0', group: :postgres
gem 'peek-rblineprof', '~> 0.2.0'
-gem 'peek-redis', '~> 1.2.0'
# Memory benchmarks
gem 'derailed_benchmarks', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index 60939ae918c..2bcc3527de4 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -76,7 +76,6 @@ GEM
asciidoctor-plantuml (0.0.9)
asciidoctor (>= 1.5.6, < 3.0.0)
ast (2.4.0)
- atomic (1.1.99)
attr_encrypted (3.1.0)
encryptor (~> 3.0.0)
attr_required (1.0.1)
@@ -658,10 +657,6 @@ GEM
peek-rblineprof (0.2.0)
peek
rblineprof
- peek-redis (1.2.0)
- atomic (>= 1.0.0)
- peek
- redis
pg (1.1.4)
po_to_json (1.0.1)
json (>= 1.6.0)
@@ -1199,7 +1194,6 @@ DEPENDENCIES
peek-mysql2 (~> 1.2.0)
peek-pg (~> 1.3.0)
peek-rblineprof (~> 0.2.0)
- peek-redis (~> 1.2.0)
pg (~> 1.1)
premailer-rails (~> 1.9.7)
prometheus-client-mmap (~> 0.9.8)
diff --git a/VERSION b/VERSION
index 6edc7fa8d35..4dd919b3b06 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-12.1.0-pre
+12.2.0-pre
diff --git a/app/assets/javascripts/behaviors/markdown/render_gfm.js b/app/assets/javascripts/behaviors/markdown/render_gfm.js
index bfb073fdcdc..789a057caf8 100644
--- a/app/assets/javascripts/behaviors/markdown/render_gfm.js
+++ b/app/assets/javascripts/behaviors/markdown/render_gfm.js
@@ -2,6 +2,7 @@ import $ from 'jquery';
import syntaxHighlight from '~/syntax_highlight';
import renderMath from './render_math';
import renderMermaid from './render_mermaid';
+import renderMetrics from './render_metrics';
import highlightCurrentUser from './highlight_current_user';
import initUserPopovers from '../../user_popovers';
import initMRPopovers from '../../mr_popover';
@@ -17,6 +18,9 @@ $.fn.renderGFM = function renderGFM() {
highlightCurrentUser(this.find('.gfm-project_member').get());
initUserPopovers(this.find('.gfm-project_member').get());
initMRPopovers(this.find('.gfm-merge_request').get());
+ if (gon.features && gon.features.gfmEmbeddedMetrics) {
+ renderMetrics(this.find('.js-render-metrics').get());
+ }
return this;
};
diff --git a/app/assets/javascripts/behaviors/markdown/render_metrics.js b/app/assets/javascripts/behaviors/markdown/render_metrics.js
new file mode 100644
index 00000000000..252b98610b6
--- /dev/null
+++ b/app/assets/javascripts/behaviors/markdown/render_metrics.js
@@ -0,0 +1,24 @@
+import Vue from 'vue';
+import Metrics from '~/monitoring/components/embed.vue';
+import { createStore } from '~/monitoring/stores';
+
+// TODO: Handle copy-pasting - https://gitlab.com/gitlab-org/gitlab-ce/issues/64369.
+export default function renderMetrics(elements) {
+ if (!elements.length) {
+ return;
+ }
+
+ elements.forEach(element => {
+ const { dashboardUrl } = element.dataset;
+ const MetricsComponent = Vue.extend(Metrics);
+
+ // eslint-disable-next-line no-new
+ new MetricsComponent({
+ el: element,
+ store: createStore(),
+ propsData: {
+ dashboardUrl,
+ },
+ });
+ });
+}
diff --git a/app/assets/javascripts/boards/components/board_blank_state.vue b/app/assets/javascripts/boards/components/board_blank_state.vue
index d8b0b60c183..9f26337d153 100644
--- a/app/assets/javascripts/boards/components/board_blank_state.vue
+++ b/app/assets/javascripts/boards/components/board_blank_state.vue
@@ -1,6 +1,6 @@
<script>
import { __ } from '~/locale';
-/* global ListLabel */
+import ListLabel from '~/boards/models/label';
import Cookies from 'js-cookie';
import boardsStore from '../stores/boards_store';
@@ -30,13 +30,17 @@ export default {
});
// Save the labels
- gl.boardService
+ boardsStore
.generateDefaultLists()
.then(res => res.data)
.then(data => {
data.forEach(listObj => {
const list = boardsStore.findList('title', listObj.title);
+ if (!list) {
+ return;
+ }
+
list.id = listObj.id;
list.label.id = listObj.label.id;
list.getIssues().catch(() => {
@@ -69,8 +73,7 @@ export default {
<span
:style="{ backgroundColor: label.color }"
class="label-color position-relative d-inline-block rounded"
- >
- </span>
+ ></span>
{{ label.title }}
</li>
</ul>
diff --git a/app/assets/javascripts/boards/components/board_form.vue b/app/assets/javascripts/boards/components/board_form.vue
new file mode 100644
index 00000000000..6754abf4019
--- /dev/null
+++ b/app/assets/javascripts/boards/components/board_form.vue
@@ -0,0 +1,216 @@
+<script>
+import Flash from '~/flash';
+import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
+import { visitUrl } from '~/lib/utils/url_utility';
+import boardsStore from '~/boards/stores/boards_store';
+
+const boardDefaults = {
+ id: false,
+ name: '',
+ labels: [],
+ milestone_id: undefined,
+ assignee: {},
+ assignee_id: undefined,
+ weight: null,
+};
+
+export default {
+ components: {
+ BoardScope: () => import('ee_component/boards/components/board_scope.vue'),
+ DeprecatedModal,
+ },
+ props: {
+ canAdminBoard: {
+ type: Boolean,
+ required: true,
+ },
+ milestonePath: {
+ type: String,
+ required: true,
+ },
+ labelsPath: {
+ type: String,
+ required: true,
+ },
+ scopedIssueBoardFeatureEnabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ projectId: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
+ groupId: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
+ weights: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ enableScopedLabels: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ scopedLabelsDocumentationLink: {
+ type: String,
+ required: false,
+ default: '#',
+ },
+ },
+ data() {
+ return {
+ board: { ...boardDefaults, ...this.currentBoard },
+ currentBoard: boardsStore.state.currentBoard,
+ currentPage: boardsStore.state.currentPage,
+ isLoading: false,
+ };
+ },
+ computed: {
+ isNewForm() {
+ return this.currentPage === 'new';
+ },
+ isDeleteForm() {
+ return this.currentPage === 'delete';
+ },
+ isEditForm() {
+ return this.currentPage === 'edit';
+ },
+ isVisible() {
+ return this.currentPage !== '';
+ },
+ buttonText() {
+ if (this.isNewForm) {
+ return 'Create board';
+ }
+ if (this.isDeleteForm) {
+ return 'Delete';
+ }
+ return 'Save changes';
+ },
+ buttonKind() {
+ if (this.isNewForm) {
+ return 'success';
+ }
+ if (this.isDeleteForm) {
+ return 'danger';
+ }
+ return 'info';
+ },
+ title() {
+ if (this.isNewForm) {
+ return 'Create new board';
+ }
+ if (this.isDeleteForm) {
+ return 'Delete board';
+ }
+ if (this.readonly) {
+ return 'Board scope';
+ }
+ return 'Edit board';
+ },
+ readonly() {
+ return !this.canAdminBoard;
+ },
+ submitDisabled() {
+ return this.isLoading || this.board.name.length === 0;
+ },
+ },
+ mounted() {
+ this.resetFormState();
+ if (this.$refs.name) {
+ this.$refs.name.focus();
+ }
+ },
+ methods: {
+ submit() {
+ if (this.board.name.length === 0) return;
+ this.isLoading = true;
+ if (this.isDeleteForm) {
+ gl.boardService
+ .deleteBoard(this.currentBoard)
+ .then(() => {
+ visitUrl(boardsStore.rootPath);
+ })
+ .catch(() => {
+ Flash('Failed to delete board. Please try again.');
+ this.isLoading = false;
+ });
+ } else {
+ gl.boardService
+ .createBoard(this.board)
+ .then(resp => resp.data)
+ .then(data => {
+ visitUrl(data.board_path);
+ })
+ .catch(() => {
+ Flash('Unable to save your changes. Please try again.');
+ this.isLoading = false;
+ });
+ }
+ },
+ cancel() {
+ boardsStore.showPage('');
+ },
+ resetFormState() {
+ if (this.isNewForm) {
+ // Clear the form when we open the "New board" modal
+ this.board = { ...boardDefaults };
+ } else if (this.currentBoard && Object.keys(this.currentBoard).length) {
+ this.board = { ...boardDefaults, ...this.currentBoard };
+ }
+ },
+ },
+};
+</script>
+
+<template>
+ <deprecated-modal
+ v-show="isVisible"
+ :hide-footer="readonly"
+ :title="title"
+ :primary-button-label="buttonText"
+ :kind="buttonKind"
+ :submit-disabled="submitDisabled"
+ modal-dialog-class="board-config-modal"
+ @cancel="cancel"
+ @submit="submit"
+ >
+ <template slot="body">
+ <p v-if="isDeleteForm">Are you sure you want to delete this board?</p>
+ <form v-else class="js-board-config-modal" @submit.prevent>
+ <div v-if="!readonly" class="append-bottom-20">
+ <label class="form-section-title label-bold" for="board-new-name"> Board name </label>
+ <input
+ id="board-new-name"
+ ref="name"
+ v-model="board.name"
+ class="form-control"
+ type="text"
+ placeholder="Enter board name"
+ @keyup.enter="submit"
+ />
+ </div>
+
+ <board-scope
+ v-if="scopedIssueBoardFeatureEnabled"
+ :collapse-scope="isNewForm"
+ :board="board"
+ :can-admin-board="canAdminBoard"
+ :milestone-path="milestonePath"
+ :labels-path="labelsPath"
+ :scoped-labels-documentation-link="scopedLabelsDocumentationLink"
+ :enable-scoped-labels="enableScopedLabels"
+ :project-id="projectId"
+ :group-id="groupId"
+ :weights="weights"
+ />
+ </form>
+ </template>
+ </deprecated-modal>
+</template>
diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue
new file mode 100644
index 00000000000..b05de4538f2
--- /dev/null
+++ b/app/assets/javascripts/boards/components/boards_selector.vue
@@ -0,0 +1,334 @@
+<script>
+import { throttle } from 'underscore';
+import {
+ GlLoadingIcon,
+ GlSearchBoxByType,
+ GlDropdown,
+ GlDropdownDivider,
+ GlDropdownHeader,
+ GlDropdownItem,
+} from '@gitlab/ui';
+
+import Icon from '~/vue_shared/components/icon.vue';
+import httpStatusCodes from '~/lib/utils/http_status';
+import boardsStore from '../stores/boards_store';
+import BoardForm from './board_form.vue';
+
+const MIN_BOARDS_TO_VIEW_RECENT = 10;
+
+export default {
+ name: 'BoardsSelector',
+ components: {
+ Icon,
+ BoardForm,
+ GlLoadingIcon,
+ GlSearchBoxByType,
+ GlDropdown,
+ GlDropdownDivider,
+ GlDropdownHeader,
+ GlDropdownItem,
+ },
+ props: {
+ currentBoard: {
+ type: Object,
+ required: true,
+ },
+ milestonePath: {
+ type: String,
+ required: true,
+ },
+ throttleDuration: {
+ type: Number,
+ default: 200,
+ },
+ boardBaseUrl: {
+ type: String,
+ required: true,
+ },
+ hasMissingBoards: {
+ type: Boolean,
+ required: true,
+ },
+ canAdminBoard: {
+ type: Boolean,
+ required: true,
+ },
+ multipleIssueBoardsAvailable: {
+ type: Boolean,
+ required: true,
+ },
+ labelsPath: {
+ type: String,
+ required: true,
+ },
+ projectId: {
+ type: Number,
+ required: true,
+ },
+ groupId: {
+ type: Number,
+ required: true,
+ },
+ scopedIssueBoardFeatureEnabled: {
+ type: Boolean,
+ required: true,
+ },
+ weights: {
+ type: Array,
+ required: true,
+ },
+ enabledScopedLabels: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ scopedLabelsDocumentationLink: {
+ type: String,
+ required: false,
+ default: '#',
+ },
+ },
+ data() {
+ return {
+ loading: true,
+ hasScrollFade: false,
+ scrollFadeInitialized: false,
+ boards: [],
+ recentBoards: [],
+ state: boardsStore.state,
+ throttledSetScrollFade: throttle(this.setScrollFade, this.throttleDuration),
+ contentClientHeight: 0,
+ maxPosition: 0,
+ store: boardsStore,
+ filterTerm: '',
+ };
+ },
+ computed: {
+ currentPage() {
+ return this.state.currentPage;
+ },
+ filteredBoards() {
+ return this.boards.filter(board =>
+ board.name.toLowerCase().includes(this.filterTerm.toLowerCase()),
+ );
+ },
+ reload: {
+ get() {
+ return this.state.reload;
+ },
+ set(newValue) {
+ this.state.reload = newValue;
+ },
+ },
+ board() {
+ return this.state.currentBoard;
+ },
+ showDelete() {
+ return this.boards.length > 1;
+ },
+ scrollFadeClass() {
+ return {
+ 'fade-out': !this.hasScrollFade,
+ };
+ },
+ showRecentSection() {
+ return (
+ this.recentBoards.length &&
+ this.boards.length > MIN_BOARDS_TO_VIEW_RECENT &&
+ !this.filterTerm.length
+ );
+ },
+ },
+ watch: {
+ filteredBoards() {
+ this.scrollFadeInitialized = false;
+ this.$nextTick(this.setScrollFade);
+ },
+ reload() {
+ if (this.reload) {
+ this.boards = [];
+ this.recentBoards = [];
+ this.loading = true;
+ this.reload = false;
+
+ this.loadBoards(false);
+ }
+ },
+ },
+ created() {
+ boardsStore.setCurrentBoard(this.currentBoard);
+ },
+ methods: {
+ showPage(page) {
+ boardsStore.showPage(page);
+ },
+ loadBoards(toggleDropdown = true) {
+ if (toggleDropdown && this.boards.length > 0) {
+ return;
+ }
+
+ const recentBoardsPromise = new Promise((resolve, reject) =>
+ gl.boardService
+ .recentBoards()
+ .then(resolve)
+ .catch(err => {
+ /**
+ * If user is unauthorized we'd still want to resolve the
+ * request to display all boards.
+ */
+ if (err.response.status === httpStatusCodes.UNAUTHORIZED) {
+ resolve({ data: [] }); // recent boards are empty
+ return;
+ }
+ reject(err);
+ }),
+ );
+
+ Promise.all([gl.boardService.allBoards(), recentBoardsPromise])
+ .then(([allBoards, recentBoards]) => [allBoards.data, recentBoards.data])
+ .then(([allBoardsJson, recentBoardsJson]) => {
+ this.loading = false;
+ this.boards = allBoardsJson;
+ this.recentBoards = recentBoardsJson;
+ })
+ .then(() => this.$nextTick()) // Wait for boards list in DOM
+ .then(() => {
+ this.setScrollFade();
+ })
+ .catch(() => {
+ this.loading = false;
+ });
+ },
+ isScrolledUp() {
+ const { content } = this.$refs;
+ const currentPosition = this.contentClientHeight + content.scrollTop;
+
+ return content && currentPosition < this.maxPosition;
+ },
+ initScrollFade() {
+ this.scrollFadeInitialized = true;
+
+ const { content } = this.$refs;
+
+ this.contentClientHeight = content.clientHeight;
+ this.maxPosition = content.scrollHeight;
+ },
+ setScrollFade() {
+ if (!this.scrollFadeInitialized) this.initScrollFade();
+
+ this.hasScrollFade = this.isScrolledUp();
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="boards-switcher js-boards-selector append-right-10">
+ <span class="boards-selector-wrapper js-boards-selector-wrapper">
+ <gl-dropdown
+ toggle-class="dropdown-menu-toggle js-dropdown-toggle"
+ menu-class="flex-column dropdown-extended-height"
+ :text="board.name"
+ @show="loadBoards"
+ >
+ <div>
+ <div class="dropdown-title mb-0" @mousedown.prevent>
+ {{ s__('IssueBoards|Switch board') }}
+ </div>
+ </div>
+
+ <gl-dropdown-header class="mt-0">
+ <gl-search-box-by-type ref="searchBox" v-model="filterTerm" />
+ </gl-dropdown-header>
+
+ <div
+ v-if="!loading"
+ ref="content"
+ class="dropdown-content flex-fill"
+ @scroll.passive="throttledSetScrollFade"
+ >
+ <gl-dropdown-item
+ v-show="filteredBoards.length === 0"
+ class="no-pointer-events text-secondary"
+ >
+ {{ s__('IssueBoards|No matching boards found') }}
+ </gl-dropdown-item>
+
+ <h6 v-if="showRecentSection" class="dropdown-bold-header my-0">
+ {{ __('Recent') }}
+ </h6>
+
+ <template v-if="showRecentSection">
+ <gl-dropdown-item
+ v-for="recentBoard in recentBoards"
+ :key="`recent-${recentBoard.id}`"
+ class="js-dropdown-item"
+ :href="`${boardBaseUrl}/${recentBoard.id}`"
+ >
+ {{ recentBoard.name }}
+ </gl-dropdown-item>
+ </template>
+
+ <hr v-if="showRecentSection" class="my-1" />
+
+ <h6 v-if="showRecentSection" class="dropdown-bold-header my-0">
+ {{ __('All') }}
+ </h6>
+
+ <gl-dropdown-item
+ v-for="otherBoard in filteredBoards"
+ :key="otherBoard.id"
+ class="js-dropdown-item"
+ :href="`${boardBaseUrl}/${otherBoard.id}`"
+ >
+ {{ otherBoard.name }}
+ </gl-dropdown-item>
+ <gl-dropdown-item v-if="hasMissingBoards" class="small unclickable">
+ {{
+ s__(
+ 'IssueBoards|Some of your boards are hidden, activate a license to see them again.',
+ )
+ }}
+ </gl-dropdown-item>
+ </div>
+
+ <div
+ v-show="filteredBoards.length > 0"
+ class="dropdown-content-faded-mask"
+ :class="scrollFadeClass"
+ ></div>
+
+ <gl-loading-icon v-if="loading" />
+
+ <div v-if="canAdminBoard">
+ <gl-dropdown-divider />
+
+ <gl-dropdown-item v-if="multipleIssueBoardsAvailable" @click.prevent="showPage('new')">
+ {{ s__('IssueBoards|Create new board') }}
+ </gl-dropdown-item>
+
+ <gl-dropdown-item
+ v-if="showDelete"
+ class="text-danger"
+ @click.prevent="showPage('delete')"
+ >
+ {{ s__('IssueBoards|Delete board') }}
+ </gl-dropdown-item>
+ </div>
+ </gl-dropdown>
+
+ <board-form
+ v-if="currentPage"
+ :milestone-path="milestonePath"
+ :labels-path="labelsPath"
+ :project-id="projectId"
+ :group-id="groupId"
+ :can-admin-board="canAdminBoard"
+ :scoped-issue-board-feature-enabled="scopedIssueBoardFeatureEnabled"
+ :weights="weights"
+ :enable-scoped-labels="enabledScopedLabels"
+ :scoped-labels-documentation-link="scopedLabelsDocumentationLink"
+ />
+ </span>
+ </div>
+</template>
diff --git a/app/assets/javascripts/boards/components/modal/footer.vue b/app/assets/javascripts/boards/components/modal/footer.vue
index eea0fb71c1a..5f100c617a0 100644
--- a/app/assets/javascripts/boards/components/modal/footer.vue
+++ b/app/assets/javascripts/boards/components/modal/footer.vue
@@ -1,4 +1,5 @@
<script>
+import footerEEMixin from 'ee_else_ce/boards/mixins/modal_footer';
import Flash from '../../../flash';
import { __, n__ } from '../../../locale';
import ListsDropdown from './lists_dropdown.vue';
@@ -10,7 +11,7 @@ export default {
components: {
ListsDropdown,
},
- mixins: [modalMixin],
+ mixins: [modalMixin, footerEEMixin],
data() {
return {
modal: ModalStore.store,
diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue
index a1cf1866faf..e8d25e84be1 100644
--- a/app/assets/javascripts/boards/components/project_select.vue
+++ b/app/assets/javascripts/boards/components/project_select.vue
@@ -68,13 +68,15 @@ export default {
<li>
<a href='#' class='dropdown-menu-link' data-project-id="${
project.id
- }" data-project-name="${project.name}">
- ${_.escape(project.name)}
+ }" data-project-name="${project.name}" data-project-name-with-namespace="${
+ project.name_with_namespace
+ }">
+ ${_.escape(project.name_with_namespace)}
</a>
</li>
`;
},
- text: project => project.name,
+ text: project => project.name_with_namespace,
});
},
};
diff --git a/app/assets/javascripts/boards/ee_functions.js b/app/assets/javascripts/boards/ee_functions.js
new file mode 100644
index 00000000000..583270fcae5
--- /dev/null
+++ b/app/assets/javascripts/boards/ee_functions.js
@@ -0,0 +1,7 @@
+export const setPromotionState = () => {};
+
+export const setWeigthFetchingState = () => {};
+export const setEpicFetchingState = () => {};
+
+export const getMilestoneTitle = () => ({});
+export const getBoardsModalData = () => ({});
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index d6a5372b22d..3bded4a3258 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -1,7 +1,6 @@
import $ from 'jquery';
import Vue from 'vue';
-import mountMultipleBoardsSwitcher from 'ee_else_ce/boards/mount_multiple_boards_switcher';
import Flash from '~/flash';
import { __ } from '~/locale';
import './models/label';
@@ -31,6 +30,14 @@ import {
} from '~/lib/utils/common_utils';
import boardConfigToggle from 'ee_else_ce/boards/config_toggle';
import toggleFocusMode from 'ee_else_ce/boards/toggle_focus';
+import {
+ setPromotionState,
+ setWeigthFetchingState,
+ setEpicFetchingState,
+ getMilestoneTitle,
+ getBoardsModalData,
+} from 'ee_else_ce/boards/ee_functions';
+import mountMultipleBoardsSwitcher from './mount_multiple_boards_switcher';
let issueBoardsApp;
@@ -129,6 +136,7 @@ export default () => {
});
boardsStore.addBlankState();
+ setPromotionState(boardsStore);
this.loading = false;
})
.catch(() => {
@@ -143,6 +151,8 @@ export default () => {
const { sidebarInfoEndpoint } = newIssue;
if (sidebarInfoEndpoint && newIssue.subscribed === undefined) {
newIssue.setFetchingState('subscriptions', true);
+ setWeigthFetchingState(newIssue, true);
+ setEpicFetchingState(newIssue, true);
BoardService.getIssueInfo(sidebarInfoEndpoint)
.then(res => res.data)
.then(data => {
@@ -157,6 +167,8 @@ export default () => {
} = convertObjectPropsToCamelCase(data);
newIssue.setFetchingState('subscriptions', false);
+ setWeigthFetchingState(newIssue, false);
+ setEpicFetchingState(newIssue, false);
newIssue.updateData({
humanTimeSpent: humanTotalTimeSpent,
timeSpent: totalTimeSpent,
@@ -169,6 +181,7 @@ export default () => {
})
.catch(() => {
newIssue.setFetchingState('subscriptions', false);
+ setWeigthFetchingState(newIssue, false);
Flash(__('An error occurred while fetching sidebar data'));
});
}
@@ -203,6 +216,7 @@ export default () => {
el: document.getElementById('js-add-list'),
data: {
filters: boardsStore.state.filters,
+ ...getMilestoneTitle($boardApp),
},
mounted() {
initNewListDropdown();
@@ -222,6 +236,7 @@ export default () => {
return {
modal: ModalStore.store,
store: boardsStore.state,
+ ...getBoardsModalData($boardApp),
canAdminList: this.$options.el.hasAttribute('data-can-admin-list'),
};
},
@@ -285,6 +300,6 @@ export default () => {
});
}
- toggleFocusMode(ModalStore, boardsStore);
+ toggleFocusMode(ModalStore, boardsStore, $boardApp);
mountMultipleBoardsSwitcher();
};
diff --git a/app/assets/javascripts/boards/mixins/modal_footer.js b/app/assets/javascripts/boards/mixins/modal_footer.js
new file mode 100644
index 00000000000..ff8b4c56321
--- /dev/null
+++ b/app/assets/javascripts/boards/mixins/modal_footer.js
@@ -0,0 +1 @@
+export default {};
diff --git a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js
index bdb14a7f2f2..8d22f009784 100644
--- a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js
+++ b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js
@@ -1,2 +1,35 @@
-// this will be moved from EE to CE as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/53811
-export default () => {};
+import Vue from 'vue';
+import { parseBoolean } from '~/lib/utils/common_utils';
+import BoardsSelector from '~/boards/components/boards_selector.vue';
+
+export default () => {
+ const boardsSwitcherElement = document.getElementById('js-multiple-boards-switcher');
+ return new Vue({
+ el: boardsSwitcherElement,
+ components: {
+ BoardsSelector,
+ },
+ data() {
+ const { dataset } = boardsSwitcherElement;
+
+ const boardsSelectorProps = {
+ ...dataset,
+ currentBoard: JSON.parse(dataset.currentBoard),
+ hasMissingBoards: parseBoolean(dataset.hasMissingBoards),
+ canAdminBoard: parseBoolean(dataset.canAdminBoard),
+ multipleIssueBoardsAvailable: parseBoolean(dataset.multipleIssueBoardsAvailable),
+ projectId: Number(dataset.projectId),
+ groupId: Number(dataset.groupId),
+ scopedIssueBoardFeatureEnabled: parseBoolean(dataset.scopedIssueBoardFeatureEnabled),
+ weights: JSON.parse(dataset.weights),
+ };
+
+ return { boardsSelectorProps };
+ },
+ render(createElement) {
+ return createElement(BoardsSelector, {
+ props: this.boardsSelectorProps,
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/commons/polyfills.js b/app/assets/javascripts/commons/polyfills.js
index a4394ab7e92..7a6ad3dc771 100644
--- a/app/assets/javascripts/commons/polyfills.js
+++ b/app/assets/javascripts/commons/polyfills.js
@@ -13,6 +13,7 @@ import 'core-js/es/string/code-point-at';
import 'core-js/es/string/from-code-point';
import 'core-js/es/string/includes';
import 'core-js/es/string/starts-with';
+import 'core-js/es/string/ends-with';
import 'core-js/es/symbol';
import 'core-js/es/map';
import 'core-js/es/weak-map';
diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue
index 9909f437fc8..830385941d8 100644
--- a/app/assets/javascripts/groups/components/group_item.vue
+++ b/app/assets/javascripts/groups/components/group_item.vue
@@ -129,7 +129,7 @@ export default {
<item-stats-value
:icon-name="visibilityIcon"
:title="visibilityTooltip"
- css-class="item-visibility d-inline-flex align-items-center prepend-top-8 append-right-4"
+ css-class="item-visibility d-inline-flex align-items-center prepend-top-8 append-right-4 text-secondary"
/>
<span v-if="group.permission" class="user-access-role prepend-top-8">
{{ group.permission }}
diff --git a/app/assets/javascripts/ide/services/index.js b/app/assets/javascripts/ide/services/index.js
index 840761f68db..ba33b6826d6 100644
--- a/app/assets/javascripts/ide/services/index.js
+++ b/app/assets/javascripts/ide/services/index.js
@@ -56,13 +56,7 @@ export default {
return Api.branchSingle(projectId, currentBranchId);
},
commit(projectId, payload) {
- // Currently the `commit` endpoint does not support `start_sha` so we
- // have to make the request in the FE. This is not ideal and will be
- // resolved soon. https://gitlab.com/gitlab-org/gitlab-ce/issues/59023
- const { branch, start_sha: ref } = payload;
- const branchPromise = ref ? Api.createBranch(projectId, { ref, branch }) : Promise.resolve();
-
- return branchPromise.then(() => Api.commitMultiple(projectId, payload));
+ return Api.commitMultiple(projectId, payload);
},
getFiles(projectUrl, branchId) {
const url = `${projectUrl}/files/${branchId}`;
diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js
index a52f1e235ed..1442ea7dbfa 100644
--- a/app/assets/javascripts/ide/stores/mutations/file.js
+++ b/app/assets/javascripts/ide/stores/mutations/file.js
@@ -43,10 +43,14 @@ export default {
[stateEntry, stagedFile, openFile, changedFile].forEach(f => {
if (f) {
- Object.assign(f, convertObjectPropsToCamelCase(data, { dropKeys: ['raw', 'baseRaw'] }), {
- raw: (stateEntry && stateEntry.raw) || null,
- baseRaw: null,
- });
+ Object.assign(
+ f,
+ convertObjectPropsToCamelCase(data, { dropKeys: ['path', 'name', 'raw', 'baseRaw'] }),
+ {
+ raw: (stateEntry && stateEntry.raw) || null,
+ baseRaw: null,
+ },
+ );
}
});
},
diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js
index 01f78a29cf6..366314536c6 100644
--- a/app/assets/javascripts/ide/stores/utils.js
+++ b/app/assets/javascripts/ide/stores/utils.js
@@ -155,11 +155,11 @@ export const createCommitPayload = ({
last_commit_id:
newBranch || f.deleted || f.prevPath || f.replaces ? undefined : f.lastCommitSha,
})),
- start_sha: newBranch ? rootGetters.lastCommit.short_id : undefined,
+ start_sha: newBranch ? rootGetters.lastCommit.id : undefined,
});
export const createNewMergeRequestUrl = (projectUrl, source, target) =>
- `${projectUrl}/merge_requests/new?merge_request[source_branch]=${source}&merge_request[target_branch]=${target}`;
+ `${projectUrl}/merge_requests/new?merge_request[source_branch]=${source}&merge_request[target_branch]=${target}&nav_source=webide`;
const sortTreesByTypeAndName = (a, b) => {
if (a.type === 'tree' && b.type === 'blob') {
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index bea43430edc..f50a6e3b19d 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -311,7 +311,8 @@ export default class LabelsSelect {
// We need to identify which items are actually labels
if (label.id) {
- selectedClass.push('label-item');
+ const selectedLayoutClasses = ['d-flex', 'flex-row', 'text-break-word'];
+ selectedClass.push('label-item', ...selectedLayoutClasses);
linkEl.dataset.labelId = label.id;
}
diff --git a/app/assets/javascripts/monitoring/components/charts/area.vue b/app/assets/javascripts/monitoring/components/charts/area.vue
index 454ff4f284e..edf9423c74c 100644
--- a/app/assets/javascripts/monitoring/components/charts/area.vue
+++ b/app/assets/javascripts/monitoring/components/charts/area.vue
@@ -37,7 +37,13 @@ export default {
},
projectPath: {
type: String,
- required: true,
+ required: false,
+ default: () => '',
+ },
+ showBorder: {
+ type: Boolean,
+ required: false,
+ default: () => false,
},
thresholds: {
type: Array,
@@ -234,52 +240,54 @@ export default {
</script>
<template>
- <div class="prometheus-graph col-12 col-lg-6">
- <div class="prometheus-graph-header">
- <h5 ref="graphTitle" class="prometheus-graph-title">{{ graphData.title }}</h5>
- <div ref="graphWidgets" class="prometheus-graph-widgets"><slot></slot></div>
- </div>
- <gl-area-chart
- ref="areaChart"
- v-bind="$attrs"
- :data="chartData"
- :option="chartOptions"
- :format-tooltip-text="formatTooltipText"
- :thresholds="thresholds"
- :width="width"
- :height="height"
- @updated="onChartUpdated"
- >
- <template v-if="tooltip.isDeployment">
- <template slot="tooltipTitle">
- {{ __('Deployed') }}
- </template>
- <div slot="tooltipContent" class="d-flex align-items-center">
- <icon name="commit" class="mr-2" />
- <gl-link :href="tooltip.commitUrl">{{ tooltip.sha }}</gl-link>
- </div>
- </template>
- <template v-else>
- <template slot="tooltipTitle">
- <div class="text-nowrap">
- {{ tooltip.title }}
+ <div class="col-12 col-lg-6" :class="[showBorder ? 'p-2' : 'p-0']">
+ <div class="prometheus-graph" :class="{ 'prometheus-graph-embed w-100 p-3': showBorder }">
+ <div class="prometheus-graph-header">
+ <h5 ref="graphTitle" class="prometheus-graph-title">{{ graphData.title }}</h5>
+ <div ref="graphWidgets" class="prometheus-graph-widgets"><slot></slot></div>
+ </div>
+ <gl-area-chart
+ ref="areaChart"
+ v-bind="$attrs"
+ :data="chartData"
+ :option="chartOptions"
+ :format-tooltip-text="formatTooltipText"
+ :thresholds="thresholds"
+ :width="width"
+ :height="height"
+ @updated="onChartUpdated"
+ >
+ <template v-if="tooltip.isDeployment">
+ <template slot="tooltipTitle">
+ {{ __('Deployed') }}
+ </template>
+ <div slot="tooltipContent" class="d-flex align-items-center">
+ <icon name="commit" class="mr-2" />
+ <gl-link :href="tooltip.commitUrl">{{ tooltip.sha }}</gl-link>
</div>
</template>
- <template slot="tooltipContent">
- <div
- v-for="(content, key) in tooltip.content"
- :key="key"
- class="d-flex justify-content-between"
- >
- <gl-chart-series-label :color="isMultiSeries ? content.color : ''">
- {{ content.name }}
- </gl-chart-series-label>
- <div class="prepend-left-32">
- {{ content.value }}
+ <template v-else>
+ <template slot="tooltipTitle">
+ <div class="text-nowrap">
+ {{ tooltip.title }}
</div>
- </div>
+ </template>
+ <template slot="tooltipContent">
+ <div
+ v-for="(content, key) in tooltip.content"
+ :key="key"
+ class="d-flex justify-content-between"
+ >
+ <gl-chart-series-label :color="isMultiSeries ? content.color : ''">
+ {{ content.name }}
+ </gl-chart-series-label>
+ <div class="prepend-left-32">
+ {{ content.value }}
+ </div>
+ </div>
+ </template>
</template>
- </template>
- </gl-area-chart>
+ </gl-area-chart>
+ </div>
</div>
</template>
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index 6eaced0c108..c7c55880040 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -11,10 +11,9 @@ import MonitorSingleStatChart from './charts/single_stat.vue';
import PanelType from './panel_type.vue';
import GraphGroup from './graph_group.vue';
import EmptyState from './empty_state.vue';
-import { timeWindows, timeWindowsKeyNames } from '../constants';
+import { sidebarAnimationDuration, timeWindows, timeWindowsKeyNames } from '../constants';
import { getTimeDiff } from '../utils';
-const sidebarAnimationDuration = 150;
let sidebarMutationObserver;
export default {
@@ -370,8 +369,8 @@ export default {
</div>
<div v-if="!showEmptyState">
<graph-group
- v-for="(groupData, index) in groupsWithData"
- :key="index"
+ v-for="groupData in groupsWithData"
+ :key="`${groupData.group}.${groupData.priority}`"
:name="groupData.group"
:show-panels="showPanels"
>
diff --git a/app/assets/javascripts/monitoring/components/embed.vue b/app/assets/javascripts/monitoring/components/embed.vue
new file mode 100644
index 00000000000..e17f03de0fd
--- /dev/null
+++ b/app/assets/javascripts/monitoring/components/embed.vue
@@ -0,0 +1,97 @@
+<script>
+import { mapActions, mapState } from 'vuex';
+import GraphGroup from './graph_group.vue';
+import MonitorAreaChart from './charts/area.vue';
+import { sidebarAnimationDuration, timeWindowsKeyNames, timeWindows } from '../constants';
+import { getTimeDiff } from '../utils';
+
+let sidebarMutationObserver;
+
+export default {
+ components: {
+ GraphGroup,
+ MonitorAreaChart,
+ },
+ props: {
+ dashboardUrl: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ params: {
+ ...getTimeDiff(timeWindows[timeWindowsKeyNames.eightHours]),
+ },
+ elWidth: 0,
+ };
+ },
+ computed: {
+ ...mapState('monitoringDashboard', ['groups', 'metricsWithData']),
+ groupData() {
+ const groupsWithData = this.groups.filter(group => this.chartsWithData(group.metrics).length);
+ if (groupsWithData.length) {
+ return groupsWithData[0];
+ }
+ return null;
+ },
+ },
+ mounted() {
+ this.setInitialState();
+ this.fetchMetricsData(this.params);
+ sidebarMutationObserver = new MutationObserver(this.onSidebarMutation);
+ sidebarMutationObserver.observe(document.querySelector('.layout-page'), {
+ attributes: true,
+ childList: false,
+ subtree: false,
+ });
+ },
+ beforeDestroy() {
+ if (sidebarMutationObserver) {
+ sidebarMutationObserver.disconnect();
+ }
+ },
+ methods: {
+ ...mapActions('monitoringDashboard', [
+ 'fetchMetricsData',
+ 'setEndpoints',
+ 'setFeatureFlags',
+ 'setShowErrorBanner',
+ ]),
+ chartsWithData(charts) {
+ return charts.filter(chart =>
+ chart.metrics.some(metric => this.metricsWithData.includes(metric.metric_id)),
+ );
+ },
+ onSidebarMutation() {
+ setTimeout(() => {
+ this.elWidth = this.$el.clientWidth;
+ }, sidebarAnimationDuration);
+ },
+ setInitialState() {
+ this.setFeatureFlags({
+ prometheusEndpointEnabled: true,
+ });
+ this.setEndpoints({
+ dashboardEndpoint: this.dashboardUrl,
+ });
+ this.setShowErrorBanner(false);
+ },
+ },
+};
+</script>
+<template>
+ <div class="metrics-embed">
+ <div v-if="groupData" class="row w-100 m-n2 pb-4">
+ <monitor-area-chart
+ v-for="graphData in chartsWithData(groupData.metrics)"
+ :key="graphData.title"
+ :graph-data="graphData"
+ :container-width="elWidth"
+ group-id="monitor-area-chart"
+ :project-path="null"
+ :show-border="true"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/monitoring/constants.js b/app/assets/javascripts/monitoring/constants.js
index 26f1bf3f68d..605c95e6da5 100644
--- a/app/assets/javascripts/monitoring/constants.js
+++ b/app/assets/javascripts/monitoring/constants.js
@@ -1,5 +1,7 @@
import { __ } from '~/locale';
+export const sidebarAnimationDuration = 300; // milliseconds.
+
export const chartHeight = 300;
export const graphTypes = {
diff --git a/app/assets/javascripts/monitoring/stores/actions.js b/app/assets/javascripts/monitoring/stores/actions.js
index 5b3da51e9a6..245cc2eaca3 100644
--- a/app/assets/javascripts/monitoring/stores/actions.js
+++ b/app/assets/javascripts/monitoring/stores/actions.js
@@ -44,6 +44,10 @@ export const setFeatureFlags = (
commit(types.SET_ADDITIONAL_PANEL_TYPES_ENABLED, additionalPanelTypesEnabled);
};
+export const setShowErrorBanner = ({ commit }, enabled) => {
+ commit(types.SET_SHOW_ERROR_BANNER, enabled);
+};
+
export const requestMetricsDashboard = ({ commit }) => {
commit(types.REQUEST_METRICS_DATA);
};
@@ -99,7 +103,9 @@ export const fetchMetricsData = ({ state, dispatch }, params) => {
})
.catch(error => {
dispatch('receiveMetricsDataFailure', error);
- createFlash(s__('Metrics|There was an error while retrieving metrics'));
+ if (state.setShowErrorBanner) {
+ createFlash(s__('Metrics|There was an error while retrieving metrics'));
+ }
});
};
@@ -119,7 +125,9 @@ export const fetchDashboard = ({ state, dispatch }, params) => {
})
.catch(error => {
dispatch('receiveMetricsDashboardFailure', error);
- createFlash(s__('Metrics|There was an error while retrieving metrics'));
+ if (state.setShowErrorBanner) {
+ createFlash(s__('Metrics|There was an error while retrieving metrics'));
+ }
});
};
diff --git a/app/assets/javascripts/monitoring/stores/mutation_types.js b/app/assets/javascripts/monitoring/stores/mutation_types.js
index c89daba3df7..4b1aadbcf05 100644
--- a/app/assets/javascripts/monitoring/stores/mutation_types.js
+++ b/app/assets/javascripts/monitoring/stores/mutation_types.js
@@ -16,3 +16,4 @@ export const SET_ALL_DASHBOARDS = 'SET_ALL_DASHBOARDS';
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';
+export const SET_SHOW_ERROR_BANNER = 'SET_SHOW_ERROR_BANNER';
diff --git a/app/assets/javascripts/monitoring/stores/mutations.js b/app/assets/javascripts/monitoring/stores/mutations.js
index 0104dcb867d..b19520d6638 100644
--- a/app/assets/javascripts/monitoring/stores/mutations.js
+++ b/app/assets/javascripts/monitoring/stores/mutations.js
@@ -96,4 +96,7 @@ export default {
[types.SET_ADDITIONAL_PANEL_TYPES_ENABLED](state, enabled) {
state.additionalPanelTypesEnabled = enabled;
},
+ [types.SET_SHOW_ERROR_BANNER](state, enabled) {
+ state.showErrorBanner = enabled;
+ },
};
diff --git a/app/assets/javascripts/monitoring/stores/state.js b/app/assets/javascripts/monitoring/stores/state.js
index e54bb712695..440bdc951e0 100644
--- a/app/assets/javascripts/monitoring/stores/state.js
+++ b/app/assets/javascripts/monitoring/stores/state.js
@@ -12,6 +12,7 @@ export default () => ({
additionalPanelTypesEnabled: false,
emptyState: 'gettingStarted',
showEmptyState: true,
+ showErrorBanner: true,
groups: [],
deploymentData: [],
environments: [],
diff --git a/app/assets/javascripts/notes/components/discussion_actions.vue b/app/assets/javascripts/notes/components/discussion_actions.vue
index f4570c1292c..7aa8580d794 100644
--- a/app/assets/javascripts/notes/components/discussion_actions.vue
+++ b/app/assets/javascripts/notes/components/discussion_actions.vue
@@ -39,30 +39,27 @@ export default {
</script>
<template>
- <div class="discussion-with-resolve-btn">
+ <div class="discussion-with-resolve-btn clearfix">
<reply-placeholder
:button-text="s__('MergeRequests|Reply...')"
class="qa-discussion-reply"
@onClick="$emit('showReplyForm')"
/>
- <resolve-discussion-button
- v-if="discussion.resolvable"
- :is-resolving="isResolving"
- :button-title="resolveButtonTitle"
- @onClick="$emit('resolve')"
- />
- <div v-if="discussion.resolvable" class="btn-group discussion-actions ml-sm-2" role="group">
- <resolve-with-issue-button v-if="resolveWithIssuePath" :url="resolveWithIssuePath" />
- <jump-to-next-discussion-button
- v-if="shouldShowJumpToNextDiscussion"
- @onClick="$emit('jumpToNextDiscussion')"
- />
+
+ <div class="btn-group discussion-actions" role="group">
+ <div class="btn-group">
+ <resolve-discussion-button
+ v-if="discussion.resolvable"
+ :is-resolving="isResolving"
+ :button-title="resolveButtonTitle"
+ @onClick="$emit('resolve')"
+ />
+ </div>
<resolve-with-issue-button
v-if="discussion.resolvable && resolveWithIssuePath"
:url="resolveWithIssuePath"
/>
</div>
-
<div
v-if="discussion.resolvable && shouldShowJumpToNextDiscussion"
class="btn-group discussion-actions ml-sm-2"
diff --git a/app/assets/javascripts/reports/components/report_item.vue b/app/assets/javascripts/reports/components/report_item.vue
index d477fafd3f5..f3f7d2648a8 100644
--- a/app/assets/javascripts/reports/components/report_item.vue
+++ b/app/assets/javascripts/reports/components/report_item.vue
@@ -45,7 +45,7 @@ export default {
<template>
<li
:class="{ 'is-dismissed': issue.isDismissed }"
- class="report-block-list-issue justify-content-center align-items-center"
+ class="report-block-list-issue align-items-center"
>
<issue-status-icon
v-if="showReportSectionStatusIcon"
diff --git a/app/assets/javascripts/reports/components/summary_row.vue b/app/assets/javascripts/reports/components/summary_row.vue
index 1caf52431e2..aba798e63d0 100644
--- a/app/assets/javascripts/reports/components/summary_row.vue
+++ b/app/assets/javascripts/reports/components/summary_row.vue
@@ -44,9 +44,7 @@ export default {
};
</script>
<template>
- <div
- class="report-block-list-issue report-block-list-issue-parent justify-content-center align-items-center"
- >
+ <div class="report-block-list-issue report-block-list-issue-parent align-items-center">
<div class="report-block-list-icon append-right-default">
<gl-loading-icon
v-if="statusIcon === 'loading'"
diff --git a/app/assets/javascripts/repository/components/table/index.vue b/app/assets/javascripts/repository/components/table/index.vue
index 0d9e992e596..610c7e8d99e 100644
--- a/app/assets/javascripts/repository/components/table/index.vue
+++ b/app/assets/javascripts/repository/components/table/index.vue
@@ -137,6 +137,7 @@ export default {
:path="entry.flatPath"
:type="entry.type"
:url="entry.webUrl"
+ :submodule-tree-url="entry.treeUrl"
:lfs-oid="entry.lfsOid"
/>
</template>
diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue
index 3e060e9ecb6..6029460d975 100644
--- a/app/assets/javascripts/repository/components/table/row.vue
+++ b/app/assets/javascripts/repository/components/table/row.vue
@@ -62,6 +62,11 @@ export default {
required: false,
default: null,
},
+ submoduleTreeUrl: {
+ type: String,
+ required: false,
+ default: null,
+ },
},
data() {
return {
@@ -112,7 +117,7 @@ export default {
</component>
<gl-badge v-if="lfsOid" variant="default" class="label-lfs ml-1">LFS</gl-badge>
<template v-if="isSubmodule">
- @ <gl-link href="#" class="commit-sha">{{ shortSha }}</gl-link>
+ @ <gl-link :href="submoduleTreeUrl" class="commit-sha">{{ shortSha }}</gl-link>
</template>
</td>
<td class="d-none d-sm-table-cell tree-commit">
diff --git a/app/assets/javascripts/repository/queries/getFiles.query.graphql b/app/assets/javascripts/repository/queries/getFiles.query.graphql
index 4c24fc4087f..b3cc0878cad 100644
--- a/app/assets/javascripts/repository/queries/getFiles.query.graphql
+++ b/app/assets/javascripts/repository/queries/getFiles.query.graphql
@@ -35,6 +35,8 @@ query getFiles(
edges {
node {
...TreeEntry
+ webUrl
+ treeUrl
}
}
pageInfo {
diff --git a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
index a75daca156c..0d1faceef11 100644
--- a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
+++ b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
@@ -73,22 +73,22 @@ export default {
<template>
<div>
- <div class="sidebar-collapsed-icon" @click="onClickCollapsedIcon">
- <span
- v-tooltip
- :title="notificationTooltip"
- data-container="body"
- data-placement="left"
- data-boundary="viewport"
- >
- <icon
- :name="notificationIcon"
- :size="16"
- aria-hidden="true"
- class="sidebar-item-icon is-active"
- />
- </span>
- </div>
+ <span
+ v-tooltip
+ class="sidebar-collapsed-icon"
+ :title="notificationTooltip"
+ data-container="body"
+ data-placement="left"
+ data-boundary="viewport"
+ @click="onClickCollapsedIcon"
+ >
+ <icon
+ :name="notificationIcon"
+ :size="16"
+ aria-hidden="true"
+ class="sidebar-item-icon is-active"
+ />
+ </span>
<span class="issuable-header-text hide-collapsed float-left"> {{ __('Notifications') }} </span>
<toggle-button
ref="toggleButton"
diff --git a/app/assets/stylesheets/_ee/application_ee.scss b/app/assets/stylesheets/_ee/application_ee.scss
new file mode 100644
index 00000000000..0fb2c9b68a9
--- /dev/null
+++ b/app/assets/stylesheets/_ee/application_ee.scss
@@ -0,0 +1,5 @@
+/*
+ This is a noop-file. In EE:
+ ee/app/assets/stylesheets/_ee/application_ee.scss
+ will take precedence over it and import more styles
+ */
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index d5ef66af31a..fbf16aa324a 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -34,4 +34,8 @@
// Styles for JS behaviors.
@import "behaviors";
+// EE-only stylesheets
+@import "application_ee";
+
+// CSS util classes
@import "utilities";
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 1bd5043ed10..61ab0476c42 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -434,6 +434,7 @@ img.emoji {
/** COMMON SIZING CLASSES **/
.w-0 { width: 0; }
+.w-8em { width: 8em; }
.h-12em { height: 12em; }
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index cff2e274390..1502cf18440 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -412,10 +412,6 @@ table.pipeline-project-metrics tr td {
font-size: $gl-font-size-large;
}
- .item-visibility {
- color: $gl-text-color-secondary;
- }
-
@include media-breakpoint-down(md) {
.title {
font-size: $gl-font-size;
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 6a0127eb51c..66b4f3bad2b 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -390,7 +390,7 @@
.block {
width: $gutter-collapsed-width - 2px;
- padding: 15px 0 0;
+ padding: 0;
border-bottom: 0;
overflow: hidden;
@@ -427,10 +427,13 @@
}
.sidebar-collapsed-icon {
- display: block;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
width: 100%;
+ height: $sidebar-toggle-height;
text-align: center;
- margin-bottom: 10px;
color: $gl-text-color-secondary;
svg {
@@ -470,6 +473,16 @@
}
.btn-clipboard {
+ /*
+ This change should be temporary, because the DOM currently gets
+ generated from a ruby definition in `app/helpers/button_helper.rb`.
+ As soon as the `copy to clipboard` button will be transfered to
+ Vue this should be adjusted as well.
+ */
+ flex: 1;
+ align-self: stretch;
+ padding: 0;
+
border: 0;
background: transparent;
color: $gl-text-color-secondary;
@@ -493,7 +506,6 @@
.sidebar-collapsed-user {
padding-bottom: 0;
- margin-bottom: 10px;
.author-link {
padding-left: 0;
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index 11e8a32389f..7d5e185834b 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -30,6 +30,10 @@
.dropdown-content {
max-height: 135px;
}
+
+ .dropdown-label-box {
+ flex: 0 0 auto;
+ }
}
.dropdown-new-label {
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index ff4fa8aacdc..5f4db37c317 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -695,6 +695,10 @@
top: -1px;
}
+ .spinner {
+ top: 2px;
+ }
+
&.play {
svg {
left: 2px;
@@ -861,6 +865,7 @@ button.mini-pipeline-graph-dropdown-toggle {
}
}
+ .spinner,
svg {
width: $ci-action-dropdown-svg-size;
height: $ci-action-dropdown-svg-size;
diff --git a/app/assets/stylesheets/pages/prometheus.scss b/app/assets/stylesheets/pages/prometheus.scss
index 2d600e3aef6..05a4cc168a8 100644
--- a/app/assets/stylesheets/pages/prometheus.scss
+++ b/app/assets/stylesheets/pages/prometheus.scss
@@ -29,6 +29,11 @@
padding: $gl-padding / 2;
}
+.prometheus-graph-embed {
+ border: 1px solid $border-color;
+ border-radius: $border-radius-default;
+}
+
.prometheus-graph-header {
display: flex;
align-items: center;
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 15f7ef881c8..6317fa7c8d1 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -90,7 +90,8 @@ class Admin::GroupsController < Admin::ApplicationController
:visibility_level,
:require_two_factor_authentication,
:two_factor_grace_period,
- :project_creation_level
+ :project_creation_level,
+ :subgroup_creation_level
]
end
end
diff --git a/app/controllers/admin/requests_profiles_controller.rb b/app/controllers/admin/requests_profiles_controller.rb
index 89d4c4f18d9..24383455064 100644
--- a/app/controllers/admin/requests_profiles_controller.rb
+++ b/app/controllers/admin/requests_profiles_controller.rb
@@ -3,17 +3,17 @@
class Admin::RequestsProfilesController < Admin::ApplicationController
def index
@profile_token = Gitlab::RequestProfiler.profile_token
- @profiles = Gitlab::RequestProfiler::Profile.all.group_by(&:request_path)
+ @profiles = Gitlab::RequestProfiler.all.group_by(&:request_path)
end
def show
clean_name = Rack::Utils.clean_path_info(params[:name])
- profile = Gitlab::RequestProfiler::Profile.find(clean_name)
+ profile = Gitlab::RequestProfiler.find(clean_name)
- if profile
- render html: profile.content.html_safe
- else
- redirect_to admin_requests_profiles_path, alert: 'Profile not found'
+ unless profile && profile.content_type
+ return redirect_to admin_requests_profiles_path, alert: 'Profile not found'
end
+
+ send_file profile.file_path, type: "#{profile.content_type}; charset=utf-8", disposition: 'inline'
end
end
diff --git a/app/controllers/chaos_controller.rb b/app/controllers/chaos_controller.rb
index 2985da35d83..ac008165c16 100644
--- a/app/controllers/chaos_controller.rb
+++ b/app/controllers/chaos_controller.rb
@@ -1,57 +1,38 @@
# frozen_string_literal: true
class ChaosController < ActionController::Base
- before_action :validate_chaos_secret, unless: :development?
- before_action :request_start_time
+ before_action :validate_chaos_secret, unless: :development_or_test?
def leakmem
- retainer = []
- # Add `n` 1mb chunks of memory to the retainer array
- memory_mb.times { retainer << "x" * 1.megabyte }
-
- Kernel.sleep(duration_left)
-
- render plain: "OK"
+ do_chaos :leak_mem, Chaos::LeakMemWorker, memory_mb, duration_s
end
def cpu_spin
- rand while Time.now < expected_end_time
-
- render plain: "OK"
+ do_chaos :cpu_spin, Chaos::CpuSpinWorker, duration_s
end
def db_spin
- while Time.now < expected_end_time
- ActiveRecord::Base.connection.execute("SELECT 1")
-
- end_interval_time = Time.now + [duration_s, interval_s].min
- rand while Time.now < end_interval_time
- end
+ do_chaos :db_spin, Chaos::DbSpinWorker, duration_s, interval_s
end
def sleep
- Kernel.sleep(duration_left)
-
- render plain: "OK"
+ do_chaos :sleep, Chaos::SleepWorker, duration_s
end
def kill
- Process.kill("KILL", Process.pid)
+ do_chaos :kill, Chaos::KillWorker
end
private
- def request_start_time
- @start_time ||= Time.now
- end
-
- def expected_end_time
- request_start_time + duration_s
- end
+ def do_chaos(method, worker, *args)
+ if async
+ worker.perform_async(*args)
+ else
+ Gitlab::Chaos.public_send(method, *args) # rubocop: disable GitlabSecurity/PublicSend
+ end
- def duration_left
- # returns 0 if over time
- [expected_end_time - Time.now, 0].max
+ render plain: "OK"
end
def validate_chaos_secret
@@ -91,7 +72,12 @@ class ChaosController < ActionController::Base
memory_mb.to_i
end
- def development?
- Rails.env.development?
+ def async
+ async = params[:async] || false
+ Gitlab::Utils.to_boolean(async)
+ end
+
+ def development_or_test?
+ Rails.env.development? || Rails.env.test?
end
end
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index dbddee47997..0176962cf0a 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -192,7 +192,8 @@ class GroupsController < Groups::ApplicationController
:chat_team_name,
:require_two_factor_authentication,
:two_factor_grace_period,
- :project_creation_level
+ :project_creation_level,
+ :subgroup_creation_level
]
end
diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb
index eeeebe430a7..af1e6cc703b 100644
--- a/app/controllers/ide_controller.rb
+++ b/app/controllers/ide_controller.rb
@@ -4,5 +4,6 @@ class IdeController < ApplicationController
layout 'fullscreen'
def index
+ Gitlab::UsageDataCounters::WebIdeCounter.increment_views_count
end
end
diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb
index 6314d9f2a9f..926592b9681 100644
--- a/app/controllers/projects/cycle_analytics/events_controller.rb
+++ b/app/controllers/projects/cycle_analytics/events_controller.rb
@@ -23,7 +23,7 @@ module Projects
end
def test
- options(events_params)[:branch] = events_params[:branch_name]
+ options(cycle_analytics_params)[:branch] = cycle_analytics_params[:branch_name]
render_events(cycle_analytics[:test].events)
end
@@ -50,13 +50,13 @@ module Projects
end
def cycle_analytics
- @cycle_analytics ||= ::CycleAnalytics::ProjectLevel.new(project, options: options(events_params))
+ @cycle_analytics ||= ::CycleAnalytics::ProjectLevel.new(project, options: options(cycle_analytics_params))
end
- def events_params
- return {} unless params[:events].present?
+ def cycle_analytics_params
+ return {} unless params[:cycle_analytics].present?
- params[:events].permit(:start_date, :branch_name)
+ params[:cycle_analytics].permit(:start_date, :branch_name)
end
end
end
diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb
index 32cefe54613..6ac5bb90706 100644
--- a/app/controllers/projects/merge_requests/creations_controller.rb
+++ b/app/controllers/projects/merge_requests/creations_controller.rb
@@ -23,6 +23,8 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
@merge_request = ::MergeRequests::CreateService.new(project, current_user, merge_request_params).execute
if @merge_request.valid?
+ incr_count_webide_merge_request
+
redirect_to(merge_request_path(@merge_request))
else
@source_project = @merge_request.source_project
@@ -135,4 +137,10 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42384')
end
+
+ def incr_count_webide_merge_request
+ return if params[:nav_source] != 'webide'
+
+ Gitlab::UsageDataCounters::WebIdeCounter.increment_merge_requests_count
+ end
end
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 255f1f3569a..59f948959d6 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -7,7 +7,8 @@ class Projects::SnippetsController < Projects::ApplicationController
include SnippetsActions
include RendersBlob
- skip_before_action :verify_authenticity_token, only: [:show], if: :js_request?
+ skip_before_action :verify_authenticity_token,
+ if: -> { action_name == 'show' && js_request? }
before_action :check_snippets_available!
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji, :mark_as_spam]
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index fa5bdbc7d49..b0998d7f3be 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -10,7 +10,8 @@ class Projects::WikisController < Projects::ApplicationController
before_action :authorize_admin_wiki!, only: :destroy
before_action :load_project_wiki
before_action :load_page, only: [:show, :edit, :update, :history, :destroy]
- before_action :valid_encoding?, only: [:show, :edit, :update], if: :load_page
+ before_action :valid_encoding?,
+ if: -> { %w[show edit update].include?(action_name) && load_page }
before_action only: [:edit, :update], unless: :valid_encoding? do
redirect_to(project_wiki_path(@project, @page))
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index feefc7f8137..37ffd28bf9e 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -18,9 +18,11 @@ class ProjectsController < Projects::ApplicationController
before_action :redirect_git_extension, only: [:show]
before_action :project, except: [:index, :new, :create, :resolve]
before_action :repository, except: [:index, :new, :create, :resolve]
- before_action :assign_ref_vars, only: [:show], if: :repo_exists?
- before_action :tree, only: [:show], if: [:repo_exists?, :project_view_files?]
- before_action :lfs_blob_ids, only: [:show], if: [:repo_exists?, :project_view_files?]
+ before_action :assign_ref_vars, if: -> { action_name == 'show' && repo_exists? }
+ before_action :tree,
+ if: -> { action_name == 'show' && repo_exists? && project_view_files? }
+ before_action :lfs_blob_ids,
+ if: -> { action_name == 'show' && repo_exists? && project_view_files? }
before_action :project_export_enabled, only: [:export, :download_export, :remove_export, :generate_new_export]
before_action :present_project, only: [:edit]
before_action :authorize_download_code!, only: [:refs]
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index b2b151bbcf0..638934694e0 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -8,8 +8,7 @@ class RegistrationsController < Devise::RegistrationsController
prepend_before_action :check_captcha, only: :create
before_action :whitelist_query_limiting, only: [:destroy]
before_action :ensure_terms_accepted,
- if: -> { Gitlab::CurrentSettings.current_application_settings.enforce_terms? },
- only: [:create]
+ if: -> { action_name == 'create' && Gitlab::CurrentSettings.current_application_settings.enforce_terms? }
def new
redirect_to(new_user_session_path)
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index a841859621e..7604b31467a 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -13,18 +13,17 @@ class SessionsController < Devise::SessionsController
prepend_before_action :check_initial_setup, only: [:new]
prepend_before_action :authenticate_with_two_factor,
- if: :two_factor_enabled?, only: [:create]
+ if: -> { action_name == 'create' && two_factor_enabled? }
prepend_before_action :check_captcha, only: [:create]
prepend_before_action :store_redirect_uri, only: [:new]
prepend_before_action :ldap_servers, only: [:new, :create]
prepend_before_action :require_no_authentication_without_flash, only: [:new, :create]
- prepend_before_action :ensure_password_authentication_enabled!, if: :password_based_login?, only: [:create]
+ prepend_before_action :ensure_password_authentication_enabled!, if: -> { action_name == 'create' && password_based_login? }
before_action :auto_sign_in_with_provider, only: [:new]
before_action :load_recaptcha
- after_action :log_failed_login, only: [:new], if: :failed_login?
-
+ after_action :log_failed_login, if: -> { action_name == 'new' && failed_login? }
helper_method :captcha_enabled?
CAPTCHA_HEADER = 'X-GitLab-Show-Login-Captcha'.freeze
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index fad036b8df8..869655e9550 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -8,7 +8,8 @@ class SnippetsController < ApplicationController
include RendersBlob
include PreviewMarkdown
- skip_before_action :verify_authenticity_token, only: [:show], if: :js_request?
+ skip_before_action :verify_authenticity_token,
+ if: -> { action_name == 'show' && js_request? }
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw]
diff --git a/app/finders/autocomplete/move_to_project_finder.rb b/app/finders/autocomplete/move_to_project_finder.rb
index edaf74c5f92..491cce2232e 100644
--- a/app/finders/autocomplete/move_to_project_finder.rb
+++ b/app/finders/autocomplete/move_to_project_finder.rb
@@ -3,7 +3,9 @@
module Autocomplete
# Finder that retrieves a list of projects that an issue can be moved to.
class MoveToProjectFinder
- attr_reader :current_user, :search, :project_id, :offset_id
+ attr_reader :current_user, :search, :project_id
+
+ LIMIT = 20
# current_user - The User object of the user that wants to view the list of
# projects.
@@ -14,13 +16,10 @@ module Autocomplete
#
# * search: An optional search query to apply to the list of projects.
# * project_id: The ID of a project to exclude from the returned relation.
- # * offset_id: The ID of a project to use for pagination. When given, only
- # projects with a lower ID are included in the list.
def initialize(current_user, params = {})
@current_user = current_user
@search = params[:search]
@project_id = params[:project_id]
- @offset_id = params[:offset_id]
end
def execute
@@ -28,8 +27,8 @@ module Autocomplete
.projects_where_can_admin_issues
.optionally_search(search)
.excluding_project(project_id)
- .paginate_in_descending_order_using_id(before: offset_id)
.eager_load_namespace_and_owner
+ .sorted_by_name_asc_limited(LIMIT)
end
end
end
diff --git a/app/graphql/types/tree/submodule_type.rb b/app/graphql/types/tree/submodule_type.rb
index 8cb1e04f5ba..2b47e5c0161 100644
--- a/app/graphql/types/tree/submodule_type.rb
+++ b/app/graphql/types/tree/submodule_type.rb
@@ -7,6 +7,9 @@ module Types
implements Types::Tree::EntryType
graphql_name 'Submodule'
+
+ field :web_url, type: GraphQL::STRING_TYPE, null: true
+ field :tree_url, type: GraphQL::STRING_TYPE, null: true
end
# rubocop: enable Graphql/AuthorizeTypes
end
diff --git a/app/graphql/types/tree/tree_type.rb b/app/graphql/types/tree/tree_type.rb
index fbdc1597461..99f2a6c0235 100644
--- a/app/graphql/types/tree/tree_type.rb
+++ b/app/graphql/types/tree/tree_type.rb
@@ -15,7 +15,9 @@ module Types
Gitlab::Graphql::Representation::TreeEntry.decorate(obj.trees, obj.repository)
end
- field :submodules, Types::Tree::SubmoduleType.connection_type, null: false, calls_gitaly: true
+ field :submodules, Types::Tree::SubmoduleType.connection_type, null: false, calls_gitaly: true, resolve: -> (obj, args, ctx) do
+ Gitlab::Graphql::Representation::SubmoduleTreeEntry.decorate(obj.submodules, obj)
+ end
field :blobs, Types::Tree::BlobType.connection_type, null: false, calls_gitaly: true, resolve: -> (obj, args, ctx) do
Gitlab::Graphql::Representation::TreeEntry.decorate(obj.blobs, obj.repository)
diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb
index 164c69ca50b..9a281065b90 100644
--- a/app/helpers/submodule_helper.rb
+++ b/app/helpers/submodule_helper.rb
@@ -9,6 +9,10 @@ module SubmoduleHelper
def submodule_links(submodule_item, ref = nil, repository = @repository)
url = repository.submodule_url_for(ref, submodule_item.path)
+ submodule_links_for_url(submodule_item.id, url, repository)
+ end
+
+ def submodule_links_for_url(submodule_item_id, url, repository)
if url == '.' || url == './'
url = File.join(Gitlab.config.gitlab.url, repository.project.full_path)
end
@@ -30,14 +34,14 @@ module SubmoduleHelper
project.sub!(/\.git\z/, '')
if self_url?(url, namespace, project)
- [namespace_project_path(namespace, project),
- namespace_project_tree_path(namespace, project, submodule_item.id)]
+ [url_helpers.namespace_project_path(namespace, project),
+ url_helpers.namespace_project_tree_path(namespace, project, submodule_item_id)]
elsif relative_self_url?(url)
- relative_self_links(url, submodule_item.id, repository.project)
+ relative_self_links(url, submodule_item_id, repository.project)
elsif github_dot_com_url?(url)
- standard_links('github.com', namespace, project, submodule_item.id)
+ standard_links('github.com', namespace, project, submodule_item_id)
elsif gitlab_dot_com_url?(url)
- standard_links('gitlab.com', namespace, project, submodule_item.id)
+ standard_links('gitlab.com', namespace, project, submodule_item_id)
else
[sanitize_submodule_url(url), nil]
end
@@ -95,8 +99,8 @@ module SubmoduleHelper
begin
[
- namespace_project_path(target_namespace_path, submodule_base),
- namespace_project_tree_path(target_namespace_path, submodule_base, commit)
+ url_helpers.namespace_project_path(target_namespace_path, submodule_base),
+ url_helpers.namespace_project_tree_path(target_namespace_path, submodule_base, commit)
]
rescue ActionController::UrlGenerationError
[nil, nil]
@@ -114,4 +118,8 @@ module SubmoduleHelper
rescue URI::InvalidURIError
nil
end
+
+ def url_helpers
+ Gitlab::Routing.url_helpers
+ end
end
diff --git a/app/models/active_session.rb b/app/models/active_session.rb
index f355b02c428..345767179eb 100644
--- a/app/models/active_session.rb
+++ b/app/models/active_session.rb
@@ -3,6 +3,8 @@
class ActiveSession
include ActiveModel::Model
+ SESSION_BATCH_SIZE = 200
+
attr_accessor :created_at, :updated_at,
:session_id, :ip_address,
:browser, :os, :device_name, :device_type,
@@ -106,10 +108,12 @@ class ActiveSession
Gitlab::Redis::SharedState.with do |redis|
session_keys = session_ids.map { |session_id| "#{Gitlab::Redis::SharedState::SESSION_NAMESPACE}:#{session_id}" }
- redis.mget(session_keys).compact.map do |raw_session|
- # rubocop:disable Security/MarshalLoad
- Marshal.load(raw_session)
- # rubocop:enable Security/MarshalLoad
+ session_keys.each_slice(SESSION_BATCH_SIZE).flat_map do |session_keys_batch|
+ redis.mget(session_keys_batch).compact.map do |raw_session|
+ # rubocop:disable Security/MarshalLoad
+ Marshal.load(raw_session)
+ # rubocop:enable Security/MarshalLoad
+ end
end
end
end
diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb
index 6ae8c3bd7f3..6533b7a186e 100644
--- a/app/models/clusters/applications/runner.rb
+++ b/app/models/clusters/applications/runner.rb
@@ -3,7 +3,7 @@
module Clusters
module Applications
class Runner < ApplicationRecord
- VERSION = '0.6.0'.freeze
+ VERSION = '0.7.0'.freeze
self.table_name = 'clusters_applications_runners'
diff --git a/app/models/concerns/deployment_platform.rb b/app/models/concerns/deployment_platform.rb
index 8f28c897eb6..e1a8725e728 100644
--- a/app/models/concerns/deployment_platform.rb
+++ b/app/models/concerns/deployment_platform.rb
@@ -12,19 +12,10 @@ module DeploymentPlatform
private
def find_deployment_platform(environment)
- find_platform_kubernetes(environment) ||
+ find_platform_kubernetes_with_cte(environment) ||
find_instance_cluster_platform_kubernetes(environment: environment)
end
- def find_platform_kubernetes(environment)
- if Feature.enabled?(:clusters_cte)
- find_platform_kubernetes_with_cte(environment)
- else
- find_cluster_platform_kubernetes(environment: environment) ||
- find_group_cluster_platform_kubernetes(environment: environment)
- end
- end
-
# EE would override this and utilize environment argument
def find_platform_kubernetes_with_cte(_environment)
Clusters::ClustersHierarchy.new(self).base_and_ancestors
@@ -33,18 +24,6 @@ module DeploymentPlatform
end
# EE would override this and utilize environment argument
- def find_cluster_platform_kubernetes(environment: nil)
- clusters.enabled.default_environment
- .last&.platform_kubernetes
- end
-
- # EE would override this and utilize environment argument
- def find_group_cluster_platform_kubernetes(environment: nil)
- Clusters::Cluster.enabled.default_environment.ancestor_clusters_for_clusterable(self)
- .first&.platform_kubernetes
- end
-
- # EE would override this and utilize environment argument
def find_instance_cluster_platform_kubernetes(environment: nil)
Clusters::Instance.new.clusters.enabled.default_environment
.first&.platform_kubernetes
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 952de92cae1..e60b6497cb7 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -427,4 +427,11 @@ module Issuable
def wipless_title_changed(old_title)
old_title != title
end
+
+ ##
+ # Overridden on EE module
+ #
+ def supports_milestone?
+ respond_to?(:milestone_id)
+ end
end
diff --git a/app/models/concerns/project_api_compatibility.rb b/app/models/concerns/project_api_compatibility.rb
index cb00efb06df..631b2a11e9a 100644
--- a/app/models/concerns/project_api_compatibility.rb
+++ b/app/models/concerns/project_api_compatibility.rb
@@ -9,12 +9,10 @@ module ProjectAPICompatibility
end
def auto_devops_enabled=(value)
- self.build_auto_devops if self.auto_devops&.enabled.nil?
- self.auto_devops.update! enabled: value
+ (auto_devops || build_auto_devops).enabled = value
end
def auto_devops_deploy_strategy=(value)
- self.build_auto_devops if self.auto_devops&.enabled.nil?
- self.auto_devops.update! deploy_strategy: value
+ (auto_devops || build_auto_devops).deploy_strategy = value
end
end
diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb
index b42adad94ba..8b536a123fc 100644
--- a/app/models/concerns/taskable.rb
+++ b/app/models/concerns/taskable.rb
@@ -15,7 +15,8 @@ module Taskable
INCOMPLETE_PATTERN = /(\[[\s]\])/.freeze
ITEM_PATTERN = %r{
^
- \s*(?:[-+*]|(?:\d+\.)) # list prefix required - task item has to be always in a list
+ (?:(?:>\s{0,4})*) # optional blockquote characters
+ \s*(?:[-+*]|(?:\d+\.)) # list prefix required - task item has to be always in a list
\s+ # whitespace prefix has to be always presented for a list item
(\[\s\]|\[[xX]\]) # checkbox
(\s.+) # followed by whitespace and some text.
diff --git a/app/models/cycle_analytics/group_level.rb b/app/models/cycle_analytics/group_level.rb
new file mode 100644
index 00000000000..508cde0ca00
--- /dev/null
+++ b/app/models/cycle_analytics/group_level.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module CycleAnalytics
+ class GroupLevel
+ include LevelBase
+ attr_reader :options, :group
+
+ def initialize(group:, options:)
+ @group = group
+ @options = options.merge(group: group)
+ end
+
+ def summary
+ @summary ||= ::Gitlab::CycleAnalytics::GroupStageSummary.new(group,
+ from: options[:from],
+ current_user: options[:current_user]).data
+ end
+
+ def permissions(*)
+ STAGES.each_with_object({}) do |stage, obj|
+ obj[stage] = true
+ end
+ end
+
+ def stats
+ @stats ||= STAGES.map do |stage_name|
+ self[stage_name].as_json(serializer: GroupAnalyticsStageSerializer)
+ end
+ end
+ end
+end
diff --git a/app/models/cycle_analytics/base.rb b/app/models/cycle_analytics/level_base.rb
index d7b28cd1b67..543349ebf8f 100644
--- a/app/models/cycle_analytics/base.rb
+++ b/app/models/cycle_analytics/level_base.rb
@@ -1,12 +1,12 @@
# frozen_string_literal: true
module CycleAnalytics
- class Base
+ module LevelBase
STAGES = %i[issue plan code test review staging production].freeze
def all_medians_by_stage
STAGES.each_with_object({}) do |stage_name, medians_per_stage|
- medians_per_stage[stage_name] = self[stage_name].median
+ medians_per_stage[stage_name] = self[stage_name].project_median
end
end
@@ -21,7 +21,7 @@ module CycleAnalytics
end
def [](stage_name)
- Gitlab::CycleAnalytics::Stage[stage_name].new(project: @project, options: @options)
+ Gitlab::CycleAnalytics::Stage[stage_name].new(options: options)
end
end
end
diff --git a/app/models/cycle_analytics/project_level.rb b/app/models/cycle_analytics/project_level.rb
index 22631cc7d41..4aa426c58a1 100644
--- a/app/models/cycle_analytics/project_level.rb
+++ b/app/models/cycle_analytics/project_level.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
module CycleAnalytics
- class ProjectLevel < Base
+ class ProjectLevel
+ include LevelBase
attr_reader :project, :options
def initialize(project, options:)
@project = project
- @options = options
+ @options = options.merge(project: project)
end
def summary
diff --git a/app/models/dashboard_group_milestone.rb b/app/models/dashboard_group_milestone.rb
index 74aa04ab7d0..ec52f1ed370 100644
--- a/app/models/dashboard_group_milestone.rb
+++ b/app/models/dashboard_group_milestone.rb
@@ -15,8 +15,7 @@ class DashboardGroupMilestone < GlobalMilestone
milestones = Milestone.of_groups(groups.select(:id))
.reorder_by_due_date_asc
.order_by_name_asc
- .active
milestones = milestones.search_title(params[:search_title]) if params[:search_title].present?
- milestones.map { |m| new(m) }
+ Milestone.filter_by_state(milestones, params[:state]).map { |m| new(m) }
end
end
diff --git a/app/models/environment.rb b/app/models/environment.rb
index b8ee54c1696..392481ea0cc 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -4,11 +4,6 @@ class Environment < ApplicationRecord
include Gitlab::Utils::StrongMemoize
include ReactiveCaching
- # Used to generate random suffixes for the slug
- LETTERS = ('a'..'z').freeze
- NUMBERS = ('0'..'9').freeze
- SUFFIX_CHARS = LETTERS.to_a + NUMBERS.to_a
-
belongs_to :project, required: true
has_many :deployments, -> { success }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
@@ -203,40 +198,6 @@ class Environment < ApplicationRecord
super.presence || generate_slug
end
- # An environment name is not necessarily suitable for use in URLs, DNS
- # or other third-party contexts, so provide a slugified version. A slug has
- # the following properties:
- # * contains only lowercase letters (a-z), numbers (0-9), and '-'
- # * begins with a letter
- # * has a maximum length of 24 bytes (OpenShift limitation)
- # * cannot end with `-`
- def generate_slug
- # Lowercase letters and numbers only
- slugified = +name.to_s.downcase.gsub(/[^a-z0-9]/, '-')
-
- # Must start with a letter
- slugified = 'env-' + slugified unless LETTERS.cover?(slugified[0])
-
- # Repeated dashes are invalid (OpenShift limitation)
- slugified.gsub!(/\-+/, '-')
-
- # Maximum length: 24 characters (OpenShift limitation)
- slugified = slugified[0..23]
-
- # Cannot end with a dash (Kubernetes label limitation)
- slugified.chop! if slugified.end_with?('-')
-
- # Add a random suffix, shortening the current string if necessary, if it
- # has been slugified. This ensures uniqueness.
- if slugified != name
- slugified = slugified[0..16]
- slugified << '-' unless slugified.end_with?('-')
- slugified << random_suffix
- end
-
- self.slug = slugified
- end
-
def external_url_for(path, commit_sha)
return unless self.external_url
@@ -274,11 +235,7 @@ class Environment < ApplicationRecord
private
- # Slugifying a name may remove the uniqueness guarantee afforded by it being
- # based on name (which must be unique). To compensate, we add a random
- # 6-byte suffix in those circumstances. This is not *guaranteed* uniqueness,
- # but the chance of collisions is vanishingly small
- def random_suffix
- (0..5).map { SUFFIX_CHARS.sample }.join
+ def generate_slug
+ self.slug = Gitlab::Slug::Environment.new(name).generate
end
end
diff --git a/app/models/group.rb b/app/models/group.rb
index 9520db1bc0a..37f30552b39 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -416,6 +416,10 @@ class Group < Namespace
super || ::Gitlab::CurrentSettings.default_project_creation
end
+ def subgroup_creation_level
+ super || ::Gitlab::Access::OWNER_SUBGROUP_ACCESS
+ end
+
private
def update_two_factor_requirement
diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb
index e6e491634ab..27c122d3559 100644
--- a/app/models/pages_domain.rb
+++ b/app/models/pages_domain.rb
@@ -22,7 +22,7 @@ class PagesDomain < ApplicationRecord
validate :validate_pages_domain
validate :validate_matching_key, if: ->(domain) { domain.certificate.present? || domain.key.present? }
- validate :validate_intermediates, if: ->(domain) { domain.certificate.present? }
+ validate :validate_intermediates, if: ->(domain) { domain.certificate.present? && domain.certificate_changed? }
attr_encrypted :key,
mode: :per_attribute_iv_and_salt,
diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb
index f69f0e2dccb..7ae431eaad7 100644
--- a/app/models/personal_access_token.rb
+++ b/app/models/personal_access_token.rb
@@ -7,6 +7,7 @@ class PersonalAccessToken < ApplicationRecord
add_authentication_token_field :token, digest: true
REDIS_EXPIRY_TIME = 3.minutes
+ TOKEN_LENGTH = 20
serialize :scopes, Array # rubocop:disable Cop/ActiveRecordSerialize
diff --git a/app/models/project.rb b/app/models/project.rb
index 2906aca75fc..ece7507e55c 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -277,7 +277,7 @@ class Project < ApplicationRecord
has_many :project_deploy_tokens
has_many :deploy_tokens, through: :project_deploy_tokens
- has_one :auto_devops, class_name: 'ProjectAutoDevops'
+ has_one :auto_devops, class_name: 'ProjectAutoDevops', inverse_of: :project, autosave: true
has_many :custom_attributes, class_name: 'ProjectCustomAttribute'
has_many :project_badges, class_name: 'ProjectBadge'
@@ -357,6 +357,7 @@ class Project < ApplicationRecord
scope :sorted_by_activity, -> { reorder(Arel.sql("GREATEST(COALESCE(last_activity_at, '1970-01-01'), COALESCE(last_repository_updated_at, '1970-01-01')) DESC")) }
scope :sorted_by_stars_desc, -> { reorder(star_count: :desc) }
scope :sorted_by_stars_asc, -> { reorder(star_count: :asc) }
+ scope :sorted_by_name_asc_limited, ->(limit) { reorder(name: :asc).limit(limit) }
scope :in_namespace, ->(namespace_ids) { where(namespace_id: namespace_ids) }
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
@@ -441,22 +442,6 @@ class Project < ApplicationRecord
without_deleted.find_by_id(id)
end
- # Paginates a collection using a `WHERE id < ?` condition.
- #
- # before - A project ID to use for filtering out projects with an equal or
- # greater ID. If no ID is given, all projects are included.
- #
- # limit - The maximum number of rows to include.
- def self.paginate_in_descending_order_using_id(
- before: nil,
- limit: Kaminari.config.default_per_page
- )
- relation = order_id_desc.limit(limit)
- relation = relation.where('projects.id < ?', before) if before
-
- relation
- end
-
def self.eager_load_namespace_and_owner
includes(namespace: :owner)
end
diff --git a/app/models/project_auto_devops.rb b/app/models/project_auto_devops.rb
index 67c12363a3c..f39f54f0434 100644
--- a/app/models/project_auto_devops.rb
+++ b/app/models/project_auto_devops.rb
@@ -5,7 +5,7 @@ class ProjectAutoDevops < ApplicationRecord
ignore_column :domain
- belongs_to :project
+ belongs_to :project, inverse_of: :auto_devops
enum deploy_strategy: {
continuous: 0,
diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb
index 7ff06655de0..78e82955342 100644
--- a/app/models/project_feature.rb
+++ b/app/models/project_feature.rb
@@ -86,6 +86,8 @@ 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
+ default_value_for(:pages_access_level, allows_nil: false) { |feature| feature.project&.public? ? ENABLED : PRIVATE }
+
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/jira_service.rb b/app/models/project_services/jira_service.rb
index e571700fd02..7ab79242cc3 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -54,7 +54,7 @@ class JiraService < IssueTrackerService
username: self.username,
password: self.password,
site: URI.join(url, '/').to_s, # Intended to find the root
- context_path: url.path.chomp('/'),
+ context_path: url.path,
auth_type: :basic,
read_timeout: 120,
use_cookies: true,
@@ -103,6 +103,12 @@ class JiraService < IssueTrackerService
"#{url}/secure/CreateIssue.jspa"
end
+ alias_method :original_url, :url
+
+ def url
+ original_url&.chomp('/')
+ end
+
def execute(push)
# This method is a no-op, because currently JiraService does not
# support any events.
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index 9219283992f..0add8bfad31 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -38,6 +38,10 @@ class GroupPolicy < BasePolicy
@subject.project_creation_level == ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS
end
+ condition(:maintainer_can_create_group) do
+ @subject.subgroup_creation_level == ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS
+ end
+
rule { public_group }.policy do
enable :read_group
enable :read_list
@@ -105,6 +109,7 @@ class GroupPolicy < BasePolicy
end
rule { owner & nested_groups_supported }.enable :create_subgroup
+ rule { maintainer & maintainer_can_create_group & nested_groups_supported }.enable :create_subgroup
rule { public_group | logged_in_viewable }.enable :view_globally
diff --git a/app/serializers/analytics_issue_entity.rb b/app/serializers/analytics_issue_entity.rb
index ab15bd0ac7a..29d4a6ae1d0 100644
--- a/app/serializers/analytics_issue_entity.rb
+++ b/app/serializers/analytics_issue_entity.rb
@@ -20,12 +20,12 @@ class AnalyticsIssueEntity < Grape::Entity
end
expose :url do |object|
- url_to(:namespace_project_issue, id: object[:iid].to_s)
+ url_to(:namespace_project_issue, object)
end
private
- def url_to(route, id)
- public_send("#{route}_url", request.project.namespace, request.project, id) # rubocop:disable GitlabSecurity/PublicSend
+ def url_to(route, object)
+ public_send("#{route}_url", object[:path], object[:name], object[:iid].to_s) # rubocop:disable GitlabSecurity/PublicSend
end
end
diff --git a/app/serializers/analytics_merge_request_entity.rb b/app/serializers/analytics_merge_request_entity.rb
index b7134da9461..21d7eeb81b0 100644
--- a/app/serializers/analytics_merge_request_entity.rb
+++ b/app/serializers/analytics_merge_request_entity.rb
@@ -4,6 +4,6 @@ class AnalyticsMergeRequestEntity < AnalyticsIssueEntity
expose :state
expose :url do |object|
- url_to(:namespace_project_merge_request, id: object[:iid].to_s)
+ url_to(:namespace_project_merge_request, object)
end
end
diff --git a/app/serializers/analytics_stage_entity.rb b/app/serializers/analytics_stage_entity.rb
index 8bc6da5aeeb..eb38b90fb18 100644
--- a/app/serializers/analytics_stage_entity.rb
+++ b/app/serializers/analytics_stage_entity.rb
@@ -8,9 +8,9 @@ class AnalyticsStageEntity < Grape::Entity
expose :legend
expose :description
- expose :median, as: :value do |stage|
+ expose :project_median, as: :value do |stage|
# median returns a BatchLoader instance which we first have to unwrap by using to_f
# we use to_f to make sure results below 1 are presented to the end-user
- stage.median.to_f.nonzero? ? distance_of_time_in_words(stage.median) : nil
+ stage.project_median.to_f.nonzero? ? distance_of_time_in_words(stage.project_median) : nil
end
end
diff --git a/app/serializers/diff_file_base_entity.rb b/app/serializers/diff_file_base_entity.rb
index d8630165e49..ee68b4b98e0 100644
--- a/app/serializers/diff_file_base_entity.rb
+++ b/app/serializers/diff_file_base_entity.rb
@@ -3,7 +3,6 @@
class DiffFileBaseEntity < Grape::Entity
include RequestAwareEntity
include BlobHelper
- include SubmoduleHelper
include DiffHelper
include TreeHelper
include ChecksCollaboration
@@ -12,12 +11,12 @@ class DiffFileBaseEntity < Grape::Entity
expose :content_sha
expose :submodule?, as: :submodule
- expose :submodule_link do |diff_file|
- memoized_submodule_links(diff_file).first
+ expose :submodule_link do |diff_file, options|
+ memoized_submodule_links(diff_file, options).first
end
expose :submodule_tree_url do |diff_file|
- memoized_submodule_links(diff_file).last
+ memoized_submodule_links(diff_file, options).last
end
expose :edit_path, if: -> (_, options) { options[:merge_request] } do |diff_file|
@@ -92,10 +91,10 @@ class DiffFileBaseEntity < Grape::Entity
private
- def memoized_submodule_links(diff_file)
+ def memoized_submodule_links(diff_file, options)
strong_memoize(:submodule_links) do
if diff_file.submodule?
- submodule_links(diff_file.blob, diff_file.content_sha, diff_file.repository)
+ options[:submodule_links].for(diff_file.blob, diff_file.content_sha)
else
[]
end
diff --git a/app/serializers/diffs_entity.rb b/app/serializers/diffs_entity.rb
index b51e4a7e6d0..1763fe5b6ab 100644
--- a/app/serializers/diffs_entity.rb
+++ b/app/serializers/diffs_entity.rb
@@ -64,7 +64,10 @@ class DiffsEntity < Grape::Entity
merge_request_path(merge_request, format: :diff)
end
- expose :diff_files, using: DiffFileEntity
+ expose :diff_files do |diffs, options|
+ submodule_links = Gitlab::SubmoduleLinks.new(merge_request.project.repository)
+ DiffFileEntity.represent(diffs.diff_files, options.merge(submodule_links: submodule_links))
+ end
expose :merge_request_diffs, using: MergeRequestDiffEntity, if: -> (_, options) { options[:merge_request_diffs]&.any? } do |diffs|
options[:merge_request_diffs]
diff --git a/app/serializers/discussion_serializer.rb b/app/serializers/discussion_serializer.rb
index 5be40e74175..8bb7e93c033 100644
--- a/app/serializers/discussion_serializer.rb
+++ b/app/serializers/discussion_serializer.rb
@@ -2,4 +2,18 @@
class DiscussionSerializer < BaseSerializer
entity DiscussionEntity
+
+ def represent(resource, opts = {}, entity_class = nil)
+ super(resource, with_additional_opts(opts), entity_class)
+ end
+
+ private
+
+ def with_additional_opts(opts)
+ additional_opts = {
+ submodule_links: Gitlab::SubmoduleLinks.new(@request.project.repository)
+ }
+
+ opts.merge(additional_opts)
+ end
end
diff --git a/app/serializers/group_analytics_stage_entity.rb b/app/serializers/group_analytics_stage_entity.rb
new file mode 100644
index 00000000000..81be20e7dd8
--- /dev/null
+++ b/app/serializers/group_analytics_stage_entity.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class GroupAnalyticsStageEntity < Grape::Entity
+ include EntityDateHelper
+
+ expose :title
+ expose :name
+ expose :legend
+ expose :description
+
+ expose :group_median, as: :value do |stage|
+ # group_median returns a BatchLoader instance which we first have to unwrap by using to_f
+ # we use to_f to make sure results below 1 are presented to the end-user
+ stage.group_median.to_f.nonzero? ? distance_of_time_in_words(stage.group_median) : nil
+ end
+end
diff --git a/app/serializers/group_analytics_stage_serializer.rb b/app/serializers/group_analytics_stage_serializer.rb
new file mode 100644
index 00000000000..ec448dea602
--- /dev/null
+++ b/app/serializers/group_analytics_stage_serializer.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class GroupAnalyticsStageSerializer < BaseSerializer
+ entity GroupAnalyticsStageEntity
+end
diff --git a/app/serializers/submodule_entity.rb b/app/serializers/submodule_entity.rb
deleted file mode 100644
index e475a4f301f..00000000000
--- a/app/serializers/submodule_entity.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# frozen_string_literal: true
-
-class SubmoduleEntity < Grape::Entity
- include RequestAwareEntity
-
- expose :id, :path, :name, :mode
-
- expose :icon do |blob|
- 'archive'
- end
-
- expose :url do |blob|
- submodule_links(blob, request).first
- end
-
- expose :tree_url do |blob|
- submodule_links(blob, request).last
- end
-
- private
-
- def submodule_links(blob, request)
- @submodule_links ||= SubmoduleHelper.submodule_links(blob, request.ref, request.repository)
- end
-end
diff --git a/app/services/commits/create_service.rb b/app/services/commits/create_service.rb
index bb34a3d3352..f3be68f9602 100644
--- a/app/services/commits/create_service.rb
+++ b/app/services/commits/create_service.rb
@@ -10,6 +10,7 @@ module Commits
@start_project = params[:start_project] || @project
@start_branch = params[:start_branch]
+ @start_sha = params[:start_sha]
@branch_name = params[:branch_name]
@force = params[:force] || false
end
@@ -40,7 +41,7 @@ module Commits
end
def different_branch?
- @start_branch != @branch_name || @start_project != @project
+ @start_project != @project || @start_branch != @branch_name || @start_sha.present?
end
def force?
@@ -49,6 +50,7 @@ module Commits
def validate!
validate_permissions!
+ validate_start_sha!
validate_on_branch!
validate_branch_existence!
@@ -63,7 +65,21 @@ module Commits
end
end
+ def validate_start_sha!
+ return unless @start_sha
+
+ if @start_branch
+ raise_error("You can't pass both start_branch and start_sha")
+ elsif !Gitlab::Git.commit_id?(@start_sha)
+ raise_error("Invalid start_sha '#{@start_sha}'")
+ elsif !@start_project.repository.commit(@start_sha)
+ raise_error("Cannot find start_sha '#{@start_sha}'")
+ end
+ end
+
def validate_on_branch!
+ return unless @start_branch
+
if !@start_project.empty_repo? && !@start_project.repository.branch_exists?(@start_branch)
raise_error('You can only create or edit files when you are on a branch')
end
diff --git a/app/services/files/multi_service.rb b/app/services/files/multi_service.rb
index d8c4e5bc5e8..65af4dd5a28 100644
--- a/app/services/files/multi_service.rb
+++ b/app/services/files/multi_service.rb
@@ -47,6 +47,7 @@ module Files
author_name: @author_name,
start_project: @start_project,
start_branch_name: @start_branch,
+ start_sha: @start_sha,
force: force?
)
rescue ArgumentError => e
diff --git a/app/services/issuable/clone/attributes_rewriter.rb b/app/services/issuable/clone/attributes_rewriter.rb
index 0300cc0d8d3..3c061d35558 100644
--- a/app/services/issuable/clone/attributes_rewriter.rb
+++ b/app/services/issuable/clone/attributes_rewriter.rb
@@ -17,6 +17,8 @@ module Issuable
private
def cloneable_milestone
+ return unless new_entity.supports_milestone?
+
title = original_entity.milestone&.title
return unless title
diff --git a/app/services/merge_requests/push_options_handler_service.rb b/app/services/merge_requests/push_options_handler_service.rb
index a24163331e8..6d70b5106c7 100644
--- a/app/services/merge_requests/push_options_handler_service.rb
+++ b/app/services/merge_requests/push_options_handler_service.rb
@@ -117,14 +117,8 @@ module MergeRequests
collect_errors_from_merge_request(merge_request) unless merge_request.valid?
end
- def create_params(branch)
- params = {
- assignees: [current_user],
- source_branch: branch,
- source_project: project,
- target_branch: push_options[:target] || target_project.default_branch,
- target_project: target_project
- }
+ def base_params
+ params = {}
if push_options.key?(:merge_when_pipeline_succeeds)
params.merge!(
@@ -133,17 +127,8 @@ module MergeRequests
)
end
- params
- end
-
- def update_params
- params = {}
-
- if push_options.key?(:merge_when_pipeline_succeeds)
- params.merge!(
- merge_when_pipeline_succeeds: push_options[:merge_when_pipeline_succeeds],
- merge_user: current_user
- )
+ if push_options.key?(:remove_source_branch)
+ params[:force_remove_source_branch] = push_options[:remove_source_branch]
end
if push_options.key?(:target)
@@ -153,6 +138,25 @@ module MergeRequests
params
end
+ def create_params(branch)
+ params = base_params
+
+ params.merge!(
+ assignees: [current_user],
+ source_branch: branch,
+ source_project: project,
+ target_project: target_project
+ )
+
+ params[:target_branch] ||= target_project.default_branch
+
+ params
+ end
+
+ def update_params
+ base_params
+ end
+
def collect_errors_from_merge_request(merge_request)
merge_request.errors.full_messages.each do |error|
errors << error
diff --git a/app/services/wiki_pages/base_service.rb b/app/services/wiki_pages/base_service.rb
index e259f5bd1bc..b9df690c2b7 100644
--- a/app/services/wiki_pages/base_service.rb
+++ b/app/services/wiki_pages/base_service.rb
@@ -8,6 +8,12 @@ module WikiPages
page_data = Gitlab::DataBuilder::WikiPage.build(page, current_user, action)
@project.execute_hooks(page_data, :wiki_page_hooks)
@project.execute_services(page_data, :wiki_page_hooks)
+ increment_usage(action)
+ end
+
+ # This method throws an error if the action is an unanticipated value.
+ def increment_usage(action)
+ Gitlab::UsageDataCounters::WikiPageCounter.count(action)
end
end
end
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 98230684d56..f9cc118a252 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -40,6 +40,11 @@
%strong
= @group.created_at.to_s(:medium)
+ %li
+ %span.light= _('ID:')
+ %strong
+ = @group.id
+
= render_if_exists 'admin/namespace_plan_info', namespace: @group
%li
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index 0fae8060b32..3eff0a221d7 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -55,6 +55,11 @@
= @project.created_at.to_s(:medium)
%li
+ %span.light ID:
+ %strong
+ = @project.id
+
+ %li
%span.light http:
%strong
= link_to @project.http_url_to_repo, project_path(@project)
diff --git a/app/views/admin/requests_profiles/index.html.haml b/app/views/admin/requests_profiles/index.html.haml
index adfc67d66d0..86bfeef580c 100644
--- a/app/views/admin/requests_profiles/index.html.haml
+++ b/app/views/admin/requests_profiles/index.html.haml
@@ -19,7 +19,8 @@
%ul.content-list
- profiles.each do |profile|
%li
- = link_to profile.time.to_s(:long), admin_requests_profile_path(profile)
+ = link_to profile.time.to_s(:long) + ' ' + profile.profile_mode.capitalize,
+ admin_requests_profile_path(profile)
- else
%p
No profiles found
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index c3d5ce0fe70..6fc7ec1bb6f 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -52,7 +52,7 @@
= icon("search", class: "search-icon")
= button_tag s_('AdminUsers|Search users') if Rails.env.test?
.dropdown.user-sort-dropdown
- - toggle_text = if @sort.present? then users_sort_options_hash[@sort] else sort_title_name end
+ - toggle_text = @sort.present? ? users_sort_options_hash[@sort] : sort_title_name
= dropdown_toggle(toggle_text, { toggle: 'dropdown' })
%ul.dropdown-menu.dropdown-menu-right
%li.dropdown-header
diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml
index 09ea7716a47..fee87c6324c 100644
--- a/app/views/devise/passwords/edit.html.haml
+++ b/app/views/devise/passwords/edit.html.haml
@@ -7,12 +7,12 @@
= f.hidden_field :reset_password_token
.form-group
= f.label 'New password', for: "user_password"
- = f.password_field :password, class: "form-control top qa-password-field", required: true, title: 'This field is required'
+ = f.password_field :password, class: "form-control top", required: true, title: 'This field is required', data: { qa_selector: 'password_field'}
.form-group
= f.label 'Confirm new password', for: "user_password_confirmation"
- = f.password_field :password_confirmation, class: "form-control bottom qa-password-confirmation", title: 'This field is required', required: true
+ = f.password_field :password_confirmation, class: "form-control bottom", title: 'This field is required', data: { qa_selector: 'password_confirmation_field' }, required: true
.clearfix
- = f.submit "Change your password", class: "btn btn-primary qa-change-password-button"
+ = f.submit "Change your password", class: "btn btn-primary", data: { qa_selector: 'change_password_button' }
.clearfix.prepend-top-20
%p
diff --git a/app/views/devise/sessions/_new_ldap.html.haml b/app/views/devise/sessions/_new_ldap.html.haml
index f856773526d..31c4bb0e33e 100644
--- a/app/views/devise/sessions/_new_ldap.html.haml
+++ b/app/views/devise/sessions/_new_ldap.html.haml
@@ -3,13 +3,13 @@
= form_tag(omniauth_callback_path(:user, server['provider_name']), id: 'new_ldap_user', class: "gl-show-field-errors") do
.form-group
= label_tag :username, "#{server['label']} Username"
- = text_field_tag :username, nil, { class: "form-control top qa-username-field", title: "This field is required.", autofocus: "autofocus", required: true }
+ = text_field_tag :username, nil, { class: "form-control top", title: "This field is required.", autofocus: "autofocus", data: { qa_selector: 'username_field' }, required: true }
.form-group
= label_tag :password
- = password_field_tag :password, nil, { class: "form-control bottom qa-password-field", title: "This field is required.", required: true }
+ = password_field_tag :password, nil, { class: "form-control bottom", title: "This field is required.", data: { qa_selector: 'password_field' }, required: true }
- if devise_mapping.rememberable?
.remember-me
%label{ for: "remember_me" }
= check_box_tag :remember_me, '1', false, id: 'remember_me'
%span Remember me
- = submit_tag "Sign in", class: "btn-success btn qa-sign-in-button"
+ = submit_tag "Sign in", class: "btn-success btn", data: { qa_selector: 'sign_in_button' }
diff --git a/app/views/devise/shared/_tabs_ldap.html.haml b/app/views/devise/shared/_tabs_ldap.html.haml
index b1a9470cf1c..db54c166a53 100644
--- a/app/views/devise/shared/_tabs_ldap.html.haml
+++ b/app/views/devise/shared/_tabs_ldap.html.haml
@@ -5,13 +5,13 @@
= render_if_exists "devise/shared/kerberos_tab"
- @ldap_servers.each_with_index do |server, i|
%li.nav-item
- = link_to server['label'], "##{server['provider_name']}", class: "nav-link #{active_when(i.zero? && form_based_auth_provider_has_active_class?(:ldapmain))} qa-ldap-tab", 'data-toggle' => 'tab'
+ = link_to server['label'], "##{server['provider_name']}", class: "nav-link #{active_when(i.zero? && form_based_auth_provider_has_active_class?(:ldapmain))}", data: { toggle: 'tab', qa_selector: 'ldap_tab' }
= render_if_exists 'devise/shared/tab_smartcard'
- if password_authentication_enabled_for_web?
%li.nav-item
- = link_to 'Standard', '#login-pane', class: 'nav-link qa-standard-tab', 'data-toggle' => 'tab'
+ = link_to 'Standard', '#login-pane', class: 'nav-link', data: { toggle: 'tab', qa_selector: 'standard_tab' }
- if allow_signup?
%li.nav-item
- = link_to 'Register', '#register-pane', class: 'nav-link qa-register-tab', 'data-toggle' => 'tab'
+ = link_to 'Register', '#register-pane', class: 'nav-link', data: { toggle: 'tab', qa_selector: 'register_tab' }
diff --git a/app/views/devise/shared/_tabs_normal.html.haml b/app/views/devise/shared/_tabs_normal.html.haml
index ab8c22532fd..b6a1b8805ee 100644
--- a/app/views/devise/shared/_tabs_normal.html.haml
+++ b/app/views/devise/shared/_tabs_normal.html.haml
@@ -1,6 +1,6 @@
%ul.nav-links.new-session-tabs.nav-tabs.nav{ role: 'tablist' }
%li.nav-item{ role: 'presentation' }
- %a.nav-link.qa-sign-in-tab.active{ href: '#login-pane', data: { toggle: 'tab' }, role: 'tab' } Sign in
+ %a.nav-link.active{ href: '#login-pane', data: { toggle: 'tab', qa_selector: 'sign_in_tab' }, role: 'tab' } Sign in
- if allow_signup?
%li.nav-item{ role: 'presentation' }
- %a.nav-link.qa-register-tab{ href: '#register-pane', data: { track_label: 'sign_in_register', track_property: '', track_event: 'click_button', track_value: '', toggle: 'tab' }, role: 'tab' } Register
+ %a.nav-link{ href: '#register-pane', data: { track_label: 'sign_in_register', track_property: '', track_event: 'click_button', track_value: '', toggle: 'tab', qa_selector: 'register_tab' }, role: 'tab' } Register
diff --git a/app/views/groups/_group_admin_settings.html.haml b/app/views/groups/_group_admin_settings.html.haml
index b8f632d11d3..733cb36cc3d 100644
--- a/app/views/groups/_group_admin_settings.html.haml
+++ b/app/views/groups/_group_admin_settings.html.haml
@@ -17,6 +17,12 @@
= f.select :project_creation_level, options_for_select(::Gitlab::Access.project_creation_options, @group.project_creation_level), {}, class: 'form-control'
.form-group.row
+ .col-sm-2.col-form-label
+ = f.label s_('SubgroupCreationlevel|Allowed to create subgroups')
+ .col-sm-10
+ = f.select :subgroup_creation_level, options_for_select(::Gitlab::Access.subgroup_creation_options, @group.subgroup_creation_level), {}, class: 'form-control'
+
+.form-group.row
.col-sm-2.col-form-label.pt-0
= f.label :require_two_factor_authentication, 'Two-factor authentication'
.col-sm-10
diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml
index 0da1f1ba7f5..d3375e00bad 100644
--- a/app/views/groups/settings/_permissions.html.haml
+++ b/app/views/groups/settings/_permissions.html.haml
@@ -20,6 +20,7 @@
= render_if_exists 'groups/settings/ip_restriction', f: f, group: @group
= render 'groups/settings/lfs', f: f
= render 'groups/settings/project_creation_level', f: f, group: @group
+ = render 'groups/settings/subgroup_creation_level', f: f, group: @group
= render 'groups/settings/two_factor_auth', f: f
= render_if_exists 'groups/member_lock_setting', f: f, group: @group
diff --git a/app/views/groups/settings/_subgroup_creation_level.html.haml b/app/views/groups/settings/_subgroup_creation_level.html.haml
new file mode 100644
index 00000000000..f36ad192bad
--- /dev/null
+++ b/app/views/groups/settings/_subgroup_creation_level.html.haml
@@ -0,0 +1,3 @@
+.form-group
+ = f.label s_('SubgroupCreationLevel|Allowed to create subgroups'), class: 'label-bold'
+ = f.select :subgroup_creation_level, options_for_select(::Gitlab::Access.subgroup_creation_options, group.subgroup_creation_level), {}, class: 'form-control'
diff --git a/app/views/layouts/header/_current_user_dropdown.html.haml b/app/views/layouts/header/_current_user_dropdown.html.haml
index 45fc39dbbdb..808290afcad 100644
--- a/app/views/layouts/header/_current_user_dropdown.html.haml
+++ b/app/views/layouts/header/_current_user_dropdown.html.haml
@@ -20,8 +20,8 @@
= link_to s_("CurrentUser|Profile"), current_user, class: 'profile-link', data: { user: current_user.username }
- if current_user_menu?(:settings)
%li
- = link_to s_("CurrentUser|Settings"), profile_path
+ = link_to s_("CurrentUser|Settings"), profile_path, data: { qa_selector: 'settings_link' }
- if current_user_menu?(:sign_out)
%li.divider
%li
- = link_to _("Sign out"), destroy_user_session_path, class: "sign-out-link qa-sign-out-link"
+ = link_to _("Sign out"), destroy_user_session_path, class: "sign-out-link", data: { qa_selector: 'sign_out_link' }
diff --git a/app/views/projects/merge_requests/creations/_new_compare.html.haml b/app/views/projects/merge_requests/creations/_new_compare.html.haml
index 11272a67f93..be01905dd35 100644
--- a/app/views/projects/merge_requests/creations/_new_compare.html.haml
+++ b/app/views/projects/merge_requests/creations/_new_compare.html.haml
@@ -2,6 +2,8 @@
New Merge Request
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], url: project_new_merge_request_path(@project), method: :get, html: { class: "merge-request-form js-requires-input" } do |f|
+ - if params[:nav_source].present?
+ = hidden_field_tag(:nav_source, params[:nav_source])
.hide.alert.alert-danger.mr-compare-errors
.js-merge-request-new-compare.row{ 'data-source-branch-url': project_new_merge_request_branch_from_path(@source_project), 'data-target-branch-url': project_new_merge_request_branch_to_path(@source_project) }
.col-lg-6
diff --git a/app/views/projects/merge_requests/creations/_new_submit.html.haml b/app/views/projects/merge_requests/creations/_new_submit.html.haml
index 464f8fa65e9..543441b9479 100644
--- a/app/views/projects/merge_requests/creations/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/creations/_new_submit.html.haml
@@ -17,6 +17,9 @@
= f.hidden_field :target_project_id
= f.hidden_field :target_branch, id: ''
+ - if params[:nav_source].present?
+ = hidden_field_tag(:nav_source, params[:nav_source])
+
.mr-compare.merge-request.js-merge-request-new-submit{ 'data-mr-submit-action': "#{j params[:tab].presence || 'new'}" }
- if @commits.empty?
.commits-empty
diff --git a/app/views/projects/pages_domains/_form.html.haml b/app/views/projects/pages_domains/_form.html.haml
index 0e5c65a2f72..4aa1e574d93 100644
--- a/app/views/projects/pages_domains/_form.html.haml
+++ b/app/views/projects/pages_domains/_form.html.haml
@@ -33,7 +33,7 @@
= sprite_icon("status_success_borderless", size: 16, css_class: "toggle-icon-svg toggle-status-checked")
= sprite_icon("status_failed_borderless", size: 16, css_class: "toggle-icon-svg toggle-status-unchecked")
%p.text-secondary.mt-3
- - docs_link_url = help_page_path("user/project/pages/lets_encrypt_for_gitlab_pages.md", anchor: "lets-encrypt-for-gitlab-pages")
+ - docs_link_url = help_page_path("user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md")
- docs_link_start = "<a href=\"%{docs_link_url}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-nowrap\">".html_safe % { docs_link_url: docs_link_url }
- docs_link_end = "</a>".html_safe
= _("Let's Encrypt is a free, automated, and open certificate authority (CA) that gives digital certificates in order to enable HTTPS (SSL/TLS) for websites. Learn more about Let's Encrypt configuration by following the %{docs_link_start}documentation on GitLab Pages%{docs_link_end}.").html_safe % { docs_link_url: docs_link_url, docs_link_start: docs_link_start, docs_link_end: docs_link_end }
diff --git a/app/views/projects/pipelines/charts.html.haml b/app/views/projects/pipelines/charts.html.haml
index 4d1d078661d..6b4110e07d2 100644
--- a/app/views/projects/pipelines/charts.html.haml
+++ b/app/views/projects/pipelines/charts.html.haml
@@ -2,12 +2,9 @@
- page_title _('CI / CD Charts')
%div{ class: container_class }
+
#charts.ci-charts
- .row
- .col-md-6
- = render 'projects/pipelines/charts/overall'
- .col-md-6
- = render 'projects/pipelines/charts/pipeline_times'
+ = render 'projects/pipelines/charts/overall'
%hr
= render 'projects/pipelines/charts/pipelines'
diff --git a/app/views/projects/pipelines/charts/_overall.haml b/app/views/projects/pipelines/charts/_overall.haml
index 66786c7ff59..651f9217455 100644
--- a/app/views/projects/pipelines/charts/_overall.haml
+++ b/app/views/projects/pipelines/charts/_overall.haml
@@ -1,15 +1,6 @@
-%h4= s_("PipelineCharts|Overall statistics")
-%ul
- %li
- = s_("PipelineCharts|Total:")
- %strong= n_("1 pipeline", "%d pipelines", @counts[:total]) % @counts[:total]
- %li
- = s_("PipelineCharts|Successful:")
- %strong= n_("1 pipeline", "%d pipelines", @counts[:success]) % @counts[:success]
- %li
- = s_("PipelineCharts|Failed:")
- %strong= n_("1 pipeline", "%d pipelines", @counts[:failed]) % @counts[:failed]
- %li
- = s_("PipelineCharts|Success ratio:")
- %strong
- #{success_ratio(@counts)}%
+%h4.mt-4.mb-4= s_("PipelineCharts|Overall statistics")
+.row
+ .col-md-6
+ = render 'projects/pipelines/charts/pipeline_statistics'
+ .col-md-6
+ = render 'projects/pipelines/charts/pipeline_times'
diff --git a/app/views/projects/pipelines/charts/_pipeline_statistics.haml b/app/views/projects/pipelines/charts/_pipeline_statistics.haml
new file mode 100644
index 00000000000..b323e290ed4
--- /dev/null
+++ b/app/views/projects/pipelines/charts/_pipeline_statistics.haml
@@ -0,0 +1,14 @@
+%ul
+ %li
+ = s_("PipelineCharts|Total:")
+ %strong= n_("1 pipeline", "%d pipelines", @counts[:total]) % @counts[:total]
+ %li
+ = s_("PipelineCharts|Successful:")
+ %strong= n_("1 pipeline", "%d pipelines", @counts[:success]) % @counts[:success]
+ %li
+ = s_("PipelineCharts|Failed:")
+ %strong= n_("1 pipeline", "%d pipelines", @counts[:failed]) % @counts[:failed]
+ %li
+ = s_("PipelineCharts|Success ratio:")
+ %strong
+ #{success_ratio(@counts)}%
diff --git a/app/views/projects/pipelines/charts/_pipelines.haml b/app/views/projects/pipelines/charts/_pipelines.haml
index 47f1f074210..afff9e82e45 100644
--- a/app/views/projects/pipelines/charts/_pipelines.haml
+++ b/app/views/projects/pipelines/charts/_pipelines.haml
@@ -1,4 +1,4 @@
-%h4= _("Pipelines charts")
+%h4.mt-4.mb-4= _("Pipelines charts")
%p
&nbsp;
%span.legend-success
diff --git a/app/views/shared/_issuable_meta_data.html.haml b/app/views/shared/_issuable_meta_data.html.haml
index 71b13a5d741..7807371285c 100644
--- a/app/views/shared/_issuable_meta_data.html.haml
+++ b/app/views/shared/_issuable_meta_data.html.haml
@@ -1,7 +1,7 @@
- note_count = @issuable_meta_data[issuable.id].user_notes_count
- issue_votes = @issuable_meta_data[issuable.id]
- upvotes, downvotes = issue_votes.upvotes, issue_votes.downvotes
-- issuable_url = @collection_type == "Issue" ? issue_path(issuable, anchor: 'notes') : merge_request_path(issuable, anchor: 'notes')
+- issuable_path = issuable_path(issuable, anchor: 'notes')
- issuable_mr = @issuable_meta_data[issuable.id].merge_requests_count(current_user)
- if issuable_mr > 0
@@ -20,6 +20,6 @@
= downvotes
%li.issuable-comments.d-none.d-sm-block
- = link_to issuable_url, class: ['has-tooltip', ('no-comments' if note_count.zero?)], title: _('Comments') do
+ = link_to issuable_path, class: ['has-tooltip', ('no-comments' if note_count.zero?)], title: _('Comments') do
= icon('comments')
= note_count
diff --git a/app/views/shared/boards/_switcher.html.haml b/app/views/shared/boards/_switcher.html.haml
new file mode 100644
index 00000000000..79118630762
--- /dev/null
+++ b/app/views/shared/boards/_switcher.html.haml
@@ -0,0 +1,16 @@
+- parent = board.parent
+- milestone_filter_opts = { format: :json }
+- milestone_filter_opts = milestone_filter_opts.merge(only_group_milestones: true) if board.group_board?
+- weights = Gitlab.ee? ? ([Issue::WEIGHT_ANY] + Issue.weight_options) : []
+
+#js-multiple-boards-switcher.inline.boards-switcher{ data: { current_board: current_board_json.to_json,
+ milestone_path: milestones_filter_path(milestone_filter_opts),
+ board_base_url: board_base_url,
+ has_missing_boards: (!multiple_boards_available? && current_board_parent.boards.size > 1).to_s,
+ can_admin_board: can?(current_user, :admin_board, parent).to_s,
+ multiple_issue_boards_available: parent.multiple_issue_boards_available?.to_s,
+ labels_path: labels_filter_path_with_defaults(only_group_labels: true, include_descendant_groups: true),
+ project_id: @project&.id,
+ group_id: @group&.id,
+ scoped_issue_board_feature_enabled: Gitlab.ee? && parent.feature_available?(:scoped_issue_board) ? 'true' : 'false',
+ weights: weights.to_json } }
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index 214e87052da..07a7b5ce9de 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -33,6 +33,8 @@
= render_if_exists 'shared/issuable/approvals', issuable: issuable, presenter: presenter, form: form
+= render_if_exists "shared/issuable/form/merge_request_blocks", issuable: issuable, form: form
+
= render 'shared/issuable/form/branch_chooser', issuable: issuable, form: form
= render 'shared/issuable/form/merge_params', issuable: issuable
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index a97ac5e2a2d..e253413929a 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -6,7 +6,7 @@
.issues-filters{ class: ("w-100" if type == :boards_modal) }
.issues-details-filters.filtered-search-block.d-flex.flex-column.flex-md-row{ class: block_css_class, "v-pre" => type == :boards_modal }
- if type == :boards
- = render_if_exists "shared/boards/switcher", board: board
+ = render "shared/boards/switcher", board: board
= form_tag page_filter_path, method: :get, class: 'filter-form js-filter-form w-100' do
- if params[:search].present?
= hidden_field_tag :search, params[:search]
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index 71bd9320593..c4d1bdad2c4 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -37,7 +37,7 @@
%span.project-name<
= project.name
- %span.metadata-info.visibility-icon.append-right-10.prepend-top-8.has-tooltip{ data: { container: 'body', placement: 'top' }, title: visibility_icon_description(project) }
+ %span.metadata-info.visibility-icon.append-right-10.prepend-top-8.text-secondary.has-tooltip{ data: { container: 'body', placement: 'top' }, title: visibility_icon_description(project) }
= visibility_level_icon(project.visibility_level, fw: true)
- if explore_projects_tab? && project.repository.license
@@ -89,4 +89,4 @@
%span.icon-wrapper.pipeline-status
= render 'ci/status/icon', status: project.commit.last_pipeline.detailed_status(current_user), type: 'commit', tooltip_placement: 'top', path: pipeline_path
.updated-note
- %span Updated #{updated_tooltip}
+ %span #{_('Updated')} #{updated_tooltip}
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 3d34bfc05c7..991a177018e 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -3,6 +3,12 @@
- auto_merge:auto_merge_process
+- chaos:chaos_cpu_spin
+- chaos:chaos_db_spin
+- chaos:chaos_kill
+- chaos:chaos_leak_mem
+- chaos:chaos_sleep
+
- cronjob:admin_email
- cronjob:expire_build_artifacts
- cronjob:gitlab_usage_ping
diff --git a/app/workers/chaos/cpu_spin_worker.rb b/app/workers/chaos/cpu_spin_worker.rb
new file mode 100644
index 00000000000..43a32c3274f
--- /dev/null
+++ b/app/workers/chaos/cpu_spin_worker.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Chaos
+ class CpuSpinWorker
+ include ApplicationWorker
+ include ChaosQueue
+
+ def perform(duration_s)
+ Gitlab::Chaos.cpu_spin(duration_s)
+ end
+ end
+end
diff --git a/app/workers/chaos/db_spin_worker.rb b/app/workers/chaos/db_spin_worker.rb
new file mode 100644
index 00000000000..217ddabbcb6
--- /dev/null
+++ b/app/workers/chaos/db_spin_worker.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Chaos
+ class DbSpinWorker
+ include ApplicationWorker
+ include ChaosQueue
+
+ def perform(duration_s, interval_s)
+ Gitlab::Chaos.db_spin(duration_s, interval_s)
+ end
+ end
+end
diff --git a/app/workers/chaos/kill_worker.rb b/app/workers/chaos/kill_worker.rb
new file mode 100644
index 00000000000..bbad53c9b86
--- /dev/null
+++ b/app/workers/chaos/kill_worker.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Chaos
+ class KillWorker
+ include ApplicationWorker
+ include ChaosQueue
+
+ def perform
+ Gitlab::Chaos.kill
+ end
+ end
+end
diff --git a/app/workers/chaos/leak_mem_worker.rb b/app/workers/chaos/leak_mem_worker.rb
new file mode 100644
index 00000000000..0caa99e0de9
--- /dev/null
+++ b/app/workers/chaos/leak_mem_worker.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Chaos
+ class LeakMemWorker
+ include ApplicationWorker
+ include ChaosQueue
+
+ def perform(memory_mb, duration_s)
+ Gitlab::Chaos.leak_mem(memory_mb, duration_s)
+ end
+ end
+end
diff --git a/app/workers/chaos/sleep_worker.rb b/app/workers/chaos/sleep_worker.rb
new file mode 100644
index 00000000000..7c724c4cb4e
--- /dev/null
+++ b/app/workers/chaos/sleep_worker.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Chaos
+ class SleepWorker
+ include ApplicationWorker
+ include ChaosQueue
+
+ def perform(duration_s)
+ Gitlab::Chaos.sleep(duration_s)
+ end
+ end
+end
diff --git a/app/workers/concerns/chaos_queue.rb b/app/workers/concerns/chaos_queue.rb
new file mode 100644
index 00000000000..e406509d12d
--- /dev/null
+++ b/app/workers/concerns/chaos_queue.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+#
+module ChaosQueue
+ extend ActiveSupport::Concern
+
+ included do
+ queue_namespace :chaos
+ end
+end
diff --git a/app/workers/gitlab_usage_ping_worker.rb b/app/workers/gitlab_usage_ping_worker.rb
index b75e724ca98..a5e22f88a3b 100644
--- a/app/workers/gitlab_usage_ping_worker.rb
+++ b/app/workers/gitlab_usage_ping_worker.rb
@@ -6,10 +6,16 @@ class GitlabUsagePingWorker
include ApplicationWorker
include CronjobQueue
+ # Retry for up to approximately three hours then give up.
+ sidekiq_options retry: 10, dead: false
+
def perform
# Multiple Sidekiq workers could run this. We should only do this at most once a day.
return unless try_obtain_lease
+ # Splay the request over a minute to avoid thundering herd problems.
+ sleep(rand(0.0..60.0).round(3))
+
SubmitUsagePingService.new.execute
end
diff --git a/changelogs/unreleased/11039-moved-code-difference-from-EE-to-CE.yml b/changelogs/unreleased/11039-moved-code-difference-from-EE-to-CE.yml
deleted file mode 100644
index 10c5eed9556..00000000000
--- a/changelogs/unreleased/11039-moved-code-difference-from-EE-to-CE.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "Moved EE/CE code differences for file `app/views/search/_category.html.haml` into CE"
-merge_request: 28755
-author: Michel Engelen
-type: other
diff --git a/changelogs/unreleased/11448-fix-cs-with-k8s-runners.yml b/changelogs/unreleased/11448-fix-cs-with-k8s-runners.yml
deleted file mode 100644
index 191e64df4f1..00000000000
--- a/changelogs/unreleased/11448-fix-cs-with-k8s-runners.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix Container Scanning job timeout when using the kubernetes executor
-merge_request: 29706
-author:
-type: fixed
diff --git a/changelogs/unreleased/11888-regression-deploy-correlation-markers-on-monitoring-graphs-not-clickable.yml b/changelogs/unreleased/11888-regression-deploy-correlation-markers-on-monitoring-graphs-not-clickable.yml
deleted file mode 100644
index 606abe818b4..00000000000
--- a/changelogs/unreleased/11888-regression-deploy-correlation-markers-on-monitoring-graphs-not-clickable.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Turn commit sha in monitor charts popover to link
-merge_request: 29914
-author:
-type: fixed
diff --git a/changelogs/unreleased/12533-shared-runners-warning.yml b/changelogs/unreleased/12533-shared-runners-warning.yml
deleted file mode 100644
index b2b526cc2e4..00000000000
--- a/changelogs/unreleased/12533-shared-runners-warning.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Removes EE differences for app/views/admin/users/show.html.haml
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/12550-fullscrean.yml b/changelogs/unreleased/12550-fullscrean.yml
deleted file mode 100644
index f20b191f411..00000000000
--- a/changelogs/unreleased/12550-fullscrean.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Removes EE differences for app/views/layouts/fullscreen.html.haml
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/12553-preferences.yml b/changelogs/unreleased/12553-preferences.yml
deleted file mode 100644
index c41a8c98e4e..00000000000
--- a/changelogs/unreleased/12553-preferences.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Removes EE diff for app/views/profiles/preferences/show.html.haml
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/17690-Protect-TeamCity-builds-for-triggering-when-a-branch-is-deleted-And-add-MR-option.yml b/changelogs/unreleased/17690-Protect-TeamCity-builds-for-triggering-when-a-branch-is-deleted-And-add-MR-option.yml
deleted file mode 100644
index 741a0faf469..00000000000
--- a/changelogs/unreleased/17690-Protect-TeamCity-builds-for-triggering-when-a-branch-is-deleted-And-add-MR-option.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Protect TeamCity builds from triggering when a branch has been deleted. And a MR-option
-merge_request: 29836
-author: Nikolay Novikov, Raphael Tweitmann
-type: fixed
diff --git a/changelogs/unreleased/29775-fix-nested-lists-unnecessary-margin.yml b/changelogs/unreleased/29775-fix-nested-lists-unnecessary-margin.yml
deleted file mode 100644
index e7e43c54bab..00000000000
--- a/changelogs/unreleased/29775-fix-nested-lists-unnecessary-margin.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix nested lists unnecessary margin
-merge_request: 29775
-author: Kuba Kopeć
-type: fixed
diff --git a/changelogs/unreleased/30355-use-hours-only-for-time-tracking.yml b/changelogs/unreleased/30355-use-hours-only-for-time-tracking.yml
deleted file mode 100644
index b0252f9e81b..00000000000
--- a/changelogs/unreleased/30355-use-hours-only-for-time-tracking.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add option to limit time tracking units to hours
-merge_request: 29469
-author: Jon Kolb
-type: added
diff --git a/changelogs/unreleased/-30974-issue-search-by-number.yml b/changelogs/unreleased/30974-issue-search-by-number.yml
index 1e6642ec102..da50ee32c83 100644
--- a/changelogs/unreleased/-30974-issue-search-by-number.yml
+++ b/changelogs/unreleased/30974-issue-search-by-number.yml
@@ -1,5 +1,5 @@
---
title: "Search issuables by iids"
-merge_request: !28302
+merge_request: 28302
author: Riccardo Padovani
type: fixed
diff --git a/changelogs/unreleased/32452-multiple-discussions.yml b/changelogs/unreleased/32452-multiple-discussions.yml
deleted file mode 100644
index 5552340ee66..00000000000
--- a/changelogs/unreleased/32452-multiple-discussions.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Resolve Multiple discussions per line in merge request diffs
-merge_request: 28748
-author:
-type: added
diff --git a/changelogs/unreleased/35757-move-issues-in-boards-pderichs.yml b/changelogs/unreleased/35757-move-issues-in-boards-pderichs.yml
deleted file mode 100644
index ae50350f70d..00000000000
--- a/changelogs/unreleased/35757-move-issues-in-boards-pderichs.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add endpoint to move multiple issues in boards
-merge_request: 30216
-author:
-type: added
diff --git a/changelogs/unreleased/38105-pre-release-tag.yml b/changelogs/unreleased/38105-pre-release-tag.yml
deleted file mode 100644
index d4c5dcacd19..00000000000
--- a/changelogs/unreleased/38105-pre-release-tag.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Renders a pre-release tag for releases
-merge_request: 29797
-author:
-type: changed
diff --git a/changelogs/unreleased/40379-CJK-search-min-chars.yml b/changelogs/unreleased/40379-CJK-search-min-chars.yml
deleted file mode 100644
index 6f6c4df464f..00000000000
--- a/changelogs/unreleased/40379-CJK-search-min-chars.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove minimum character limits for fuzzy searches when using a CTE
-merge_request: 29810
-author:
-type: fixed
diff --git a/changelogs/unreleased/42399-registry-confirm-deletion.yml b/changelogs/unreleased/42399-registry-confirm-deletion.yml
deleted file mode 100644
index 4d720e16721..00000000000
--- a/changelogs/unreleased/42399-registry-confirm-deletion.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add confirmation for registry image deletion
-merge_request: 29505
-author:
-type: added
diff --git a/changelogs/unreleased/44106-include-subgroups-in-group-activity.yml b/changelogs/unreleased/44106-include-subgroups-in-group-activity.yml
deleted file mode 100644
index 6e78d61ebc4..00000000000
--- a/changelogs/unreleased/44106-include-subgroups-in-group-activity.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Include events from subgroups in group's activity
-merge_request: 29953
-author: Fabian Schneider @fabsrc
-type: changed
diff --git a/changelogs/unreleased/44949-do-not-update-updated_at-on-an-issue-when-reordering-it.yml b/changelogs/unreleased/44949-do-not-update-updated_at-on-an-issue-when-reordering-it.yml
deleted file mode 100644
index efc6af7845c..00000000000
--- a/changelogs/unreleased/44949-do-not-update-updated_at-on-an-issue-when-reordering-it.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Will not update issue timestamps when changing positions in a list
-merge_request: 29677
-author:
-type: changed
diff --git a/changelogs/unreleased/45104-special-characters-in-project-name-path-prevent-users-from-using-the-container-registry.yml b/changelogs/unreleased/45104-special-characters-in-project-name-path-prevent-users-from-using-the-container-registry.yml
deleted file mode 100644
index ddde0cc9c39..00000000000
--- a/changelogs/unreleased/45104-special-characters-in-project-name-path-prevent-users-from-using-the-container-registry.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Updated container registry to display error message when special characters in path. Documentation has also been updated.
-merge_request: 29616
-author:
-type: changed
diff --git a/changelogs/unreleased/45120-fix-ide-editor-to-update-size-on-show-change.yml b/changelogs/unreleased/45120-fix-ide-editor-to-update-size-on-show-change.yml
deleted file mode 100644
index 592612c2615..00000000000
--- a/changelogs/unreleased/45120-fix-ide-editor-to-update-size-on-show-change.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix IDE editor not showing when switching back from preview
-merge_request: 30135
-author:
-type: fixed
diff --git a/changelogs/unreleased/49814-display-in-admin-area-if-emails-are-verified-or-not.yml b/changelogs/unreleased/49814-display-in-admin-area-if-emails-are-verified-or-not.yml
deleted file mode 100644
index db1391edd73..00000000000
--- a/changelogs/unreleased/49814-display-in-admin-area-if-emails-are-verified-or-not.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add a verified pill next to email addresses under the admin users section.
-merge_request: 29669
-author:
-type: added
diff --git a/changelogs/unreleased/50228-deploy-tokens-custom-username.yml b/changelogs/unreleased/50228-deploy-tokens-custom-username.yml
deleted file mode 100644
index fdafa7b1113..00000000000
--- a/changelogs/unreleased/50228-deploy-tokens-custom-username.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow custom username for deploy tokens
-merge_request: 29639
-author:
-type: added
diff --git a/changelogs/unreleased/50834-change-http-status-code-when-repository-disabled.yml b/changelogs/unreleased/50834-change-http-status-code-when-repository-disabled.yml
deleted file mode 100644
index b51079d5c74..00000000000
--- a/changelogs/unreleased/50834-change-http-status-code-when-repository-disabled.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "Changed HTTP Status Code for disabled repository on /branches and /commits to 404"
-merge_request: 29585
-author: Sam Battalio
-type: changed
diff --git a/changelogs/unreleased/51794-add-ordering-to-runner-jobs-api.yml b/changelogs/unreleased/51794-add-ordering-to-runner-jobs-api.yml
deleted file mode 100644
index 908a132688c..00000000000
--- a/changelogs/unreleased/51794-add-ordering-to-runner-jobs-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add order_by and sort params to list runner jobs api
-merge_request: 29629
-author: Sujay Patel
-type: added
diff --git a/changelogs/unreleased/51952-forking-via-webide.yml b/changelogs/unreleased/51952-forking-via-webide.yml
deleted file mode 100644
index 4497c6b6ca4..00000000000
--- a/changelogs/unreleased/51952-forking-via-webide.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Resolve "500 error when forking via the web IDE button"
-merge_request: 29909
-author:
-type: fixed
diff --git a/changelogs/unreleased/51952-redirect-to-webide-in-fork.yml b/changelogs/unreleased/51952-redirect-to-webide-in-fork.yml
deleted file mode 100644
index ada0efca968..00000000000
--- a/changelogs/unreleased/51952-redirect-to-webide-in-fork.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Open WebIDE in fork when user doesn't have access
-merge_request: 30642
-author:
-type: changed
diff --git a/changelogs/unreleased/52366-improved-group-lists-ui.yml b/changelogs/unreleased/52366-improved-group-lists-ui.yml
deleted file mode 100644
index 99e5b67a56c..00000000000
--- a/changelogs/unreleased/52366-improved-group-lists-ui.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve group list UI
-merge_request: 26542
-author:
-type: changed
diff --git a/changelogs/unreleased/52442-minimal-remove-mysql-support.yml b/changelogs/unreleased/52442-minimal-remove-mysql-support.yml
deleted file mode 100644
index f1a50657383..00000000000
--- a/changelogs/unreleased/52442-minimal-remove-mysql-support.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove MySQL support
-merge_request: 29790
-author:
-type: removed
diff --git a/changelogs/unreleased/52954-allow-developers-to-delete-tags.yml b/changelogs/unreleased/52954-allow-developers-to-delete-tags.yml
deleted file mode 100644
index 38c65a67f2a..00000000000
--- a/changelogs/unreleased/52954-allow-developers-to-delete-tags.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow developers to delete tags
-merge_request: 29668
-author:
-type: changed
diff --git a/changelogs/unreleased/53357-fix-plus-in-upload-file-names.yml b/changelogs/unreleased/53357-fix-plus-in-upload-file-names.yml
deleted file mode 100644
index c11d74bd4fe..00000000000
--- a/changelogs/unreleased/53357-fix-plus-in-upload-file-names.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix broken URLs for uploads with a plus in the filename
-merge_request: 29915
-author:
-type: fixed
diff --git a/changelogs/unreleased/53811-issue-boards-to-core-projects-backend-ce.yml b/changelogs/unreleased/53811-issue-boards-to-core-projects-backend-ce.yml
deleted file mode 100644
index d8fbd7ec362..00000000000
--- a/changelogs/unreleased/53811-issue-boards-to-core-projects-backend-ce.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Move Multiple Issue Boards for Projects to Core
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/54117-transactional-rebase.yml b/changelogs/unreleased/54117-transactional-rebase.yml
deleted file mode 100644
index d0c93114c49..00000000000
--- a/changelogs/unreleased/54117-transactional-rebase.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow asynchronous rebase operations to be monitored
-merge_request: 29940
-author:
-type: fixed
diff --git a/changelogs/unreleased/54595-incorrect-reaction-emoji-placement-in-discussion.yml b/changelogs/unreleased/54595-incorrect-reaction-emoji-placement-in-discussion.yml
deleted file mode 100644
index 639eefb50cb..00000000000
--- a/changelogs/unreleased/54595-incorrect-reaction-emoji-placement-in-discussion.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix incorrect emoji placement in commit diff discussion
-merge_request: 29445
-author:
-type: fixed
diff --git a/changelogs/unreleased/55487-enable-group-terminals-button.yml b/changelogs/unreleased/55487-enable-group-terminals-button.yml
deleted file mode 100644
index 6bf16eaadd1..00000000000
--- a/changelogs/unreleased/55487-enable-group-terminals-button.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable terminals button for group clusters
-merge_request: 30255
-author:
-type: added
diff --git a/changelogs/unreleased/55564-remove-if-in-before-after-action.yml b/changelogs/unreleased/55564-remove-if-in-before-after-action.yml
new file mode 100644
index 00000000000..a787faa8a9c
--- /dev/null
+++ b/changelogs/unreleased/55564-remove-if-in-before-after-action.yml
@@ -0,0 +1,5 @@
+---
+title: Rewrite `if:` argument in before_action and alike when `only:` is also used
+merge_request: 24412
+author: George Thomas @thegeorgeous
+type: other
diff --git a/changelogs/unreleased/55623-group-cluster-apis.yml b/changelogs/unreleased/55623-group-cluster-apis.yml
deleted file mode 100644
index fe987ef4a82..00000000000
--- a/changelogs/unreleased/55623-group-cluster-apis.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add API for CRUD group clusters
-merge_request: 30213
-author:
-type: added
diff --git a/changelogs/unreleased/55902-disable-creation-of-non-rbac-kubernetes-clusters.yml b/changelogs/unreleased/55902-disable-creation-of-non-rbac-kubernetes-clusters.yml
deleted file mode 100644
index 2af2d229c6c..00000000000
--- a/changelogs/unreleased/55902-disable-creation-of-non-rbac-kubernetes-clusters.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove support for creating non-RBAC kubernetes clusters
-merge_request: 29614
-author:
-type: removed
diff --git a/changelogs/unreleased/55953-renamed-discussion-to-thread.yml b/changelogs/unreleased/55953-renamed-discussion-to-thread.yml
deleted file mode 100644
index a5203ecf2e0..00000000000
--- a/changelogs/unreleased/55953-renamed-discussion-to-thread.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "renamed discussion to thread in merge-request and issue timeline"
-merge_request: 29553
-author: Michel Engelen
-type: changed
diff --git a/changelogs/unreleased/57538-normalize-users-private-profile-field.yml b/changelogs/unreleased/57538-normalize-users-private-profile-field.yml
deleted file mode 100644
index 85fc4cbf33c..00000000000
--- a/changelogs/unreleased/57538-normalize-users-private-profile-field.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Migrate NULL values for users.private_profile column and update users API to reject null value for private_profile.
-merge_request: 29888
-author:
-type: changed
diff --git a/changelogs/unreleased/57793-fix-line-age.yml b/changelogs/unreleased/57793-fix-line-age.yml
deleted file mode 100644
index cf4e328e54a..00000000000
--- a/changelogs/unreleased/57793-fix-line-age.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Support note position tracing on an image
-merge_request: 30158
-author:
-type: fixed
diff --git a/changelogs/unreleased/57815.yml b/changelogs/unreleased/57815.yml
deleted file mode 100644
index ae8ec7036ce..00000000000
--- a/changelogs/unreleased/57815.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enforced requirements for UltraAuth users
-merge_request: 28941
-author: Kartikey Tanna
-type: changed
diff --git a/changelogs/unreleased/57918-encrypt-feature-flags-tokens-changelog.yml b/changelogs/unreleased/57918-encrypt-feature-flags-tokens-changelog.yml
deleted file mode 100644
index 9701c8bc4a5..00000000000
--- a/changelogs/unreleased/57918-encrypt-feature-flags-tokens-changelog.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add token_encrypted column to operations_feature_flags_clients table
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/57973-errors-in-application-settings-panel-shows-wrong-panel.yml b/changelogs/unreleased/57973-errors-in-application-settings-panel-shows-wrong-panel.yml
deleted file mode 100644
index 2b62992eda0..00000000000
--- a/changelogs/unreleased/57973-errors-in-application-settings-panel-shows-wrong-panel.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Show poper panel when validation error occurs in admin settings panels
-merge_request: 25434
-author:
-type: fixed
diff --git a/changelogs/unreleased/58065-uniform-html-txt-email.yml b/changelogs/unreleased/58065-uniform-html-txt-email.yml
deleted file mode 100644
index be34e93ff83..00000000000
--- a/changelogs/unreleased/58065-uniform-html-txt-email.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Always shows author of created issue/started discussion/comment in HTML body and text of email
-merge_request: 29886
-author: Frank van Rest
-type: fixed
diff --git a/changelogs/unreleased/58689-regroup-jump-button-in-discussion.yml b/changelogs/unreleased/58689-regroup-jump-button-in-discussion.yml
deleted file mode 100644
index bf6f314f0ce..00000000000
--- a/changelogs/unreleased/58689-regroup-jump-button-in-discussion.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Improve discussion reply buttons layout and how jump to next discussion button
- appears
-merge_request: 29779
-author:
-type: changed
diff --git a/changelogs/unreleased/58802-rename-webide.yml b/changelogs/unreleased/58802-rename-webide.yml
deleted file mode 100644
index 40471d967ce..00000000000
--- a/changelogs/unreleased/58802-rename-webide.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Re-name files in Web IDE in a more natural way
-merge_request: 29948
-author:
-type: changed
diff --git a/changelogs/unreleased/58808-fix-image-diff-on-text.yml b/changelogs/unreleased/58808-fix-image-diff-on-text.yml
deleted file mode 100644
index 78955c24186..00000000000
--- a/changelogs/unreleased/58808-fix-image-diff-on-text.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Don't show image diff note on text file
-merge_request: 30221
-author:
-type: fixed
diff --git a/changelogs/unreleased/59028-fix-extra-plus-in-diffs.yml b/changelogs/unreleased/59028-fix-extra-plus-in-diffs.yml
deleted file mode 100644
index 0786f4dbc10..00000000000
--- a/changelogs/unreleased/59028-fix-extra-plus-in-diffs.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove duplicate trailing +/- char in merge request discussions
-merge_request: 29518
-author:
-type: fixed
diff --git a/changelogs/unreleased/59257-find-new-branches-harder.yml b/changelogs/unreleased/59257-find-new-branches-harder.yml
deleted file mode 100644
index 631eaa22ef0..00000000000
--- a/changelogs/unreleased/59257-find-new-branches-harder.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Look for new branches more carefully
-merge_request: 29761
-author:
-type: fixed
diff --git a/changelogs/unreleased/60617-enable-project-cluster-jit.yml b/changelogs/unreleased/60617-enable-project-cluster-jit.yml
deleted file mode 100644
index b7d745d4385..00000000000
--- a/changelogs/unreleased/60617-enable-project-cluster-jit.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable just-in-time Kubernetes resource creation for project-level clusters
-merge_request: 29515
-author:
-type: changed
diff --git a/changelogs/unreleased/60666-kubernetes-applications-uninstall-runner.yml b/changelogs/unreleased/60666-kubernetes-applications-uninstall-runner.yml
deleted file mode 100644
index 3632c8eec20..00000000000
--- a/changelogs/unreleased/60666-kubernetes-applications-uninstall-runner.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow GitLab Runner to be uninstalled from the UI
-merge_request: 30176
-author:
-type: added
diff --git a/changelogs/unreleased/60856-deleting-binary-file.yml b/changelogs/unreleased/60856-deleting-binary-file.yml
deleted file mode 100644
index 9b587ed591b..00000000000
--- a/changelogs/unreleased/60856-deleting-binary-file.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Removing an image should not output binary data
-merge_request: 30314
-author:
-type: fixed
diff --git a/changelogs/unreleased/60859-upload-after-delete.yml b/changelogs/unreleased/60859-upload-after-delete.yml
deleted file mode 100644
index c36dfb84bfe..00000000000
--- a/changelogs/unreleased/60859-upload-after-delete.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: In WebIDE allow adding new entries of the same name as deleted entry
-merge_request: 30239
-author:
-type: fixed
diff --git a/changelogs/unreleased/60860-keep-empty-folders-in-tree.yml b/changelogs/unreleased/60860-keep-empty-folders-in-tree.yml
deleted file mode 100644
index 237d0fd6aef..00000000000
--- a/changelogs/unreleased/60860-keep-empty-folders-in-tree.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Keep the empty folders in the tree
-merge_request: 29196
-author:
-type: fixed
diff --git a/changelogs/unreleased/60879-fix-reports-timing-out.yml b/changelogs/unreleased/60879-fix-reports-timing-out.yml
deleted file mode 100644
index 845162fe10f..00000000000
--- a/changelogs/unreleased/60879-fix-reports-timing-out.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix reports jobs timing out because of cache
-merge_request: 29780
-author:
-type: fixed
diff --git a/changelogs/unreleased/60948-display-groupid-on-group-admin-page.yml b/changelogs/unreleased/60948-display-groupid-on-group-admin-page.yml
new file mode 100644
index 00000000000..17763b4c69e
--- /dev/null
+++ b/changelogs/unreleased/60948-display-groupid-on-group-admin-page.yml
@@ -0,0 +1,5 @@
+---
+title: Display group id on group admin page
+merge_request: 29735
+author: Zsolt Kovari
+type: added
diff --git a/changelogs/unreleased/60949-display-projectid-on-project-admin-page.yml b/changelogs/unreleased/60949-display-projectid-on-project-admin-page.yml
new file mode 100644
index 00000000000..3ff83ede2fa
--- /dev/null
+++ b/changelogs/unreleased/60949-display-projectid-on-project-admin-page.yml
@@ -0,0 +1,5 @@
+---
+title: Display project id on project admin page
+merge_request: 29734
+author: Zsolt Kovari
+type: added
diff --git a/changelogs/unreleased/61005-grafanaInAdminSettingsMonitoringMenu.yml b/changelogs/unreleased/61005-grafanaInAdminSettingsMonitoringMenu.yml
deleted file mode 100644
index 3ee512f3448..00000000000
--- a/changelogs/unreleased/61005-grafanaInAdminSettingsMonitoringMenu.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds link to Grafana in Admin > Monitoring settings when grafana is enabled in config
-merge_request: 28937
-author: Romain Maneschi
-type: added
diff --git a/changelogs/unreleased/61156-instance-level-cluster-pod-terminal-access.yml b/changelogs/unreleased/61156-instance-level-cluster-pod-terminal-access.yml
deleted file mode 100644
index 0b8d301352b..00000000000
--- a/changelogs/unreleased/61156-instance-level-cluster-pod-terminal-access.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable terminals for instance and group clusters
-merge_request: 28613
-author:
-type: added
diff --git a/changelogs/unreleased/61201-pass-identities-to-external-authorization.yml b/changelogs/unreleased/61201-pass-identities-to-external-authorization.yml
deleted file mode 100644
index 82eea653de6..00000000000
--- a/changelogs/unreleased/61201-pass-identities-to-external-authorization.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add identity information to external authorization requests
-merge_request: 29461
-author:
-type: changed
diff --git a/changelogs/unreleased/61207-adjusted-hoverable-area-in-sidebar.yml b/changelogs/unreleased/61207-adjusted-hoverable-area-in-sidebar.yml
new file mode 100644
index 00000000000..99fc817d703
--- /dev/null
+++ b/changelogs/unreleased/61207-adjusted-hoverable-area-in-sidebar.yml
@@ -0,0 +1,5 @@
+---
+title: "Adjusted the clickable area of collapsed sidebar elements"
+merge_request: 30974
+author: Michel Engelen
+type: changed
diff --git a/changelogs/unreleased/61284-frontend-follow-up-from-add-packages_size-to-projectstatistics.yml b/changelogs/unreleased/61284-frontend-follow-up-from-add-packages_size-to-projectstatistics.yml
deleted file mode 100644
index 3445057f7fb..00000000000
--- a/changelogs/unreleased/61284-frontend-follow-up-from-add-packages_size-to-projectstatistics.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improved readability of storage statistics in group / project admin area
-merge_request: 30406
-author:
-type: other
diff --git a/changelogs/unreleased/61342-commit-search-result-doesn-t-pass-wcag-color-audit.yml b/changelogs/unreleased/61342-commit-search-result-doesn-t-pass-wcag-color-audit.yml
deleted file mode 100644
index f4ed4551aab..00000000000
--- a/changelogs/unreleased/61342-commit-search-result-doesn-t-pass-wcag-color-audit.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Change color for namespace in commit search
-merge_request: 30312
-author:
-type: other
diff --git a/changelogs/unreleased/61613-spacing-mr-widgets.yml b/changelogs/unreleased/61613-spacing-mr-widgets.yml
deleted file mode 100644
index 7d37ef8da2e..00000000000
--- a/changelogs/unreleased/61613-spacing-mr-widgets.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Left align mr widget icons and text
-merge_request: 28561
-author:
-type: fixed
diff --git a/changelogs/unreleased/62088-search-back.yml b/changelogs/unreleased/62088-search-back.yml
deleted file mode 100644
index 4758e927880..00000000000
--- a/changelogs/unreleased/62088-search-back.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed back navigation for projects filter
-merge_request: 30373
-author:
-type: fixed
diff --git a/changelogs/unreleased/62124-new-threaded-discussion-design.yml b/changelogs/unreleased/62124-new-threaded-discussion-design.yml
deleted file mode 100644
index 6614e05be74..00000000000
--- a/changelogs/unreleased/62124-new-threaded-discussion-design.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Implement borderless discussion design with new reply field
-merge_request: 28580
-author:
-type: added
diff --git a/changelogs/unreleased/62183-update-response-code-for-bulk-delete-api-for-container-registry.yml b/changelogs/unreleased/62183-update-response-code-for-bulk-delete-api-for-container-registry.yml
deleted file mode 100644
index ce75a55efdc..00000000000
--- a/changelogs/unreleased/62183-update-response-code-for-bulk-delete-api-for-container-registry.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Return 400 when deleting tags more often than once per hour.
-merge_request: 29448
-author:
-type: changed
diff --git a/changelogs/unreleased/62772-disable-kubernetes-credential-passthrough.yml b/changelogs/unreleased/62772-disable-kubernetes-credential-passthrough.yml
deleted file mode 100644
index 35771e80821..00000000000
--- a/changelogs/unreleased/62772-disable-kubernetes-credential-passthrough.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Disable Kubernetes credential passthrough for managed project-level clusters
-merge_request: 29262
-author:
-type: removed
diff --git a/changelogs/unreleased/62772-migrate-managed-clusters-to-unmanaged.yml b/changelogs/unreleased/62772-migrate-managed-clusters-to-unmanaged.yml
deleted file mode 100644
index 62a67c7b78d..00000000000
--- a/changelogs/unreleased/62772-migrate-managed-clusters-to-unmanaged.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Migrate GitLab managed project-level clusters to unmanaged if a Kubernetes
- namespace was unable to be created
-merge_request: 29251
-author:
-type: other
diff --git a/changelogs/unreleased/62826-graphql-emoji-mutations.yml b/changelogs/unreleased/62826-graphql-emoji-mutations.yml
deleted file mode 100644
index 0c0aaedf844..00000000000
--- a/changelogs/unreleased/62826-graphql-emoji-mutations.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: GraphQL mutations for add, remove and toggle emoji
-merge_request: 29919
-author:
-type: added
diff --git a/changelogs/unreleased/62826-graphql-note-mutations.yml b/changelogs/unreleased/62826-graphql-note-mutations.yml
deleted file mode 100644
index 85273186bb6..00000000000
--- a/changelogs/unreleased/62826-graphql-note-mutations.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: GraphQL mutations for managing Notes
-merge_request: 30210
-author:
-type: added
diff --git a/changelogs/unreleased/62938-wcag-aa-edited-text-color.yml b/changelogs/unreleased/62938-wcag-aa-edited-text-color.yml
deleted file mode 100644
index 6652e495869..00000000000
--- a/changelogs/unreleased/62938-wcag-aa-edited-text-color.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use darker gray color for system note metadata and edited text
-merge_request: 30054
-author:
-type: other
diff --git a/changelogs/unreleased/62968-environment-details-header-border-misaligned.yml b/changelogs/unreleased/62968-environment-details-header-border-misaligned.yml
deleted file mode 100644
index 749fe6a9cb0..00000000000
--- a/changelogs/unreleased/62968-environment-details-header-border-misaligned.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Resolve Environment details header border misaligned
-merge_request: 30011
-author:
-type: fixed
diff --git a/changelogs/unreleased/62980-username-availability-checker-breaks-inline-validation.yml b/changelogs/unreleased/62980-username-availability-checker-breaks-inline-validation.yml
deleted file mode 100644
index 7436f5d278e..00000000000
--- a/changelogs/unreleased/62980-username-availability-checker-breaks-inline-validation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix the signup form's username validation messages not displaying
-merge_request: 29678
-author: Jiaan Louw
-type: fixed
diff --git a/changelogs/unreleased/63079-migrate-clusters-with-no-token-to-unmanaged.yml b/changelogs/unreleased/63079-migrate-clusters-with-no-token-to-unmanaged.yml
deleted file mode 100644
index 92133af03f7..00000000000
--- a/changelogs/unreleased/63079-migrate-clusters-with-no-token-to-unmanaged.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Migrate GitLab managed project-level clusters to unmanaged if they are missing
- a Kubernetes service account token
-merge_request: 29648
-author:
-type: other
diff --git a/changelogs/unreleased/63200-reply-button-broken.yml b/changelogs/unreleased/63200-reply-button-broken.yml
deleted file mode 100644
index 11f81dbd925..00000000000
--- a/changelogs/unreleased/63200-reply-button-broken.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix unresponsive reply button in discussions
-merge_request: 29936
-author:
-type: fixed
diff --git a/changelogs/unreleased/63227-fix-double-border.yml b/changelogs/unreleased/63227-fix-double-border.yml
deleted file mode 100644
index 6cc4040d333..00000000000
--- a/changelogs/unreleased/63227-fix-double-border.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix Double Border in Profile Page
-merge_request: 29784
-author: Yoginth <@yo>
-type: fixed
diff --git a/changelogs/unreleased/63247-add-conf-toast-and-link.yml b/changelogs/unreleased/63247-add-conf-toast-and-link.yml
deleted file mode 100644
index 915cc20dcc8..00000000000
--- a/changelogs/unreleased/63247-add-conf-toast-and-link.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Include a link back to the MR for Visual Review feedback form
-merge_request: 29719
-author:
-type: changed
diff --git a/changelogs/unreleased/63261-the-graphql-query-for-the-mr-popover-failes-on-the-frontend.yml b/changelogs/unreleased/63261-the-graphql-query-for-the-mr-popover-failes-on-the-frontend.yml
deleted file mode 100644
index 87d74e3f4b4..00000000000
--- a/changelogs/unreleased/63261-the-graphql-query-for-the-mr-popover-failes-on-the-frontend.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Make sure we are receiving the proper information on the MR Popover by updating
- the IID in the graphql query
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/63298-prevent-excessive-sanitization-asciidoc.yml b/changelogs/unreleased/63298-prevent-excessive-sanitization-asciidoc.yml
deleted file mode 100644
index cd8206cdb99..00000000000
--- a/changelogs/unreleased/63298-prevent-excessive-sanitization-asciidoc.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "Prevent excessive sanitization of AsciiDoc ouptut"
-merge_request: 30290
-author: Guillaume Grossetie
-type: added \ No newline at end of file
diff --git a/changelogs/unreleased/63438-oauth2-support-with-gitlab-personal-access-token.yml b/changelogs/unreleased/63438-oauth2-support-with-gitlab-personal-access-token.yml
new file mode 100644
index 00000000000..815010e15ae
--- /dev/null
+++ b/changelogs/unreleased/63438-oauth2-support-with-gitlab-personal-access-token.yml
@@ -0,0 +1,5 @@
+---
+title: Personal access tokens are accepted using OAuth2 header format
+merge_request: 30277
+author:
+type: added
diff --git a/changelogs/unreleased/63475-fix-n-1.yml b/changelogs/unreleased/63475-fix-n-1.yml
deleted file mode 100644
index 3ed825290fd..00000000000
--- a/changelogs/unreleased/63475-fix-n-1.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve performance of MergeRequestsController#ci_environment_status endpoint
-merge_request: 30224
-author:
-type: performance
diff --git a/changelogs/unreleased/63479-jira-capitalization.yml b/changelogs/unreleased/63479-jira-capitalization.yml
deleted file mode 100644
index a4cc32beba6..00000000000
--- a/changelogs/unreleased/63479-jira-capitalization.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Replace 'JIRA' with 'Jira'
-merge_request: 29849
-author: Takuya Noguchi
-type: other
diff --git a/changelogs/unreleased/63507-fix-race-condition-fetching-token.yml b/changelogs/unreleased/63507-fix-race-condition-fetching-token.yml
deleted file mode 100644
index 7f2b59fc9eb..00000000000
--- a/changelogs/unreleased/63507-fix-race-condition-fetching-token.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Retry fetching Kubernetes Secret#token (#63507)
-merge_request: 29922
-author:
-type: fixed
diff --git a/changelogs/unreleased/63559-remove-avatar-from-sign-in.yml b/changelogs/unreleased/63559-remove-avatar-from-sign-in.yml
deleted file mode 100644
index 3f7a8e19de5..00000000000
--- a/changelogs/unreleased/63559-remove-avatar-from-sign-in.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Resolve Avatar in Please sign in pattern too large
-merge_request: 29944
-author:
-type: fixed
diff --git a/changelogs/unreleased/63590-pipeline-actions-cause-full-refresh.yml b/changelogs/unreleased/63590-pipeline-actions-cause-full-refresh.yml
deleted file mode 100644
index a1e7d4679d8..00000000000
--- a/changelogs/unreleased/63590-pipeline-actions-cause-full-refresh.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix pipelines table to update without refreshing after action
-merge_request: 30190
-author:
-type: fixed
diff --git a/changelogs/unreleased/63656-runner-tags-search-dropdown-is-empty.yml b/changelogs/unreleased/63656-runner-tags-search-dropdown-is-empty.yml
deleted file mode 100644
index 08c415f4a1c..00000000000
--- a/changelogs/unreleased/63656-runner-tags-search-dropdown-is-empty.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix runner tags search dropdown being empty when there are tags
-merge_request: 29985
-author:
-type: fixed
diff --git a/changelogs/unreleased/63667-hashed-storage-migration-count-correctly.yml b/changelogs/unreleased/63667-hashed-storage-migration-count-correctly.yml
deleted file mode 100644
index ead79be2505..00000000000
--- a/changelogs/unreleased/63667-hashed-storage-migration-count-correctly.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Display the correct amount of projects being migrated/rolled-back to Hashed Storage when specifying ranges
-merge_request: 29996
-author:
-type: fixed
diff --git a/changelogs/unreleased/63691-fix-doc-link.yml b/changelogs/unreleased/63691-fix-doc-link.yml
deleted file mode 100644
index e63756e8227..00000000000
--- a/changelogs/unreleased/63691-fix-doc-link.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Correct link to docs for External Dashboard
-merge_request: 30019
-author:
-type: fixed
diff --git a/changelogs/unreleased/63833-fix-jira-issues-url.yml b/changelogs/unreleased/63833-fix-jira-issues-url.yml
new file mode 100644
index 00000000000..24d6bca3842
--- /dev/null
+++ b/changelogs/unreleased/63833-fix-jira-issues-url.yml
@@ -0,0 +1,5 @@
+---
+title: Handle trailing slashes when generating Jira issue URLs
+merge_request: 30911
+author:
+type: fixed
diff --git a/changelogs/unreleased/63873-process-start-time.yml b/changelogs/unreleased/63873-process-start-time.yml
deleted file mode 100644
index b11a66ca106..00000000000
--- a/changelogs/unreleased/63873-process-start-time.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Change ruby_process_start_time_seconds metric to unix timestamp instead of
- seconds from boot.
-merge_request: 30195
-author:
-type: fixed
diff --git a/changelogs/unreleased/63945-update-mixin-deep-to-1-3-2.yml b/changelogs/unreleased/63945-update-mixin-deep-to-1-3-2.yml
deleted file mode 100644
index a0ef34f3700..00000000000
--- a/changelogs/unreleased/63945-update-mixin-deep-to-1-3-2.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update mixin-deep to 1.3.2
-merge_request: 30223
-author: Takuya Noguchi
-type: other
diff --git a/changelogs/unreleased/63971-remove-istanbul.yml b/changelogs/unreleased/63971-remove-istanbul.yml
deleted file mode 100644
index 674df82db35..00000000000
--- a/changelogs/unreleased/63971-remove-istanbul.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove istanbul JavaScript package
-merge_request: 30232
-author: Takuya Noguchi
-type: other
diff --git a/changelogs/unreleased/64066-fix-uneven-click-areas.yml b/changelogs/unreleased/64066-fix-uneven-click-areas.yml
deleted file mode 100644
index ce0572cad34..00000000000
--- a/changelogs/unreleased/64066-fix-uneven-click-areas.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix spacing issues for toasts
-merge_request: 30345
-author:
-type: fixed
diff --git a/changelogs/unreleased/64070-asciidoctor-enable-section-anchors.yml b/changelogs/unreleased/64070-asciidoctor-enable-section-anchors.yml
deleted file mode 100644
index 51c1537a159..00000000000
--- a/changelogs/unreleased/64070-asciidoctor-enable-section-anchors.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "Enable section anchors in Asciidoctor"
-merge_request: 30666
-author: Guillaume Grossetie
-type: added \ No newline at end of file
diff --git a/changelogs/unreleased/64091-fix-broken-terminal.yml b/changelogs/unreleased/64091-fix-broken-terminal.yml
deleted file mode 100644
index 156f6de5008..00000000000
--- a/changelogs/unreleased/64091-fix-broken-terminal.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix environments broken terminal
-merge_request: 30401
-author:
-type: fixed
diff --git a/changelogs/unreleased/64091-fix-sprockets-paths.yml b/changelogs/unreleased/64091-fix-sprockets-paths.yml
new file mode 100644
index 00000000000..fcd8b2faa49
--- /dev/null
+++ b/changelogs/unreleased/64091-fix-sprockets-paths.yml
@@ -0,0 +1,5 @@
+---
+title: Fix xterm css not loading for environment terminal
+merge_request: 31023
+author:
+type: fixed
diff --git a/changelogs/unreleased/64160-fix-duplicate-buttons.yml b/changelogs/unreleased/64160-fix-duplicate-buttons.yml
new file mode 100644
index 00000000000..12416a611ed
--- /dev/null
+++ b/changelogs/unreleased/64160-fix-duplicate-buttons.yml
@@ -0,0 +1,5 @@
+---
+title: Remove duplicate buttons in diff discussion
+merge_request: 30757
+author:
+type: fixed
diff --git a/changelogs/unreleased/64161-gitlab-fqdn.yml b/changelogs/unreleased/64161-gitlab-fqdn.yml
deleted file mode 100644
index 2946be37caa..00000000000
--- a/changelogs/unreleased/64161-gitlab-fqdn.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add CI variable to provide GitLab HOST
-merge_request: 30417
-author:
-type: added
diff --git a/changelogs/unreleased/64176-fix-error-handling.yml b/changelogs/unreleased/64176-fix-error-handling.yml
deleted file mode 100644
index e7a9a5897ae..00000000000
--- a/changelogs/unreleased/64176-fix-error-handling.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix invalid SSL certificate errors on Drone CI service
-merge_request: 30422
-author:
-type: fixed
diff --git a/changelogs/unreleased/64249-align-container-registry-empty-state-with-design-guidelines.yml b/changelogs/unreleased/64249-align-container-registry-empty-state-with-design-guidelines.yml
deleted file mode 100644
index ecdb4b6bed1..00000000000
--- a/changelogs/unreleased/64249-align-container-registry-empty-state-with-design-guidelines.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Alignign empty container registry message with design guidelines
-merge_request: 30502
-author:
-type: other
diff --git a/changelogs/unreleased/64265-center-loading-icon.yml b/changelogs/unreleased/64265-center-loading-icon.yml
new file mode 100644
index 00000000000..cd4253b63c6
--- /dev/null
+++ b/changelogs/unreleased/64265-center-loading-icon.yml
@@ -0,0 +1,5 @@
+---
+title: Center loading icon in CI action component
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/64295-predictable-environment-slugs.yml b/changelogs/unreleased/64295-predictable-environment-slugs.yml
new file mode 100644
index 00000000000..de581b2b8e1
--- /dev/null
+++ b/changelogs/unreleased/64295-predictable-environment-slugs.yml
@@ -0,0 +1,5 @@
+---
+title: Use predictable environment slugs
+merge_request: 30551
+author:
+type: added
diff --git a/changelogs/unreleased/64314-ci-icon.yml b/changelogs/unreleased/64314-ci-icon.yml
deleted file mode 100644
index 8a550b6fa5b..00000000000
--- a/changelogs/unreleased/64314-ci-icon.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Aligns CI icon in Merge Request dashboard
-merge_request: 30558
-author:
-type: fixed
diff --git a/changelogs/unreleased/64321-wrong-url-when-creating-milestones-from-instance-milestones-dashboard.yml b/changelogs/unreleased/64321-wrong-url-when-creating-milestones-from-instance-milestones-dashboard.yml
deleted file mode 100644
index 825247db3e7..00000000000
--- a/changelogs/unreleased/64321-wrong-url-when-creating-milestones-from-instance-milestones-dashboard.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix wrong URL when creating milestones from instance milestones dashboard
-merge_request: 30512
-author:
-type: fixed
diff --git a/changelogs/unreleased/64331-Assignee-field-in-a-new-issue-has-an-incorrect-line-wrap.yml b/changelogs/unreleased/64331-Assignee-field-in-a-new-issue-has-an-incorrect-line-wrap.yml
deleted file mode 100644
index 7d67b94869d..00000000000
--- a/changelogs/unreleased/64331-Assignee-field-in-a-new-issue-has-an-incorrect-line-wrap.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed incorrect line wrap for assignee label in issues
-merge_request: 30523
-author: Marc Schwede
-type: fixed
diff --git a/changelogs/unreleased/64407-vfazio-quirk-omniauth-strategies-openidconnect.yml b/changelogs/unreleased/64407-vfazio-quirk-omniauth-strategies-openidconnect.yml
deleted file mode 100644
index c2e300863fb..00000000000
--- a/changelogs/unreleased/64407-vfazio-quirk-omniauth-strategies-openidconnect.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow client authentication method to be configured for OpenID Connect
-merge_request: 30683
-author: Vincent Fazio
-type: fixed
diff --git a/changelogs/unreleased/64416-lodash-4-6-2-for-prototype-pollution.yml b/changelogs/unreleased/64416-lodash-4-6-2-for-prototype-pollution.yml
deleted file mode 100644
index cd8885233de..00000000000
--- a/changelogs/unreleased/64416-lodash-4-6-2-for-prototype-pollution.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update lodash to 4.7.14 and lodash.mergewith to 4.6.2
-merge_request: 30602
-author: Takuya Noguchi
-type: security
diff --git a/changelogs/unreleased/64697-markdown-issues-checkbox-inside-blockquote-status-won-t-be-saved.yml b/changelogs/unreleased/64697-markdown-issues-checkbox-inside-blockquote-status-won-t-be-saved.yml
new file mode 100644
index 00000000000..00664d64050
--- /dev/null
+++ b/changelogs/unreleased/64697-markdown-issues-checkbox-inside-blockquote-status-won-t-be-saved.yml
@@ -0,0 +1,5 @@
+---
+title: Better support clickable tasklists inside blockquotes
+merge_request: 30952
+author:
+type: fixed
diff --git a/changelogs/unreleased/64700-fix-the-color-of-the-visibility-icon-on-project-lists.yml b/changelogs/unreleased/64700-fix-the-color-of-the-visibility-icon-on-project-lists.yml
new file mode 100644
index 00000000000..0d2fbaf01ed
--- /dev/null
+++ b/changelogs/unreleased/64700-fix-the-color-of-the-visibility-icon-on-project-lists.yml
@@ -0,0 +1,5 @@
+---
+title: Ensure visibility icons in group/project listings are grey
+merge_request: 30858
+author:
+type: fixed
diff --git a/changelogs/unreleased/64731-fix-project-auto-devops-api.yml b/changelogs/unreleased/64731-fix-project-auto-devops-api.yml
new file mode 100644
index 00000000000..7fe2c036773
--- /dev/null
+++ b/changelogs/unreleased/64731-fix-project-auto-devops-api.yml
@@ -0,0 +1,5 @@
+---
+title: Fix the project auto devops API
+merge_request: 30946
+author:
+type: fixed
diff --git a/changelogs/unreleased/64870-can-t-save-pages-domain-form-with-let-s-encrypt-enabled-if-current-certificate-is-outdated.yml b/changelogs/unreleased/64870-can-t-save-pages-domain-form-with-let-s-encrypt-enabled-if-current-certificate-is-outdated.yml
new file mode 100644
index 00000000000..291901d64ed
--- /dev/null
+++ b/changelogs/unreleased/64870-can-t-save-pages-domain-form-with-let-s-encrypt-enabled-if-current-certificate-is-outdated.yml
@@ -0,0 +1,6 @@
+---
+title: Fix "Certificate misses intermediates" UI error when enabling Let's Encrypt
+ integration for pages domain
+merge_request: 30995
+author:
+type: fixed
diff --git a/changelogs/unreleased/FixLocaleEN.yml b/changelogs/unreleased/FixLocaleEN.yml
new file mode 100644
index 00000000000..49738a6d127
--- /dev/null
+++ b/changelogs/unreleased/FixLocaleEN.yml
@@ -0,0 +1,5 @@
+---
+title: Remove duplicated mapping key in config/locales/en.yml
+merge_request: 30980
+author: Peter Dave Hello
+type: fixed
diff --git a/changelogs/unreleased/Remove-unresolved-class-in-discussion-header.yml b/changelogs/unreleased/Remove-unresolved-class-in-discussion-header.yml
deleted file mode 100644
index 3695f3063f3..00000000000
--- a/changelogs/unreleased/Remove-unresolved-class-in-discussion-header.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove unresolved class and fixed height in discussion header
-merge_request: 28440
-author: David Palubin
-type: other
diff --git a/changelogs/unreleased/add-clusters-to-deployment.yml b/changelogs/unreleased/add-clusters-to-deployment.yml
deleted file mode 100644
index c85bd3635cc..00000000000
--- a/changelogs/unreleased/add-clusters-to-deployment.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Persist the cluster a deployment was deployed to
-merge_request: 29960
-author:
-type: fixed
diff --git a/changelogs/unreleased/add-metrics-dashboard-permission-check.yml b/changelogs/unreleased/add-metrics-dashboard-permission-check.yml
deleted file mode 100644
index 0ea2c4c8e41..00000000000
--- a/changelogs/unreleased/add-metrics-dashboard-permission-check.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add permission check to metrics dashboards endpoint
-merge_request: 30017
-author:
-type: added
diff --git a/changelogs/unreleased/add-salesforce-logo.yml b/changelogs/unreleased/add-salesforce-logo.yml
deleted file mode 100644
index 13766821b88..00000000000
--- a/changelogs/unreleased/add-salesforce-logo.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add salesforce logo for salesforce SSO
-merge_request: 28857
-author:
-type: changed
diff --git a/changelogs/unreleased/add-strategies-column-to-scopes-table.yml b/changelogs/unreleased/add-strategies-column-to-scopes-table.yml
deleted file mode 100644
index 0bb87fca014..00000000000
--- a/changelogs/unreleased/add-strategies-column-to-scopes-table.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add strategies column to operations_feature_flag_scopes table
-merge_request: 29808
-author:
-type: other
diff --git a/changelogs/unreleased/add-support-for-start-sha-to-commits-api.yml b/changelogs/unreleased/add-support-for-start-sha-to-commits-api.yml
new file mode 100644
index 00000000000..f810c2c5ada
--- /dev/null
+++ b/changelogs/unreleased/add-support-for-start-sha-to-commits-api.yml
@@ -0,0 +1,5 @@
+---
+title: Add support for start_sha to commits API
+merge_request: 29598
+author:
+type: changed
diff --git a/changelogs/unreleased/allow-reactive-caching-of-nil.yml b/changelogs/unreleased/allow-reactive-caching-of-nil.yml
deleted file mode 100644
index abd88c09d74..00000000000
--- a/changelogs/unreleased/allow-reactive-caching-of-nil.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow ReactiveCaching to support nil value
-merge_request: 30456
-author:
-type: performance
diff --git a/changelogs/unreleased/always-allow-prometheus-access-in-dev.yml b/changelogs/unreleased/always-allow-prometheus-access-in-dev.yml
deleted file mode 100644
index acd944ea684..00000000000
--- a/changelogs/unreleased/always-allow-prometheus-access-in-dev.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Always allow access to health endpoints from localhost in dev
-merge_request: 29930
-author:
-type: other
diff --git a/changelogs/unreleased/always-display-environment-selector.yml b/changelogs/unreleased/always-display-environment-selector.yml
deleted file mode 100644
index 7a55e8f3e5d..00000000000
--- a/changelogs/unreleased/always-display-environment-selector.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix broken environment selector and always display it on monitoring dashboard
-merge_request: 29705
-author:
-type: fixed
diff --git a/changelogs/unreleased/an-sidekiq-chaos.yml b/changelogs/unreleased/an-sidekiq-chaos.yml
new file mode 100644
index 00000000000..cede35c95cc
--- /dev/null
+++ b/changelogs/unreleased/an-sidekiq-chaos.yml
@@ -0,0 +1,5 @@
+---
+title: Adds chaos endpoints to Sidekiq
+merge_request: 30814
+author:
+type: other
diff --git a/changelogs/unreleased/api-doc-negative-commit-message-push-rule.yml b/changelogs/unreleased/api-doc-negative-commit-message-push-rule.yml
deleted file mode 100644
index 0500978a2e1..00000000000
--- a/changelogs/unreleased/api-doc-negative-commit-message-push-rule.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "Document the negative commit message push rule for the API."
-merge_request: 14004
-author: Maikel Vlasman
-type: added
diff --git a/changelogs/unreleased/asciidoc-enable-syntax-highlighting.yml b/changelogs/unreleased/asciidoc-enable-syntax-highlighting.yml
deleted file mode 100644
index 558ee7b6224..00000000000
--- a/changelogs/unreleased/asciidoc-enable-syntax-highlighting.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "Enable syntax highlighting for AsciiDoc"
-merge_request: 29835
-author: Guillaume Grossetie
-type: added \ No newline at end of file
diff --git a/changelogs/unreleased/asciidoctor-upgrade.yml b/changelogs/unreleased/asciidoctor-upgrade.yml
deleted file mode 100644
index 50a7cb21e7d..00000000000
--- a/changelogs/unreleased/asciidoctor-upgrade.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade asciidoctor version to 2.0.10
-merge_request: 29741
-author: Rajendra Kadam
-type: added
diff --git a/changelogs/unreleased/backstage-gb-improve-performance-environment-statuses-endpoint.yml b/changelogs/unreleased/backstage-gb-improve-performance-environment-statuses-endpoint.yml
deleted file mode 100644
index f614e076268..00000000000
--- a/changelogs/unreleased/backstage-gb-improve-performance-environment-statuses-endpoint.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve performance of fetching environments statuses
-merge_request: 30560
-author:
-type: performance
diff --git a/changelogs/unreleased/bjk-64064_cache_metrics.yml b/changelogs/unreleased/bjk-64064_cache_metrics.yml
new file mode 100644
index 00000000000..c9baff7cb7b
--- /dev/null
+++ b/changelogs/unreleased/bjk-64064_cache_metrics.yml
@@ -0,0 +1,5 @@
+---
+title: Adjust redis cache metrics
+merge_request: 30572
+author:
+type: changed
diff --git a/changelogs/unreleased/bjk-fix_prom_example.yml b/changelogs/unreleased/bjk-fix_prom_example.yml
deleted file mode 100644
index 2f81bc6196b..00000000000
--- a/changelogs/unreleased/bjk-fix_prom_example.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update example Prometheus scrape config
-merge_request: 30739
-author:
-type: other
diff --git a/changelogs/unreleased/bjk-usage_ping.yml b/changelogs/unreleased/bjk-usage_ping.yml
new file mode 100644
index 00000000000..dee6c1ad291
--- /dev/null
+++ b/changelogs/unreleased/bjk-usage_ping.yml
@@ -0,0 +1,5 @@
+---
+title: Update usage ping cron behavior
+merge_request: 30842
+author:
+type: performance
diff --git a/changelogs/unreleased/bvl-markdown-graphql.yml b/changelogs/unreleased/bvl-markdown-graphql.yml
deleted file mode 100644
index c2432b3ae81..00000000000
--- a/changelogs/unreleased/bvl-markdown-graphql.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Render GFM in GraphQL
-merge_request: 29700
-author:
-type: added
diff --git a/changelogs/unreleased/bvl-rename-routes-after-user-rename.yml b/changelogs/unreleased/bvl-rename-routes-after-user-rename.yml
deleted file mode 100644
index 8b0a01dea53..00000000000
--- a/changelogs/unreleased/bvl-rename-routes-after-user-rename.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update a user's routes after updating their name
-merge_request: 23272
-author:
-type: fixed
diff --git a/changelogs/unreleased/caneldem-master-patch-77839.yml b/changelogs/unreleased/caneldem-master-patch-77839.yml
deleted file mode 100644
index 6239bcf67c4..00000000000
--- a/changelogs/unreleased/caneldem-master-patch-77839.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Propagate python version variable
-merge_request:
-author: Can Eldem
-type: changed
diff --git a/changelogs/unreleased/ce-11098-update-merge-request-settings-description-text.yml b/changelogs/unreleased/ce-11098-update-merge-request-settings-description-text.yml
deleted file mode 100644
index 9f6a2040095..00000000000
--- a/changelogs/unreleased/ce-11098-update-merge-request-settings-description-text.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update merge requests section description text on project settings page
-merge_request: 27838
-author:
-type: changed \ No newline at end of file
diff --git a/changelogs/unreleased/centralize-markdownlint-config.yml b/changelogs/unreleased/centralize-markdownlint-config.yml
deleted file mode 100644
index 9ca36078cf1..00000000000
--- a/changelogs/unreleased/centralize-markdownlint-config.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Centralize markdownlint configuration
-merge_request: 30263
-author:
-type: other
diff --git a/changelogs/unreleased/check-min-schema-migrate.yml b/changelogs/unreleased/check-min-schema-migrate.yml
deleted file mode 100644
index d0f4ae1f5d7..00000000000
--- a/changelogs/unreleased/check-min-schema-migrate.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added a min schema version check to db:migrate
-merge_request: 29882
-author:
-type: added
diff --git a/changelogs/unreleased/clusters-group-cte.yml b/changelogs/unreleased/clusters-group-cte.yml
deleted file mode 100644
index 4b51249062d..00000000000
--- a/changelogs/unreleased/clusters-group-cte.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use CTE to fetch clusters hierarchy in single query
-merge_request: 30063
-author:
-type: performance
diff --git a/changelogs/unreleased/create-merge-train-ref-ce.yml b/changelogs/unreleased/create-merge-train-ref-ce.yml
deleted file mode 100644
index b0b95275f58..00000000000
--- a/changelogs/unreleased/create-merge-train-ref-ce.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Extend `MergeToRefService` to create merge ref from an arbitrary ref
-merge_request: 30361
-author:
-type: added
diff --git a/changelogs/unreleased/db-update-geo-nodes-primary.yml b/changelogs/unreleased/db-update-geo-nodes-primary.yml
deleted file mode 100644
index 7c5203353ac..00000000000
--- a/changelogs/unreleased/db-update-geo-nodes-primary.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Disallow `NULL` values for `geo_nodes.primary` column
-merge_request: 29818
-author: Arun Kumar Mohan
-type: other
diff --git a/changelogs/unreleased/dm-submodule-helper-routing.yml b/changelogs/unreleased/dm-submodule-helper-routing.yml
new file mode 100644
index 00000000000..779d4d167df
--- /dev/null
+++ b/changelogs/unreleased/dm-submodule-helper-routing.yml
@@ -0,0 +1,5 @@
+---
+title: Fix bug that caused diffs not to show on MRs with changes to submodules
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/dohtaset.yml b/changelogs/unreleased/dohtaset.yml
deleted file mode 100644
index 5b917bd06d8..00000000000
--- a/changelogs/unreleased/dohtaset.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix charts on Cluster health page
-merge_request: 30073
-author:
-type: fixed
diff --git a/changelogs/unreleased/dz-remove-deprecated-group-routes.yml b/changelogs/unreleased/dz-remove-deprecated-group-routes.yml
deleted file mode 100644
index bfa62c620d5..00000000000
--- a/changelogs/unreleased/dz-remove-deprecated-group-routes.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove deprecated group routes
-merge_request: 29351
-author:
-type: removed
diff --git a/changelogs/unreleased/dz-remove-deprecated-user-routes.yml b/changelogs/unreleased/dz-remove-deprecated-user-routes.yml
deleted file mode 100644
index 92c2e39dd20..00000000000
--- a/changelogs/unreleased/dz-remove-deprecated-user-routes.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove depreated /u/:username routing
-merge_request: 30044
-author:
-type: removed
diff --git a/changelogs/unreleased/embedded-metrics-be-2.yml b/changelogs/unreleased/embedded-metrics-be-2.yml
deleted file mode 100644
index 2623b4a2e0c..00000000000
--- a/changelogs/unreleased/embedded-metrics-be-2.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Expose placeholder element for metrics charts in GFM
-merge_request: 29861
-author:
-type: added
diff --git a/changelogs/unreleased/expose-saml-provider-id-to-users-api.yml b/changelogs/unreleased/expose-saml-provider-id-to-users-api.yml
deleted file mode 100644
index 6f0e4f4cf7f..00000000000
--- a/changelogs/unreleased/expose-saml-provider-id-to-users-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Expose saml_provider_id in the users API
-merge_request: 14045
-author:
-type: added
diff --git a/changelogs/unreleased/fe-delete-old-boardservice-on-component-modal-footer.yml b/changelogs/unreleased/fe-delete-old-boardservice-on-component-modal-footer.yml
deleted file mode 100644
index 4392b72443b..00000000000
--- a/changelogs/unreleased/fe-delete-old-boardservice-on-component-modal-footer.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: change the use of boardService in favor of boardsStore on footer for the board
- component
-merge_request: 30616
-author: eduarmreyes
-type: other
diff --git a/changelogs/unreleased/fe-delete-old-boardservice.yml b/changelogs/unreleased/fe-delete-old-boardservice.yml
new file mode 100644
index 00000000000..bb06bfe80d5
--- /dev/null
+++ b/changelogs/unreleased/fe-delete-old-boardservice.yml
@@ -0,0 +1,6 @@
+---
+title: Change BoardService in favor of boardsStore on board blank state of the component
+ board
+merge_request: 30546
+author: eduarmreyes
+type: other
diff --git a/changelogs/unreleased/fe-issue-reorder.yml b/changelogs/unreleased/fe-issue-reorder.yml
deleted file mode 100644
index aca334b6149..00000000000
--- a/changelogs/unreleased/fe-issue-reorder.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Bring Manual Ordering on Issue List
-merge_request: 29410
-author:
-type: added
diff --git a/changelogs/unreleased/feature-gb-serverless-app-deployment-template.yml b/changelogs/unreleased/feature-gb-serverless-app-deployment-template.yml
new file mode 100644
index 00000000000..bd9001bd671
--- /dev/null
+++ b/changelogs/unreleased/feature-gb-serverless-app-deployment-template.yml
@@ -0,0 +1,5 @@
+---
+title: Deploy serverless apps with gitlabktl
+merge_request: 30740
+author:
+type: added
diff --git a/changelogs/unreleased/feature-uninstall_cluster_ingress.yml b/changelogs/unreleased/feature-uninstall_cluster_ingress.yml
deleted file mode 100644
index c3f8464c4b4..00000000000
--- a/changelogs/unreleased/feature-uninstall_cluster_ingress.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow Ingress to be uninstalled from the UI
-merge_request: 29977
-author:
-type: added
diff --git a/changelogs/unreleased/feature-uninstall_jupyter_hub_app.yml b/changelogs/unreleased/feature-uninstall_jupyter_hub_app.yml
deleted file mode 100644
index 28753aa719c..00000000000
--- a/changelogs/unreleased/feature-uninstall_jupyter_hub_app.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow JupyterHub to be uninstalled from the UI
-merge_request: 30097
-author:
-type: added
diff --git a/changelogs/unreleased/fix-alignment-on-security-reports.yml b/changelogs/unreleased/fix-alignment-on-security-reports.yml
new file mode 100644
index 00000000000..5339b6d764d
--- /dev/null
+++ b/changelogs/unreleased/fix-alignment-on-security-reports.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes alignment issues with reports
+merge_request: 30839
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-broken-vue-i18n-strings.yml b/changelogs/unreleased/fix-broken-vue-i18n-strings.yml
deleted file mode 100644
index 69cec8a6b1b..00000000000
--- a/changelogs/unreleased/fix-broken-vue-i18n-strings.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix broken warnings while Editing Issues and Edit File on MR
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-comment-race-condition.yml b/changelogs/unreleased/fix-comment-race-condition.yml
deleted file mode 100644
index d1f5b8fe01f..00000000000
--- a/changelogs/unreleased/fix-comment-race-condition.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix commenting before discussions are loaded
-merge_request: 30724
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-facivon-url-if-uploads-object-store-enabled.yml b/changelogs/unreleased/fix-facivon-url-if-uploads-object-store-enabled.yml
deleted file mode 100644
index 4e6d9c087ef..00000000000
--- a/changelogs/unreleased/fix-facivon-url-if-uploads-object-store-enabled.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Fix favicon path with uploads of object store'
-merge_request: 29482
-author: Roger Meier
-type: fixed
diff --git a/changelogs/unreleased/fix-i18n-updated-projects.yml b/changelogs/unreleased/fix-i18n-updated-projects.yml
new file mode 100644
index 00000000000..408ee438480
--- /dev/null
+++ b/changelogs/unreleased/fix-i18n-updated-projects.yml
@@ -0,0 +1,5 @@
+---
+title: Properly translate term in projects list
+merge_request: 30958
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-jupyter-git-v3.yml b/changelogs/unreleased/fix-jupyter-git-v3.yml
deleted file mode 100644
index 8aaaaf249fb..00000000000
--- a/changelogs/unreleased/fix-jupyter-git-v3.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix Jupyter-Git integration
-merge_request: 30020
-author: Amit Rathi
-type: fixed
diff --git a/changelogs/unreleased/fix-median-counting-for-cycle-analytics.yml b/changelogs/unreleased/fix-median-counting-for-cycle-analytics.yml
deleted file mode 100644
index 6ae6db08ba1..00000000000
--- a/changelogs/unreleased/fix-median-counting-for-cycle-analytics.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix median counting for cycle analytics
-merge_request: 30229
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-pipeline-schedule-edge-case.yml b/changelogs/unreleased/fix-pipeline-schedule-edge-case.yml
deleted file mode 100644
index 2b7e3611567..00000000000
--- a/changelogs/unreleased/fix-pipeline-schedule-edge-case.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Fix pipeline schedule does not run correctly when it's scheduled at the same
- time with the cron worker
-merge_request: 29848
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-sidekiq-transaction-check-race.yml b/changelogs/unreleased/fix-sidekiq-transaction-check-race.yml
deleted file mode 100644
index 89ae4abfe11..00000000000
--- a/changelogs/unreleased/fix-sidekiq-transaction-check-race.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix race in forbid_sidekiq_in_transactions.rb
-merge_request: 30359
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-unicorn-sampler-workers-count.yml b/changelogs/unreleased/fix-unicorn-sampler-workers-count.yml
deleted file mode 100644
index 9a263b7f460..00000000000
--- a/changelogs/unreleased/fix-unicorn-sampler-workers-count.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make unicorn_workers to return meaningful results
-merge_request: 30506
-author:
-type: fixed
diff --git a/changelogs/unreleased/fj-avoid-incresaing-usage-ping-when-not-enabled.yml b/changelogs/unreleased/fj-avoid-incresaing-usage-ping-when-not-enabled.yml
new file mode 100644
index 00000000000..f1077f2d56d
--- /dev/null
+++ b/changelogs/unreleased/fj-avoid-incresaing-usage-ping-when-not-enabled.yml
@@ -0,0 +1,5 @@
+---
+title: Avoid increasing redis counters when usage_ping is disabled
+merge_request: 30949
+author:
+type: changed
diff --git a/changelogs/unreleased/fj-count-web-ide-merge-requests.yml b/changelogs/unreleased/fj-count-web-ide-merge-requests.yml
new file mode 100644
index 00000000000..adcd0af37e8
--- /dev/null
+++ b/changelogs/unreleased/fj-count-web-ide-merge-requests.yml
@@ -0,0 +1,5 @@
+---
+title: Add Web IDE Usage Ping for Create SMAU
+merge_request: 30800
+author:
+type: changed
diff --git a/changelogs/unreleased/fj-fix-subgroup-search-url.yml b/changelogs/unreleased/fj-fix-subgroup-search-url.yml
deleted file mode 100644
index bee6e97fb6f..00000000000
--- a/changelogs/unreleased/fj-fix-subgroup-search-url.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix subgroup url in search drop down
-merge_request: 30457
-author:
-type: fixed
diff --git a/changelogs/unreleased/georgekoltsov-63955-fix-import-with-source-branch-deleted.yml b/changelogs/unreleased/georgekoltsov-63955-fix-import-with-source-branch-deleted.yml
deleted file mode 100644
index 72e2621c52a..00000000000
--- a/changelogs/unreleased/georgekoltsov-63955-fix-import-with-source-branch-deleted.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix a bug that prevented projects containing merge request diff comments from being imported
-merge_request: 30630
-author:
-type: fixed
diff --git a/changelogs/unreleased/georgekoltsov-64311-set-visibility-private-if-internal-restricted.yml b/changelogs/unreleased/georgekoltsov-64311-set-visibility-private-if-internal-restricted.yml
new file mode 100644
index 00000000000..18af16e5216
--- /dev/null
+++ b/changelogs/unreleased/georgekoltsov-64311-set-visibility-private-if-internal-restricted.yml
@@ -0,0 +1,5 @@
+---
+title: Set visibility level 'Private' for restricted 'Internal' imported projects when 'Internal' visibility setting is restricted in admin settings
+merge_request: 30522
+author:
+type: other
diff --git a/changelogs/unreleased/gitaly-version-v1-53-0.yml b/changelogs/unreleased/gitaly-version-v1-53-0.yml
deleted file mode 100644
index 7d3e5ce3cb3..00000000000
--- a/changelogs/unreleased/gitaly-version-v1-53-0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade to Gitaly v1.53.0
-merge_request: 30614
-author:
-type: changed
diff --git a/changelogs/unreleased/gitaly-version-v1.49.0.yml b/changelogs/unreleased/gitaly-version-v1.49.0.yml
deleted file mode 100644
index 8795bab0209..00000000000
--- a/changelogs/unreleased/gitaly-version-v1.49.0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade to Gitaly v1.49.0
-merge_request: 29990
-author:
-type: changed
diff --git a/changelogs/unreleased/gitaly-version-v1.51.0.yml b/changelogs/unreleased/gitaly-version-v1.51.0.yml
deleted file mode 100644
index 00d52a190f3..00000000000
--- a/changelogs/unreleased/gitaly-version-v1.51.0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade to Gitaly v1.51.0
-merge_request: 30353
-author:
-type: changed
diff --git a/changelogs/unreleased/gitaly-version-v1.52.0.yml b/changelogs/unreleased/gitaly-version-v1.52.0.yml
deleted file mode 100644
index d56a392c4ff..00000000000
--- a/changelogs/unreleased/gitaly-version-v1.52.0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade to Gitaly v1.52.0
-merge_request: 30568
-author:
-type: changed
diff --git a/changelogs/unreleased/graphql-tree-last-commit.yml b/changelogs/unreleased/graphql-tree-last-commit.yml
deleted file mode 100644
index 5104ca6687e..00000000000
--- a/changelogs/unreleased/graphql-tree-last-commit.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added commit type to tree GraphQL response
-merge_request: 29412
-author:
-type: added
diff --git a/changelogs/unreleased/group-milestones-dashboard-blunceford.yml b/changelogs/unreleased/group-milestones-dashboard-blunceford.yml
new file mode 100644
index 00000000000..a6ded27cb8c
--- /dev/null
+++ b/changelogs/unreleased/group-milestones-dashboard-blunceford.yml
@@ -0,0 +1,5 @@
+---
+title: Fix bug in dashboard display of closed milestones
+merge_request: 30820
+author:
+type: fixed
diff --git a/changelogs/unreleased/gt-remove-tooltip-directive-on-project-avatar-image-component.yml b/changelogs/unreleased/gt-remove-tooltip-directive-on-project-avatar-image-component.yml
deleted file mode 100644
index d9ca20d9d4d..00000000000
--- a/changelogs/unreleased/gt-remove-tooltip-directive-on-project-avatar-image-component.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove tooltip directive on project avatar image component
-merge_request: 29631
-author: George Tsiolis
-type: performance
diff --git a/changelogs/unreleased/hfy-apply-knative-cluster-role-on-service-account-creation.yml b/changelogs/unreleased/hfy-apply-knative-cluster-role-on-service-account-creation.yml
deleted file mode 100644
index 958334cc28e..00000000000
--- a/changelogs/unreleased/hfy-apply-knative-cluster-role-on-service-account-creation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Create Knative role and binding with service account
-merge_request: 30235
-author:
-type: changed
diff --git a/changelogs/unreleased/hide-restricted-visibility-radio.yml b/changelogs/unreleased/hide-restricted-visibility-radio.yml
deleted file mode 100644
index ebc8b787890..00000000000
--- a/changelogs/unreleased/hide-restricted-visibility-radio.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Hide restricted and disallowed visibility radios
-merge_request: 30590
-author:
-type: fixed
diff --git a/changelogs/unreleased/id-clean-up-mr-assignees.yml b/changelogs/unreleased/id-clean-up-mr-assignees.yml
deleted file mode 100644
index 7ff03c9f00b..00000000000
--- a/changelogs/unreleased/id-clean-up-mr-assignees.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add cleanup migration for MR's multiple assignees
-merge_request: 30261
-author:
-type: changed
diff --git a/changelogs/unreleased/id-extract-widget-into-different-request.yml b/changelogs/unreleased/id-extract-widget-into-different-request.yml
deleted file mode 100644
index 3b9f5fdd6bd..00000000000
--- a/changelogs/unreleased/id-extract-widget-into-different-request.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add a separate endpoint for fetching MRs serialized as widgets
-merge_request: 29979
-author:
-type: performance
diff --git a/changelogs/unreleased/id-stale-branches.yml b/changelogs/unreleased/id-stale-branches.yml
deleted file mode 100644
index 2f35c5a12c9..00000000000
--- a/changelogs/unreleased/id-stale-branches.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add endpoint for fetching diverging commit counts
-merge_request: 29802
-author:
-type: performance
diff --git a/changelogs/unreleased/issue-63222.yml b/changelogs/unreleased/issue-63222.yml
deleted file mode 100644
index bc6520b9fc5..00000000000
--- a/changelogs/unreleased/issue-63222.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Set default sort method for dashboard projects list
-merge_request: 29830
-author: David Palubin
-type: fixed
diff --git a/changelogs/unreleased/issue-zoom-url.yml b/changelogs/unreleased/issue-zoom-url.yml
deleted file mode 100644
index e0bd5478192..00000000000
--- a/changelogs/unreleased/issue-zoom-url.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Extract zoom link from issue and pass to frontend
-merge_request: 29910
-author: raju249
-type: added
diff --git a/changelogs/unreleased/issue_64021.yml b/changelogs/unreleased/issue_64021.yml
deleted file mode 100644
index 088d9348619..00000000000
--- a/changelogs/unreleased/issue_64021.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Skip spam check for task list updates
-merge_request: 30279
-author:
-type: fixed
diff --git a/changelogs/unreleased/jc-detect-nfs-for-rugged.yml b/changelogs/unreleased/jc-detect-nfs-for-rugged.yml
deleted file mode 100644
index ca738181a55..00000000000
--- a/changelogs/unreleased/jc-detect-nfs-for-rugged.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use Rugged if we detect storage is NFS and we can access the disk
-merge_request: 29725
-author:
-type: performance
diff --git a/changelogs/unreleased/jc-remove-catfile-flag.yml b/changelogs/unreleased/jc-remove-catfile-flag.yml
deleted file mode 100644
index 6b72de7bc45..00000000000
--- a/changelogs/unreleased/jc-remove-catfile-flag.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove catfile cache feature flag
-merge_request: 30750
-author:
-type: performance
diff --git a/changelogs/unreleased/jprovazn-project-search.yml b/changelogs/unreleased/jprovazn-project-search.yml
new file mode 100644
index 00000000000..f76d4858924
--- /dev/null
+++ b/changelogs/unreleased/jprovazn-project-search.yml
@@ -0,0 +1,5 @@
+---
+title: Order projects in 'Move issue' dropdown by name.
+merge_request: 30778
+author:
+type: fixed
diff --git a/changelogs/unreleased/jramsay-enable-object-dedupe-by-default.yml b/changelogs/unreleased/jramsay-enable-object-dedupe-by-default.yml
deleted file mode 100644
index b953d7c0fc8..00000000000
--- a/changelogs/unreleased/jramsay-enable-object-dedupe-by-default.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable Git object pools
-merge_request: 29595
-author: jramsay
-type: changed
diff --git a/changelogs/unreleased/knative-0-6.yml b/changelogs/unreleased/knative-0-6.yml
deleted file mode 100644
index 66c5c7f343d..00000000000
--- a/changelogs/unreleased/knative-0-6.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Knative version bump 0.5 -> 0.6
-merge_request: 28798
-author: Chris Baumbauer
-type: changed
diff --git a/changelogs/unreleased/limit-amount-of-tests-returned.yml b/changelogs/unreleased/limit-amount-of-tests-returned.yml
deleted file mode 100644
index 0e80a64b6b7..00000000000
--- a/changelogs/unreleased/limit-amount-of-tests-returned.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Limit amount of JUnit tests returned
-merge_request: 30274
-author:
-type: performance
diff --git a/changelogs/unreleased/maintainers-can-create-subgroup.yml b/changelogs/unreleased/maintainers-can-create-subgroup.yml
new file mode 100644
index 00000000000..180f0f7247f
--- /dev/null
+++ b/changelogs/unreleased/maintainers-can-create-subgroup.yml
@@ -0,0 +1,5 @@
+---
+title: Maintainers can create subgroups
+merge_request: 29718
+author: Fabio Papa
+type: changed
diff --git a/changelogs/unreleased/mh-board-tooltips.yml b/changelogs/unreleased/mh-board-tooltips.yml
deleted file mode 100644
index 06fc64c52a7..00000000000
--- a/changelogs/unreleased/mh-board-tooltips.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "'Open' and 'Closed' issue board lists no longer display a redundant tooltip"
-merge_request: 30187
-author:
-type: fixed
diff --git a/changelogs/unreleased/mh-boards-filter-bar.yml b/changelogs/unreleased/mh-boards-filter-bar.yml
deleted file mode 100644
index 3e91b5ef443..00000000000
--- a/changelogs/unreleased/mh-boards-filter-bar.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Display boards filter bar on mobile
-merge_request: 30120
-author:
-type: fixed
diff --git a/changelogs/unreleased/mh-collapsible-boards.yml b/changelogs/unreleased/mh-collapsible-boards.yml
deleted file mode 100644
index b69d6e81cc4..00000000000
--- a/changelogs/unreleased/mh-collapsible-boards.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Labeled issue boards can now collapse
-merge_request: 29955
-author:
-type: added
diff --git a/changelogs/unreleased/mh-colon-autocomplete.yml b/changelogs/unreleased/mh-colon-autocomplete.yml
deleted file mode 100644
index 8b169c22588..00000000000
--- a/changelogs/unreleased/mh-colon-autocomplete.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow auto-completing scoped labels
-merge_request: 29749
-author:
-type: added
diff --git a/changelogs/unreleased/mh-mermaid-linebreaks.yml b/changelogs/unreleased/mh-mermaid-linebreaks.yml
deleted file mode 100644
index e38820d8ce3..00000000000
--- a/changelogs/unreleased/mh-mermaid-linebreaks.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix linebreak rendering in Mermaid flowcharts
-merge_request: 30730
-author:
-type: fixed
diff --git a/changelogs/unreleased/move-all-configs-to-global.yml b/changelogs/unreleased/move-all-configs-to-global.yml
deleted file mode 100644
index ff311d57f8d..00000000000
--- a/changelogs/unreleased/move-all-configs-to-global.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Introduce default: for gitlab-ci.yml'
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/mw-project-list-color-fix.yml b/changelogs/unreleased/mw-project-list-color-fix.yml
deleted file mode 100644
index 6f8b2742ec6..00000000000
--- a/changelogs/unreleased/mw-project-list-color-fix.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add text-secondary to controls in project list
-merge_request: 30567
-author:
-type: fixed
diff --git a/changelogs/unreleased/osw-persist-tmp-snippet-uploads.yml b/changelogs/unreleased/osw-persist-tmp-snippet-uploads.yml
deleted file mode 100644
index 9348626c41d..00000000000
--- a/changelogs/unreleased/osw-persist-tmp-snippet-uploads.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Persist tmp snippet uploads at users
-merge_request:
-author:
-type: security
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 d2744cddebd..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: 29569
-author:
-type: added
diff --git a/changelogs/unreleased/paginate-license-management.yml b/changelogs/unreleased/paginate-license-management.yml
deleted file mode 100644
index c5134978612..00000000000
--- a/changelogs/unreleased/paginate-license-management.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Backport and Docs for Paginate license management and add license search
-merge_request: 27602
-author:
-type: changed
diff --git a/changelogs/unreleased/patch-29.yml b/changelogs/unreleased/patch-29.yml
deleted file mode 100644
index 674c06e1259..00000000000
--- a/changelogs/unreleased/patch-29.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Updates PHP template to php:latest to ensure always targeting latest stable
-merge_request: 30319
-author: Paul Giberson
-type: changed
diff --git a/changelogs/unreleased/po-raw-changes-encoding.yml b/changelogs/unreleased/po-raw-changes-encoding.yml
deleted file mode 100644
index 051d18f50c7..00000000000
--- a/changelogs/unreleased/po-raw-changes-encoding.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Expect bytes from Gitaly RPC GetRawChanges
-merge_request: 28164
-author:
-type: fixed
diff --git a/changelogs/unreleased/pre-releases-38105a.yml b/changelogs/unreleased/pre-releases-38105a.yml
deleted file mode 100644
index 8b7cf6065d4..00000000000
--- a/changelogs/unreleased/pre-releases-38105a.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Show an Upcoming Status for Releases
-merge_request: 29577
-author:
-type: added
diff --git a/changelogs/unreleased/prepare-cycle-analytics-for-group-level.yml b/changelogs/unreleased/prepare-cycle-analytics-for-group-level.yml
deleted file mode 100644
index d7bfc67b208..00000000000
--- a/changelogs/unreleased/prepare-cycle-analytics-for-group-level.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Modify cycle analytics on project level
-merge_request: 30356
-author:
-type: changed
diff --git a/changelogs/unreleased/project_api.yml b/changelogs/unreleased/project_api.yml
deleted file mode 100644
index a04f9bb5608..00000000000
--- a/changelogs/unreleased/project_api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve Project API
-merge_request: 28327
-author: Mathieu Parent
-type: added
diff --git a/changelogs/unreleased/refactor-sentry.yml b/changelogs/unreleased/refactor-sentry.yml
deleted file mode 100644
index 25c5534fae0..00000000000
--- a/changelogs/unreleased/refactor-sentry.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove Sentry from application settings
-merge_request: 28447
-author: Roger Meier
-type: added
diff --git a/changelogs/unreleased/registry-fix-multi-delete-modal.yml b/changelogs/unreleased/registry-fix-multi-delete-modal.yml
deleted file mode 100644
index 94a2df7a7e7..00000000000
--- a/changelogs/unreleased/registry-fix-multi-delete-modal.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Prevent multiple confirmation modals from opening when deleting a repository
-merge_request: 30532
-author:
-type: fixed
diff --git a/changelogs/unreleased/remove-auto-ssl-ff.yml b/changelogs/unreleased/remove-auto-ssl-ff.yml
deleted file mode 100644
index 1b4d0dcde08..00000000000
--- a/changelogs/unreleased/remove-auto-ssl-ff.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Add support for generating SSL certificates for custon pages domains through
- Let's Encrypt
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/remove-kubernetes-service-deployment-platform.yml b/changelogs/unreleased/remove-kubernetes-service-deployment-platform.yml
deleted file mode 100644
index 17421fca234..00000000000
--- a/changelogs/unreleased/remove-kubernetes-service-deployment-platform.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove Kubernetes service integration and Kubernetes service template from available deployment platforms
-merge_request: 29786
-author:
-type: removed
diff --git a/changelogs/unreleased/remove-support-for-legacy-pipeline-triggers.yml b/changelogs/unreleased/remove-support-for-legacy-pipeline-triggers.yml
deleted file mode 100644
index 3f4d4bbd432..00000000000
--- a/changelogs/unreleased/remove-support-for-legacy-pipeline-triggers.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove support for legacy pipeline triggers
-merge_request: 30133
-author:
-type: removed
diff --git a/changelogs/unreleased/remove_group_and_instance_clusters_feature_flag.yml b/changelogs/unreleased/remove_group_and_instance_clusters_feature_flag.yml
deleted file mode 100644
index fcc6c564345..00000000000
--- a/changelogs/unreleased/remove_group_and_instance_clusters_feature_flag.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove group and instance clusters feature flag
-merge_request: 30124
-author:
-type: changed
diff --git a/changelogs/unreleased/require-pipeline-when-enabling-only-allow-merge-if-pipeline-succeeds.yml b/changelogs/unreleased/require-pipeline-when-enabling-only-allow-merge-if-pipeline-succeeds.yml
deleted file mode 100644
index c105287532b..00000000000
--- a/changelogs/unreleased/require-pipeline-when-enabling-only-allow-merge-if-pipeline-succeeds.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enforce presence of pipeline when "Pipeline must succeed" project setting is enabled
-merge_request: 29926
-author:
-type: fixed
diff --git a/changelogs/unreleased/rj-fix-manual-order.yml b/changelogs/unreleased/rj-fix-manual-order.yml
deleted file mode 100644
index ecc39b78b06..00000000000
--- a/changelogs/unreleased/rj-fix-manual-order.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Don't let logged out user do manual order
-merge_request: 30264
-author:
-type: fixed
diff --git a/changelogs/unreleased/rm-src-branch.yml b/changelogs/unreleased/rm-src-branch.yml
new file mode 100644
index 00000000000..03b91d0c7db
--- /dev/null
+++ b/changelogs/unreleased/rm-src-branch.yml
@@ -0,0 +1,5 @@
+---
+title: Support remove source branch on merge w/ push options
+merge_request: 30728
+author:
+type: added
diff --git a/changelogs/unreleased/sanitize_rake_ldap_check_output.yml b/changelogs/unreleased/sanitize_rake_ldap_check_output.yml
deleted file mode 100644
index 92824d1dd48..00000000000
--- a/changelogs/unreleased/sanitize_rake_ldap_check_output.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Sanitize LDAP output in Rake tasks
-merge_request: 28427
-author:
-type: fixed
diff --git a/changelogs/unreleased/search-blob-basenames.yml b/changelogs/unreleased/search-blob-basenames.yml
deleted file mode 100644
index 48ad1130e3f..00000000000
--- a/changelogs/unreleased/search-blob-basenames.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Build correct basenames for title search results
-merge_request: 29898
-author:
-type: fixed
diff --git a/changelogs/unreleased/security-2858-fix-color-validation.yml b/changelogs/unreleased/security-2858-fix-color-validation.yml
deleted file mode 100644
index 3430207a2b6..00000000000
--- a/changelogs/unreleased/security-2858-fix-color-validation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix DoS vulnerability in color validation regex
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/security-59581-related-merge-requests-count.yml b/changelogs/unreleased/security-59581-related-merge-requests-count.yml
deleted file mode 100644
index 83faa2f7c13..00000000000
--- a/changelogs/unreleased/security-59581-related-merge-requests-count.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Expose merge requests count based on user access
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/security-DOS_issue_comments_banzai.yml b/changelogs/unreleased/security-DOS_issue_comments_banzai.yml
deleted file mode 100644
index 2405b1a4f5f..00000000000
--- a/changelogs/unreleased/security-DOS_issue_comments_banzai.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix Denial of Service for comments when rendering issues/MR comments
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/security-bvl-enforce-graphql-type-authorization.yml b/changelogs/unreleased/security-bvl-enforce-graphql-type-authorization.yml
deleted file mode 100644
index 7dedb9f6230..00000000000
--- a/changelogs/unreleased/security-bvl-enforce-graphql-type-authorization.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add missing authorizations in GraphQL
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/security-fp-prevent-billion-laughs-attack.yml b/changelogs/unreleased/security-fp-prevent-billion-laughs-attack.yml
deleted file mode 100644
index 4e0cf848931..00000000000
--- a/changelogs/unreleased/security-fp-prevent-billion-laughs-attack.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Prevent Billion Laughs attack
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/security-mr-head-pipeline-leak.yml b/changelogs/unreleased/security-mr-head-pipeline-leak.yml
deleted file mode 100644
index fe8c4dfb3c8..00000000000
--- a/changelogs/unreleased/security-mr-head-pipeline-leak.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Gate MR head_pipeline behind read_pipeline ability.
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/security-notes-in-private-snippets.yml b/changelogs/unreleased/security-notes-in-private-snippets.yml
deleted file mode 100644
index 907d98cb16d..00000000000
--- a/changelogs/unreleased/security-notes-in-private-snippets.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Correctly check permissions when creating snippet notes
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/security-prevent-detection-of-merge-request-template-name.yml b/changelogs/unreleased/security-prevent-detection-of-merge-request-template-name.yml
deleted file mode 100644
index d7bb884cb4b..00000000000
--- a/changelogs/unreleased/security-prevent-detection-of-merge-request-template-name.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Prevent the detection of merge request templates by unauthorized users
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/set-higher-ttl-for-trace-write.yml b/changelogs/unreleased/set-higher-ttl-for-trace-write.yml
deleted file mode 100644
index 9f17172100c..00000000000
--- a/changelogs/unreleased/set-higher-ttl-for-trace-write.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Set higher TTL for write lock of trace to prevent concurrent archiving
-merge_request: 30064
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-add-force-random-password-user-api.yml b/changelogs/unreleased/sh-add-force-random-password-user-api.yml
deleted file mode 100644
index 29f36978a0f..00000000000
--- a/changelogs/unreleased/sh-add-force-random-password-user-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add support for creating random passwords in user creation API
-merge_request: 30138
-author:
-type: changed
diff --git a/changelogs/unreleased/sh-add-gitaly-ref-caching-search-controller.yml b/changelogs/unreleased/sh-add-gitaly-ref-caching-search-controller.yml
deleted file mode 100644
index d4be28e9883..00000000000
--- a/changelogs/unreleased/sh-add-gitaly-ref-caching-search-controller.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable Gitaly ref caching for SearchController
-merge_request: 30105
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-add-rugged-logs.yml b/changelogs/unreleased/sh-add-rugged-logs.yml
new file mode 100644
index 00000000000..1f464dd92ff
--- /dev/null
+++ b/changelogs/unreleased/sh-add-rugged-logs.yml
@@ -0,0 +1,5 @@
+---
+title: Add Rugged calls and duration to API and Rails logs
+merge_request: 30871
+author:
+type: other
diff --git a/changelogs/unreleased/sh-add-thread-memory-cache.yml b/changelogs/unreleased/sh-add-thread-memory-cache.yml
deleted file mode 100644
index 025ad6d9f14..00000000000
--- a/changelogs/unreleased/sh-add-thread-memory-cache.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add a memory cache local to the thread to reduce Redis load
-merge_request: 30233
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-audit-event-json-log-format-from-and-to.yml b/changelogs/unreleased/sh-audit-event-json-log-format-from-and-to.yml
deleted file mode 100644
index 5bd21d7ef2b..00000000000
--- a/changelogs/unreleased/sh-audit-event-json-log-format-from-and-to.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Format `from` and `to` fields in JSON audit log
-merge_request: 30333
-author:
-type: changed
diff --git a/changelogs/unreleased/sh-avoid-loading-pipeline-status.yml b/changelogs/unreleased/sh-avoid-loading-pipeline-status.yml
deleted file mode 100644
index 2dead948786..00000000000
--- a/changelogs/unreleased/sh-avoid-loading-pipeline-status.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Avoid loading pipeline status in search results
-merge_request: 30111
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-bump-fog-aws.yml b/changelogs/unreleased/sh-bump-fog-aws.yml
deleted file mode 100644
index a936b81ff02..00000000000
--- a/changelogs/unreleased/sh-bump-fog-aws.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Bump fog-aws to v3.5.2
-merge_request: 30803
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-cache-feature-flag-names.yml b/changelogs/unreleased/sh-cache-feature-flag-names.yml
deleted file mode 100644
index 6120c4870f8..00000000000
--- a/changelogs/unreleased/sh-cache-feature-flag-names.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Cache feature flag names in Redis for a minute
-merge_request: 29816
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-cache-flipper-checks-in-memory.yml b/changelogs/unreleased/sh-cache-flipper-checks-in-memory.yml
deleted file mode 100644
index 125b6244d80..00000000000
--- a/changelogs/unreleased/sh-cache-flipper-checks-in-memory.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Cache Flipper feature flags in L1 and L2 caches
-merge_request: 30276
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-cache-flipper-names-memory-cache.yml b/changelogs/unreleased/sh-cache-flipper-names-memory-cache.yml
deleted file mode 100644
index 00443e81244..00000000000
--- a/changelogs/unreleased/sh-cache-flipper-names-memory-cache.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Cache Flipper persisted names directly to local memory storage
-merge_request: 30265
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-cache-negative-entries-find-commit.yml b/changelogs/unreleased/sh-cache-negative-entries-find-commit.yml
deleted file mode 100644
index 98eb13ee620..00000000000
--- a/changelogs/unreleased/sh-cache-negative-entries-find-commit.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow caching of negative FindCommit matches
-merge_request: 29952
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-clean-up-bitbucket-import-errors.yml b/changelogs/unreleased/sh-clean-up-bitbucket-import-errors.yml
deleted file mode 100644
index e4c9de74e6a..00000000000
--- a/changelogs/unreleased/sh-clean-up-bitbucket-import-errors.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Avoid storing backtraces from Bitbucket Cloud imports in the database
-merge_request: 29862
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-disable-reactive-caching-automatic-retries.yml b/changelogs/unreleased/sh-disable-reactive-caching-automatic-retries.yml
deleted file mode 100644
index a0db68adb78..00000000000
--- a/changelogs/unreleased/sh-disable-reactive-caching-automatic-retries.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Prevent amplification of ReactiveCachingWorker jobs upon failures
-merge_request: 30432
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-enable-ref-name-caching-discussions.yml b/changelogs/unreleased/sh-enable-ref-name-caching-discussions.yml
deleted file mode 100644
index 12f4a5a499d..00000000000
--- a/changelogs/unreleased/sh-enable-ref-name-caching-discussions.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable Gitaly ref name caching for discussions.json
-merge_request: 29951
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-fix-httpclient-ssl.yml b/changelogs/unreleased/sh-fix-httpclient-ssl.yml
deleted file mode 100644
index fda4e2e7084..00000000000
--- a/changelogs/unreleased/sh-fix-httpclient-ssl.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make httpclient respect system SSL configuration
-merge_request: 30749
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-issue-63349.yml b/changelogs/unreleased/sh-fix-issue-63349.yml
deleted file mode 100644
index 0e51a6b7b20..00000000000
--- a/changelogs/unreleased/sh-fix-issue-63349.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make Housekeeping button do a full garbage collection
-merge_request: 30289
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-issue-63910.yml b/changelogs/unreleased/sh-fix-issue-63910.yml
deleted file mode 100644
index 50312558c57..00000000000
--- a/changelogs/unreleased/sh-fix-issue-63910.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix attachments using the wrong URLs in e-mails
-merge_request: 30197
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-handle-nil-replication-lag.yml b/changelogs/unreleased/sh-handle-nil-replication-lag.yml
deleted file mode 100644
index 5638d7e79e3..00000000000
--- a/changelogs/unreleased/sh-handle-nil-replication-lag.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix background migrations failing with unused replication slot
-merge_request: 30042
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-improve-redis-peek.yml b/changelogs/unreleased/sh-improve-redis-peek.yml
deleted file mode 100644
index 940be103ab7..00000000000
--- a/changelogs/unreleased/sh-improve-redis-peek.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add Redis call details in Peek performance bar
-merge_request: 30191
-author:
-type: changed
diff --git a/changelogs/unreleased/sh-make-githost-json.yml b/changelogs/unreleased/sh-make-githost-json.yml
new file mode 100644
index 00000000000..f4113a693cc
--- /dev/null
+++ b/changelogs/unreleased/sh-make-githost-json.yml
@@ -0,0 +1,5 @@
+---
+title: Convert githost.log to JSON format
+merge_request: 30967
+author:
+type: changed
diff --git a/changelogs/unreleased/sh-optimize-todos-controller.yml b/changelogs/unreleased/sh-optimize-todos-controller.yml
deleted file mode 100644
index 181ddd1b3bc..00000000000
--- a/changelogs/unreleased/sh-optimize-todos-controller.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Eliminate N+1 queries in Dashboard::TodosController
-merge_request: 29954
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-remove-import-columns-from-projects.yml b/changelogs/unreleased/sh-remove-import-columns-from-projects.yml
deleted file mode 100644
index f4052b2bef5..00000000000
--- a/changelogs/unreleased/sh-remove-import-columns-from-projects.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove import columns from projects table
-merge_request: 29863
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-service-template-bug.yml b/changelogs/unreleased/sh-service-template-bug.yml
deleted file mode 100644
index 1ea5ac84f26..00000000000
--- a/changelogs/unreleased/sh-service-template-bug.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Disable Rails SQL query cache when applying service templates
-merge_request: 30060
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-strong-memoize-appearances.yml b/changelogs/unreleased/sh-strong-memoize-appearances.yml
deleted file mode 100644
index dc4fe1c4d8e..00000000000
--- a/changelogs/unreleased/sh-strong-memoize-appearances.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Memoize non-existent custom appearances
-merge_request: 29957
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-support-subnets-ip-rate-limiter.yml b/changelogs/unreleased/sh-support-subnets-ip-rate-limiter.yml
deleted file mode 100644
index 3e78c58c764..00000000000
--- a/changelogs/unreleased/sh-support-subnets-ip-rate-limiter.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Support CIDR notation in IP rate limiter
-merge_request: 30146
-author:
-type: changed
diff --git a/changelogs/unreleased/sh-update-mermaid.yml b/changelogs/unreleased/sh-update-mermaid.yml
deleted file mode 100644
index 9a7726cf716..00000000000
--- a/changelogs/unreleased/sh-update-mermaid.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update Mermaid to 8.1.0
-merge_request: 30036
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-upgrade-rouge-3-5-1.yml b/changelogs/unreleased/sh-upgrade-rouge-3-5-1.yml
deleted file mode 100644
index b408019c736..00000000000
--- a/changelogs/unreleased/sh-upgrade-rouge-3-5-1.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade Rouge to 3.5.1
-merge_request: 30431
-author:
-type: changed
diff --git a/changelogs/unreleased/slugify.yml b/changelogs/unreleased/slugify.yml
deleted file mode 100644
index 853e90b8bed..00000000000
--- a/changelogs/unreleased/slugify.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Replace slugifyWithHyphens with improved slugify function
-merge_request: 30172
-author: Luke Ward
-type: fixed
diff --git a/changelogs/unreleased/small-s-in-elasticsearch-in-code.yml b/changelogs/unreleased/small-s-in-elasticsearch-in-code.yml
deleted file mode 100644
index 20d7a822cde..00000000000
--- a/changelogs/unreleased/small-s-in-elasticsearch-in-code.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix typo in code comments about Elasticsearch
-merge_request: 30163
-author: Takuya Noguchi
-type: other
diff --git a/changelogs/unreleased/small-s-in-elasticsearch.yml b/changelogs/unreleased/small-s-in-elasticsearch.yml
deleted file mode 100644
index 7cab5c37125..00000000000
--- a/changelogs/unreleased/small-s-in-elasticsearch.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix typo in docs about Elasticsearch
-merge_request: 30162
-author: Takuya Noguchi
-type: other
diff --git a/changelogs/unreleased/support-jsonb-default-value.yml b/changelogs/unreleased/support-jsonb-default-value.yml
deleted file mode 100644
index d46156276f9..00000000000
--- a/changelogs/unreleased/support-jsonb-default-value.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Support jsonb default in add_column_with_default migration helper
-merge_request: 29871
-author:
-type: other
diff --git a/changelogs/unreleased/tc-rake-orphan-artifacts.yml b/changelogs/unreleased/tc-rake-orphan-artifacts.yml
deleted file mode 100644
index 7081bee640a..00000000000
--- a/changelogs/unreleased/tc-rake-orphan-artifacts.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add rake task to clean orphan artifact files
-merge_request: 29681
-author:
-type: added
diff --git a/changelogs/unreleased/transaction-metrics.yml b/changelogs/unreleased/transaction-metrics.yml
deleted file mode 100644
index 8b6e9c7d9d1..00000000000
--- a/changelogs/unreleased/transaction-metrics.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds metrics to measure cost of expensive operations
-merge_request: 29928
-author:
-type: other
diff --git a/changelogs/unreleased/tz-update-mr-count-over-tabs.yml b/changelogs/unreleased/tz-update-mr-count-over-tabs.yml
deleted file mode 100644
index 61a49dd8ecc..00000000000
--- a/changelogs/unreleased/tz-update-mr-count-over-tabs.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: New API for User Counts, updates on success of an MR the count on top and in
- other tabs
-merge_request: 29441
-author:
-type: added
diff --git a/changelogs/unreleased/unicorn-sampler-fix.yml b/changelogs/unreleased/unicorn-sampler-fix.yml
deleted file mode 100644
index 3f0e509f15f..00000000000
--- a/changelogs/unreleased/unicorn-sampler-fix.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make sure UnicornSampler is started only in master process.
-merge_request: 30215
-author:
-type: fixed
diff --git a/changelogs/unreleased/update-clair-version.yml b/changelogs/unreleased/update-clair-version.yml
deleted file mode 100644
index 59b6e113fd5..00000000000
--- a/changelogs/unreleased/update-clair-version.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Extract clair version as CLAIR_EXECUTABLE_VERSION variable and update clair
- executable from v8 to v11
-merge_request: 30396
-author:
-type: changed
diff --git a/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-6-0.yml b/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-6-0.yml
deleted file mode 100644
index 6719fa94b19..00000000000
--- a/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-6-0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update GitLab Runner Helm Chart to 0.6.0
-merge_request: 29982
-author:
-type: other
diff --git a/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-7-0.yml b/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-7-0.yml
new file mode 100644
index 00000000000..ab1e7d77520
--- /dev/null
+++ b/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-7-0.yml
@@ -0,0 +1,5 @@
+---
+title: Update GitLab Runner Helm Chart to 0.7.0
+merge_request: 30950
+author:
+type: other
diff --git a/changelogs/unreleased/update-pages-to-1-7-0.yml b/changelogs/unreleased/update-pages-to-1-7-0.yml
deleted file mode 100644
index d41346f021c..00000000000
--- a/changelogs/unreleased/update-pages-to-1-7-0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update GitLab Pages to v1.7.0
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/update-pagination-texts.yml b/changelogs/unreleased/update-pagination-texts.yml
deleted file mode 100644
index 6a398e26242..00000000000
--- a/changelogs/unreleased/update-pagination-texts.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update pagination prev and next texts
-merge_request: 29911
-author:
-type: other
diff --git a/changelogs/unreleased/update-tar-to-2-2-2.yml b/changelogs/unreleased/update-tar-to-2-2-2.yml
deleted file mode 100644
index f142fe59448..00000000000
--- a/changelogs/unreleased/update-tar-to-2-2-2.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update tar to 2.2.2
-merge_request: 29949
-author: Takuya Noguchi
-type: security
diff --git a/changelogs/unreleased/update-todo-in-ui.yml b/changelogs/unreleased/update-todo-in-ui.yml
deleted file mode 100644
index dddcf0f3983..00000000000
--- a/changelogs/unreleased/update-todo-in-ui.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Changes "Todo" to "To Do" in the UI for clarity
-merge_request: 28844
-author:
-type: other
diff --git a/changelogs/unreleased/use-pg-9-6-11-on-ci.yml b/changelogs/unreleased/use-pg-9-6-11-on-ci.yml
deleted file mode 100644
index 785eb352895..00000000000
--- a/changelogs/unreleased/use-pg-9-6-11-on-ci.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use PostgreSQL 9.6.11 in CI tests
-merge_request: 30270
-author: Takuya Noguchi
-type: other
diff --git a/changelogs/unreleased/wiki-usage-pings.yml b/changelogs/unreleased/wiki-usage-pings.yml
new file mode 100644
index 00000000000..c3d084228c3
--- /dev/null
+++ b/changelogs/unreleased/wiki-usage-pings.yml
@@ -0,0 +1,5 @@
+---
+title: Count wiki creation, update and delete events
+merge_request: 30864
+author:
+type: added
diff --git a/changelogs/unreleased/winh-jest-markdown-header.yml b/changelogs/unreleased/winh-jest-markdown-header.yml
deleted file mode 100644
index 6bf9d75cc93..00000000000
--- a/changelogs/unreleased/winh-jest-markdown-header.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Migrate markdown header_spec.js to Jest
-merge_request: 30228
-author: Martin Hobert
-type: other
diff --git a/changelogs/unreleased/winh-notes-service-applySuggestion.yml b/changelogs/unreleased/winh-notes-service-applySuggestion.yml
deleted file mode 100644
index 30e540237b6..00000000000
--- a/changelogs/unreleased/winh-notes-service-applySuggestion.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove applySuggestion from notes service
-merge_request: 30399
-author: Frank van Rest
-type: other
diff --git a/changelogs/unreleased/winh-notes-service-deleteNote.yml b/changelogs/unreleased/winh-notes-service-deleteNote.yml
deleted file mode 100644
index cf2b37755a2..00000000000
--- a/changelogs/unreleased/winh-notes-service-deleteNote.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove deleteNote from notes service
-merge_request: 30537
-author: Frank van Rest
-type: other
diff --git a/changelogs/unreleased/winh-notes-service-toggleAward.yml b/changelogs/unreleased/winh-notes-service-toggleAward.yml
deleted file mode 100644
index 0471888c285..00000000000
--- a/changelogs/unreleased/winh-notes-service-toggleAward.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove toggleAward from notes service
-merge_request: 30536
-author: Frank van Rest
-type: other
diff --git a/changelogs/unreleased/winh-updateResolvableDiscussionsCounts-typo.yml b/changelogs/unreleased/winh-updateResolvableDiscussionsCounts-typo.yml
deleted file mode 100644
index 24b10bb3edc..00000000000
--- a/changelogs/unreleased/winh-updateResolvableDiscussionsCounts-typo.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix typo in updateResolvableDiscussionsCounts action
-merge_request: 30278
-author: Frank van Rest
-type: other
diff --git a/changelogs/unreleased/z-index-fix-for-diff-file-dropdown.yml b/changelogs/unreleased/z-index-fix-for-diff-file-dropdown.yml
deleted file mode 100644
index 72108f6ab77..00000000000
--- a/changelogs/unreleased/z-index-fix-for-diff-file-dropdown.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: I fixed z index bug in diff page
-merge_request: 30657
-author: Faruk Can
-type: fixed
diff --git a/changelogs/unreleased/z-index-tools.yml b/changelogs/unreleased/z-index-tools.yml
deleted file mode 100644
index 1102612670b..00000000000
--- a/changelogs/unreleased/z-index-tools.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Review Tools: Add large z-index to toolbar'
-merge_request: 30583
-author:
-type: fixed
diff --git a/changelogs/unreleased/zj-gitaly-usage-data.yml b/changelogs/unreleased/zj-gitaly-usage-data.yml
deleted file mode 100644
index ce5087292ed..00000000000
--- a/changelogs/unreleased/zj-gitaly-usage-data.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add Gitaly data to the usage ping
-merge_request:
-author:
-type: added
diff --git a/config/application.rb b/config/application.rb
index de386506233..df5079ba993 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -168,7 +168,6 @@ module Gitlab
# Import gitlab-svgs directly from vendored directory
config.assets.paths << "#{config.root}/node_modules/@gitlab/svgs/dist"
- config.assets.paths << "#{config.root}/node_modules"
config.assets.precompile << "icons.svg"
config.assets.precompile << "icons.json"
config.assets.precompile << "illustrations/*.svg"
@@ -183,9 +182,19 @@ module Gitlab
config.assets.precompile << "pages/jira_connect.css"
end
+ # Import path for EE specific SCSS entry point
+ # In CE it will import a noop file, in EE a functioning file
+ # Order is important, so that the ee file takes precedence:
+ config.assets.paths << "#{config.root}/ee/app/assets/stylesheets/_ee"
+ config.assets.paths << "#{config.root}/app/assets/stylesheets/_ee"
+
config.assets.paths << "#{config.root}/vendor/assets/javascripts/"
config.assets.precompile << "snowplow/sp.js"
+ # This path must come last to avoid confusing sprockets
+ # See https://gitlab.com/gitlab-org/gitlab-ce/issues/64091#note_194512508
+ config.assets.paths << "#{config.root}/node_modules"
+
# Compile non-JS/CSS assets in the ee/app/assets folder by default
# Mimic sprockets-rails default: https://github.com/rails/sprockets-rails/blob/v3.2.1/lib/sprockets/railtie.rb#L84-L87
LOOSE_EE_APP_ASSETS = lambda do |logical_path, filename|
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 3a121addc98..494c4dd1f93 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -217,6 +217,7 @@ Gitlab.ee do
Settings['elasticsearch'] ||= Settingslogic.new({})
Settings.elasticsearch['enabled'] = false if Settings.elasticsearch['enabled'].nil?
Settings.elasticsearch['url'] = ENV['ELASTIC_URL'] || "http://localhost:9200"
+ Settings.elasticsearch['indexer_path'] ||= Gitlab::Utils.which('gitlab-elasticsearch-indexer')
end
#
diff --git a/config/initializers/7_prometheus_metrics.rb b/config/initializers/7_prometheus_metrics.rb
index 53c3eac3c74..3f2691dde95 100644
--- a/config/initializers/7_prometheus_metrics.rb
+++ b/config/initializers/7_prometheus_metrics.rb
@@ -1,5 +1,4 @@
require 'prometheus/client'
-require 'prometheus/client/support/unicorn'
# Keep separate directories for separate processes
def prometheus_default_multiproc_dir
@@ -23,7 +22,7 @@ Prometheus::Client.configure do |config|
config.multiprocess_files_dir = ENV['prometheus_multiproc_dir'] || prometheus_default_multiproc_dir
- config.pid_provider = Prometheus::Client::Support::Unicorn.method(:worker_pid_provider)
+ config.pid_provider = Prometheus::PidProvider.method(:worker_id)
end
Gitlab::Application.configure do |config|
diff --git a/config/initializers/lograge.rb b/config/initializers/lograge.rb
index fbec28186eb..3d84b4e44ce 100644
--- a/config/initializers/lograge.rb
+++ b/config/initializers/lograge.rb
@@ -34,6 +34,13 @@ unless Sidekiq.server?
payload[:gitaly_duration] = Gitlab::GitalyClient.query_time_ms
end
+ rugged_calls = Gitlab::RuggedInstrumentation.query_count
+
+ if rugged_calls > 0
+ payload[:rugged_calls] = rugged_calls
+ payload[:rugged_duration_ms] = Gitlab::RuggedInstrumentation.query_time_ms
+ end
+
payload[:response] = event.payload[:response] if event.payload[:response]
payload[Labkit::Correlation::CorrelationId::LOG_KEY] = Labkit::Correlation::CorrelationId.current_id
diff --git a/config/initializers/peek.rb b/config/initializers/peek.rb
index d492b60705d..2aec32e878c 100644
--- a/config/initializers/peek.rb
+++ b/config/initializers/peek.rb
@@ -29,7 +29,7 @@ end
Peek.into PEEK_DB_VIEW
Peek.into Peek::Views::Gitaly
Peek.into Peek::Views::Rblineprof
-Peek.into Peek::Views::Redis
+Peek.into Peek::Views::RedisDetailed
Peek.into Peek::Views::GC
Peek.into Peek::Views::Tracing if Labkit::Tracing.tracing_url_enabled?
diff --git a/config/initializers/zz_metrics.rb b/config/initializers/zz_metrics.rb
index 4f309094447..b005fdf159b 100644
--- a/config/initializers/zz_metrics.rb
+++ b/config/initializers/zz_metrics.rb
@@ -6,6 +6,7 @@
# that we can stub it for testing, as it is only called when metrics are
# enabled.
#
+# rubocop:disable Metrics/AbcSize
def instrument_classes(instrumentation)
instrumentation.instrument_instance_methods(Gitlab::Shell)
@@ -86,12 +87,42 @@ def instrument_classes(instrumentation)
instrumentation.instrument_methods(Gitlab::Highlight)
instrumentation.instrument_instance_methods(Gitlab::Highlight)
+ Gitlab.ee do
+ instrumentation.instrument_methods(Elasticsearch::Git::Repository)
+ instrumentation.instrument_instance_methods(Elasticsearch::Git::Repository)
+
+ instrumentation.instrument_instance_methods(Search::GlobalService)
+ instrumentation.instrument_instance_methods(Search::ProjectService)
+
+ instrumentation.instrument_instance_methods(Gitlab::Elastic::SearchResults)
+ instrumentation.instrument_instance_methods(Gitlab::Elastic::ProjectSearchResults)
+ instrumentation.instrument_instance_methods(Gitlab::Elastic::Indexer)
+ instrumentation.instrument_instance_methods(Gitlab::Elastic::SnippetSearchResults)
+ instrumentation.instrument_methods(Gitlab::Elastic::Helper)
+
+ instrumentation.instrument_instance_methods(Elastic::ApplicationSearch)
+ instrumentation.instrument_instance_methods(Elastic::IssuesSearch)
+ instrumentation.instrument_instance_methods(Elastic::MergeRequestsSearch)
+ instrumentation.instrument_instance_methods(Elastic::MilestonesSearch)
+ instrumentation.instrument_instance_methods(Elastic::NotesSearch)
+ instrumentation.instrument_instance_methods(Elastic::ProjectsSearch)
+ instrumentation.instrument_instance_methods(Elastic::RepositoriesSearch)
+ instrumentation.instrument_instance_methods(Elastic::SnippetsSearch)
+ instrumentation.instrument_instance_methods(Elastic::WikiRepositoriesSearch)
+
+ instrumentation.instrument_instance_methods(Gitlab::BitbucketImport::Importer)
+ instrumentation.instrument_instance_methods(Bitbucket::Connection)
+
+ instrumentation.instrument_instance_methods(Geo::RepositorySyncWorker)
+ end
+
# This is a Rails scope so we have to instrument it manually.
instrumentation.instrument_method(Project, :visible_to_user)
# Needed for https://gitlab.com/gitlab-org/gitlab-ce/issues/30224#note_32306159
instrumentation.instrument_instance_method(MergeRequestDiff, :load_commits)
end
+# rubocop:enable Metrics/AbcSize
# With prometheus enabled by default this breaks all specs
# that stubs methods using `any_instance_of` for the models reloaded here.
diff --git a/config/karma.config.js b/config/karma.config.js
index 2a5bf3581e0..97794225a3f 100644
--- a/config/karma.config.js
+++ b/config/karma.config.js
@@ -107,7 +107,8 @@ if (specFilters.length) {
module.exports = function(config) {
process.env.TZ = 'Etc/UTC';
- const fixturesPath = `${IS_EE ? 'ee/' : ''}spec/javascripts/fixtures`;
+ const fixturesPath = `tmp/tests/frontend/fixtures${IS_EE ? '-ee' : ''}`;
+ const staticFixturesPath = 'spec/frontend/fixtures/static';
const karmaConfig = {
basePath: ROOT_PATH,
@@ -131,8 +132,13 @@ module.exports = function(config) {
frameworks: ['jasmine'],
files: [
{ pattern: 'spec/javascripts/test_bundle.js', watched: false },
- { pattern: `${fixturesPath}/**/*@(.json|.html|.png|.bmpr|.pdf)`, included: false },
+ { pattern: `${fixturesPath}/**/*`, included: false },
+ { pattern: `${staticFixturesPath}/**/*`, included: false },
],
+ proxies: {
+ '/fixtures/': `/base/${fixturesPath}/`,
+ '/fixtures/static/': `/base/${staticFixturesPath}/`,
+ },
preprocessors: {
'spec/javascripts/**/*.js': ['webpack', 'sourcemap'],
'ee/spec/javascripts/**/*.js': ['webpack', 'sourcemap'],
diff --git a/config/locales/en.yml b/config/locales/en.yml
index a3dceb2fb62..a60f86e1d80 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -16,12 +16,6 @@ en:
api_url: "Sentry API URL"
project/metrics_setting:
external_dashboard_url: "External dashboard URL"
- errors:
- messages:
- label_already_exists_at_group_level: "already exists at group level for %{group}. Please choose another one."
- wrong_size: "is the wrong size (should be %{file_size})"
- size_too_small: "is too small (should be at least %{file_size})"
- size_too_big: "is too big (should be at most %{file_size})"
views:
pagination:
previous: "Prev"
diff --git a/config/routes.rb b/config/routes.rb
index 641807203bf..459f2b22bf0 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -116,7 +116,7 @@ Rails.application.routes.draw do
end
end
- if ENV['GITLAB_CHAOS_SECRET'] || Rails.env.development?
+ if ENV['GITLAB_CHAOS_SECRET'] || Rails.env.development? || Rails.env.test?
resource :chaos, only: [] do
get :leakmem
get :cpu_spin
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index f609739d9fd..6f9a5552564 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -73,7 +73,7 @@ namespace :admin do
resource :background_jobs, controller: 'background_jobs', only: [:show]
resource :system_info, controller: 'system_info', only: [:show]
- resources :requests_profiles, only: [:index, :show], param: :name, constraints: { name: /.+\.html/ }
+ resources :requests_profiles, only: [:index, :show], param: :name, constraints: { name: /.+\.(html|txt)/ }
resources :projects, only: [:index]
diff --git a/config/settings.rb b/config/settings.rb
index da459afcce2..8756c120645 100644
--- a/config/settings.rb
+++ b/config/settings.rb
@@ -1,4 +1,5 @@
require 'settingslogic'
+require 'digest/md5'
# We can not use `Rails.root` here, as this file might be loaded without the
# full Rails environment being loaded. We can not use `require_relative` either,
@@ -170,14 +171,17 @@ class Settings < Settingslogic
URI.parse(url_without_path).host
end
- # Runs every minute in a random ten-minute period on Sundays, to balance the
- # load on the server receiving these pings. The usage ping is safe to run
- # multiple times because of a 24 hour exclusive lock.
+ # Runs at a random time of day on a consistent day of the week based on
+ # the instance UUID. This is to balance the load on the service receiving
+ # these pings. The sidekiq job handles temporary http failures.
def cron_for_usage_ping
hour = rand(24)
- minute = rand(6)
+ minute = rand(60)
+ # Set a default UUID for the case when the UUID hasn't been initialized.
+ uuid = Gitlab::CurrentSettings.uuid || 'uuid-not-set'
+ day_of_week = Digest::MD5.hexdigest(uuid).to_i(16) % 7
- "#{minute}0-#{minute}9 #{hour} * * 0"
+ "#{minute} #{hour} * * #{day_of_week}"
end
end
end
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index 80791795390..c7586aa1e38 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -95,6 +95,7 @@
- [update_project_statistics, 1]
- [phabricator_import_import_tasks, 1]
- [update_namespace_statistics, 1]
+ - [chaos, 2]
# EE-specific queues
- [ldap_group_sync, 2]
diff --git a/danger/database/Dangerfile b/danger/database/Dangerfile
index 083e95b8da7..3550cb7eabf 100644
--- a/danger/database/Dangerfile
+++ b/danger/database/Dangerfile
@@ -8,6 +8,21 @@ updated too (unless the migration isn't changing the DB schema
and isn't the most recent one).
MSG
+DB_MESSAGE = <<~MSG
+This merge request requires a database review. To make sure these
+changes are reviewed, take the following steps:
+
+1. Ensure the merge request has ~database and ~"database::review pending" labels.
+ If the merge request modifies database files, Danger will do this for you.
+1. Use the [Database changes checklist](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab/merge_request_templates/Database%20changes.md)
+ template or add the appropriate items to the MR description.
+1. Assign and mention the database reviewer suggested by Reviewer Roulette.
+MSG
+
+DB_FILES_MESSAGE = <<~MSG
+The following files require a review from the Database team:
+MSG
+
non_geo_db_schema_updated = !git.modified_files.grep(%r{\Adb/schema\.rb}).empty?
geo_db_schema_updated = !git.modified_files.grep(%r{\Aee/db/geo/schema\.rb}).empty?
@@ -24,27 +39,18 @@ end
db_paths_to_review = helper.changes_by_category[:database]
-unless db_paths_to_review.empty?
+if gitlab.mr_labels.include?('database') || db_paths_to_review.any?
message 'This merge request adds or changes files that require a ' \
- 'review from the Database team.'
-
- markdown(<<~MARKDOWN.strip)
-## Database Review
-
-The following files require a review from the Database team:
-
-* #{db_paths_to_review.map { |path| "`#{path}`" }.join("\n* ")}
+ 'review from the [Database team](https://gitlab.com/groups/gl-database/-/group_members).'
-To make sure these changes are reviewed, take the following steps:
+ markdown(DB_MESSAGE)
+ markdown(DB_FILES_MESSAGE + helper.markdown_list(db_paths_to_review)) if db_paths_to_review.any?
-1. Edit your merge request, and add `gl-database` to the list of Group
- approvers.
-1. Mention `@gl-database` in a separate comment, and explain what needs to be
- reviewed by the team. Please don't mention the team until your changes are
- ready for review.
- MARKDOWN
+ database_labels = helper.missing_database_labels(gitlab.mr_labels)
- unless gitlab.mr_labels.include?('database')
- warn 'This merge request is missing the ~database label.'
+ if database_labels.any?
+ gitlab.api.update_merge_request(gitlab.mr_json['project_id'],
+ gitlab.mr_json['iid'],
+ labels: (gitlab.mr_labels + database_labels).join(','))
end
end
diff --git a/danger/metadata/Dangerfile b/danger/metadata/Dangerfile
index 1adca152736..f2d68e64eb6 100644
--- a/danger/metadata/Dangerfile
+++ b/danger/metadata/Dangerfile
@@ -1,5 +1,13 @@
# rubocop:disable Style/SignalException
+THROUGHPUT_LABELS = [
+ 'Community contribution',
+ 'security',
+ 'bug',
+ 'feature',
+ 'backstage'
+].freeze
+
if gitlab.mr_body.size < 5
fail "Please provide a proper merge request description."
end
@@ -8,6 +16,10 @@ if gitlab.mr_labels.empty?
fail "Please add labels to this merge request."
end
+if (THROUGHPUT_LABELS & gitlab.mr_labels).empty?
+ warn 'Please add a [throughput label](https://about.gitlab.com/handbook/engineering/management/throughput/#implementation) to this merge request.'
+end
+
unless gitlab.mr_json["assignee"]
warn "This merge request does not have any assignee yet. Setting an assignee clarifies who needs to take action on the merge request at any given time."
end
diff --git a/danger/roulette/Dangerfile b/danger/roulette/Dangerfile
index 6718e218233..19a5076778c 100644
--- a/danger/roulette/Dangerfile
+++ b/danger/roulette/Dangerfile
@@ -55,22 +55,15 @@ def spin_for_category(team, project, category, branch_name)
"| #{helper.label_for_category(category)} | #{reviewer&.markdown_name || NO_REVIEWER} | #{maintainer&.markdown_name || NO_MAINTAINER} |"
end
-def build_list(items)
- list = items.map { |filename| "* `#{filename}`" }.join("\n")
-
- if items.size > 10
- "\n<details>\n\n#{list}\n\n</details>"
- else
- list
- end
-end
-
changes = helper.changes_by_category
# Ignore any files that are known but uncategorized. Prompt for any unknown files
changes.delete(:none)
categories = changes.keys - [:unknown]
+# Ensure to spin for database reviewer/maintainer when ~database is applied (e.g. to review SQL queries)
+categories << :database if gitlab.mr_labels.include?('database') && !categories.include?(:database)
+
# Single codebase MRs are reviewed using a slightly different process, so we
# disable the review roulette for such MRs.
# CSS Clean up MRs are reviewed using a slightly different process, so we
@@ -95,5 +88,5 @@ if changes.any? && !gitlab.mr_labels.include?('single codebase') && !gitlab.mr_l
markdown(MESSAGE)
markdown(CATEGORY_TABLE_HEADER + rows.join("\n")) unless rows.empty?
- markdown(UNKNOWN_FILES_MESSAGE + build_list(unknown)) unless unknown.empty?
+ markdown(UNKNOWN_FILES_MESSAGE + helper.markdown_list(unknown)) unless unknown.empty?
end
diff --git a/db/fixtures/development/14_pipelines.rb b/db/fixtures/development/14_pipelines.rb
index db043e39d2c..05bda7d3672 100644
--- a/db/fixtures/development/14_pipelines.rb
+++ b/db/fixtures/development/14_pipelines.rb
@@ -79,9 +79,17 @@ class Gitlab::Seeder::Pipelines
def create_master_pipelines
@project.repository.commits('master', limit: 4).map do |commit|
- create_pipeline!(@project, 'master', commit)
+ create_pipeline!(@project, 'master', commit).tap do |pipeline|
+ random_pipeline.tap do |triggered_by_pipeline|
+ triggered_by_pipeline.try(:sourced_pipelines)&.create(
+ source_job: triggered_by_pipeline.builds.all.sample,
+ source_project: triggered_by_pipeline.project,
+ project: pipeline.project,
+ pipeline: pipeline)
+ end
+ end
end
- rescue
+ rescue ActiveRecord::ActiveRecordError
[]
end
@@ -98,7 +106,7 @@ class Gitlab::Seeder::Pipelines
end
pipelines.flatten
- rescue
+ rescue ActiveRecord::ActiveRecordError
[]
end
@@ -231,6 +239,10 @@ class Gitlab::Seeder::Pipelines
@project.team.users.sample
end
+ def random_pipeline
+ Ci::Pipeline.limit(4).all.sample
+ end
+
def build_status
Ci::Build::AVAILABLE_STATUSES.sample
end
diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb
new file mode 100644
index 00000000000..3b75c92e518
--- /dev/null
+++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ add_column(:namespaces, :subgroup_creation_level, :integer)
+ change_column_default(:namespaces,
+ :subgroup_creation_level,
+ ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS)
+ end
+
+ def down
+ remove_column(:namespaces, :subgroup_creation_level)
+ end
+end
diff --git a/db/migrate/20190709204413_add_rule_type_to_approval_project_rules.rb b/db/migrate/20190709204413_add_rule_type_to_approval_project_rules.rb
new file mode 100644
index 00000000000..87a228c9bf9
--- /dev/null
+++ b/db/migrate/20190709204413_add_rule_type_to_approval_project_rules.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddRuleTypeToApprovalProjectRules < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default :approval_project_rules, :rule_type, :integer, limit: 2, default: 0, allow_null: false
+ end
+
+ def down
+ remove_column :approval_project_rules, :rule_type
+ end
+end
diff --git a/db/migrate/20190710151229_add_index_to_approval_project_rules_rule_type.rb b/db/migrate/20190710151229_add_index_to_approval_project_rules_rule_type.rb
new file mode 100644
index 00000000000..64123c53c4a
--- /dev/null
+++ b/db/migrate/20190710151229_add_index_to_approval_project_rules_rule_type.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexToApprovalProjectRulesRuleType < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :approval_project_rules, :rule_type
+ end
+
+ def down
+ remove_concurrent_index :approval_project_rules, :rule_type
+ end
+end
diff --git a/db/post_migrate/20190703185326_fix_wrong_pages_access_level.rb b/db/post_migrate/20190703185326_fix_wrong_pages_access_level.rb
new file mode 100644
index 00000000000..e5981956cf5
--- /dev/null
+++ b/db/post_migrate/20190703185326_fix_wrong_pages_access_level.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+class FixWrongPagesAccessLevel < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ MIGRATION = 'FixPagesAccessLevel'
+ BATCH_SIZE = 20_000
+ BATCH_TIME = 2.minutes
+
+ disable_ddl_transaction!
+
+ class ProjectFeature < ActiveRecord::Base
+ include ::EachBatch
+
+ self.table_name = 'project_features'
+ self.inheritance_column = :_type_disabled
+ end
+
+ def up
+ queue_background_migration_jobs_by_range_at_intervals(
+ ProjectFeature,
+ MIGRATION,
+ BATCH_TIME,
+ batch_size: BATCH_SIZE)
+ end
+end
diff --git a/db/post_migrate/20190715114644_drop_project_features_pages_access_level_default.rb b/db/post_migrate/20190715114644_drop_project_features_pages_access_level_default.rb
new file mode 100644
index 00000000000..2fb0aa0f460
--- /dev/null
+++ b/db/post_migrate/20190715114644_drop_project_features_pages_access_level_default.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+class DropProjectFeaturesPagesAccessLevelDefault < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ ENABLED_VALUE = 20
+
+ def change
+ change_column_default :project_features, :pages_access_level, from: ENABLED_VALUE, to: nil
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 644ca1fe970..79cd1a3a797 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2019_07_03_130053) do
+ActiveRecord::Schema.define(version: 2019_07_15_114644) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
@@ -282,7 +282,9 @@ ActiveRecord::Schema.define(version: 2019_07_03_130053) do
t.integer "project_id", null: false
t.integer "approvals_required", limit: 2, default: 0, null: false
t.string "name", null: false
+ t.integer "rule_type", limit: 2, default: 0, null: false
t.index ["project_id"], name: "index_approval_project_rules_on_project_id", using: :btree
+ t.index ["rule_type"], name: "index_approval_project_rules_on_rule_type", using: :btree
end
create_table "approval_project_rules_groups", force: :cascade do |t|
@@ -2117,6 +2119,7 @@ ActiveRecord::Schema.define(version: 2019_07_03_130053) do
t.string "ldap_sync_status", default: "ready", null: false
t.boolean "membership_lock", default: false
t.integer "last_ci_minutes_usage_notification_level"
+ t.integer "subgroup_creation_level", default: 1
t.index ["created_at"], name: "index_namespaces_on_created_at", using: :btree
t.index ["custom_project_templates_group_id", "type"], name: "index_namespaces_on_custom_project_templates_group_id_and_type", where: "(custom_project_templates_group_id IS NOT NULL)", using: :btree
t.index ["file_template_project_id"], name: "index_namespaces_on_file_template_project_id", using: :btree
@@ -2507,7 +2510,7 @@ ActiveRecord::Schema.define(version: 2019_07_03_130053) do
t.datetime "created_at"
t.datetime "updated_at"
t.integer "repository_access_level", default: 20, null: false
- t.integer "pages_access_level", default: 20, null: false
+ t.integer "pages_access_level", null: false
t.index ["project_id"], name: "index_project_features_on_project_id", unique: true, using: :btree
end
diff --git a/doc/administration/auth/ldap-ee.md b/doc/administration/auth/ldap-ee.md
index 2f2ee8a27d3..1b7af2d945a 100644
--- a/doc/administration/auth/ldap-ee.md
+++ b/doc/administration/auth/ldap-ee.md
@@ -193,7 +193,7 @@ group, as opposed to the full DN.
to lock down user abilities to invite new members to a group. When enabled following happens:
1. Only administrator can manage memberships of any group including access levels.
-2. Users are not allowed to share project with other groups or invite members to a project created in a group.
+1. Users are not allowed to share project with other groups or invite members to a project created in a group.
## Adjusting LDAP user sync schedule
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index 4407facfca9..0f547ef03bf 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -2,45 +2,35 @@
[Gitaly](https://gitlab.com/gitlab-org/gitaly) is the service that
provides high-level RPC access to Git repositories. Without it, no other
-components can read or write Git data.
+components can read or write Git data. GitLab components that access Git
+repositories (gitlab-rails, gitlab-shell, gitlab-workhorse, etc.) act as clients
+to Gitaly. End users do not have direct access to Gitaly.
-GitLab components that access Git repositories (gitlab-rails,
-gitlab-shell, gitlab-workhorse) act as clients to Gitaly. End users do
-not have direct access to Gitaly.
+In the rest of this page, Gitaly server is referred to the standalone node that
+only runs Gitaly, and Gitaly client to the GitLab Rails node that runs all other
+processes except Gitaly.
## Configuring Gitaly
The Gitaly service itself is configured via a TOML configuration file.
-This file is documented [in the gitaly
+This file is documented [in the Gitaly
repository](https://gitlab.com/gitlab-org/gitaly/blob/master/doc/configuration/README.md).
-To change a Gitaly setting in Omnibus you can use
-`gitaly['my_setting']` in `/etc/gitlab/gitlab.rb`. Changes will be applied
-when you run `gitlab-ctl reconfigure`.
+In case you want to change some of its settings:
-```ruby
-gitaly['prometheus_listen_addr'] = 'localhost:9236'
-```
-
-To change a Gitaly setting in installations from source you can edit
-`/home/git/gitaly/config.toml`. Changes will be applied when you run
-`service gitlab restart`.
+**For Omnibus GitLab**
-```toml
-prometheus_listen_addr = "localhost:9236"
-```
+1. Edit `/etc/gitlab/gitlab.rb` and add or change the [Gitaly settings](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/1dd07197c7e5ae23626aad5a4a070a800b670380/files/gitlab-config-template/gitlab.rb.template#L1622-1676).
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
-## Client-side GRPC logs
+**For installations from source**
-Gitaly uses the [gRPC](https://grpc.io/) RPC framework. The Ruby gRPC
-client has its own log file which may contain useful information when
-you are seeing Gitaly errors. You can control the log level of the
-gRPC client with the `GRPC_LOG_LEVEL` environment variable. The
-default level is `WARN`.
+1. Edit `/home/git/gitaly/config.toml` and add or change the [Gitaly settings](https://gitlab.com/gitlab-org/gitaly/blob/master/config.toml.example).
+1. Save the file and [restart GitLab](../restart_gitlab.md#installations-from-source).
## Running Gitaly on its own server
-> This is an optional way to deploy Gitaly which can benefit GitLab
+This is an optional way to deploy Gitaly which can benefit GitLab
installations that are larger than a single machine. Most
installations will be better served with the default configuration
used by Omnibus and the GitLab source installation guide.
@@ -53,76 +43,78 @@ But since 11.8 the indexer uses Gitaly for data access as well. NFS can still
be leveraged for redudancy on block level of the Git data. But only has to
be mounted on the Gitaly server.
-NOTE: **Note:** While Gitaly can be used as a replacement for NFS, we do not recommend
-using EFS as it may impact GitLab's performance. Please review the [relevant documentation](../high_availability/nfs.md#avoid-using-awss-elastic-file-system-efs)
+NOTE: **Note:** While Gitaly can be used as a replacement for NFS, it's not recommended
+to use EFS as it may impact GitLab's performance. Review the [relevant documentation](../high_availability/nfs.md#avoid-using-awss-elastic-file-system-efs)
for more details.
### Network architecture
-- gitlab-rails shards repositories into "repository storages"
-- `gitlab-rails/config/gitlab.yml` contains a map from storage names to
- (Gitaly address, Gitaly token) pairs
-- the `storage name` -\> `(Gitaly address, Gitaly token)` map in
- `gitlab.yml` is the single source of truth for the Gitaly network
- topology
-- a (Gitaly address, Gitaly token) corresponds to a Gitaly server
-- a Gitaly server hosts one or more storages
-- Gitaly addresses must be specified in such a way that they resolve
- correctly for ALL Gitaly clients
-- Gitaly clients are: unicorn, sidekiq, gitlab-workhorse,
- gitlab-shell, Elasticsearch Indexer, and Gitaly itself
-- special case: a Gitaly server must be able to make RPC calls **to
- itself** via its own (Gitaly address, Gitaly token) pair as
- specified in `gitlab-rails/config/gitlab.yml`
-- Gitaly servers must not be exposed to the public internet
-
-Gitaly network traffic is unencrypted by default, but supports
-[TLS](#tls-support). Authentication is done through a static token.
-
-NOTE: **Note:** Gitaly network traffic is unencrypted so we recommend a firewall to
-restrict access to your Gitaly server.
+The following list depicts what the network architecture of Gitaly is:
+
+- GitLab Rails shards repositories into [repository storages](../repository_storage_paths.md).
+- `/config/gitlab.yml` contains a map from storage names to
+ `(Gitaly address, Gitaly token)` pairs.
+- the `storage name` -\> `(Gitaly address, Gitaly token)` map in
+ `/config/gitlab.yml` is the single source of truth for the Gitaly network
+ topology.
+- A `(Gitaly address, Gitaly token)` corresponds to a Gitaly server.
+- A Gitaly server hosts one or more storages.
+- Gitaly addresses must be specified in such a way that they resolve
+ correctly for ALL Gitaly clients.
+- Gitaly clients are: Unicorn, Sidekiq, gitlab-workhorse,
+ gitlab-shell, Elasticsearch Indexer, and Gitaly itself.
+- A Gitaly server must be able to make RPC calls **to itself** via its own
+ `(Gitaly address, Gitaly token)` pair as specified in `/config/gitlab.yml`.
+- Gitaly servers must not be exposed to the public internet as Gitaly's network
+ traffic is unencrypted by default. The use of firewall is highly recommended
+ to restrict access to the Gitaly server. Another option is to
+ [use TLS](#tls-support).
+- Authentication is done through a static token which is shared among the Gitaly
+ and GitLab Rails nodes.
Below we describe how to configure a Gitaly server at address
`gitaly.internal:8075` with secret token `abc123secret`. We assume
your GitLab installation has two repository storages, `default` and
`storage1`.
-### Installation
+### 1. Installation
-First install Gitaly using either Omnibus or from source.
+First install Gitaly using either Omnibus GitLab or install it from source:
-Omnibus: [Download/install](https://about.gitlab.com/install/) the Omnibus GitLab
-package you want using **steps 1 and 2** from the GitLab downloads page but
-**_do not_** provide the `EXTERNAL_URL=` value.
+- For Omnibus GitLab: [Download/install](https://about.gitlab.com/install/) the Omnibus GitLab
+ package you want using **steps 1 and 2** from the GitLab downloads page but
+ **_do not_** provide the `EXTERNAL_URL=` value.
+- From source: [Install Gitaly](../../install/installation.md#install-gitaly).
-Source: [Install Gitaly](../../install/installation.md#install-gitaly)
+### 2. Client side token configuration
-### Client side token configuration
+Configure a token on the instance that runs the GitLab Rails application.
-Configure a token on the client side.
+**For Omnibus GitLab**
-Omnibus installations:
+1. On the client node(s), edit `/etc/gitlab/gitlab.rb`:
-```ruby
-# /etc/gitlab/gitlab.rb
-gitlab_rails['gitaly_token'] = 'abc123secret'
-```
+ ```ruby
+ gitlab_rails['gitaly_token'] = 'abc123secret'
+ ```
-Source installations:
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
-```yaml
-# /home/git/gitlab/config/gitlab.yml
-gitlab:
- gitaly:
- token: 'abc123secret'
-```
+**For installations from source**
+
+1. On the client node(s), edit `/home/git/gitlab/config/gitlab.yml`:
+
+ ```yaml
+ gitlab:
+ gitaly:
+ token: 'abc123secret'
+ ```
-You need to reconfigure (Omnibus) or restart (source) for these
-changes to be picked up.
+1. Save the file and [restart GitLab](../restart_gitlab.md#installations-from-source).
-### Gitaly server configuration
+### 3. Gitaly server configuration
-Next, on the Gitaly server, we need to configure storage paths, enable
+Next, on the Gitaly server, you need to configure storage paths, enable
the network listener and configure the token.
NOTE: **Note:** if you want to reduce the risk of downtime when you enable
@@ -137,88 +129,99 @@ the Gitaly server. The easiest way to accomplish this is to copy `/etc/gitlab/gi
from an existing GitLab server to the Gitaly server. Without this shared secret,
Git operations in GitLab will result in an API error.
-NOTE: **Note:** In most or all cases the storage paths below end in `/repositories` which is
-different than `path` in `git_data_dirs` of Omnibus installations. Check the
-directory layout on your Gitaly server to be sure.
-
-Omnibus installations:
-
-<!--
-updates to following example must also be made at
-https://gitlab.com/charts/gitlab/blob/master/doc/advanced/external-gitaly/external-omnibus-gitaly.md#configure-omnibus-gitlab
--->
-
-```ruby
-# /etc/gitlab/gitlab.rb
-
-# Avoid running unnecessary services on the Gitaly server
-postgresql['enable'] = false
-redis['enable'] = false
-nginx['enable'] = false
-prometheus['enable'] = false
-unicorn['enable'] = false
-sidekiq['enable'] = false
-gitlab_workhorse['enable'] = false
-
-# Prevent database connections during 'gitlab-ctl reconfigure'
-gitlab_rails['rake_cache_clear'] = false
-gitlab_rails['auto_migrate'] = false
-
-# Configure the gitlab-shell API callback URL. Without this, `git push` will
-# fail. This can be your 'front door' GitLab URL or an internal load
-# balancer.
-# Don't forget to copy `/etc/gitlab/gitlab-secrets.json` from web server to Gitaly server.
-gitlab_rails['internal_api_url'] = 'https://gitlab.example.com'
-
-# Make Gitaly accept connections on all network interfaces. You must use
-# firewalls to restrict access to this address/port.
-gitaly['listen_addr'] = "0.0.0.0:8075"
-gitaly['auth_token'] = 'abc123secret'
-
-gitaly['storage'] = [
- { 'name' => 'default', 'path' => '/mnt/gitlab/default/repositories' },
- { 'name' => 'storage1', 'path' => '/mnt/gitlab/storage1/repositories' },
-]
-
-# To use TLS for Gitaly you need to add
-gitaly['tls_listen_addr'] = "0.0.0.0:9999"
-gitaly['certificate_path'] = "path/to/cert.pem"
-gitaly['key_path'] = "path/to/key.pem"
-```
+NOTE: **Note:**
+In most or all cases, the storage paths below end in `/repositories` which is
+not that case with `path` in `git_data_dirs` of Omnibus GitLab installations.
+Check the directory layout on your Gitaly server to be sure.
-Source installations:
+**For Omnibus GitLab**
-```toml
-# /home/git/gitaly/config.toml
-listen_addr = '0.0.0.0:8075'
-tls_listen_addr = '0.0.0.0:9999'
+1. Edit `/etc/gitlab/gitlab.rb`:
-[tls]
-certificate_path = /path/to/cert.pem
-key_path = /path/to/key.pem
+ <!--
+ updates to following example must also be made at
+ https://gitlab.com/charts/gitlab/blob/master/doc/advanced/external-gitaly/external-omnibus-gitaly.md#configure-omnibus-gitlab
+ -->
-[auth]
-token = 'abc123secret'
+ ```ruby
+ # /etc/gitlab/gitlab.rb
-[[storage]]
-name = 'default'
-path = '/mnt/gitlab/default/repositories'
+ # Avoid running unnecessary services on the Gitaly server
+ postgresql['enable'] = false
+ redis['enable'] = false
+ nginx['enable'] = false
+ prometheus['enable'] = false
+ unicorn['enable'] = false
+ sidekiq['enable'] = false
+ gitlab_workhorse['enable'] = false
-[[storage]]
-name = 'storage1'
-path = '/mnt/gitlab/storage1/repositories'
-```
+ # Prevent database connections during 'gitlab-ctl reconfigure'
+ gitlab_rails['rake_cache_clear'] = false
+ gitlab_rails['auto_migrate'] = false
+
+ # Configure the gitlab-shell API callback URL. Without this, `git push` will
+ # fail. This can be your 'front door' GitLab URL or an internal load
+ # balancer.
+ # Don't forget to copy `/etc/gitlab/gitlab-secrets.json` from web server to Gitaly server.
+ gitlab_rails['internal_api_url'] = 'https://gitlab.example.com'
-Again, reconfigure (Omnibus) or restart (source).
+ # Make Gitaly accept connections on all network interfaces. You must use
+ # firewalls to restrict access to this address/port.
+ gitaly['listen_addr'] = "0.0.0.0:8075"
+ gitaly['auth_token'] = 'abc123secret'
-### Converting clients to use the Gitaly server
+ gitaly['storage'] = [
+ { 'name' => 'default' },
+ { 'name' => 'storage1' },
+ ]
-Now as the final step update the client machines to switch from using
-their local Gitaly service to the new Gitaly server you just
-configured. This is a risky step because if there is any sort of
-network, firewall, or name resolution problem preventing your GitLab
-server from reaching the Gitaly server then all Gitaly requests will
-fail.
+ # To use TLS for Gitaly you need to add
+ gitaly['tls_listen_addr'] = "0.0.0.0:9999"
+ gitaly['certificate_path'] = "path/to/cert.pem"
+ gitaly['key_path'] = "path/to/key.pem"
+ ```
+
+ NOTE: **Note:**
+ In some cases, you'll have to set `path` for `gitaly['storage']` in the
+ format `'path' => '/mnt/gitlab/<storage name>/repositories'`.
+
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
+
+**For installations from source**
+
+1. On the client node(s), edit `/home/git/gitaly/config.toml`:
+
+ ```toml
+ listen_addr = '0.0.0.0:8075'
+ tls_listen_addr = '0.0.0.0:9999'
+
+ [tls]
+ certificate_path = /path/to/cert.pem
+ key_path = /path/to/key.pem
+
+ [auth]
+ token = 'abc123secret'
+
+ [[storage]]
+ name = 'default'
+
+ [[storage]]
+ name = 'storage1'
+ ```
+
+ NOTE: **Note:**
+ In some cases, you'll have to set `path` for each `[[storage]]` in the
+ format `path = '/mnt/gitlab/<storage name>/repositories'`.
+
+1. Save the file and [restart GitLab](../restart_gitlab.md#installations-from-source).
+
+### 4. Converting clients to use the Gitaly server
+
+As the final step, you need to update the client machines to switch from using
+their local Gitaly service to the new Gitaly server you just configured. This
+is a risky step because if there is any sort of network, firewall, or name
+resolution problem preventing your GitLab server from reaching the Gitaly server,
+then all Gitaly requests will fail.
Additionally, you need to
[disable Rugged if previously manually enabled](../high_availability/nfs.md#improving-nfs-performance-with-gitlab).
@@ -227,41 +230,93 @@ We assume that your Gitaly server can be reached at
`gitaly.internal:8075` from your GitLab server, and that Gitaly can read and
write to `/mnt/gitlab/default` and `/mnt/gitlab/storage1` respectively.
-Omnibus installations:
+**For Omnibus GitLab**
-```ruby
-# /etc/gitlab/gitlab.rb
-git_data_dirs({
- 'default' => { 'gitaly_address' => 'tcp://gitaly.internal:8075' },
- 'storage1' => { 'gitaly_address' => 'tcp://gitaly.internal:8075' },
-})
+1. Edit `/etc/gitlab/gitlab.rb`:
-gitlab_rails['gitaly_token'] = 'abc123secret'
-```
+ ```ruby
+ git_data_dirs({
+ 'default' => { 'gitaly_address' => 'tcp://gitaly.internal:8075' },
+ 'storage1' => { 'gitaly_address' => 'tcp://gitaly.internal:8075' },
+ })
-Source installations:
-
-```yaml
-# /home/git/gitlab/config/gitlab.yml
-gitlab:
- repositories:
- storages:
- default:
- path: /mnt/gitlab/default/repositories
- gitaly_address: tcp://gitaly.internal:8075
- storage1:
- path: /mnt/gitlab/storage1/repositories
- gitaly_address: tcp://gitaly.internal:8075
-
- gitaly:
- token: 'abc123secret'
-```
+ gitlab_rails['gitaly_token'] = 'abc123secret'
+ ```
-Now reconfigure (Omnibus) or restart (source). When you tail the
-Gitaly logs on your Gitaly server (`sudo gitlab-ctl tail gitaly` or
-`tail -f /home/git/gitlab/log/gitaly.log`) you should see requests
-coming in. One sure way to trigger a Gitaly request is to clone a
-repository from your GitLab server over HTTP.
+ NOTE: **Note:**
+ In some cases, you'll have to set `path` for each `git_data_dirs` in the
+ format `'path' => '/mnt/gitlab/<storage name>'`.
+
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
+1. Tail the logs to see the requests:
+
+ ```sh
+ sudo gitlab-ctl tail gitaly
+ ```
+
+**For installations from source**
+
+1. Edit `/home/git/gitlab/config/gitlab.yml`:
+
+ ```yaml
+ gitlab:
+ repositories:
+ storages:
+ default:
+ gitaly_address: tcp://gitaly.internal:8075
+ storage1:
+ gitaly_address: tcp://gitaly.internal:8075
+
+ gitaly:
+ token: 'abc123secret'
+ ```
+
+ NOTE: **Note:**
+ In some cases, you'll have to set `path` for each of the `storages` in the
+ format `path: /mnt/gitlab/<storage name>/repositories`.
+
+1. Save the file and [restart GitLab](../restart_gitlab.md#installations-from-source).
+1. Tail the logs to see the requests:
+
+ ```sh
+ tail -f /home/git/gitlab/log/gitaly.log
+ ```
+
+When you tail the Gitaly logs on your Gitaly server you should see requests
+coming in. One sure way to trigger a Gitaly request is to clone a repository
+from your GitLab server over HTTP.
+
+### Disabling the Gitaly service in a cluster environment
+
+If you are running Gitaly [as a remote
+service](#running-gitaly-on-its-own-server) you may want to disable
+the local Gitaly service that runs on your GitLab server by default.
+Disabling Gitaly only makes sense when you run GitLab in a custom
+cluster configuration, where different services run on different
+machines. Disabling Gitaly on all machines in the cluster is not a
+valid configuration.
+
+To disable Gitaly on a client node:
+
+**For Omnibus GitLab**
+
+1. Edit `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ gitaly['enable'] = false
+ ```
+
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
+
+**For installations from source**
+
+1. Edit `/etc/default/gitlab`:
+
+ ```shell
+ gitaly_enabled=false
+ ```
+
+1. Save the file and [restart GitLab](../restart_gitlab.md#installations-from-source).
## TLS support
@@ -271,168 +326,128 @@ Gitaly supports TLS encryption. To be able to communicate
with a Gitaly instance that listens for secure connections you will need to use `tls://` url
scheme in the `gitaly_address` of the corresponding storage entry in the GitLab configuration.
-The admin needs to bring their own certificate as we do not provide that automatically.
-The certificate to be used needs to be installed on all Gitaly nodes and on all client nodes that communicate with it following procedures described in [GitLab custom certificate configuration](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates).
+You will need to bring your own certificates as this isn't provided automatically.
+The certificate to be used needs to be installed on all Gitaly nodes and on all
+client nodes that communicate with it following the procedure described in
+[GitLab custom certificate configuration](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates).
-Note that it is possible to configure Gitaly servers with both an
+NOTE: **Note:**
+It is possible to configure Gitaly servers with both an
unencrypted listening address `listen_addr` and an encrypted listening
address `tls_listen_addr` at the same time. This allows you to do a
gradual transition from unencrypted to encrypted traffic, if necessary.
-To observe what type of connections are actually being used in a
-production environment you can use the following Prometheus query:
-
-```
-sum(rate(gitaly_connections_total[5m])) by (type)
-```
+To configure Gitaly with TLS:
-### Example TLS configuration
+**For Omnibus GitLab**
-### Omnibus installations:
+1. On the client nodes, edit `/etc/gitlab/gitlab.rb`:
-#### On client nodes:
+ ```ruby
+ git_data_dirs({
+ 'default' => { 'gitaly_address' => 'tls://gitaly.internal:9999' },
+ 'storage1' => { 'gitaly_address' => 'tls://gitaly.internal:9999' },
+ })
-```ruby
-# /etc/gitlab/gitlab.rb
-git_data_dirs({
- 'default' => { 'gitaly_address' => 'tls://gitaly.internal:9999' },
- 'storage1' => { 'gitaly_address' => 'tls://gitaly.internal:9999' },
-})
+ gitlab_rails['gitaly_token'] = 'abc123secret'
+ ```
-gitlab_rails['gitaly_token'] = 'abc123secret'
-```
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
+1. On the Gitaly server nodes, edit `/etc/gitlab/gitlab.rb`:
-#### On Gitaly server nodes:
+ ```ruby
+ gitaly['tls_listen_addr'] = "0.0.0.0:9999"
+ gitaly['certificate_path'] = "path/to/cert.pem"
+ gitaly['key_path'] = "path/to/key.pem"
+ ```
-```ruby
-gitaly['tls_listen_addr'] = "0.0.0.0:9999"
-gitaly['certificate_path'] = "path/to/cert.pem"
-gitaly['key_path'] = "path/to/key.pem"
-```
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
-### Source installations:
+**For installations from source**
-#### On client nodes:
+1. On the client nodes, edit `/home/git/gitlab/config/gitlab.yml`:
-```yaml
-# /home/git/gitlab/config/gitlab.yml
-gitlab:
- repositories:
- storages:
- default:
- path: /mnt/gitlab/default/repositories
- gitaly_address: tls://gitaly.internal:9999
- storage1:
- path: /mnt/gitlab/storage1/repositories
- gitaly_address: tls://gitaly.internal:9999
+ ```yaml
+ gitlab:
+ repositories:
+ storages:
+ default:
+ gitaly_address: tls://gitaly.internal:9999
+ storage1:
+ gitaly_address: tls://gitaly.internal:9999
- gitaly:
- token: 'abc123secret'
-```
+ gitaly:
+ token: 'abc123secret'
+ ```
-#### On Gitaly server nodes:
+ NOTE: **Note:**
+ In some cases, you'll have to set `path` for each of the `storages` in the
+ format `path: /mnt/gitlab/<storage name>/repositories`.
-```toml
-# /home/git/gitaly/config.toml
-tls_listen_addr = '0.0.0.0:9999'
+1. Save the file and [restart GitLab](../restart_gitlab.md#installations-from-source).
+1. On the Gitaly server nodes, edit `/home/git/gitaly/config.toml`:
-[tls]
-certificate_path = '/path/to/cert.pem'
-key_path = '/path/to/key.pem'
-```
+ ```toml
+ tls_listen_addr = '0.0.0.0:9999'
-## Gitaly-ruby
+ [tls]
+ certificate_path = '/path/to/cert.pem'
+ key_path = '/path/to/key.pem'
+ ```
-Gitaly was developed to replace Ruby application code in gitlab-ce/ee.
-In order to save time and/or avoid the risk of rewriting existing
-application logic, in some cases we chose to copy some application code
-from gitlab-ce into Gitaly almost as-is. To be able to run that code, we
-made gitaly-ruby, which is a sidecar process for the main Gitaly Go
-process. Some examples of things that are implemented in gitaly-ruby are
-RPC's that deal with wiki's, and RPC's that create commits on behalf of
-a user, such as merge commits.
-
-### Number of gitaly-ruby workers
-
-Gitaly-ruby has much less capacity than Gitaly itself. If your Gitaly
-server has to handle a lot of request, the default setting of having
-just 1 active gitaly-ruby sidecar might not be enough. If you see
-ResourceExhausted errors from Gitaly it's very likely that you have not
-enough gitaly-ruby capacity.
-
-You can increase the number of gitaly-ruby processes on your Gitaly
-server with the following settings.
+1. Save the file and [restart GitLab](../restart_gitlab.md#installations-from-source).
-Omnibus:
+To observe what type of connections are actually being used in a
+production environment you can use the following Prometheus query:
-```ruby
-# /etc/gitlab/gitlab.rb
-# Default is 2 workers. The minimum is 2; 1 worker is always reserved as
-# a passive stand-by.
-gitaly['ruby_num_workers'] = 4
```
-
-Source:
-
-```toml
-# /home/git/gitaly/config.toml
-[gitaly-ruby]
-num_workers = 4
+sum(rate(gitaly_connections_total[5m])) by (type)
```
-### Observing gitaly-ruby traffic
-
-Gitaly-ruby is a somewhat hidden, internal implementation detail of
-Gitaly. There is not that much visibility into what goes on inside
-gitaly-ruby processes.
+## `gitaly-ruby`
-If you have Prometheus set up to scrape your Gitaly process, you can see
-request rates and error codes for individual RPC's in gitaly-ruby by
-querying `grpc_client_handled_total`. Strictly speaking this metric does
-not differentiate between gitaly-ruby and other RPC's, but in practice
-(as of GitLab 11.9), all gRPC calls made by Gitaly itself are internal
-calls from the main Gitaly process to one of its gitaly-ruby sidecars.
+Gitaly was developed to replace the Ruby application code in GitLab.
+In order to save time and/or avoid the risk of rewriting existing
+application logic, in some cases we chose to copy some application code
+from GitLab into Gitaly almost as-is. To be able to run that code,
+`gitaly-ruby` was created, which is a "sidecar" process for the main Gitaly Go
+process. Some examples of things that are implemented in `gitaly-ruby` are
+RPCs that deal with wikis, and RPCs that create commits on behalf of
+a user, such as merge commits.
-Assuming your `grpc_client_handled_total` counter only observes Gitaly,
-the following query shows you RPC's are (most likely) internally
-implemented as calls to gitaly-ruby.
+### Number of `gitaly-ruby` workers
-```
-sum(rate(grpc_client_handled_total[5m])) by (grpc_method) > 0
-```
+`gitaly-ruby` has much less capacity than Gitaly itself. If your Gitaly
+server has to handle a lot of requests, the default setting of having
+just one active `gitaly-ruby` sidecar might not be enough. If you see
+`ResourceExhausted` errors from Gitaly, it's very likely that you have not
+enough `gitaly-ruby` capacity.
-## Disabling or enabling the Gitaly service in a cluster environment
+You can increase the number of `gitaly-ruby` processes on your Gitaly
+server with the following settings.
-If you are running Gitaly [as a remote
-service](#running-gitaly-on-its-own-server) you may want to disable
-the local Gitaly service that runs on your GitLab server by default.
+**For Omnibus GitLab**
-> 'Disabling Gitaly' only makes sense when you run GitLab in a custom
-cluster configuration, where different services run on different
-machines. Disabling Gitaly on all machines in the cluster is not a
-valid configuration.
+1. Edit `/etc/gitlab/gitlab.rb`:
-If you are setting up a GitLab cluster where Gitaly does not need to
-run on all machines, you can disable the Gitaly service in your
-Omnibus installation, add the following line to `/etc/gitlab/gitlab.rb`:
+ ```ruby
+ # Default is 2 workers. The minimum is 2; 1 worker is always reserved as
+ # a passive stand-by.
+ gitaly['ruby_num_workers'] = 4
+ ```
-```ruby
-gitaly['enable'] = false
-```
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
-When you run `gitlab-ctl reconfigure` the Gitaly service will be
-disabled.
+**For installations from source**
-To disable the Gitaly service in a GitLab cluster where you installed
-GitLab from source, add the following to `/etc/default/gitlab` on the
-machine where you want to disable Gitaly.
+1. Edit `/home/git/gitaly/config.toml`:
-```shell
-gitaly_enabled=false
-```
+ ```toml
+ [gitaly-ruby]
+ num_workers = 4
+ ```
-When you run `service gitlab restart` Gitaly will be disabled on this
-particular machine.
+1. Save the file and [restart GitLab](../restart_gitlab.md#installations-from-source).
## Eliminating NFS altogether
@@ -440,19 +455,23 @@ If you are planning to use Gitaly without NFS for your storage needs
and want to eliminate NFS from your environment altogether, there are
a few things that you need to do:
- 1. Make sure the [`git` user home directory](https://docs.gitlab.com/omnibus/settings/configuration.html#moving-the-home-directory-for-a-user) is on local disk.
- 1. Configure [database lookup of SSH keys](../operations/fast_ssh_key_lookup.md)
- to eliminate the need for a shared authorized_keys file.
- 1. Configure [object storage for job artifacts](../job_artifacts.md#using-object-storage)
- including [live tracing](../job_traces.md#new-live-trace-architecture).
- 1. Configure [object storage for LFS objects](../../workflow/lfs/lfs_administration.md#storing-lfs-objects-in-remote-object-storage).
- 1. Configure [object storage for uploads](../uploads.md#using-object-storage-core-only).
-
-NOTE: **Note:** One current feature of GitLab still requires a shared directory (NFS): [GitLab Pages](../../user/project/pages/index.md).
+1. Make sure the [`git` user home directory](https://docs.gitlab.com/omnibus/settings/configuration.html#moving-the-home-directory-for-a-user) is on local disk.
+1. Configure [database lookup of SSH keys](../operations/fast_ssh_key_lookup.md)
+ to eliminate the need for a shared authorized_keys file.
+1. Configure [object storage for job artifacts](../job_artifacts.md#using-object-storage)
+ including [live tracing](../job_traces.md#new-live-trace-architecture).
+1. Configure [object storage for LFS objects](../../workflow/lfs/lfs_administration.md#storing-lfs-objects-in-remote-object-storage).
+1. Configure [object storage for uploads](../uploads.md#using-object-storage-core-only).
+
+NOTE: **Note:**
+One current feature of GitLab that still requires a shared directory (NFS) is
+[GitLab Pages](../../user/project/pages/index.md).
There is [work in progress](https://gitlab.com/gitlab-org/gitlab-pages/issues/196)
to eliminate the need for NFS to support GitLab Pages.
-## Troubleshooting Gitaly in production
+## Troubleshooting
+
+### `gitaly-debug`
Since GitLab 11.6, Gitaly comes with a command-line tool called
`gitaly-debug` that can be run on a Gitaly server to aid in
@@ -462,3 +481,32 @@ Git clone speed for a specific repository on the server.
For an up to date list of sub-commands see [the gitaly-debug
README](https://gitlab.com/gitlab-org/gitaly/blob/master/cmd/gitaly-debug/README.md).
+
+### Client side GRPC logs
+
+Gitaly uses the [gRPC](https://grpc.io/) RPC framework. The Ruby gRPC
+client has its own log file which may contain useful information when
+you are seeing Gitaly errors. You can control the log level of the
+gRPC client with the `GRPC_LOG_LEVEL` environment variable. The
+default level is `WARN`.
+
+### Observing `gitaly-ruby` traffic
+
+[`gitaly-ruby`](#gitaly-ruby) is an internal implementation detail of Gitaly,
+so, there's not that much visibility into what goes on inside
+`gitaly-ruby` processes.
+
+If you have Prometheus set up to scrape your Gitaly process, you can see
+request rates and error codes for individual RPCs in `gitaly-ruby` by
+querying `grpc_client_handled_total`. Strictly speaking, this metric does
+not differentiate between `gitaly-ruby` and other RPCs, but in practice
+(as of GitLab 11.9), all gRPC calls made by Gitaly itself are internal
+calls from the main Gitaly process to one of its `gitaly-ruby` sidecars.
+
+Assuming your `grpc_client_handled_total` counter only observes Gitaly,
+the following query shows you RPCs are (most likely) internally
+implemented as calls to `gitaly-ruby`:
+
+```
+sum(rate(grpc_client_handled_total[5m])) by (grpc_method) > 0
+```
diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md
index 5490a59ceac..2b624f8de77 100644
--- a/doc/administration/job_artifacts.md
+++ b/doc/administration/job_artifacts.md
@@ -115,6 +115,7 @@ The connection settings match those provided by [Fog](https://github.com/fog), a
| `aws_access_key_id` | AWS credentials, or compatible | |
| `aws_secret_access_key` | AWS credentials, or compatible | |
| `aws_signature_version` | AWS signature version to use. 2 or 4 are valid options. Digital Ocean Spaces and other providers may need 2. | 4 |
+| `enable_signature_v4_streaming` | Set to true to enable HTTP chunked transfers with AWS v4 signatures (https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html). Oracle Cloud S3 needs this to be false | true
| `region` | AWS region | us-east-1 |
| `host` | S3 compatible host for when not using AWS, e.g. `localhost` or `storage.example.com` | s3.amazonaws.com |
| `endpoint` | Can be used when configuring an S3 compatible service such as [Minio](https://www.minio.io), by entering a URL such as `http://127.0.0.1:9000` | (optional) |
diff --git a/doc/administration/logs.md b/doc/administration/logs.md
index 5a2f389d298..563701b8677 100644
--- a/doc/administration/logs.md
+++ b/doc/administration/logs.md
@@ -157,16 +157,22 @@ This file lives in `/var/log/gitlab/gitlab-rails/githost.log` for
Omnibus GitLab packages or in `/home/git/gitlab/log/githost.log` for
installations from source.
+NOTE: **Note:**
+After 12.2, this file will be stored in JSON format.
+
GitLab has to interact with Git repositories but in some rare cases
something can go wrong and in this case you will know what exactly
happened. This log file contains all failed requests from GitLab to Git
repositories. In the majority of cases this file will be useful for developers
only. For example:
-```
-December 03, 2014 13:20 -> ERROR -> Command failed [1]: /usr/bin/git --git-dir=/Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/gitlab-satellites/group184/gitlabhq/.git --work-tree=/Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/gitlab-satellites/group184/gitlabhq merge --no-ff -mMerge branch 'feature_conflict' into 'feature' source/feature_conflict
-
-error: failed to push some refs to '/Users/vsizov/gitlab-development-kit/repositories/gitlabhq/gitlab_git.git'
+```json
+{
+ "severity":"ERROR",
+ "time":"2019-07-19T22:16:12.528Z",
+ "correlation_id":"FeGxww5Hj64",
+ "message":"Command failed [1]: /usr/bin/git --git-dir=/Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/gitlab-satellites/group184/gitlabhq/.git --work-tree=/Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/gitlab-satellites/group184/gitlabhq merge --no-ff -mMerge branch 'feature_conflict' into 'feature' source/feature_conflict\n\nerror: failed to push some refs to '/Users/vsizov/gitlab-development-kit/repositories/gitlabhq/gitlab_git.git'"
+}
```
## `audit_json.log`
diff --git a/doc/administration/merge_request_diffs.md b/doc/administration/merge_request_diffs.md
index 99cd9051778..0b065082ded 100644
--- a/doc/administration/merge_request_diffs.md
+++ b/doc/administration/merge_request_diffs.md
@@ -90,6 +90,7 @@ The connection settings match those provided by [Fog](https://github.com/fog), a
| `aws_access_key_id` | AWS credentials, or compatible | |
| `aws_secret_access_key` | AWS credentials, or compatible | |
| `aws_signature_version` | AWS signature version to use. 2 or 4 are valid options. Digital Ocean Spaces and other providers may need 2. | 4 |
+| `enable_signature_v4_streaming` | Set to true to enable HTTP chunked transfers with AWS v4 signatures (https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html). Oracle Cloud S3 needs this to be false | true
| `region` | AWS region | us-east-1 |
| `host` | S3 compatible host for when not using AWS, e.g. `localhost` or `storage.example.com` | s3.amazonaws.com |
| `endpoint` | Can be used when configuring an S3 compatible service such as [Minio](https://www.minio.io), by entering a URL such as `http://127.0.0.1:9000` | (optional) |
diff --git a/doc/administration/monitoring/performance/img/request_profile_result.png b/doc/administration/monitoring/performance/img/request_profile_result.png
index 1b06e240fa0..3b34f207974 100644
--- a/doc/administration/monitoring/performance/img/request_profile_result.png
+++ b/doc/administration/monitoring/performance/img/request_profile_result.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/request_profiling.md b/doc/administration/monitoring/performance/request_profiling.md
index 726882fbb87..9f671e0db11 100644
--- a/doc/administration/monitoring/performance/request_profiling.md
+++ b/doc/administration/monitoring/performance/request_profiling.md
@@ -5,9 +5,9 @@
1. Grab the profiling token from **Monitoring > Requests Profiles** admin page
(highlighted in a blue in the image below).
![Profile token](img/request_profiling_token.png)
-1. Pass the header `X-Profile-Token: <token>` to the request you want to profile. You can use:
+1. Pass the header `X-Profile-Token: <token>` and `X-Profile-Mode: <mode>`(where <mode> can be `execution` or `memory`) to the request you want to profile. You can use:
- Browser extensions. For example, [ModHeader](https://chrome.google.com/webstore/detail/modheader/idgpnmonknjnojddfkpgkljpfnnfcklj) Chrome extension.
- - `curl`. For example, `curl --header 'X-Profile-Token: <token>' https://gitlab.example.com/group/project`.
+ - `curl`. For example, `curl --header 'X-Profile-Token: <token>' --header 'X-Profile-Mode: <mode>' https://gitlab.example.com/group/project`.
1. Once request is finished (which will take a little longer than usual), you can
view the profiling output from **Monitoring > Requests Profiles** admin page.
![Profiling output](img/request_profile_result.png)
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index 89501f20d99..054fa547704 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -34,6 +34,9 @@ The following metrics are available:
| filesystem_writable | Gauge | 9.4 | Whether or not the filesystem is writable |
| filesystem_read_latency_seconds | Gauge | 9.4 | Read latency of a specific filesystem |
| filesystem_readable | Gauge | 9.4 | Whether or not the filesystem is readable |
+| gitlab_cache_misses_total | Counter | 10.2 | Cache read miss |
+| gitlab_cache_operation_duration_seconds | Histogram | 10.2 | Cache access time |
+| gitlab_cache_operations_total | Counter | 12.2 | Cache operations by controller/action |
| http_requests_total | Counter | 9.4 | Rack request count |
| http_request_duration_seconds | Histogram | 9.4 | HTTP response time from rack middleware |
| pipelines_created_total | Counter | 9.4 | Counter of pipelines created |
diff --git a/doc/administration/pages/img/lets_encrypt_integration_v12_1.png b/doc/administration/pages/img/lets_encrypt_integration_v12_1.png
new file mode 100644
index 00000000000..5ab63074e12
--- /dev/null
+++ b/doc/administration/pages/img/lets_encrypt_integration_v12_1.png
Binary files differ
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index 3cabe8eb16e..774e7056845 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -265,6 +265,23 @@ verification requirement. Navigate to `Admin area ➔ Settings` and uncheck
**Require users to prove ownership of custom domains** in the Pages section.
This setting is enabled by default.
+### Let's Encrypt integration
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/28996) in GitLab 12.1.
+
+[GitLab Pages' Let's Encrypt integration](../../user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md)
+allows users to add Let's Encrypt SSL certificates for GitLab Pages
+sites served under a custom domain.
+
+To enable it, you'll need to:
+
+1. Choose an email on which you will recieve notifications about expiring domains.
+1. Navigate to your instance's **Admin Area > Settings > Preferences** and expand **Pages** settings.
+1. Enter the email for receiving notifications and accept Let's Encrypt's Terms of Service as shown below.
+1. Click **Save changes**.
+
+![Let's Encrypt settings](img/lets_encrypt_integration_v12_1.png)
+
### Access control
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/33422) in GitLab 11.5.
diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md
index 7cf8f20a9dc..05a7cb006a3 100644
--- a/doc/administration/repository_checks.md
+++ b/doc/administration/repository_checks.md
@@ -33,8 +33,8 @@ in `repocheck.log`:
- in the [admin panel](logs.md#repochecklog)
- or on disk, see:
- - `/var/log/gitlab/gitlab-rails` for Omnibus installations
- - `/home/git/gitlab/log` for installations from source
+ - `/var/log/gitlab/gitlab-rails` for Omnibus installations
+ - `/home/git/gitlab/log` for installations from source
If for some reason the periodic repository check caused a lot of false
alarms you can choose to clear *all* repository check states by
diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md
index 9dea6074a3f..1669f8b128c 100644
--- a/doc/administration/repository_storage_types.md
+++ b/doc/administration/repository_storage_types.md
@@ -105,7 +105,7 @@ question.
To start a migration, enable Hashed Storage for new projects:
1. Go to **Admin > Settings > Repository** and expand the **Repository Storage** section.
-2. Select the **Use hashed storage paths for newly created and renamed projects** checkbox.
+1. Select the **Use hashed storage paths for newly created and renamed projects** checkbox.
Check if the change breaks any existing integration you may have that
either runs on the same machine as your repositories are located, or may login to that machine
@@ -133,7 +133,7 @@ Similar to the migration, to disable Hashed Storage for new
projects:
1. Go to **Admin > Settings > Repository** and expand the **Repository Storage** section.
-2. Uncheck the **Use hashed storage paths for newly created and renamed projects** checkbox.
+1. Uncheck the **Use hashed storage paths for newly created and renamed projects** checkbox.
To schedule a complete rollback, see the
[rake task documentation for storage rollback](raketasks/storage.md#rollback-from-hashed-storage-to-legacy-storage) for instructions.
diff --git a/doc/administration/uploads.md b/doc/administration/uploads.md
index 51e267c865e..99f1c386183 100644
--- a/doc/administration/uploads.md
+++ b/doc/administration/uploads.md
@@ -78,6 +78,7 @@ The connection settings match those provided by [Fog](https://github.com/fog), a
| `aws_access_key_id` | AWS credentials, or compatible | |
| `aws_secret_access_key` | AWS credentials, or compatible | |
| `aws_signature_version` | AWS signature version to use. 2 or 4 are valid options. Digital Ocean Spaces and other providers may need 2. | 4 |
+| `enable_signature_v4_streaming` | Set to true to enable HTTP chunked transfers with AWS v4 signatures (https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html). Oracle Cloud S3 needs this to be false | true
| `region` | AWS region | us-east-1 |
| `host` | S3 compatible host for when not using AWS, e.g. `localhost` or `storage.example.com` | s3.amazonaws.com |
| `endpoint` | Can be used when configuring an S3 compatible service such as [Minio](https://www.minio.io), by entering a URL such as `http://127.0.0.1:9000` | (optional) |
@@ -140,6 +141,22 @@ _The uploads are stored by default in
1. Save the file and [restart GitLab][] for the changes to take effect.
1. Migrate any existing local uploads to the object storage using [`gitlab:uploads:migrate` rake task](raketasks/uploads/migrate.md).
+### Oracle Cloud S3 connection settings
+
+Note that Oracle Cloud S3 must be sure to use the following settings:
+
+| Setting | Value |
+|---------|-------|
+| `enable_signature_v4_streaming` | false |
+| `path_style` | true |
+
+If `enable_signature_v4_streaming` is set to `true`, you may see the
+following error:
+
+```
+STREAMING-AWS4-HMAC-SHA256-PAYLOAD is not supported
+```
+
### OpenStack compatible connection settings
The connection settings match those provided by [Fog](https://github.com/fog), and are as follows:
diff --git a/doc/api/README.md b/doc/api/README.md
index 8e60d1c61df..f9cc1a09870 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -272,6 +272,12 @@ Example of using the personal access token in a header:
curl --header "Private-Token: <your_access_token>" https://gitlab.example.com/api/v4/projects
```
+You can also use personal access tokens with OAuth-compliant headers:
+
+```shell
+curl --header "Authorization: Bearer <your_access_token>" https://gitlab.example.com/api/v4/projects
+```
+
Read more about [personal access tokens][pat].
### Session cookie
diff --git a/doc/api/commits.md b/doc/api/commits.md
index 6eb4c47415f..1a835c0a872 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -72,15 +72,16 @@ POST /projects/:id/repository/commits
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
-| `branch` | string | yes | Name of the branch to commit into. To create a new branch, also provide `start_branch`. |
+| `branch` | string | yes | Name of the branch to commit into. To create a new branch, also provide either `start_branch` or `start_sha`, and optionally `start_project`. |
| `commit_message` | string | yes | Commit message |
-| `start_branch` | string | no | Name of the branch to start the new commit from |
-| `start_project` | integer/string | no | The project ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) to start the commit from. Defaults to the value of `id`. |
+| `start_branch` | string | no | Name of the branch to start the new branch from |
+| `start_sha` | string | no | SHA of the commit to start the new branch from |
+| `start_project` | integer/string | no | The project ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) to start the new branch from. Defaults to the value of `id`. |
| `actions[]` | array | yes | An array of action hashes to commit as a batch. See the next table for what attributes it can take. |
| `author_email` | string | no | Specify the commit author's email address |
| `author_name` | string | no | Specify the commit author's name |
| `stats` | boolean | no | Include commit stats. Default is true |
-| `force` | boolean | no | When `true` overwrites the target branch with a new commit based on the `start_branch` |
+| `force` | boolean | no | When `true` overwrites the target branch with a new commit based on the `start_branch` or `start_sha` |
| `actions[]` Attribute | Type | Required | Description |
| --------------------- | ---- | -------- | ----------- |
diff --git a/doc/api/container_registry.md b/doc/api/container_registry.md
index 64ea15bca93..174b93a4f7a 100644
--- a/doc/api/container_registry.md
+++ b/doc/api/container_registry.md
@@ -193,13 +193,13 @@ Examples:
curl --request DELETE --data 'name_regex=[0-9a-z]{40}' --data 'keep_n=5' --data 'older_than=2d' --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/registry/repositories/2/tags"
```
-2. Remove all tags, but keep always the latest 5:
+1. Remove all tags, but keep always the latest 5:
```bash
curl --request DELETE --data 'name_regex=.*' --data 'keep_n=5' --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/registry/repositories/2/tags"
```
-3. Remove all tags that are older than 1 month:
+1. Remove all tags that are older than 1 month:
```bash
curl --request DELETE --data 'name_regex=.*' --data 'older_than=1month' --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/registry/repositories/2/tags"
diff --git a/doc/api/lint.md b/doc/api/lint.md
index b9b49f3df27..79f5e629c7f 100644
--- a/doc/api/lint.md
+++ b/doc/api/lint.md
@@ -5,7 +5,7 @@
Checks if your `.gitlab-ci.yml` file is valid.
```
-POST /lint
+POST /ci/lint
```
| Attribute | Type | Required | Description |
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 781192fb92e..ba7e28c279b 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -799,7 +799,7 @@ POST /projects/user/:user_id
| `auto_devops_deploy_strategy` | string | no | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`) |
| `repository_storage` | string | no | Which storage shard the repository is on. Available only to admins |
| `approvals_before_merge` | integer | no | **(STARTER)** How many approvers should approve merge requests by default |
-| `external_authorization_classification_label` | string | no | **(CORE ONLY)** The classification label for the project |
+| `external_authorization_classification_label` | string | no | **(PREMIUM)** The classification label for the project |
| `mirror` | boolean | no | **(STARTER)** Enables pull mirroring in a project |
| `mirror_trigger_builds` | boolean | no | **(STARTER)** Pull mirroring triggers builds |
@@ -856,7 +856,7 @@ PUT /projects/:id
| `auto_devops_deploy_strategy` | string | no | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`) |
| `repository_storage` | string | no | Which storage shard the repository is on. Available only to admins |
| `approvals_before_merge` | integer | no | **(STARTER)** How many approvers should approve merge request by default |
-| `external_authorization_classification_label` | string | no | **(CORE ONLY)** The classification label for the project |
+| `external_authorization_classification_label` | string | no | **(PREMIUM)** The classification label for the project |
| `mirror` | boolean | no | **(STARTER)** Enables pull mirroring in a project |
| `mirror_user_id` | integer | no | **(STARTER)** User responsible for all the activity surrounding a pull mirror event |
| `mirror_trigger_builds` | boolean | no | **(STARTER)** Pull mirroring triggers builds |
diff --git a/doc/api/users.md b/doc/api/users.md
index fdc84826680..b41fd106fc5 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -349,6 +349,9 @@ Note that `force_random_password` and `reset_password` take priority
over `password`. In addition, `reset_password` and
`force_random_password` can be used together.
+NOTE: **Note:**
+From [GitLab 12.1](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/29888/), `private_profile` will default to `false`.
+
```
POST /users
```
diff --git a/doc/ci/caching/index.md b/doc/ci/caching/index.md
index 9a5a3624c73..5b2c3a8765c 100644
--- a/doc/ci/caching/index.md
+++ b/doc/ci/caching/index.md
@@ -8,7 +8,7 @@ GitLab CI/CD provides a caching mechanism that can be used to save time
when your jobs are running.
Caching is about speeding the time a job is executed by reusing the same
-content of a previous job. It can be particularly useful when your are
+content of a previous job. It can be particularly useful when you are
developing software that depends on other libraries which are fetched via the
internet during build time.
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 38fcf05f519..38d0d86ffa2 100644
--- a/doc/ci/examples/end_to_end_testing_webdriverio/index.md
+++ b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
@@ -143,7 +143,7 @@ Which brings us to the exciting part: how do we run this in GitLab CI/CD? There
need to do for this:
1. Set up [CI/CD jobs](../../yaml/README.md#introduction) that actually have a browser available.
-2. Update our WebdriverIO configuration to use those browsers to visit the review apps.
+1. Update our WebdriverIO configuration to use those browsers to visit the review apps.
For the scope of this article, we've defined an additional [CI/CD stage](../../yaml/README.md#stages)
`confidence-check` that is executed _after_ the stage that deploys the review app. It uses the `node:latest` [Docker
diff --git a/doc/ci/introduction/index.md b/doc/ci/introduction/index.md
index fc89f0fc94f..6401ff90a0a 100644
--- a/doc/ci/introduction/index.md
+++ b/doc/ci/introduction/index.md
@@ -146,14 +146,14 @@ so, GitLab CI/CD:
- Runs automated scripts (sequential or parallel) to:
- Build and test your app.
- Preview the changes per merge request with Review Apps, as you
- would see in your `localhost`.
+ would see in your `localhost`.
Once you're happy with your implementation:
- Get your code reviewed and approved.
- Merge the feature branch into the default branch.
- GitLab CI/CD deploys your changes automatically to a production environment.
-- And finally, you and your team can easily roll it back if something goes wrong.
+- And finally, you and your team can easily roll it back if something goes wrong.
![GitLab workflow example](img/gitlab_workflow_example_11_9.png)
@@ -176,24 +176,24 @@ you'll see some of the features available in GitLab
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](../../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.
+ - Automatically build and test your application with Continuous Integration.
+ - 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](../../user/project/packages/npm_registry.md). **(PREMIUM)**
- - Store Maven artifacts with [Maven Repository](../../user/project/packages/maven_repository.md). **(PREMIUM)**
+ - Store Docker images with [Container Registry](../../user/project/container_registry.md).
+ - 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](../../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](../../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).
+ - 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](../../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](../../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:
diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md
index a13857bee25..f63b17a9e5a 100644
--- a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md
+++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md
@@ -6,7 +6,6 @@ last_update: 2019-07-03
# Pipelines for Merged Results **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7380) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.10.
-> This feature is disabled by default until we resolve issues with [contention handling](https://gitlab.com/gitlab-org/gitlab-ee/issues/11222), but [can be enabled manually](#enabling-pipelines-for-merged-results).
It's possible for your source and target branches to diverge, which can result
in the scenario that source branch's pipeline was green, the target's pipeline was green,
@@ -25,8 +24,10 @@ new ref and a pipeline for this ref validates the result prior to merging.
There are some cases where creating a combined ref is not possible or not wanted.
For example, a source branch that has conflicts with the target branch
-or a merge request that is still in WIP status. In this case, the merge request pipeline falls back to a "detached" state
-and runs on the source branch ref as if it was a regular pipeline.
+or a merge request that is still in WIP status. In this case,
+GitLab doesn't create a merge commit and the pipeline runs on source branch, instead,
+which is a default behavior of [Pipelines for merge requests](../index.md)
+ i.e. `detached` label is shown to the pipelines.
The detached state serves to warn you that you are working in a situation
subjected to merge problems, and helps to highlight that you should
@@ -42,7 +43,7 @@ Pipelines for merged results require:
In addition, pipelines for merged results have the following limitations:
- Forking/cross-repo workflows are not currently supported. To follow progress,
- see [#9713](https://gitlab.com/gitlab-org/gitlab-ee/issues/9713).
+ see [#11934](https://gitlab.com/gitlab-org/gitlab-ee/issues/11934).
- This feature is not available for
[fast forward merges](../../../user/project/merge_requests/fast_forward_merge.md) yet.
To follow progress, see [#58226](https://gitlab.com/gitlab-org/gitlab-ce/issues/58226).
diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/img/merge_train_position_v12_0.png b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/img/merge_train_position_v12_0.png
new file mode 100644
index 00000000000..70916bc0e00
--- /dev/null
+++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/img/merge_train_position_v12_0.png
Binary files differ
diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md
index 57358434c02..44cbcde264c 100644
--- a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md
+++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md
@@ -6,7 +6,6 @@ last_update: 2019-07-03
# Merge Trains **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9186) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.0.
-> This feature is disabled by default, but [can be enabled manually](#enabling-merge-trains).
[Pipelines for merged results](../index.md#pipelines-for-merged-results-premium) introduces
running a build on the result of the merged code prior to merging, as a way to keep master green.
@@ -33,35 +32,40 @@ Merge trains have the following requirements and limitations:
- This feature requires that
[pipelines for merged results](../index.md#pipelines-for-merged-results-premium) are
**configured properly**.
-- Each merge train can generate a merge ref and run a pipeline **one at a time**.
- We plan to make the pipelines for merged results
- [run in parallel](https://gitlab.com/gitlab-org/gitlab-ee/issues/11222) in a
- future release.
+- Each merge train can run a maximum of **four** pipelines in parallel.
+ If more than four merge requests are added to the merge train, the merge requests
+ will be queued until a slot in the merge train is free. There is no limit to the
+ number of merge requests that can be queued.
+- This feature does not support [squash and merge](../../../../user/project/merge_requests/squash_and_merge.md).
-## Enabling Merge Trains
-
-To enable merge trains at the project level:
-
-1. Visit your project's **Settings > General** and expand **Merge requests**.
-1. Check **Allow merge trains**.
-1. Click **Save changes** button.
-
-![Merge request pipeline config](img/merge_train_config_v12_0.png)
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+Watch this video for a demonstration on [how parallel execution
+of Merge Trains can prevent commits from breaking the default
+branch](https://www.youtube.com/watch?v=D4qCqXgZkHQ).
## How to add a merge request to a merge train
To add a merge request to a merge train:
-1. Click "Start/Add merge train" button in a merge request
+1. Visit a merge request.
+1. Click the **Start/Add to merge train** button.
![Start merge train](img/merge_train_start_v12_0.png)
## How to remove a merge request from a merge train
-1. Click "Remove from merge train" button in the merge request widget.
+1. Visit a merge request.
+1. Click the **Remove from merge train** button.
![Cancel merge train](img/merge_train_cancel_v12_0.png)
+## How to view a merge request's current position on the merge train
+
+After a merge request has been added to the merge train, the merge request's
+current position will be displayed under the pipeline widget:
+
+![Merge train position indicator](img/merge_train_position_v12_0.png)
+
## Start/Add to merge train when pipeline succeeds
You can add a merge request to a merge train only when the latest pipeline in the
@@ -70,7 +74,8 @@ the merge request to a train because the current change of the merge request may
be broken thus it could affect the following merge requests.
In this case, you can schedule to add the merge request to a merge train **when the latest
-pipeline succeeds**. You can see the following button instead of the regular "Start/Add merge train"
+pipeline succeeds** (This pipeline is [Pipelines for merged results](../index.md), not Pipelines for merge train).
+You can see the following button instead of the regular **Start/Add to merge train**
button while the latest pipeline is running.
![Add to merge train when pipeline succeeds](img/merge_train_start_when_pipeline_succeeds_v12_0.png)
diff --git a/doc/development/README.md b/doc/development/README.md
index a74770ae383..ea5d9e10e2c 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -17,6 +17,7 @@ description: 'Learn how to contribute to GitLab.'
- [GitLab core team & GitLab Inc. contribution process](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/PROCESS.md)
- [Generate a changelog entry with `bin/changelog`](changelog.md)
- [Code review guidelines](code_review.md) for reviewing code and having code reviewed
+- [Database review guidelines](database_review.md) for reviewing database-related changes and complex SQL queries
- [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)
@@ -63,6 +64,7 @@ description: 'Learn how to contribute to GitLab.'
- [Routing](routing.md)
- [Repository mirroring](repository_mirroring.md)
- [Git LFS](lfs.md)
+- [Developing against interacting components or features](interacting_components.md)
## Performance guides
diff --git a/doc/development/chaos_endpoints.md b/doc/development/chaos_endpoints.md
index b3406275937..eb6dde2d24e 100644
--- a/doc/development/chaos_endpoints.md
+++ b/doc/development/chaos_endpoints.md
@@ -36,6 +36,10 @@ Replace `secret` with your own secret token.
Once you have enabled the chaos endpoints and restarted the application, you can start testing using the endpoints.
+By default, when invoking a chaos endpoint, the web worker process which receives the request will handle it. This means, for example, that if the Kill
+operation is invoked, the Puma or Unicorn worker process handling the request will be killed. To test these operations in Sidekiq, the `async` parameter on
+each endpoint can be set to `true`. This will run the chaos process in a Sidekiq worker.
+
## Memory leaks
To simulate a memory leak in your application, use the `/-/chaos/leakmem` endpoint.
@@ -47,12 +51,14 @@ The memory is not retained after the request finishes. Once the request has comp
GET /-/chaos/leakmem
GET /-/chaos/leakmem?memory_mb=1024
GET /-/chaos/leakmem?memory_mb=1024&duration_s=50
+GET /-/chaos/leakmem?memory_mb=1024&duration_s=50&async=true
```
-| Attribute | Type | Required | Description |
-| ------------ | ------- | -------- | ---------------------------------------------------------------------------------- |
-| `memory_mb` | integer | no | How much memory, in MB, should be leaked. Defaults to 100MB. |
+| Attribute | Type | Required | Description |
+| ------------ | ------- | -------- | ------------------------------------------------------------------------------------ |
+| `memory_mb` | integer | no | How much memory, in MB, should be leaked. Defaults to 100MB. |
| `duration_s` | integer | no | Minimum duration_s, in seconds, that the memory should be retained. Defaults to 30s. |
+| `async` | boolean | no | Set to true to leak memory in a Sidekiq background worker process |
```bash
curl http://localhost:3000/-/chaos/leakmem?memory_mb=1024&duration_s=10 --header 'X-Chaos-Secret: secret'
@@ -69,11 +75,13 @@ If you're using Unicorn, this is done by killing the worker process.
```
GET /-/chaos/cpu_spin
GET /-/chaos/cpu_spin?duration_s=50
+GET /-/chaos/cpu_spin?duration_s=50&async=true
```
| Attribute | Type | Required | Description |
| ------------ | ------- | -------- | --------------------------------------------------------------------- |
| `duration_s` | integer | no | Duration, in seconds, that the core will be utilised. Defaults to 30s |
+| `async` | boolean | no | Set to true to consume CPU in a Sidekiq background worker process |
```bash
curl http://localhost:3000/-/chaos/cpu_spin?duration_s=60 --header 'X-Chaos-Secret: secret'
@@ -91,12 +99,14 @@ If you're using Unicorn, this is done by killing the worker process.
```
GET /-/chaos/db_spin
GET /-/chaos/db_spin?duration_s=50
+GET /-/chaos/db_spin?duration_s=50&async=true
```
-| Attribute | Type | Required | Description |
-| ------------ | ------- | -------- | --------------------------------------------------------------------- |
-| `interval_s` | float | no | Interval, in seconds, for every DB request. Defaults to 1s |
-| `duration_s` | integer | no | Duration, in seconds, that the core will be utilised. Defaults to 30s |
+| Attribute | Type | Required | Description |
+| ------------ | ------- | -------- | --------------------------------------------------------------------------- |
+| `interval_s` | float | no | Interval, in seconds, for every DB request. Defaults to 1s |
+| `duration_s` | integer | no | Duration, in seconds, that the core will be utilised. Defaults to 30s |
+| `async` | boolean | no | Set to true to perform the operation in a Sidekiq background worker process |
```bash
curl http://localhost:3000/-/chaos/db_spin?interval_s=1&duration_s=60 --header 'X-Chaos-Secret: secret'
@@ -112,11 +122,13 @@ As with the CPU Spin endpoint, this may lead to your request timing out if durat
```
GET /-/chaos/sleep
GET /-/chaos/sleep?duration_s=50
+GET /-/chaos/sleep?duration_s=50&async=true
```
| Attribute | Type | Required | Description |
| ------------ | ------- | -------- | ---------------------------------------------------------------------- |
| `duration_s` | integer | no | Duration, in seconds, that the request will sleep for. Defaults to 30s |
+| `async` | boolean | no | Set to true to sleep in a Sidekiq background worker process |
```bash
curl http://localhost:3000/-/chaos/sleep?duration_s=60 --header 'X-Chaos-Secret: secret'
@@ -132,8 +144,13 @@ Since this endpoint uses the `KILL` signal, the worker is not given a chance to
```
GET /-/chaos/kill
+GET /-/chaos/kill?async=true
```
+| Attribute | Type | Required | Description |
+| ------------ | ------- | -------- | ---------------------------------------------------------------------- |
+| `async` | boolean | no | Set to true to kill a Sidekiq background worker process |
+
```bash
curl http://localhost:3000/-/chaos/kill --header 'X-Chaos-Secret: secret'
curl http://localhost:3000/-/chaos/kill?token=secret
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index 35ce6b8cbe4..709cd0c806b 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -45,9 +45,9 @@ page, with these behaviours:
1. It will not pick people whose [GitLab status](../user/profile/index.md#current-status)
contains the string 'OOO'.
-2. [Trainee maintainers](https://about.gitlab.com/handbook/engineering/workflow/code-review/#trainee-maintainer)
+1. [Trainee maintainers](https://about.gitlab.com/handbook/engineering/workflow/code-review/#trainee-maintainer)
are three times as likely to be picked as other reviewers.
-3. It always picks the same reviewers and maintainers for the same
+1. It always picks the same reviewers and maintainers for the same
branch name (unless their OOO status changes, as in point 1). It
removes leading `ce-` and `ee-`, and trailing `-ce` and `-ee`, so
that it can be stable for backport branches.
@@ -58,20 +58,21 @@ As described in the section on the responsibility of the maintainer below, you
are recommended to get your merge request approved and merged by maintainer(s)
from teams other than your own.
- 1. If your merge request includes backend changes [^1], it must be
- **approved by a [backend maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab-ce_maintainers_backend)**.
- 1. If your merge request includes database migrations or changes to expensive queries [^2], it must be
- **approved by a [database maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab-ce_maintainers_database)**.
- 1. If your merge request includes frontend changes [^1], it must be
- **approved by a [frontend maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab-ce_maintainers_frontend)**.
- 1. If your merge request includes UX changes [^1], it must be
- **approved by a [UX team member][team]**.
- 1. If your merge request includes adding a new JavaScript library [^1], it must be
- **approved by a [frontend lead][team]**.
- 1. If your merge request includes adding a new UI/UX paradigm [^1], it must be
- **approved by a [UX lead][team]**.
- 1. If your merge request includes a new dependency or a filesystem change, it must be
- **approved by a [Distribution team member][team]**. See how to work with the [Distribution team](https://about.gitlab.com/handbook/engineering/dev-backend/distribution/) for more details.
+1. If your merge request includes backend changes [^1], it must be
+ **approved by a [backend maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab-ce_maintainers_backend)**.
+1. If your merge request includes database migrations or changes to expensive queries [^2], it must be
+ **approved by a [database maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab-ce_maintainers_database)**.
+ Read the [database review guidelines](database_review.md) for more details.
+1. If your merge request includes frontend changes [^1], it must be
+ **approved by a [frontend maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab-ce_maintainers_frontend)**.
+1. If your merge request includes UX changes [^1], it must be
+ **approved by a [UX team member][team]**.
+1. If your merge request includes adding a new JavaScript library [^1], it must be
+ **approved by a [frontend lead][team]**.
+1. If your merge request includes adding a new UI/UX paradigm [^1], it must be
+ **approved by a [UX lead][team]**.
+1. If your merge request includes a new dependency or a filesystem change, it must be
+ **approved by a [Distribution team member][team]**. See how to work with the [Distribution team](https://about.gitlab.com/handbook/engineering/dev-backend/distribution/) for more details.
#### Security requirements
@@ -341,8 +342,7 @@ Enterprise Edition instance. This has some implications:
- [Background migrations](background_migrations.md) run in Sidekiq, and
should only be done for migrations that would take an extreme amount of
time at GitLab.com scale.
-1. **Sidekiq workers**
- [cannot change in a backwards-incompatible way](sidekiq_style_guide.md#removing-or-renaming-queues):
+1. **Sidekiq workers** [cannot change in a backwards-incompatible way](sidekiq_style_guide.md#removing-or-renaming-queues):
1. Sidekiq queues are not drained before a deploy happens, so there will be
workers in the queue from the previous version of GitLab.
1. If you need to change a method signature, try to do so across two releases,
diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md
index abe11b8d1a8..79c701d7abf 100644
--- a/doc/development/contributing/issue_workflow.md
+++ b/doc/development/contributing/issue_workflow.md
@@ -102,20 +102,7 @@ any issue.
Stage labels specify which [DevOps stage][devops-stages] the issue belongs to.
-The current stage labels are:
-
-- ~"devops::manage"
-- ~"devops::plan"
-- ~"devops::create"
-- ~"devops::verify"
-- ~"devops::package"
-- ~"devops::release"
-- ~"devops::configure"
-- ~"devops::monitor"
-- ~"devops::secure"
-- ~"devops::defend"
-- ~"devops::growth"
-- ~"devops::enablement"
+The current stage labels can be found by [searching the labels list for `devops::`](https://gitlab.com/groups/gitlab-org/-/labels?search=devops%3A%3A).
These labels are [scoped labels](../../user/project/labels.md#scoped-labels-premium)
and thus are mutually exclusive.
@@ -138,44 +125,8 @@ The Stage labels are used to generate the [direction pages][direction-pages] aut
Group labels specify which [groups][structure-groups] the issue belongs to.
-The current group labels are:
-
-- ~"group::access"
-- ~"group::measure"
-- ~"group::source code"
-- ~"group::knowledge"
-- ~"group::editor"
-- ~"group::gitaly"
-- ~"group::gitter"
-- ~"group::team planning"
-- ~"group::enterprise planning"
-- ~"group::certify"
-- ~"group::ci and runner"
-- ~"group::testing"
-- ~"group::package"
-- ~"group::progressive delivery"
-- ~"group::release management"
-- ~"group::autodevops and kubernetes"
-- ~"group::serverless and paas"
-- ~"group::apm"
-- ~"group::health"
-- ~"group::static analysis"
-- ~"group::dynamic analysis"
-- ~"group::software composition analysis"
-- ~"group::runtime application security"
-- ~"group::threat management"
-- ~"group::application infrastructure security"
-- ~"group::activation"
-- ~"group::adoption"
-- ~"group::upsell"
-- ~"group::retention"
-- ~"group::fulfillment"
-- ~"group::telemetry"
-- ~"group::distribution"
-- ~"group::geo"
-- ~"group::memory"
-- ~"group::ecosystem"
-
+The current group labels can be found by [searching the labels list for `group::`](https://gitlab.com/groups/gitlab-org/-/labels?search=group%3A%3A).
+
These labels are [scoped labels](../../user/project/labels.md#scoped-labels-premium)
and thus are mutually exclusive.
@@ -248,7 +199,7 @@ If a bug seems to fall between two severity labels, assign it to the higher-seve
- Example(s) of ~S1
- Data corruption/loss.
- Security breach.
- - Unable to create an issue or merge request.
+ - Unable to create an issue or merge request.
- Unable to add a comment or thread to the issue or merge request.
- Example(s) of ~S2
- Cannot submit changes through the web IDE but the commandline works.
@@ -259,7 +210,7 @@ If a bug seems to fall between two severity labels, assign it to the higher-seve
- Example(s) of ~S4
- Label colors are incorrect.
- UI elements are not fully aligned.
-
+
## Label for community contributors
Issues that are beneficial to our users, 'nice to haves', that we currently do
diff --git a/doc/development/contributing/merge_request_workflow.md b/doc/development/contributing/merge_request_workflow.md
index 3325d3e074e..17328762c5b 100644
--- a/doc/development/contributing/merge_request_workflow.md
+++ b/doc/development/contributing/merge_request_workflow.md
@@ -103,7 +103,8 @@ If you would like quick feedback on your merge request feel free to mention some
from the [core team](https://about.gitlab.com/community/core-team/) or one of the
[merge request coaches](https://about.gitlab.com/team/). When having your code reviewed
and when reviewing merge requests, please keep the [code review guidelines](../code_review.md)
-in mind.
+in mind. And if your code also makes changes to the database, or does expensive queries,
+check the [database review guidelines](../database_review.md).
### Keep it simple
diff --git a/doc/development/database_debugging.md b/doc/development/database_debugging.md
index 0311eda1ff1..eb3b227473b 100644
--- a/doc/development/database_debugging.md
+++ b/doc/development/database_debugging.md
@@ -9,31 +9,31 @@ An easy first step is to search for your error in Slack or google "GitLab (my er
Available `RAILS_ENV`
- - `production` (generally not for your main GDK db, but you may need this for e.g. omnibus)
- - `development` (this is your main GDK db)
- - `test` (used for tests like rspec)
+- `production` (generally not for your main GDK db, but you may need this for e.g. omnibus)
+- `development` (this is your main GDK db)
+- `test` (used for tests like rspec)
## Nuke everything and start over
If you just want to delete everything and start over with an empty DB (~1 minute):
- - `bundle exec rake db:reset RAILS_ENV=development`
+- `bundle exec rake db:reset RAILS_ENV=development`
If you just want to delete everything and start over with dummy data (~40 minutes). This also does `db:reset` and runs DB-specific migrations:
- - `bundle exec rake dev:setup RAILS_ENV=development`
+- `bundle exec rake dev:setup RAILS_ENV=development`
If your test DB is giving you problems, it is safe to nuke it because it doesn't contain important data:
- - `bundle exec rake db:reset RAILS_ENV=test`
+- `bundle exec rake db:reset RAILS_ENV=test`
## Migration wrangling
- - `bundle exec rake db:migrate RAILS_ENV=development`: Execute any pending migrations that you may have picked up from a MR
- - `bundle exec rake db:migrate:status RAILS_ENV=development`: Check if all migrations are `up` or `down`
- - `bundle exec rake db:migrate:down VERSION=20170926203418 RAILS_ENV=development`: Tear down a migration
- - `bundle exec rake db:migrate:up VERSION=20170926203418 RAILS_ENV=development`: Set up a migration
- - `bundle exec rake db:migrate:redo VERSION=20170926203418 RAILS_ENV=development`: Re-run a specific migration
+- `bundle exec rake db:migrate RAILS_ENV=development`: Execute any pending migrations that you may have picked up from a MR
+- `bundle exec rake db:migrate:status RAILS_ENV=development`: Check if all migrations are `up` or `down`
+- `bundle exec rake db:migrate:down VERSION=20170926203418 RAILS_ENV=development`: Tear down a migration
+- `bundle exec rake db:migrate:up VERSION=20170926203418 RAILS_ENV=development`: Set up a migration
+- `bundle exec rake db:migrate:redo VERSION=20170926203418 RAILS_ENV=development`: Re-run a specific migration
## Manually access the database
@@ -45,12 +45,12 @@ bundle exec rails dbconsole RAILS_ENV=development
bundle exec rails db RAILS_ENV=development
```
- - `\q`: Quit/exit
- - `\dt`: List all tables
- - `\d+ issues`: List columns for `issues` table
- - `CREATE TABLE board_labels();`: Create a table called `board_labels`
- - `SELECT * FROM schema_migrations WHERE version = '20170926203418';`: Check if a migration was run
- - `DELETE FROM schema_migrations WHERE version = '20170926203418';`: Manually remove a migration
+- `\q`: Quit/exit
+- `\dt`: List all tables
+- `\d+ issues`: List columns for `issues` table
+- `CREATE TABLE board_labels();`: Create a table called `board_labels`
+- `SELECT * FROM schema_migrations WHERE version = '20170926203418';`: Check if a migration was run
+- `DELETE FROM schema_migrations WHERE version = '20170926203418';`: Manually remove a migration
## FAQ
diff --git a/doc/development/database_review.md b/doc/development/database_review.md
new file mode 100644
index 00000000000..1413c2f69fb
--- /dev/null
+++ b/doc/development/database_review.md
@@ -0,0 +1,101 @@
+# Database Review Guidelines
+
+This page is specific to database reviews. Please refer to our
+[code review guide](code_review.md) for broader advice and best
+practices for code review in general.
+
+## General process
+
+A database review is required for:
+
+- Changes that touch the database schema or perform data migrations,
+ including files in:
+ - `db/`
+ - `lib/gitlab/background_migration/`
+- Changes to the database tooling, e.g.:
+ - migration or ActiveRecord helpers in `lib/gitlab/database/`
+ - load balancing
+- Changes that produce SQL queries that are beyond the obvious. It is
+ generally up to the author of a merge request to decide whether or
+ not complex queries are being introduced and if they require a
+ database review.
+
+A database reviewer is expected to look out for obviously complex
+queries in the change and review those closer. If the author does not
+point out specific queries for review and there are no obviously
+complex queries, it is enough to concentrate on reviewing the
+migration only.
+
+It is preferable to review queries in SQL form and generally accepted
+to ask the author to translate any ActiveRecord queries in SQL form
+for review.
+
+### Roles and process
+
+A Merge Request author's role is to:
+
+- Decide whether a database review is needed.
+- If database review is needed, add the ~database label.
+- Use the [database changes](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab/merge_request_templates/Database%20changes.md)
+ merge request template, or include the appropriate items in the MR description.
+
+A database **reviewer**'s role is to:
+
+- Perform a first-pass review on the MR and suggest improvements to the author.
+- Once satisfied, relabel the MR with ~"database::reviewed", approve it, and
+ reassign MR to the database **maintainer** suggested by Reviewer
+ Roulette.
+
+A database **maintainer**'s role is to:
+
+- Perform the final database review on the MR.
+- Discuss further improvements or other relevant changes with the
+ database reviewer and the MR author.
+- Finally approve the MR and relabel the MR with ~"database::approved"
+- Merge the MR if no other approvals are pending or pass it on to
+ other maintainers as required (frontend, backend, docs).
+
+### Distributing review workload
+
+Review workload is distributed using [reviewer roulette](code_review.md#reviewer-roulette)
+([example](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/25181#note_147551725)).
+The MR author should then co-assign the suggested database
+**reviewer**. When they give their sign-off, they will hand over to
+the suggested database **maintainer**.
+
+If reviewer roulette didn't suggest a database reviewer & maintainer,
+make sure you have applied the ~database label and rerun the
+`danger-review` CI job, or pick someone from the
+[`@gl-database` team](https://gitlab.com/groups/gl-database/-/group_members).
+
+### How to review for database
+
+- Check migrations
+ - Review relational modeling and design choices
+ - Review migrations follow [database migration style guide](migration_style_guide.md),
+ for example
+ - [Check ordering of columns](ordering_table_columns.md)
+ - [Check indexes are present for foreign keys](migration_style_guide.md#adding-foreign-key-constraints)
+ - Ensure that migrations execute in a transaction or only contain
+ concurrent index/foreign key helpers (with transactions disabled)
+ - Check consistency with `db/schema.rb` and that migrations are [reversible](migration_style_guide.md#reversibility)
+ - For data migrations, establish a time estimate for execution
+- Check post deploy migration
+ - Make sure we can expect post deploy migrations to finish within 1 hour max.
+- Check background migrations
+ - Review queries (for example, make sure batch sizes are fine)
+ - Establish a time estimate for execution
+- Query performance
+ - Check for any obviously complex queries and queries the author specifically
+ points out for review (if any)
+ - If not present yet, ask the author to provide SQL queries and query plans
+ (e.g. by using [chatops](understanding_explain_plans.md#chatops) or direct
+ database access)
+ - For given queries, review parameters regarding data distribution
+ - [Check query plans](understanding_explain_plans.md) and suggest improvements
+ to queries (changing the query, schema or adding indexes and similar)
+ - General guideline is for queries to come in below 100ms execution time
+ - If queries rely on prior migrations that are not present yet on production
+ (eg indexes, columns), you can use a [one-off instance from the restore
+ pipeline](https://ops.gitlab.net/gitlab-com/gl-infra/gitlab-restore/postgres-gprd)
+ in order to establish a proper testing environment.
diff --git a/doc/development/diffs.md b/doc/development/diffs.md
index 5655398c886..ac0b8555360 100644
--- a/doc/development/diffs.md
+++ b/doc/development/diffs.md
@@ -133,4 +133,4 @@ File diff will be suppressed (technically different from collapsed, but behaves
Diff Viewers, which can be found on `models/diff_viewer/*` are classes used to map metadata about each type of Diff File. It has information
whether it's a binary, which partial should be used to render it or which File extensions this class accounts for.
-`DiffViewer::Base` validates _blobs_ (old and new versions) content, extension and file type in order to check if it can be rendered. \ No newline at end of file
+`DiffViewer::Base` validates _blobs_ (old and new versions) content, extension and file type in order to check if it can be rendered.
diff --git a/doc/development/documentation/feature-change-workflow.md b/doc/development/documentation/feature-change-workflow.md
index ca29353ecbe..ac93ada5a4b 100644
--- a/doc/development/documentation/feature-change-workflow.md
+++ b/doc/development/documentation/feature-change-workflow.md
@@ -121,27 +121,27 @@ All reviewers can help ensure accuracy, clarity, completeness, and adherence to
- **Prior to merging**, documentation changes committed by the developer must be reviewed by:
- 1. **The code reviewer** for the MR, to confirm accuracy, clarity, and completeness.
- 1. Optionally: Others involved in the work, such as other devs or the PM.
- 1. Optionally: The technical writer for the DevOps stage. If not prior to merging, the technical writer will review after the merge.
- This helps us ensure that the developer has time to merge good content by the freeze, and that it can be further refined by the release, if needed.
- - To decide whether to request this review before the merge, consider the amount of time left before the code freeze, the size of the change,
- and your degree of confidence in having users of an RC use your docs as written.
- - Pre-merge tech writer reviews should be most common when the code is complete well in advance of the freeze and/or for larger documentation changes.
- - You can request a review and if there is not sufficient time to complete it prior to the freeze,
- the maintainer can merge the current doc changes (if complete) and create a follow-up doc review issue.
- - The technical writer can also help decide what docs to merge before the freeze and whether to work on further changes in a follow up MR.
- - **To request a pre-merge technical writer review**, assign the writer listed for the applicable [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages).
- - **To request a post-merge technical writer review**, [create an issue for one using the Doc Review template](https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issuable_template=Doc%20Review) and link it from the MR that makes the doc change.
- 1. **The maintainer** who is assigned to merge the MR, to verify clarity, completeness, and quality, to the best of their ability.
+ 1. **The code reviewer** for the MR, to confirm accuracy, clarity, and completeness.
+ 1. Optionally: Others involved in the work, such as other devs or the PM.
+ 1. Optionally: The technical writer for the DevOps stage. If not prior to merging, the technical writer will review after the merge.
+ This helps us ensure that the developer has time to merge good content by the freeze, and that it can be further refined by the release, if needed.
+ - To decide whether to request this review before the merge, consider the amount of time left before the code freeze, the size of the change,
+ and your degree of confidence in having users of an RC use your docs as written.
+ - Pre-merge tech writer reviews should be most common when the code is complete well in advance of the freeze and/or for larger documentation changes.
+ - You can request a review and if there is not sufficient time to complete it prior to the freeze,
+ the maintainer can merge the current doc changes (if complete) and create a follow-up doc review issue.
+ - The technical writer can also help decide what docs to merge before the freeze and whether to work on further changes in a follow up MR.
+ - **To request a pre-merge technical writer review**, assign the writer listed for the applicable [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages).
+ - **To request a post-merge technical writer review**, [create an issue for one using the Doc Review template](https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issuable_template=Doc%20Review) and link it from the MR that makes the doc change.
+ 1. **The maintainer** who is assigned to merge the MR, to verify clarity, completeness, and quality, to the best of their ability.
- Upon merging, if a technical writer review has not been performed and there is not yet a linked issue for a follow-up review, the maintainer should [create an issue using the Doc Review template](https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issuable_template=Doc%20Review), link it from the MR, and
mention the original MR author in the new issue. Alternatively, the maintainer can ask the MR author to create and link this issue before the MR is merged.
- After merging, documentation changes are reviewed by:
- 1. The technical writer--**if** their review was not performed prior to the merge.
- 2. Optionally: by the PM (for accuracy and to ensure it's consistent with the vision for how the product will be used).
+ 1. The technical writer -- **if** their review was not performed prior to the merge.
+ 1. Optionally: by the PM (for accuracy and to ensure it's consistent with the vision for how the product will be used).
Any party can raise the item to the PM for review at any point: the dev, the technical writer, or the PM, who can request/plan a review at the outset.
### Technical Writer role
diff --git a/doc/development/documentation/improvement-workflow.md b/doc/development/documentation/improvement-workflow.md
index a12c3d5ea7b..80fbd4b6427 100644
--- a/doc/development/documentation/improvement-workflow.md
+++ b/doc/development/documentation/improvement-workflow.md
@@ -52,9 +52,9 @@ To request a post-merge review, [create an issue for one using the Doc Review te
**3. Maintainer**
1. Review by assigned maintainer, who can always request/require the above reviews. Maintainer review can occur before or after a technical writer review.
-2. Ensure a release milestone of the format XX.Y is set. If the freeze for that release has begun, add the label `pick into <XX.Y>` unless this change is not required for the release. In that case, simply change the milestone.
-3. If EE and CE MRs exist, merge the EE MR first, then the CE MR.
-4. After merging, if there has not been a technical writer review and an issue for a follow-up review was not already created and linked from the MR, [create the issue using the Doc Review template](https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issuable_template=Doc%20Review) and link it from the MR.
+1. Ensure a release milestone of the format XX.Y is set. If the freeze for that release has begun, add the label `pick into <XX.Y>` unless this change is not required for the release. In that case, simply change the milestone.
+1. If EE and CE MRs exist, merge the EE MR first, then the CE MR.
+1. After merging, if there has not been a technical writer review and an issue for a follow-up review was not already created and linked from the MR, [create the issue using the Doc Review template](https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issuable_template=Doc%20Review) and link it from the MR.
## Other ways to help
diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md
index 36a2f47a55b..8ce855594df 100644
--- a/doc/development/documentation/index.md
+++ b/doc/development/documentation/index.md
@@ -488,15 +488,17 @@ Currently, the following tests are in place:
that all cURL examples in API docs use the full switches. It's recommended
to [check locally](#previewing-the-changes-live) before pushing to GitLab by executing the command
`bundle exec nanoc check internal_links` on your local
- [`gitlab-docs`](https://gitlab.com/gitlab-org/gitlab-docs) directory.
+ [`gitlab-docs`](https://gitlab.com/gitlab-org/gitlab-docs) directory. In addition,
+ `docs-lint` also runs [markdownlint](styleguide.md#markdown-rules) to ensure the
+ markdown is consistent across all documentation.
1. [`ee_compat_check`](../automatic_ce_ee_merge.md#avoiding-ce-ee-merge-conflicts-beforehand) (runs on CE only):
- When you submit a merge request to GitLab Community Edition (CE),
- there is this additional job that runs against Enterprise Edition (EE)
- and checks if your changes can apply cleanly to the EE codebase.
- If that job fails, read the instructions in the job log for what to do next.
- As CE is merged into EE once a day, it's important to avoid merge conflicts.
- Submitting an EE-equivalent merge request cherry-picking all commits from CE to EE is
- essential to avoid them.
+ When you submit a merge request to GitLab Community Edition (CE),
+ there is this additional job that runs against Enterprise Edition (EE)
+ and checks if your changes can apply cleanly to the EE codebase.
+ If that job fails, read the instructions in the job log for what to do next.
+ As CE is merged into EE once a day, it's important to avoid merge conflicts.
+ Submitting an EE-equivalent merge request cherry-picking all commits from CE to EE is
+ essential to avoid them.
1. [`ee-files-location-check`/`ee-specific-lines-check`](#ee-specific-lines-check) (runs on EE only):
This test ensures that no new files/directories are created/changed in EE.
All docs should be submitted in CE instead, regardless the tier they are on.
@@ -559,15 +561,16 @@ A file with `proselint` configuration must be placed in a
#### `markdownlint`
`markdownlint` checks that certain rules ([example](https://github.com/DavidAnson/markdownlint/blob/master/README.md#rules--aliases))
- are followed for Markdown syntax.
- Our [Documentation Style Guide](styleguide.md) and [Markdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/)
- elaborate on which choices must be made when selecting Markdown syntax for
- GitLab documentation. This tool helps catch deviations from those guidelines.
+are followed for Markdown syntax. Our [Documentation Style Guide](styleguide.md) and
+[Markdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/)
+elaborate on which choices must be made when selecting Markdown syntax for GitLab
+documentation. This tool helps catch deviations from those guidelines, and matches the
+tests run on the documentation by [`docs-lint`](#testing).
`markdownlint` can be used [on the command line](https://github.com/igorshubovych/markdownlint-cli#markdownlint-cli--),
- either on a single Markdown file or on all Markdown files in a project. For example, to run
- `markdownlint` on all documentation in the [`gitlab-ce` project](https://gitlab.com/gitlab-org/gitlab-ce),
- run the following commands from within the `gitlab-ce` project:
+either on a single Markdown file or on all Markdown files in a project. For example, to run
+`markdownlint` on all documentation in the [`gitlab-ce` project](https://gitlab.com/gitlab-org/gitlab-ce),
+run the following commands from within the `gitlab-ce` project:
```sh
cd doc
@@ -597,7 +600,7 @@ The following sample `markdownlint` configuration modifies the available default
"line-length": false,
"no-trailing-punctuation": false,
"ol-prefix": { "style": "one" },
- "blanks-around-fences": false,
+ "blanks-around-fences": true,
"no-inline-html": {
"allowed_elements": [
"table",
@@ -612,11 +615,15 @@ The following sample `markdownlint` configuration modifies the available default
"a",
"strong",
"i",
- "div"
+ "div",
+ "b"
]
},
"hr-style": { "style": "---" },
- "fenced-code-language": false
+ "code-block-style": { "style": "fenced" },
+ "fenced-code-language": false,
+ "no-duplicate-header": { "allow_different_nesting": true },
+ "commands-show-output": false
}
```
diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md
index 1ca965f8ae9..36ffc02644e 100644
--- a/doc/development/documentation/styleguide.md
+++ b/doc/development/documentation/styleguide.md
@@ -44,9 +44,9 @@ Include any media types/sources if the content is relevant to readers. You can f
In the software industry, it is a best practice to organize documentatioin in different types. For example, [Divio recommends](https://www.divio.com/blog/documentation/):
1. Tutorials
-2. How-to guides
-3. Explanation
-4. Reference (for example, a glossary)
+1. How-to guides
+1. Explanation
+1. Reference (for example, a glossary)
At GitLab, we have so many product changes in our monthly releases that we can't afford to continually update multiple types of information.
If we have multiple types, the information will become outdated. Therefore, we have a [single template](structure.md) for documentation.
@@ -107,6 +107,22 @@ Hard-coded HTML is valid, although it's discouraged to be used while we have `/h
- Special styling is required.
- Reviewed and approved by a technical writer.
+### Markdown Rules
+
+GitLab ensures that the Markdown used across all documentation is consistent, as
+well as easy to review and maintain, by testing documentation changes with
+[Markdownlint (mdl)](https://github.com/markdownlint/markdownlint). This lint test
+checks many common problems with Markdown, and fails when any document has an issue
+with Markdown formatting that may cause the page to render incorrectly within GitLab.
+It will also fail when a document is using non-standard Markdown (which may render
+correctly, but is not the current standard in GitLab documentation).
+
+Each formatting issue that mdl checks has an associated [rule](https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md),
+and the rules that are currently enabled for GitLab documentation are listed in the
+[`.mdlrc.style`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.mdlrc.style) file.
+Configuration options are set in the [`.mdlrc`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.mdlrc.style)
+file.
+
## Structure
### Organize by topic, not by type
@@ -142,9 +158,9 @@ The table below shows what kind of documentation goes where.
### Working with directories and files
1. When you create a new directory, always start with an `index.md` file.
- Do not use another file name and **do not** create `README.md` files.
+ Do not use another file name and **do not** create `README.md` files.
1. **Do not** use special characters and spaces, or capital letters in file names,
- directory names, branch names, and anything that generates a path.
+ directory names, branch names, and anything that generates a path.
1. When creating a new document and it has more than one word in its name,
make sure to use underscores instead of spaces or dashes (`-`). For example,
a proper naming would be `import_projects_from_github.md`. The same rule
diff --git a/doc/development/fe_guide/frontend_faq.md b/doc/development/fe_guide/frontend_faq.md
index e4225f2bc39..0d2aeffeac0 100644
--- a/doc/development/fe_guide/frontend_faq.md
+++ b/doc/development/fe_guide/frontend_faq.md
@@ -5,12 +5,12 @@
1. **You talk about Frontend FAQ.**
Please share links to it whenever applicable, so more eyes catch when content
gets outdated.
-2. **Keep it short and simple.**
+1. **Keep it short and simple.**
Whenever an answer needs more than two sentences it does not belong here.
-3. **Provide background when possible.**
+1. **Provide background when possible.**
Linking to relevant source code, issue / epic, or other documentation helps
to understand the answer.
-4. **If you see something, do something.**
+1. **If you see something, do something.**
Please remove or update any content that is outdated as soon as you see it.
## FAQ
diff --git a/doc/development/fe_guide/icons.md b/doc/development/fe_guide/icons.md
index 533e2001300..4f687d8642e 100644
--- a/doc/development/fe_guide/icons.md
+++ b/doc/development/fe_guide/icons.md
@@ -21,10 +21,10 @@ To use a sprite Icon in HAML or Rails we use a specific helper function :
sprite_icon(icon_name, size: nil, css_class: '')
```
-- **icon_name** Use the icon_name that you can find in the SVG Sprite
- ([Overview is available here][svg-preview]).
-- **size (optional)** Use one of the following sizes : 16, 24, 32, 48, 72 (this will be translated into a `s16` class)
-- **css_class (optional)** If you want to add additional css classes
+- **icon_name** Use the icon_name that you can find in the SVG Sprite
+ ([Overview is available here][svg-preview]).
+- **size (optional)** Use one of the following sizes : 16, 24, 32, 48, 72 (this will be translated into a `s16` class)
+- **css_class (optional)** If you want to add additional css classes
**Example**
@@ -65,10 +65,10 @@ export default {
</template>
```
-- **name** Name of the Icon in the SVG Sprite ([Overview is available here][svg-preview]).
-- **size (optional)** Number value for the size which is then mapped to a specific CSS class
- (Available Sizes: 8, 12, 16, 18, 24, 32, 48, 72 are mapped to `sXX` css classes)
-- **css-classes (optional)** Additional CSS Classes to add to the svg tag.
+- **name** Name of the Icon in the SVG Sprite ([Overview is available here][svg-preview]).
+- **size (optional)** Number value for the size which is then mapped to a specific CSS class
+ (Available Sizes: 8, 12, 16, 18, 24, 32, 48, 72 are mapped to `sXX` css classes)
+- **css-classes (optional)** Additional CSS Classes to add to the svg tag.
### Usage in HTML/JS
diff --git a/doc/development/fe_guide/style_guide_js.md b/doc/development/fe_guide/style_guide_js.md
index b50159c2b75..18ef754642d 100644
--- a/doc/development/fe_guide/style_guide_js.md
+++ b/doc/development/fe_guide/style_guide_js.md
@@ -548,7 +548,7 @@ Please check this [rules][eslint-plugin-vue-rules] for more documentation.
<component @click="eventHandler"/>
```
-2. Shorthand `:` is preferable over `v-bind`
+1. Shorthand `:` is preferable over `v-bind`
```javascript
// bad
@@ -558,7 +558,7 @@ Please check this [rules][eslint-plugin-vue-rules] for more documentation.
<component :class="btn"/>
```
-3. Shorthand `#` is preferable over `v-slot`
+1. Shorthand `#` is preferable over `v-slot`
```javascript
// bad
diff --git a/doc/development/fe_guide/style_guide_scss.md b/doc/development/fe_guide/style_guide_scss.md
index 5220c9eeea3..f895cfd36ac 100644
--- a/doc/development/fe_guide/style_guide_scss.md
+++ b/doc/development/fe_guide/style_guide_scss.md
@@ -35,7 +35,7 @@ New utility classes should be added to [`utilities.scss`](https://gitlab.com/git
We recommend a "utility-first" approach.
1. Start with utility classes.
-2. If composing utility classes into a component class removes code duplication and encapsulates a clear responsibility, do it.
+1. If composing utility classes into a component class removes code duplication and encapsulates a clear responsibility, do it.
This encourages an organic growth of component classes and prevents the creation of one-off unreusable classes. Also, the kind of classes that emerge from "utility-first" tend to be design-centered (e.g. `.button`, `.alert`, `.card`) rather than domain-centered (e.g. `.security-report-widget`, `.commit-header-icon`).
diff --git a/doc/development/file_storage.md b/doc/development/file_storage.md
index 02874d18a30..475d1c1611e 100644
--- a/doc/development/file_storage.md
+++ b/doc/development/file_storage.md
@@ -92,8 +92,8 @@ in your uploader, you need to either 1) include `RecordsUpload::Concern` and pre
The `CarrierWave::Uploader#store_dir` is overridden to
- - `GitlabUploader.base_dir` + `GitlabUploader.dynamic_segment` when the store is LOCAL
- - `GitlabUploader.dynamic_segment` when the store is REMOTE (the bucket name is used to namespace)
+- `GitlabUploader.base_dir` + `GitlabUploader.dynamic_segment` when the store is LOCAL
+- `GitlabUploader.dynamic_segment` when the store is REMOTE (the bucket name is used to namespace)
### Using `ObjectStorage::Extension::RecordsUploads`
diff --git a/doc/development/geo.md b/doc/development/geo.md
index 685d4e44ad3..24f16eae9fa 100644
--- a/doc/development/geo.md
+++ b/doc/development/geo.md
@@ -341,9 +341,9 @@ not used, so sessions etc. aren't shared between nodes.
GitLab can optionally use Object Storage to store data it would
otherwise store on disk. These things can be:
- - LFS Objects
- - CI Job Artifacts
- - Uploads
+- LFS Objects
+- CI Job Artifacts
+- Uploads
Objects that are stored in object storage, are not handled by Geo. Geo
ignores items in object storage. Either:
@@ -412,15 +412,15 @@ The Geo **primary** stores events in the `geo_event_log` table. Each
entry in the log contains a specific type of event. These type of
events include:
- - Repository Deleted event
- - Repository Renamed event
- - Repositories Changed event
- - Repository Created event
- - Hashed Storage Migrated event
- - Lfs Object Deleted event
- - Hashed Storage Attachments event
- - Job Artifact Deleted event
- - Upload Deleted event
+- Repository Deleted event
+- Repository Renamed event
+- Repositories Changed event
+- Repository Created event
+- Hashed Storage Migrated event
+- Lfs Object Deleted event
+- Hashed Storage Attachments event
+- Job Artifact Deleted event
+- Upload Deleted event
### Geo Log Cursor
@@ -526,4 +526,4 @@ old method:
- Replication is synchronous and we preserve the order of events.
- Replication of the events happen at the same time as the changes in the
- database.
+ database.
diff --git a/doc/development/git_object_deduplication.md b/doc/development/git_object_deduplication.md
index c103a4527ff..5ce59891afa 100644
--- a/doc/development/git_object_deduplication.md
+++ b/doc/development/git_object_deduplication.md
@@ -79,11 +79,11 @@ at the Rails application level in SQL.
In conclusion, we need three things for effective object deduplication
across a collection of GitLab project repositories at the Git level:
-1. A pool repository must exist.
-2. The participating project repositories must be linked to the pool
- repository via their respective `objects/info/alternates` files.
-3. The pool repository must contain Git object data common to the
- participating project repositories.
+1. A pool repository must exist.
+1. The participating project repositories must be linked to the pool
+ repository via their respective `objects/info/alternates` files.
+1. The pool repository must contain Git object data common to the
+ participating project repositories.
### Deduplication factor
@@ -105,71 +105,71 @@ With pool repositories we made a fresh start. These live in their own
`pool_repositories` SQL table. The relations between these two tables
are as follows:
-- a `Project` belongs to at most one `PoolRepository`
- (`project.pool_repository`)
-- as an automatic consequence of the above, a `PoolRepository` has
- many `Project`s
-- a `PoolRepository` has exactly one "source `Project`"
- (`pool.source_project`)
+- a `Project` belongs to at most one `PoolRepository`
+ (`project.pool_repository`)
+- as an automatic consequence of the above, a `PoolRepository` has
+ many `Project`s
+- 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
- storage](../administration/repository_storage_types.md). This is so
- that we don't have to ever worry about updating paths in
- `object/info/alternates` files.
-- All repositories in a pool must be on the same Gitaly storage shard.
- 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.
-- 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.
+- All repositories in a pool must use [hashed
+ storage](../administration/repository_storage_types.md). This is so
+ that we don't have to ever worry about updating paths in
+ `object/info/alternates` files.
+- All repositories in a pool must be on the same Gitaly storage shard.
+ 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.
+- 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.
### Creating pools and pool memberships
-- When a pool gets created, it must have a source project. The initial
- contents of the pool repository are a Git clone of the source
- project repository.
-- The occasion for creating a pool is when an existing eligible
- (public, hashed storage, non-forked) GitLab project gets forked and
- this project does not belong to a pool repository yet. The fork
- parent project becomes the source project of the new pool, and both
- the fork parent and the fork child project become members of the new
- pool.
-- Once project A has become the source project of a pool, all future
- eligible forks of A will become pool members.
-- If the fork source is itself a fork, the resulting repository will
- neither join the repository nor will a new pool repository be
- seeded.
-
- eg:
-
- Suppose fork A is part of a pool repository, any forks created off
- of fork A *will not* be a part of the pool repository that fork A is
- a part of.
-
- Suppose B is a fork of A, and A does not belong to an object pool.
- Now C gets created as a fork of B. C will not be part of a pool
- repository.
+- When a pool gets created, it must have a source project. The initial
+ contents of the pool repository are a Git clone of the source
+ project repository.
+- The occasion for creating a pool is when an existing eligible
+ (public, hashed storage, non-forked) GitLab project gets forked and
+ this project does not belong to a pool repository yet. The fork
+ parent project becomes the source project of the new pool, and both
+ the fork parent and the fork child project become members of the new
+ pool.
+- Once project A has become the source project of a pool, all future
+ eligible forks of A will become pool members.
+- If the fork source is itself a fork, the resulting repository will
+ neither join the repository nor will a new pool repository be
+ seeded.
+
+ eg:
+
+ Suppose fork A is part of a pool repository, any forks created off
+ of fork A *will not* be a part of the pool repository that fork A is
+ a part of.
+
+ Suppose B is a fork of A, and A does not belong to an object pool.
+ Now C gets created as a fork of B. C will not be part of a pool
+ repository.
> TODO should forks of forks be deduplicated?
> <https://gitlab.com/gitlab-org/gitaly/issues/1532>
### Consequences
-- If a normal Project participating in a pool gets moved to another
- 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 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.
+- If a normal Project participating in a pool gets moved to another
+ 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 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
diff --git a/doc/development/go_guide/index.md b/doc/development/go_guide/index.md
index f09339eb3a4..f827d240bf6 100644
--- a/doc/development/go_guide/index.md
+++ b/doc/development/go_guide/index.md
@@ -129,17 +129,50 @@ deploy a new pod, migrating the data automatically.
## Testing
+### Testing frameworks
+
We should not use any specific library or framework for testing, as the
[standard library](https://golang.org/pkg/) provides already everything to get
-started. For example, some external dependencies might be worth considering in
-case we decide to use a specific library or framework:
+started. If there is a need for more sophisticated testing tools, the following
+external dependencies might be worth considering in case we decide to use a specific
+library or framework:
- [Testify](https://github.com/stretchr/testify)
- [httpexpect](https://github.com/gavv/httpexpect)
+### Subtests
+
Use [subtests](https://blog.golang.org/subtests) whenever possible to improve
code readability and test output.
+### Better output in tests
+
+When comparing expected and actual values in tests, use
+[testify/require.Equal](https://godoc.org/github.com/stretchr/testify/require#Equal),
+[testify/require.EqualError](https://godoc.org/github.com/stretchr/testify/require#EqualError),
+[testify/require.EqualValues](https://godoc.org/github.com/stretchr/testify/require#EqualValues),
+and others to improve readability when comparing structs, errors,
+large portions of text, or JSON documents:
+
+```go
+type TestData struct {
+ // ...
+}
+
+func FuncUnderTest() TestData {
+ // ...
+}
+
+func Test(t *testing.T) {
+ t.Run("FuncUnderTest", func(t *testing.T) {
+ want := TestData{}
+ got := FuncUnderTest()
+
+ require.Equal(t, want, got) // note that expected value comes first, then comes the actual one ("diff" semantics)
+ })
+}
+```
+
### Benchmarks
Programs handling a lot of IO or complex operations should always include
diff --git a/doc/development/gotchas.md b/doc/development/gotchas.md
index 13dda17bb7d..941eea2609e 100644
--- a/doc/development/gotchas.md
+++ b/doc/development/gotchas.md
@@ -53,7 +53,7 @@ When run, this spec doesn't do what we might expect:
(compared using ==)
```
-That's because FactoryBot sequences are not reseted for each example.
+This is because FactoryBot sequences are not reset for each example.
Please remember that sequence-generated values exist only to avoid having to
explicitly set attributes that have a uniqueness constraint when using a factory.
diff --git a/doc/development/import_export.md b/doc/development/import_export.md
index 64c91f151c5..4c78c62f5f5 100644
--- a/doc/development/import_export.md
+++ b/doc/development/import_export.md
@@ -229,8 +229,8 @@ meaning that we want to bump the version up in the next version (or patch releas
For example:
1. Add rename to `RelationRenameService` in X.Y
-2. Remove it from `RelationRenameService` in X.Y + 1
-3. Bump Import/Export version in X.Y + 1
+1. Remove it from `RelationRenameService` in X.Y + 1
+1. Bump Import/Export version in X.Y + 1
```ruby
module Gitlab
diff --git a/doc/development/interacting_components.md b/doc/development/interacting_components.md
new file mode 100644
index 00000000000..74d52d808e2
--- /dev/null
+++ b/doc/development/interacting_components.md
@@ -0,0 +1,29 @@
+# Developing against interacting components or features
+
+It's not uncommon that a single code change can reflect and interact with multiple parts of GitLab
+codebase. Furthermore, an existing feature might have an underlying integration or behavior that
+might go unnoticed even by reviewers and maintainers.
+
+The goal of this section is to briefly list interacting pieces to think about
+when making _backend_ changes that might involve multiple features or [components](architecture.md#components).
+
+## Uploads
+
+GitLab supports uploads to [object storage]. That means every feature and
+change that affects uploads should also be tested against [object storage],
+which is _not_ enabled by default in [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit).
+
+When working on a related feature, make sure to enable and test it
+against [Minio](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/object_storage.md).
+
+See also [File Storage in GitLab](file_storage.md).
+
+## Merge requests
+
+### Forks
+
+GitLab supports a great amount of features for [merge requests](../user/project/merge_requests/index.md). One
+of them is the ability to create merge requests from and to [forks](../gitlab-basics/fork-project.md),
+which should also be highly considered and tested upon development phase.
+
+[object storage]: https://docs.gitlab.com/charts/advanced/external-object-storage/
diff --git a/doc/development/licensed_feature_availability.md b/doc/development/licensed_feature_availability.md
index 80ec7b8c0cf..29e4ace157b 100644
--- a/doc/development/licensed_feature_availability.md
+++ b/doc/development/licensed_feature_availability.md
@@ -14,7 +14,7 @@ it should be restricted on namespace scope.
1. Add the feature symbol on `EES_FEATURES`, `EEP_FEATURES` or `EEU_FEATURES` constants in
`ee/app/models/license.rb`. Note on `ee/app/models/ee/namespace.rb` that _Bronze_ GitLab.com
features maps to on-premise _EES_, _Silver_ to _EEP_ and _Gold_ to _EEU_.
-2. Check using:
+1. Check using:
```ruby
project.feature_available?(:feature_symbol)
@@ -29,8 +29,8 @@ the instance license.
1. Add the feature symbol on `EES_FEATURES`, `EEP_FEATURES` or `EEU_FEATURES` constants in
`ee/app/models/license.rb`.
-2. Add the same feature symbol to `GLOBAL_FEATURES`
-3. Check using:
+1. Add the same feature symbol to `GLOBAL_FEATURES`
+1. Check using:
```ruby
License.feature_available?(:feature_symbol)
diff --git a/doc/development/new_fe_guide/style/javascript.md b/doc/development/new_fe_guide/style/javascript.md
index 3019eaa089c..802ebd12d92 100644
--- a/doc/development/new_fe_guide/style/javascript.md
+++ b/doc/development/new_fe_guide/style/javascript.md
@@ -71,7 +71,6 @@ class myClass {
}
const instance = new myClass();
instance.makeRequest();
-
```
## Avoid classes to handle DOM events
@@ -189,8 +188,8 @@ disabled due to legacy compatibility reasons but they are in the process of bein
Do not disable specific ESLint rules. Due to technical debt, you may disable the following
rules only if you are invoking/instantiating existing code modules.
- - [no-new](http://eslint.org/docs/rules/no-new)
- - [class-method-use-this](http://eslint.org/docs/rules/class-methods-use-this)
+- [no-new](http://eslint.org/docs/rules/no-new)
+- [class-method-use-this](http://eslint.org/docs/rules/class-methods-use-this)
> Note: Disable these rules on a per line basis. This makes it easier to refactor
in the future. E.g. use `eslint-disable-next-line` or `eslint-disable-line`.
diff --git a/doc/development/performance.md b/doc/development/performance.md
index 8b569a677b6..14b3f8204d2 100644
--- a/doc/development/performance.md
+++ b/doc/development/performance.md
@@ -267,7 +267,7 @@ piece of code is worth optimizing. The only two things you can do are:
1. Think about what the code does, how it's used, how many times it's called and
how much time is spent in it relative to the total execution time (e.g., the
total time spent in a web request).
-2. Ask others (preferably in the form of an issue).
+1. Ask others (preferably in the form of an issue).
Some examples of changes that aren't really important/worth the effort:
@@ -285,10 +285,10 @@ directly in a web request as much as possible. This has numerous benefits such
as:
1. An error won't prevent the request from completing.
-2. The process being slow won't affect the loading time of a page.
-3. In case of a failure it's easy to re-try the process (Sidekiq takes care of
+1. The process being slow won't affect the loading time of a page.
+1. In case of a failure it's easy to re-try the process (Sidekiq takes care of
this automatically).
-4. By isolating the code from a web request it will hopefully be easier to test
+1. By isolating the code from a web request it will hopefully be easier to test
and maintain.
It's especially important to use Sidekiq as much as possible when dealing with
diff --git a/doc/development/policies.md b/doc/development/policies.md
index c4ac42bb40a..833b0acb13e 100644
--- a/doc/development/policies.md
+++ b/doc/development/policies.md
@@ -89,8 +89,8 @@ Each line represents a rule that was evaluated. There are a few things to note:
1. The `-` or `+` symbol indicates whether the rule block was evaluated to be
`false` or `true`, respectively.
-2. The number inside the brackets indicates the score.
-3. The last part of the line (e.g. `@john : Issue/1`) shows the username
+1. The number inside the brackets indicates the score.
+1. The last part of the line (e.g. `@john : Issue/1`) shows the username
and subject for that rule.
Here you can see that the first four rules were evaluated `false` for
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 05cb03eb4bd..52957d1a1ab 100644
--- a/doc/development/testing_guide/end_to_end/page_objects.md
+++ b/doc/development/testing_guide/end_to_end/page_objects.md
@@ -27,7 +27,7 @@ When someone later changes `t.text_field :login` in the view associated with
this page to `t.text_field :username` it will generate a different field
identifier, what would effectively break all tests.
-Because we are using `Page::Main::Login.act { sign_in_using_credentials }`
+Because we are using `Page::Main::Login.perform(&:sign_in_using_credentials)`
everywhere, when we want to sign into GitLab, the page object is the single
source of truth, and we will need to update `fill_in :user_login`
to `fill_in :user_username` only in a one place.
@@ -92,20 +92,25 @@ end
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 will need to be added to the view file.
+`data-qa-selector=element_name_snaked` data attribute 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:
- Consistency: there is only one way to define an element
-- Separation of concerns: QA uses dedicated CSS classes instead of reusing code
+- Separation of concerns: QA uses dedicated `data-qa-*` attributes instead of reusing code
or classes used by other components (e.g. `js-*` classes etc.)
```ruby
view 'app/views/my/view.html.haml' do
- # Implicitly require `.qa-logout-button` CSS class to be present in the view
+
+ ### Good ###
+
+ # Implicitly require the CSS selector `[data-qa-selector="logout_button"]` to be present in the view
element :logout_button
+ ### Bad ###
+
## This is deprecated and forbidden by the `QA/ElementWithPattern` RuboCop cop.
# Require `f.submit "Sign in"` to be present in `my/view.html.haml
element :my_button, 'f.submit "Sign in"' # rubocop:disable QA/ElementWithPattern
@@ -129,24 +134,39 @@ view 'app/views/my/view.html.haml' do
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
+To add these elements to the view, you must change the rails View, partial, or vue component by adding a `data-qa-selector` attribute
for each element defined.
-In our case, `qa-login-field`, `qa-password-field` and `qa-sign-in-button`
+In our case, `data-qa-selector="login_field"`, `data-qa-selector="password_field"` and `data-qa-selector="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"
+= f.text_field :login, class: "form-control top", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off", required: true, title: "This field is required.", data: { qa_selector: 'login_field' }
+= f.password_field :password, class: "form-control bottom", required: true, title: "This field is required.", data: { qa_selector: 'password_field' }
+= f.submit "Sign in", class: "btn btn-success", data: { qa_selector: 'sign_in_button' }
```
Things to note:
-- The CSS class must be `kebab-cased` (separated with hyphens "`-`")
+- The name of the element and the qa_selector must match and be snake_cased
- If the element appears on the page unconditionally, add `required: true` to the element. See
[Dynamic element validation](dynamic_element_validation.md)
+- You may see `.qa-selector` classes in existing Page Objects. We should prefer the [`data-qa-selector`](#data-qa-selector-vs-qa-selector)
+ method of definition over the `.qa-selector` CSS class
+
+
+### `data-qa-selector` vs `.qa-selector`
+
+> Introduced in GitLab 12.1
+
+There are two supported methods of defining elements within a view.
+
+1. `data-qa-selector` attribute
+1. `.qa-selector` class
+
+Any existing `.qa-selector` class should be considered deprecated
+and we should prefer the `data-qa-selector` method of definition.
## Running the test locally
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 efcfd44bc22..14a169dcc1d 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
@@ -101,7 +101,7 @@ it 'replaces an existing label if it has the same key' do
page.find('#content-body').click
page.refresh
- labels_block = page.find('.qa-labels-block')
+ labels_block = page.find(%q([data-qa-selector="labels_block"]))
expect(labels_block).to have_content('animal::dolphin')
expect(labels_block).not_to have_content('animal::fox')
@@ -116,9 +116,9 @@ end
Below are the steps that the test covers:
1. The test finds the 'Edit' link for the labels and clicks on it.
-2. Then it fills in the 'Assign labels' input field with the value 'animal::dolphin' and press enters.
-3. Then it clicks in the content body to apply the label and refreshes the page.
-4. Finally, the expectations check that the previous scoped label was removed and that the new one was added.
+1. Then it fills in the 'Assign labels' input field with the value 'animal::dolphin' and press enters.
+1. Then it clicks in the content body to apply the label and refreshes the page.
+1. Finally, the expectations check that the previous scoped label was removed and that the new one was added.
Let's now see how the second test case would look.
@@ -130,7 +130,7 @@ it 'keeps both scoped labels when adding a label with a different key' do
page.find('#content-body').click
page.refresh
- labels_block = page.find('.qa-labels-block')
+ labels_block = page.find(%q([data-qa-selector="labels_block"]))
expect(labels_block).to have_content('animal::fox')
expect(labels_block).to have_content('plant::orchid')
@@ -139,14 +139,14 @@ it 'keeps both scoped labels when adding a label with a different key' do
end
```
-> Note that elements are always located using CSS selectors, and a good practice is to add test-specific selectors (this is called adding testability to the application and we will talk more about it later.) For example, the `labels_block` element uses the selector `.qa-labels-block`, which was added specifically for testing purposes.
+> Note that elements are always located using CSS selectors, and a good practice is to add test-specific selectors (this is called "testability"). For example, the `labels_block` element uses the CSS selector [`data-qa-selector="labels_block"`](page_objects.md#data-qa-selector-vs-qa-selector), which was added specifically for testing purposes.
Below are the steps that the test covers:
1. The test finds the 'Edit' link for the labels and clicks on it.
-2. Then it fills in the 'Assign labels' input field with the value 'plant::orchid' and press enters.
-3. Then it clicks in the content body to apply the label and refreshes the page.
-4. Finally, the expectations check that both scoped labels are present.
+1. Then it fills in the 'Assign labels' input field with the value 'plant::orchid' and press enters.
+1. Then it clicks in the content body to apply the label and refreshes the page.
+1. Finally, the expectations check that both scoped labels are present.
> Similar to the previous test, this one is also very straightforward, but there is some code duplication. Let's address it.
@@ -168,7 +168,7 @@ end
it 'replaces an existing label if it has the same key' do
select_label_and_refresh @new_label_same_scope
- labels_block = page.find('.qa-labels-block')
+ labels_block = page.find(%q([data-qa-selector="labels_block"]))
expect(labels_block).to have_content(@new_label_same_scope)
expect(labels_block).not_to have_content(@initial_label)
@@ -179,7 +179,7 @@ end
it 'keeps both scoped label when adding a label with a different key' do
select_label_and_refresh @new_label_different_scope
- labels_block = page.find('.qa-labels-block')
+ labels_block = page.find(%q([data-qa-selector="labels_block"]))
expect(labels_blocks).to have_content(@new_label_different_scope)
expect(labels_blocks).to have_content(@initial_label)
@@ -290,7 +290,7 @@ As already mentioned in the [best practices](best_practices.md) document, end-to
Some improvements that we could make in our test suite to optimize its time to run are:
1. Having a single test case (an `it` block) that exercises both scenarios to avoid "wasting" time in the tests' pre-conditions, instead of having two different test cases.
-2. Making the selection of labels more performant by allowing for the selection of more than one label in the same reusable method.
+1. Making the selection of labels more performant by allowing for the selection of more than one label in the same reusable method.
Let's look at a suggestion that addresses the above points, one by one:
@@ -305,7 +305,7 @@ module QA
it 'correctly applies scoped labels depending on if they are from the same or a different scope' do
select_labels_and_refresh [@new_label_same_scope, @new_label_different_scope]
- labels_block = page.all('.qa-labels-block')
+ labels_block = page.all(%q([data-qa-selector="labels_block"]))
expect(labels_block).to have_content(@new_label_same_scope)
expect(labels_block).to have_content(@new_label_different_scope)
@@ -332,8 +332,8 @@ To address point 1, we changed the test implementation from two `it` blocks into
> Notice that the implementation of the new and unique `it` block had to change a little bit. Below we describe in details what it does.
1. It selects two scoped labels simultaneously, one from the same scope of the one already applied in the issue during the setup phase (in the `before` block), and another one from a different scope.
-2. It asserts that the correct labels are visible in the `labels_block`, and that the labels were correctly added and removed;
-3. Finally, the `select_label_and_refresh` method is changed to `select_labels_and_refresh`, which accepts an array of labels instead of a single label, and it iterates on them for faster label selection (this is what is used in step 1 explained above.)
+1. It asserts that the correct labels are visible in the `labels_block`, and that the labels were correctly added and removed;
+1. Finally, the `select_label_and_refresh` method is changed to `select_labels_and_refresh`, which accepts an array of labels instead of a single label, and it iterates on them for faster label selection (this is what is used in step 1 explained above.)
### 7. Resources
@@ -542,9 +542,9 @@ end
Notice that we have not only moved the `select_labels_and_refresh` method, but we have also changed its implementation to:
1. Click the `:edit_link_labels` element previously defined, instead of using `find('.block.labels .edit-link').click`
-2. Use `within_element(:dropdown_menu_labels, text: label)`, and inside of it, we call `send_keys_to_element(:dropdown_input_field, [label, :enter])`, which is a method that we will implement in the `QA::Page::Base` class to replace `find('.dropdown-menu-labels .dropdown-input-field').send_keys [label, :enter]`
-3. Use `click_body` after iterating on each label, instead of using `find('#content-body').click`
-4. Iterate on every label again, and then we use `has_element?(:labels_block, text: label)` after clicking the page body (which applies the labels), and before refreshing the page, to avoid test flakiness due to refreshing too fast.
+1. Use `within_element(:dropdown_menu_labels, text: label)`, and inside of it, we call `send_keys_to_element(:dropdown_input_field, [label, :enter])`, which is a method that we will implement in the `QA::Page::Base` class to replace `find('.dropdown-menu-labels .dropdown-input-field').send_keys [label, :enter]`
+1. Use `click_body` after iterating on each label, instead of using `find('#content-body').click`
+1. Iterate on every label again, and then we use `has_element?(:labels_block, text: label)` after clicking the page body (which applies the labels), and before refreshing the page, to avoid test flakiness due to refreshing too fast.
##### Details of `text_of_labels_block`
@@ -552,37 +552,36 @@ The `text_of_labels_block` method is a simple method that returns the `:labels_b
#### Updates in the view (*.html.haml) and `dropdowns_helper.rb` files
-Now let's change the view and the `dropdowns_helper` files to add the selectors that relate to the Page Object.
+Now let's change the view and the `dropdowns_helper` files to add the selectors that relate to the [Page Objects].
-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`.
+In [`app/views/shared/issuable/_sidebar.html.haml:105`](https://gitlab.com/gitlab-org/gitlab-ee/blob/7ca12defc7a965987b162a6ebef302f95dc8867f/app/views/shared/issuable/_sidebar.html.haml#L105), add a `data: { qa_selector: 'edit_link_labels' }` data attribute.
The code should look like this:
```haml
-= link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right qa-edit-link-labels'
+= link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right', data: { qa_selector: '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`.
+In the same file, on [line 121](https://gitlab.com/gitlab-org/gitlab-ee/blob/7ca12defc7a965987b162a6ebef302f95dc8867f/app/views/shared/issuable/_sidebar.html.haml#L121), add a `data: { qa_selector: 'dropdown_menu_labels' }` data attribute.
The code should look like this:
```haml
-.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable.qa-dropdown-menu-labels
+.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable.dropdown-extended-height{ data: { qa_selector: '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`.
+In [`app/helpers/dropdowns_helper.rb:94`](https://gitlab.com/gitlab-org/gitlab-ee/blob/7ca12defc7a965987b162a6ebef302f95dc8867f/app/helpers/dropdowns_helper.rb#L94), add a `data: { qa_selector: 'dropdown_input_field' }` data attribute.
The code should look like this:
```ruby
-filter_output = search_field_tag search_id, nil, class: "dropdown-input-field qa-dropdown-input-field", placeholder: placeholder, autocomplete: 'off'
+filter_output = search_field_tag search_id, nil, class: "dropdown-input-field", placeholder: placeholder, autocomplete: 'off', data: { qa_selector: 'dropdown_input_field' }
```
-> Classes starting with `qa-` are used for testing purposes only, and by defining such classes in the elements we add **testability** in the application.
+> `data-qa-*` data attributes and CSS classes starting with `qa-` are used solely for the purpose of QA and testing.
+> By defining these, we add **testability** to 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 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.
+> When defining a data attribute like: `qa_selector: 'labels_block'`, it should match the element definition: `element :labels_block`. 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 selectors in the specified views.
#### Updates in the `QA::Page::Base` class
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index bb44cc595e9..ff28c2ea5e2 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -79,6 +79,37 @@ describe('Component', () => {
Remember that the performance of each test depends on the environment.
+### Manual module mocks
+
+Jest supports [manual module mocks](https://jestjs.io/docs/en/manual-mocks) by placing a mock in a `__mocks__/` directory next to the source module. **Don't do this.** We want to keep all of our test-related code in one place (the `spec/` folder), and the logic that Jest uses to apply mocks from `__mocks__/` is rather inconsistent.
+
+Instead, our test runner detects manual mocks from `spec/frontend/mocks/`. Any mock placed here is automatically picked up and injected whenever you import its source module.
+
+- Files in `spec/frontend/mocks/ce` will mock the corresponding CE module from `app/assets/javascripts`, mirroring the source module's path.
+ - Example: `spec/frontend/mocks/ce/lib/utils/axios_utils` will mock the module `~/lib/utils/axios_utils`.
+- Files in `spec/frontend/mocks/node` will mock NPM packages of the same name or path.
+- We don't support mocking EE modules yet.
+
+If a mock is found for which a source module doesn't exist, the test suite will fail. 'Virtual' mocks, or mocks that don't have a 1-to-1 association with a source module, are not supported yet.
+
+#### Writing a mock
+
+Create a JS module in the appropriate place in `spec/frontend/mocks/`. That's it. It will automatically mock its source package in all tests.
+
+Make sure that your mock's export has the same format as the mocked module. So, if you're mocking a CommonJS module, you'll need to use `module.exports` instead of the ES6 `export`.
+
+It might be useful for a mock to expose a property that indicates if the mock was loaded. This way, tests can assert the presence of a mock without calling any logic and causing side-effects. The `~/lib/utils/axios_utils` module mock has such a property, `isMock`, that is `true` in the mock and undefined in the original class. Jest's mock functions also have a `mock` property that you can test.
+
+#### Bypassing mocks
+
+If you ever need to import the original module in your tests, use [`jest.requireActual()`](https://jestjs.io/docs/en/jest-object#jestrequireactualmodulename) (or `jest.requireActual().default` for the default export). The `jest.mock()` and `jest.unmock()` won't have an effect on modules that have a manual mock, because mocks are imported and cached before any tests are run.
+
+#### Keep mocks light
+
+Global mocks introduce magic and can affect how modules are imported in your tests. Try to keep them as light as possible and dependency-free. A global mock should be useful for any unit test. For example, the `axios_utils` and `jquery` module mocks throw an error when an HTTP request is attempted, since this is useful behaviour in &gt;99% of tests.
+
+When in doubt, construct mocks in your test file using [`jest.mock()`](https://jestjs.io/docs/en/jest-object#jestmockmodulename-factory-options), [`jest.spyOn()`](https://jestjs.io/docs/en/jest-object#jestspyonobject-methodname), etc.
+
## Karma test suite
GitLab uses the [Karma][karma] test runner with [Jasmine] as its test
@@ -434,7 +465,7 @@ See this [section][vue-test].
For running the frontend tests, you need the following commands:
-- `rake karma:fixtures` (re-)generates [fixtures](#frontend-test-fixtures).
+- `rake frontend:fixtures` (re-)generates [fixtures](#frontend-test-fixtures).
- `yarn test` executes the tests.
As long as the fixtures don't change, `yarn test` is sufficient (and saves you some time).
@@ -487,8 +518,8 @@ Information on setting up and running RSpec integration tests with
Code that is added to HAML templates (in `app/views/`) or makes Ajax requests to the backend has tests that require HTML or JSON from the backend.
Fixtures for these tests are located at:
-- `spec/javascripts/fixtures/`, for running tests in CE.
-- `ee/spec/javascripts/fixtures/`, for running tests in EE.
+- `spec/frontend/fixtures/`, for running tests in CE.
+- `ee/spec/frontend/fixtures/`, for running tests in EE.
Fixture files in:
@@ -499,7 +530,7 @@ The following are examples of tests that work for both Karma and Jest:
```javascript
it('makes a request', () => {
- const responseBody = getJSONFixture('some/fixture.json'); // loads spec/javascripts/fixtures/some/fixture.json
+ const responseBody = getJSONFixture('some/fixture.json'); // loads spec/frontend/fixtures/some/fixture.json
axiosMock.onGet(endpoint).reply(200, responseBody);
myButton.click();
@@ -508,7 +539,7 @@ it('makes a request', () => {
});
it('uses some HTML element', () => {
- loadFixtures('some/page.html'); // loads spec/javascripts/fixtures/some/page.html and adds it to the DOM
+ loadFixtures('some/page.html'); // loads spec/frontend/fixtures/some/page.html and adds it to the DOM
const element = document.getElementById('#my-id');
@@ -516,12 +547,12 @@ it('uses some HTML element', () => {
});
```
-HTML and JSON fixtures are generated from backend views and controllers using RSpec (see `spec/javascripts/fixtures/*.rb`).
+HTML and JSON fixtures are generated from backend views and controllers using RSpec (see `spec/frontend/fixtures/*.rb`).
For each fixture, the content of the `response` variable is stored in the output file.
This variable gets automagically set if the test is marked as `type: :request` or `type: :controller`.
-Fixtures are regenerated using the `bin/rake karma:fixtures` command but you can also generate them individually,
-for example `bin/rspec spec/javascripts/fixtures/merge_requests.rb`.
+Fixtures are regenerated using the `bin/rake frontend:fixtures` command but you can also generate them individually,
+for example `bin/rspec spec/frontend/fixtures/merge_requests.rb`.
When creating a new fixture, it often makes sense to take a look at the corresponding tests for the endpoint in `(ee/)spec/controllers/` or `(ee/)spec/requests/`.
## Gotchas
diff --git a/doc/development/testing_guide/review_apps.md b/doc/development/testing_guide/review_apps.md
index ae40d628717..96761622cfe 100644
--- a/doc/development/testing_guide/review_apps.md
+++ b/doc/development/testing_guide/review_apps.md
@@ -137,8 +137,8 @@ secure note named **gitlab-{ce,ee} Review App's root password**.
### Run a Rails console
-1. [Filter Workloads by your Review App slug](https://console.cloud.google.com/kubernetes/workload?project=gitlab-review-apps)
- , e.g. `review-qa-raise-e-12chm0`.
+1. [Filter Workloads by your Review App slug](https://console.cloud.google.com/kubernetes/workload?project=gitlab-review-apps),
+ e.g. `review-qa-raise-e-12chm0`.
1. Find and open the `task-runner` Deployment, e.g. `review-qa-raise-e-12chm0-task-runner`.
1. Click on the Pod in the "Managed pods" section, e.g. `review-qa-raise-e-12chm0-task-runner-d5455cc8-2lsvz`.
1. Click on the `KUBECTL` dropdown, then `Exec` -> `task-runner`.
@@ -196,7 +196,7 @@ For the record, the debugging steps to find out this issue were:
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)
+ 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
@@ -211,7 +211,7 @@ For the record, the debugging steps to find out this issue were:
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`
+ 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
diff --git a/doc/gitlab-basics/create-project.md b/doc/gitlab-basics/create-project.md
index ccba72f0ef8..2caf7dbbc7a 100644
--- a/doc/gitlab-basics/create-project.md
+++ b/doc/gitlab-basics/create-project.md
@@ -23,18 +23,18 @@ To create a project in GitLab:
To create a new blank project on the **New project** page:
1. On the **Blank project** tab, provide the following information:
- - The name of your project in the **Project name** field. You can't use
- special characters, but you can use spaces, hyphens, underscores or even
- emoji.
- - The **Project description (optional)** field enables you to enter a
- description for your project's dashboard, which will help others
- understand what your project is about. Though it's not required, it's a good
- idea to fill this in.
- - Changing the **Visibility Level** modifies the project's
- [viewing and access rights](../public_access/public_access.md) for users.
- - Selecting the **Initialize repository with a README** option creates a
- README file so that the Git repository is initialized, has a default branch, and
- can be cloned.
+ - The name of your project in the **Project name** field. You can't use
+ special characters, but you can use spaces, hyphens, underscores or even
+ emoji.
+ - The **Project description (optional)** field enables you to enter a
+ description for your project's dashboard, which will help others
+ understand what your project is about. Though it's not required, it's a good
+ idea to fill this in.
+ - Changing the **Visibility Level** modifies the project's
+ [viewing and access rights](../public_access/public_access.md) for users.
+ - Selecting the **Initialize repository with a README** option creates a
+ README file so that the Git repository is initialized, has a default branch, and
+ can be cloned.
1. Click **Create project**.
## Project templates
@@ -60,8 +60,8 @@ To use a built-in template on the **New project** page:
1. On the **Create from template** tab, select the **Built-in** tab.
1. From the list of available built-in templates, click the:
- - **Preview** button to look at the template source itself.
- - **Use template** button to start creating the project.
+ - **Preview** button to look at the template source itself.
+ - **Use template** button to start creating the project.
1. Finish creating the project by filling out the project's details. The process is the same as for
using a [blank project](#blank-projects).
@@ -83,8 +83,8 @@ To use a custom project template on the **New project** page:
1. On the **Create from template** tab, select the **Instance** tab or the **Group** tab.
1. From the list of available custom templates, click the:
- - **Preview** button to look at the template source itself.
- - **Use template** button to start creating the project.
+ - **Preview** button to look at the template source itself.
+ - **Use template** button to start creating the project.
1. Finish creating the project by filling out the project's details. The process is the same as for
using a [blank project](#blank-projects).
diff --git a/doc/integration/elasticsearch.md b/doc/integration/elasticsearch.md
index 20caac4cb74..626bd259ed6 100644
--- a/doc/integration/elasticsearch.md
+++ b/doc/integration/elasticsearch.md
@@ -354,6 +354,8 @@ There are several rake tasks available to you via the command line:
- Does the same thing as `sudo gitlab-rake gitlab:elastic:create_empty_index`
- [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:projects_not_indexed](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake)
+ - Displays which projects are not indexed.
### Environment Variables
@@ -469,6 +471,10 @@ Here are some common pitfalls and how to overcome them:
The more data present in your GitLab instance, the longer the indexing process takes.
+- **There are some projects that weren't indexed, but we don't know which ones**
+
+ You can run `sudo gitlab-rake gitlab:elastic:projects_not_indexed` to display projects that aren't indexed.
+
- **No new data is added to the Elasticsearch index when I push code**
When performing the initial indexing of blobs, we lock all projects until the project finishes indexing. It could
diff --git a/doc/integration/jenkins_deprecated.md b/doc/integration/jenkins_deprecated.md
index eae705c9637..bac89ae2dc6 100644
--- a/doc/integration/jenkins_deprecated.md
+++ b/doc/integration/jenkins_deprecated.md
@@ -19,7 +19,7 @@ Requirements:
## Jenkins
1. Install [GitLab Hook plugin](https://wiki.jenkins.io/display/JENKINS/GitLab+Hook+Plugin)
-2. Set up jenkins project
+1. Set up jenkins project
![screen](img/jenkins_project.png)
diff --git a/doc/integration/jira_development_panel.md b/doc/integration/jira_development_panel.md
index 60c7bdabf93..5e906e5af16 100644
--- a/doc/integration/jira_development_panel.md
+++ b/doc/integration/jira_development_panel.md
@@ -59,7 +59,7 @@ There are no special requirements if you are using GitLab.com.
![GitLab Application setup](img/jira_dev_panel_gl_setup_1.png)
- Check `api` in the Scopes section.
-2. Click `Save application`. You will see the generated 'Application Id' and 'Secret' values.
+1. Click `Save application`. You will see the generated 'Application Id' and 'Secret' values.
Copy these values that you will use on the Jira configuration side.
## Jira Configuration
@@ -70,7 +70,7 @@ There are no special requirements if you are using GitLab.com.
![Jira DVCS from Dashboard](img/jira_dev_panel_jira_setup_1.png)
-2. Complete the form
+1. Complete the form
Select GitHub Enterprise for the `Host` field.
@@ -92,7 +92,7 @@ There are no special requirements if you are using GitLab.com.
Ensure that the rest of the checkboxes are checked.
-3. Click `Add` to complete and create the integration.
+1. Click `Add` to complete and create the integration.
Jira takes up to a few minutes to know about (import behind the scenes) all the commits and branches
for all the projects in the GitLab group you specified in the previous step. These are refreshed
diff --git a/doc/security/reset_root_password.md b/doc/security/reset_root_password.md
index 6a6c5262179..ec360e2d338 100644
--- a/doc/security/reset_root_password.md
+++ b/doc/security/reset_root_password.md
@@ -23,7 +23,7 @@ user = User.where(id: 1).first
or
```bash
-user = User.find_by(email: 'admin@local.host')
+user = User.find_by(email: 'admin@example.com')
```
Now you can change your password:
diff --git a/doc/subscriptions/index.md b/doc/subscriptions/index.md
index 745a2253e84..68e62fff106 100644
--- a/doc/subscriptions/index.md
+++ b/doc/subscriptions/index.md
@@ -69,6 +69,14 @@ Once your GitLab.com account is linked, you can go to your [Subscriptions](https
Please note that you need to be a group owner to associate a group to your subscription.
+### GitLab.com: Upgrade your subscription plan
+
+GitLab.com subscriptions can be upgraded directly through the [Subscriptions portal](https://customers.gitlab.com/subscriptions).
+
+The Subscriptions portal provides an **Upgrade** button below each GitLab.com
+subscription, which will lead you to a simple
+checkout process.
+
### Confirm or upgrade your GitLab.com subscription details within GitLab
To see the status of your GitLab.com subscription, you can click on the Billings
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index f9ad952aaad..7a92a672801 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -789,11 +789,11 @@ To configure your application variables:
1. Go to your project's **Settings > CI/CD**, then expand the section
called **Variables**.
-2. Create a CI Variable, ensuring the key is prefixed with
+1. Create a CI Variable, ensuring the key is prefixed with
`K8S_SECRET_`. For example, you can create a variable with key
`K8S_SECRET_RAILS_MASTER_KEY`.
-3. Run an Auto Devops pipeline either by manually creating a new
+1. Run an Auto Devops pipeline either by manually creating a new
pipeline or by pushing a code change to GitLab.
Auto DevOps pipelines will take your application secret variables to
diff --git a/doc/university/training/topics/getting_started.md b/doc/university/training/topics/getting_started.md
index 08027c5d15b..e8ff7916590 100644
--- a/doc/university/training/topics/getting_started.md
+++ b/doc/university/training/topics/getting_started.md
@@ -8,14 +8,15 @@ comments: false
- Create a new repository by instantiating it through:
- ```bash
- git init
- ```
+ ```bash
+ git init
+ ```
+
- Copy an existing project by cloning the repository through:
- ```bash
- git clone <url>
- ```
+ ```bash
+ git clone <url>
+ ```
## Central Repos
@@ -23,9 +24,9 @@ comments: false
- Bare repositories don't allow file editing or committing changes.
- Create a bare repo with:
- ```bash
- git init --bare project-name.git
- ```
+ ```bash
+ git init --bare project-name.git
+ ```
## Instantiate workflow with clone
diff --git a/doc/user/admin_area/settings/img/email_confirmation.png b/doc/user/admin_area/settings/img/email_confirmation.png
new file mode 100644
index 00000000000..4d888da3416
--- /dev/null
+++ b/doc/user/admin_area/settings/img/email_confirmation.png
Binary files differ
diff --git a/doc/user/admin_area/settings/sign_up_restrictions.md b/doc/user/admin_area/settings/sign_up_restrictions.md
index d77e91156f8..1b1bcbcd6e8 100644
--- a/doc/user/admin_area/settings/sign_up_restrictions.md
+++ b/doc/user/admin_area/settings/sign_up_restrictions.md
@@ -4,19 +4,26 @@ type: reference
# Sign-up restrictions
-By implementing sign-up restrictions, you can blacklist or whitelist email addresses
-belonging to specific domains.
+You can use sign-up restrictions to require user email confirmation, as well as
+to blacklist or whitelist email addresses belonging to specific domains.
>**Note**: These restrictions are only applied during sign-up. An admin is
able to add a user through the admin panel with a disallowed domain. Also
note that the users can change their email addresses after signup to
disallowed domains.
+## Require email confirmation
+
+You can send confirmation emails during sign-up and require that users confirm
+their email address before they are allowed to sign in.
+
+![Email confirmation](img/email_confirmation.png)
+
## Whitelist email domains
> [Introduced][ce-598] in GitLab 7.11.0
-You can restrict users to only signup using email addresses matching the given
+You can restrict users to only sign up using email addresses matching the given
domains list.
## Blacklist email domains
@@ -24,7 +31,9 @@ domains list.
> [Introduced][ce-5259] in GitLab 8.10.
With this feature enabled, you can block email addresses of a specific domain
-from creating an account on your GitLab server. This is particularly useful to prevent spam. Disposable email addresses are usually used by malicious users to create dummy accounts and spam issues.
+from creating an account on your GitLab server. This is particularly useful
+to prevent malicious users from creating spam accounts with disposable email
+addresses.
## Settings
@@ -33,10 +42,10 @@ To access this feature:
1. Navigate to the **Settings > General** in the Admin area.
1. Expand the **Sign-up restrictions** section.
-For the:
+For the blacklist, you can enter the list manually or upload a `.txt` file that
+contains list entries.
-- Blacklist, you can enter the list manually, or upload a `.txt` file with it.
-- Whitelist you must enter the list manually.
+For the whitelist, you must enter the list manually.
Both the whitelist and blacklist accept wildcards. For example, you can use
`*.company.com` to accept every `company.com` subdomain, or `*.io` to block all
diff --git a/doc/user/application_security/dast/index.md b/doc/user/application_security/dast/index.md
index a0a917c5ebd..4b98dd73d76 100644
--- a/doc/user/application_security/dast/index.md
+++ b/doc/user/application_security/dast/index.md
@@ -162,6 +162,28 @@ As the DAST job belongs to a separate `dast` stage that runs after all
[default stages](../../../ci/yaml/README.md#stages),
don't forget to add `stage: dast` when you override the template job definition.
+## Available variables
+
+DAST can be [configured](#customizing-the-dast-settings) using environment variables.
+Since it's a wrapper around the ZAP scanning scripts
+([baseline](https://github.com/zaproxy/zaproxy/wiki/ZAP-Baseline-Scan)
+or [full](https://github.com/zaproxy/zaproxy/wiki/ZAP-Full-Scan) scan), it
+accepts all arguments those scripts recognize (the arguments are the same).
+The choice of the scan type depends on the `DAST_FULL_SCAN_ENABLED` environment
+variable value.
+
+| Environment variable | Required | Description |
+|-----------------------------| ----------|--------------------------------------------------------------------------------|
+| `DAST_WEBSITE` | yes | The URL of the website to scan. |
+| `DAST_AUTH_URL` | no | The authentication URL of the website to scan. |
+| `DAST_USERNAME` | no | The username to authenticate to in the website. |
+| `DAST_PASSWORD` | no | The password to authenticate to in the website. |
+| `DAST_USERNAME_FIELD` | no | The name of username field at the sign-in HTML form. |
+| `DAST_PASSWORD_FIELD` | no | The name of password field at the sign-in HTML form. |
+| `DAST_AUTH_EXCLUDE_URLS` | no | The URLs to skip during the authenticated scan; comma-separated, no spaces in between. |
+| `DAST_TARGET_AVAILABILITY_TIMEOUT` | no | Time limit in seconds to wait for target availability. Scan is attempted nevertheless if it runs out. Integer. Defaults to `60`. |
+| `DAST_FULL_SCAN_ENABLED` | no | Switches the tool to execute [ZAP Full Scan](https://github.com/zaproxy/zaproxy/wiki/ZAP-Full-Scan) instead of [ZAP Baseline Scan](https://github.com/zaproxy/zaproxy/wiki/ZAP-Baseline-Scan). Boolean. `true`, `True`, or `1` are considered as true value, otherwise false. Defaults to `false`. |
+
## Security Dashboard
The Security Dashboard is a good place to get an overview of all the security
diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md
index 91e79f6c23b..56a4cbd26d2 100644
--- a/doc/user/application_security/index.md
+++ b/doc/user/application_security/index.md
@@ -115,9 +115,9 @@ generated by GitLab. To apply the fix:
1. Click on the vulnerability.
1. Download and review the patch file `remediation.patch`.
-2. Ensure your local project has the same commit checked out that was used to generate the patch.
-3. Run `git apply remediation.patch`.
-4. Verify and commit the changes to your branch.
+1. Ensure your local project has the same commit checked out that was used to generate the patch.
+1. Run `git apply remediation.patch`.
+1. Verify and commit the changes to your branch.
![Apply patch for dependency scanning](img/vulnerability_solution.png)
diff --git a/doc/user/application_security/sast/analyzers.md b/doc/user/application_security/sast/analyzers.md
new file mode 100644
index 00000000000..59835aeba01
--- /dev/null
+++ b/doc/user/application_security/sast/analyzers.md
@@ -0,0 +1,143 @@
+---
+table_display_block: true
+---
+
+# SAST Analyzers **(ULTIMATE)**
+
+SAST relies on underlying third party tools that are wrapped into what we call
+"Analyzers". An analyzer is a
+[dedicated project](https://gitlab.com/gitlab-org/security-products/analyzers)
+that wraps a particular tool to:
+
+- Expose its detection logic.
+- Handle its execution.
+- Convert its output to the common format.
+
+This is achieved by implementing the [common API](https://gitlab.com/gitlab-org/security-products/analyzers/common).
+
+SAST supports the following official analyzers:
+
+- [Bandit](https://gitlab.com/gitlab-org/security-products/analyzers/bandit)
+- [Brakeman](https://gitlab.com/gitlab-org/security-products/analyzers/brakeman)
+- [ESLint (Javascript)](https://gitlab.com/gitlab-org/security-products/analyzers/eslint)
+- [SpotBugs with the Find Sec Bugs plugin (Ant, Gradle and wrapper, Grails, Maven and wrapper, SBT)](https://gitlab.com/gitlab-org/security-products/analyzers/spotbugs)
+- [Flawfinder](https://gitlab.com/gitlab-org/security-products/analyzers/flawfinder)
+- [Gosec](https://gitlab.com/gitlab-org/security-products/analyzers/gosec)
+- [NodeJsScan](https://gitlab.com/gitlab-org/security-products/analyzers/nodejs-scan)
+- [PHP CS security-audit](https://gitlab.com/gitlab-org/security-products/analyzers/phpcs-security-audit)
+- [Secrets (Gitleaks, TruffleHog & Diffence secret detectors)](https://gitlab.com/gitlab-org/security-products/analyzers/secrets)
+- [Security Code Scan (.NET)](https://gitlab.com/gitlab-org/security-products/analyzers/security-code-scan)
+- [TSLint (Typescript)](https://gitlab.com/gitlab-org/security-products/analyzers/tslint)
+- [Sobelow (Elixir Phoenix)](https://gitlab.com/gitlab-org/security-products/analyzers/sobelow)
+
+The analyzers are published as Docker images that SAST will use to launch
+dedicated containers for each analysis.
+
+SAST is pre-configured with a set of **default images** that are maintained by
+GitLab, but users can also integrate their own **custom images**.
+
+## Official default analyzers
+
+Any custom change to the official analyzers can be achieved by using an
+[environment variable in your `.gitlab-ci.yml`](index.md#customizing-the-sast-settings).
+
+### Using a custom Docker mirror
+
+You can switch to a custom Docker registry that provides the official analyzer
+images under a different prefix. For instance, the following instructs
+SAST to pull `my-docker-registry/gl-images/bandit`
+instead of `registry.gitlab.com/gitlab-org/security-products/analyzers/bandit`.
+In `.gitlab-ci.yml` define:
+
+```yaml
+include:
+ template: SAST.gitlab-ci.yml
+
+variables:
+ SAST_ANALYZER_IMAGE_PREFIX: my-docker-registry/gl-images
+```
+
+This configuration requires that your custom registry provides images for all
+the official analyzers.
+
+### Selecting specific analyzers
+
+You can select the official analyzers you want to run. Here's how to enable
+`bandit` and `flawfinder` while disabling all the other default ones.
+In `.gitlab-ci.yml` define:
+
+```yaml
+include:
+ template: SAST.gitlab-ci.yml
+
+variables:
+ SAST_DEFAULT_ANALYZERS: "bandit,flawfinder"
+```
+
+`bandit` runs first. When merging the reports, SAST will
+remove the duplicates and will keep the `bandit` entries.
+
+### Disabling default analyzers
+
+Setting `SAST_DEFAULT_ANALYZERS` to an empty string will disable all the official
+default analyzers. In `.gitlab-ci.yml` define:
+
+```yaml
+include:
+ template: SAST.gitlab-ci.yml
+
+variables:
+ SAST_DEFAULT_ANALYZERS: ""
+```
+
+That's needed when one totally relies on [custom analyzers](#custom-analyzers).
+
+## Custom Analyzers
+
+You can provide your own analyzers as a comma separated list of Docker images.
+Here's how to add `analyzers/csharp` and `analyzers/perl` to the default images:
+In `.gitlab-ci.yml` define:
+
+```yaml
+include:
+ template: SAST.gitlab-ci.yml
+
+variables:
+ SAST_ANALYZER_IMAGES: "my-docker-registry/analyzers/csharp,amy-docker-registry/analyzers/perl"
+```
+
+The values must be the full path to the container registry images,
+like what you would feed to the `docker pull` command.
+
+NOTE: **Note:**
+This configuration doesn't benefit from the integrated detection step.
+SAST has to fetch and spawn each Docker image to establish whether the
+custom analyzer can scan the source code.
+
+## Analyzers Data
+
+| Property \ Tool | Bandit | Brakeman | ESLint security | Find Sec Bugs | Flawfinder | Go AST Scanner | NodeJsScan | Php CS Security Audit | Security code Scan (.NET) | TSLint Security | Sobelow |
+| --------------------------------------- | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :---------------------: | :-------------------------: | :-------------: | :----------------: |
+| Severity | ✓ | 𐄂 | 𐄂 | ✓ | 𐄂 | ✓ | 𐄂 | ✓ | 𐄂 | ✓ | 𐄂 |
+| Title | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Description | 𐄂 | 𐄂 | ✓ | ✓ | 𐄂 | 𐄂 | ✓ | 𐄂 | 𐄂 | ✓ | ✓ |
+| File | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Start line | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
+| End line | ✓ | 𐄂 | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | ✓ | 𐄂 |
+| Start column | 𐄂 | 𐄂 | ✓ | ✓ | ✓ | ✓ | 𐄂 | ✓ | ✓ | ✓ | 𐄂 |
+| End column | 𐄂 | 𐄂 | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | ✓ | 𐄂 |
+| External id (e.g. CVE) | 𐄂 | ⚠ | 𐄂 | ⚠ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
+| URLs | 𐄂 | ✓ | 𐄂 | ⚠ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
+| Internal doc/explanation | ⚠ | ✓ | 𐄂 | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | ✓ |
+| Solution | 𐄂 | 𐄂 | 𐄂 | ⚠ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
+| Confidence | ✓ | ✓ | 𐄂 | ✓ | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | ✓ |
+| Affected item (e.g. class or package) | 𐄂 | ✓ | 𐄂 | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
+| Source code extract | ✓ | ✓ | ✓ | 𐄂 | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
+| Internal ID | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 𐄂 | ✓ | ✓ | ✓ | ✓ |
+
+- ✓ => we have that data
+- ⚠ => we have that data but it's partially reliable, or we need to extract it from unstructured content
+- 𐄂 => we don't have that data or it would need to develop specific or inefficient/unreliable logic to obtain it.
+
+The values provided by these tools are heterogeneous so they are sometimes
+normalized into common values (e.g., `severity`, `confidence`, etc).
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index 1f9fd9d4e18..7df86eedd18 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -135,6 +135,58 @@ sast:
CI_DEBUG_TRACE: "true"
```
+### Available variables
+
+SAST can be [configured](#customizing-the-sast-settings) using environment variables.
+
+#### Docker images
+
+The following are Docker image-related variables.
+
+| Environment variable | Description |
+|-------------------------------|--------------------------------------------------------------------------------|
+| `SAST_ANALYZER_IMAGES` | Comma separated list of custom images. Default images are still enabled. Read more about [customizing analyzers](analyzers.md). |
+| `SAST_ANALYZER_IMAGE_PREFIX` | Override the name of the Docker registry providing the default images (proxy). Read more about [customizing analyzers](analyzers.md). |
+| `SAST_ANALYZER_IMAGE_TAG` | Override the Docker tag of the default images. Read more about [customizing analyzers](analyzers.md). |
+| `SAST_DEFAULT_ANALYZERS` | Override the names of default images. Read more about [customizing analyzers](analyzers.md). |
+| `SAST_PULL_ANALYZER_IMAGES` | Pull the images from the Docker registry (set to 0 to disable). Read more about [customizing analyzers](analyzers.md). |
+
+### Vulnerability filters
+
+Some analyzers make it possible to filter out vulnerabilities under a given threshold.
+
+| `SAST_BANDIT_EXCLUDED_PATHS` | - | comma-separated list of paths to exclude from scan. Uses Python's [`fnmatch` syntax](https://docs.python.org/2/library/fnmatch.html) |
+| `SAST_BRAKEMAN_LEVEL` | 1 | Ignore Brakeman vulnerabilities under given confidence level. Integer, 1=Low 3=High. |
+| `SAST_FLAWFINDER_LEVEL` | 1 | Ignore Flawfinder vulnerabilities under given risk level. Integer, 0=No risk, 5=High risk. |
+| `SAST_GITLEAKS_ENTROPY_LEVEL` | 8.0 | Minimum entropy for secret detection. Float, 0.0 = low, 8.0 = high. |
+| `SAST_GOSEC_LEVEL` | 0 | Ignore gosec vulnerabilities under given confidence level. Integer, 0=Undefined, 1=Low, 1=Medium, 3=High. |
+| `SAST_EXCLUDED_PATHS` | - | Exclude vulnerabilities from output based on the paths. This is a comma-separated list of patterns. Patterns can be globs, file or folder paths. Parent directories will also match patterns. |
+
+### Timeouts
+
+The following variables configure timeouts.
+
+| `SAST_DOCKER_CLIENT_NEGOTIATION_TIMEOUT` | 2m | Time limit for Docker client negotiation. Timeouts are parsed using Go's [`ParseDuration`](https://golang.org/pkg/time/#ParseDuration). Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". For example, "300ms", "1.5h" or "2h45m". |
+| `SAST_PULL_ANALYZER_IMAGE_TIMEOUT` | 5m | Time limit when pulling the image of an analyzer. Timeouts are parsed using Go's [`ParseDuration`](https://golang.org/pkg/time/#ParseDuration). Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". For example, "300ms", "1.5h" or "2h45m". |
+| `SAST_RUN_ANALYZER_TIMEOUT` | 20m | Time limit when running an analyzer. Timeouts are parsed using Go's [`ParseDuration`](https://golang.org/pkg/time/#ParseDuration). Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". For example, "300ms", "1.5h" or "2h45m".|
+
+### Analyzer settings
+
+Some analyzers can be customized with environment variables.
+
+| Environment variable | Analyzer | Description |
+|-------------------------|----------|----------|
+| `ANT_HOME` | spotbugs | The `ANT_HOME` environment variable. |
+| `ANT_PATH` | spotbugs | Path to the `ant` executable. |
+| `GRADLE_PATH` | spotbugs | Path to the `gradle` executable. |
+| `JAVA_OPTS` | spotbugs | Additional arguments for the `java` executable. |
+| `JAVA_PATH` | spotbugs | Path to the `java` executable. |
+| `MAVEN_CLI_OPTS` | spotbugs | Additional arguments for the `mvn` or `mvnw` executable. |
+| `MAVEN_PATH` | spotbugs | Path to the `mvn` executable. |
+| `MAVEN_REPO_PATH` | spotbugs | Path to the Maven local repository (shortcut for the `maven.repo.local` property). |
+| `SBT_PATH` | spotbugs | Path to the `sbt` executable. |
+| `FAIL_NEVER` | spotbugs | Set to `1` to ignore compilation failure. |
+
## Reports JSON format
CAUTION: **Caution:**
diff --git a/doc/user/application_security/security_dashboard/index.md b/doc/user/application_security/security_dashboard/index.md
index 3b01fe66e03..2a2385c00ae 100644
--- a/doc/user/application_security/security_dashboard/index.md
+++ b/doc/user/application_security/security_dashboard/index.md
@@ -26,8 +26,8 @@ To use the project or group security dashboard:
1. At least one project inside a group must be configured with at least one of
the [supported reports](#supported-reports).
-2. The configured jobs must use the [new `reports` syntax](../../../ci/yaml/README.md#artifactsreports).
-3. [GitLab Runner](https://docs.gitlab.com/runner/) 11.5 or newer must be used.
+1. The configured jobs must use the [new `reports` syntax](../../../ci/yaml/README.md#artifactsreports).
+1. [GitLab Runner](https://docs.gitlab.com/runner/) 11.5 or newer must be used.
If you're using the shared Runners on GitLab.com, this is already the case.
## Project Security Dashboard
diff --git a/doc/user/clusters/applications.md b/doc/user/clusters/applications.md
index 6956086c382..d5c836554ce 100644
--- a/doc/user/clusters/applications.md
+++ b/doc/user/clusters/applications.md
@@ -19,8 +19,8 @@ This namespace:
To see a list of available applications to install:
1. For a:
- - Project-level cluster, navigate to your project's **Operations > Kubernetes**.
- - Group-level cluster, navigate to your group's **Kubernetes** page.
+ - Project-level cluster, navigate to your project's **Operations > Kubernetes**.
+ - Group-level cluster, navigate to your group's **Kubernetes** page.
Install Helm first as it's used to install other applications.
@@ -232,8 +232,8 @@ The applications below can be upgraded.
To upgrade an application:
1. For a:
- - Project-level cluster, navigate to your project's **Operations > Kubernetes**.
- - Group-level cluster, navigate to your group's **Kubernetes** page.
+ - Project-level cluster, navigate to your project's **Operations > Kubernetes**.
+ - Group-level cluster, navigate to your group's **Kubernetes** page.
1. Select your cluster.
1. If an upgrade is available, the **Upgrade** button is displayed. Click the button to upgrade.
@@ -259,8 +259,8 @@ The applications below can be uninstalled.
To uninstall an application:
1. For a:
- - Project-level cluster, navigate to your project's **Operations > Kubernetes**.
- - Group-level cluster, navigate to your group's **Kubernetes** page.
+ - Project-level cluster, navigate to your project's **Operations > Kubernetes**.
+ - Group-level cluster, navigate to your group's **Kubernetes** page.
1. Select your cluster.
1. Click the **Uninstall** button for the application.
diff --git a/doc/user/group/bulk_editing/img/bulk-editing.png b/doc/user/group/bulk_editing/img/bulk-editing.png
new file mode 100644
index 00000000000..d08503dc312
--- /dev/null
+++ b/doc/user/group/bulk_editing/img/bulk-editing.png
Binary files differ
diff --git a/doc/user/group/bulk_editing/index.md b/doc/user/group/bulk_editing/index.md
new file mode 100644
index 00000000000..117a46da0ea
--- /dev/null
+++ b/doc/user/group/bulk_editing/index.md
@@ -0,0 +1,22 @@
+# Bulk editing issue milestones **(PREMIUM)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7249) in
+[GitLab Ultimate](https://about.gitlab.com/pricing/) 12.1.
+
+NOTE: **Note:**
+A permission level of `Reporter` or higher is required in order to manage issues.
+
+Milestones can be updated simultaneously across multiple issues by using the bulk editing feature.
+
+![Bulk editing](img/bulk-editing.png)
+
+To bulk update group issue milestones:
+
+1. Navigate to the issues list.
+1. Click **Edit issues**.
+ - This will open a sidebar on the right-hand side of your screen where an editable field
+ for milestones will be displayed.
+ - Checkboxes will also appear beside each issue.
+1. Check the checkbox beside each issue to be edited.
+1. Select the desired milestone from the sidebar.
+1. Click **Update all**.
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index db348c678eb..7597105f36f 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -78,6 +78,10 @@ Issues and merge requests are part of projects. For a given group, you can view
[issues](../project/issues/index.md#issues-list) and [merge requests](../project/merge_requests/index.md#merge-requests-per-group) across all projects in that group,
together in a single list view.
+### Bulk editing issues
+
+For details, see [bulk editing issues](../group/bulk_editing/index.md).
+
## Create a new group
> For a list of words that are not allowed to be used as group names see the
diff --git a/doc/user/group/saml_sso/img/group_saml_settings.png b/doc/user/group/saml_sso/img/group_saml_settings.png
index d95acb5075f..8c5dbe36f98 100644
--- a/doc/user/group/saml_sso/img/group_saml_settings.png
+++ b/doc/user/group/saml_sso/img/group_saml_settings.png
Binary files differ
diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md
index 54923ab69ff..e3f657af564 100644
--- a/doc/user/group/saml_sso/index.md
+++ b/doc/user/group/saml_sso/index.md
@@ -71,7 +71,7 @@ Once you've set up your identity provider to work with GitLab, you'll need to co
1. Navigate to the group's **Settings > SAML SSO**.
1. Find the SSO URL from your Identity Provider and enter it the **Identity provider single sign on URL** field.
1. Find and enter the fingerprint for the SAML token signing certificate in the **Certificate** field.
-1. Check the **Enable SAML authentication for this group** checkbox.
+1. Click the **Enable SAML authentication for this group** toggle switch.
1. Click the **Save changes** button.
![Group SAML Settings for GitLab.com](img/group_saml_settings.png)
diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md
index 1c6cca049c5..2eb3fae1a85 100644
--- a/doc/user/group/subgroups/index.md
+++ b/doc/user/group/subgroups/index.md
@@ -28,39 +28,39 @@ only 1 parent group. It resembles a directory behavior or a nested items list:
- Group 1
- Group 1.1
- Group 1.2
- - Group 1.2.1
- - Group 1.2.2
- - Group 1.2.2.1
+ - Group 1.2.1
+ - Group 1.2.2
+ - 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 distribution, and subsequent groups split as follows:
- Organization Group - GNU/Linux distro
- Category Subgroup - Packages
- - (project) Package01
- - (project) Package02
+ - (project) Package01
+ - (project) Package02
- Category Subgroup - Software
- - (project) Core
- - (project) CLI
- - (project) Android app
- - (project) iOS app
+ - (project) Core
+ - (project) CLI
+ - (project) Android app
+ - (project) iOS app
- Category Subgroup - Infra tools
- - (project) Ansible playbooks
+ - (project) Ansible playbooks
Another example of GitLab as a company would be the following:
- Organization Group - GitLab
- Category Subgroup - Marketing
- - (project) Design
- - (project) General
+ - (project) Design
+ - (project) General
- Category Subgroup - Software
- - (project) GitLab CE
- - (project) GitLab EE
- - (project) Omnibus GitLab
- - (project) GitLab Runner
- - (project) GitLab Pages daemon
+ - (project) GitLab CE
+ - (project) GitLab EE
+ - (project) Omnibus GitLab
+ - (project) GitLab Runner
+ - (project) GitLab Pages daemon
- Category Subgroup - Infra tools
- - (project) Chef cookbooks
+ - (project) Chef cookbooks
- Category Subgroup - Executive team
---
@@ -74,27 +74,37 @@ structure.
## Creating a subgroup
-NOTE: **Note:**
-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
+To create a subgroup you must either be an Owner or a Maintainer of the
+group, depending on the group's setting.
+
+By default, groups created in:
+
+- GitLab 12.2 or later allow both Owners and Maintainers to create subgroups.
+- GitLab 12.1 or earlier only allow Owners to create subgroups.
+
+This setting can be for any group by an Owner or Administrator.
+
+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
-settings.
+
+Users can always create subgroups if they are explicitly added as an Owner (or
+Maintainer, if that setting is enabled) to a parent group, even if group
+creation is disabled by an administrator in their settings.
To create a subgroup:
1. In the group's dashboard expand the **New project** split button, select
**New subgroup** and click the **New subgroup** button.
- ![Subgroups page](img/create_subgroup_button.png)
+ ![Subgroups page](img/create_subgroup_button.png)
1. Create a new group like you would normally do. Notice that the parent group
namespace is fixed under **Group path**. The visibility level can differ from
the parent group.
- ![Subgroups page](img/create_new_group.png)
+ ![Subgroups page](img/create_new_group.png)
1. Click the **Create group** button and you will be taken to the new group's
dashboard page.
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index 96e74125801..37f3f21f539 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -1160,13 +1160,15 @@ Examples:
+ Or pluses
```
+<!-- The "2." and "4." in the example above are changed to "1." below, only to match the standards on docs.gitlab.com -->
+
1. First ordered list item
-2. Another item
+1. Another item
- Unordered sub-list.
1. Actual numbers don't matter, just that it's a number
1. Ordered sub-list
1. Next ordered sub-list item
-4. And another item.
+1. And another item.
* Unordered lists can use asterisks
- Or minuses
@@ -1184,14 +1186,14 @@ Example:
Second paragraph of first item.
-2. Another item
+1. Another item
```
1. First ordered list item
Second paragraph of first item.
-2. Another item
+1. Another item
---
@@ -1205,14 +1207,14 @@ Example:
Paragraph of first item.
-2. Another item
+1. Another item
```
1. First ordered list item
Paragraph of first item.
-2. Another item
+1. Another item
### Superscripts / Subscripts
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 619cf34b5c3..044be05b932 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -209,13 +209,16 @@ group.
| Create project in group | | | ✓ | ✓ | ✓ |
| Create/edit/delete group milestones | | | ✓ | ✓ | ✓ |
| Enable/disable a dependency proxy **(PREMIUM)** | | | ✓ | ✓ | ✓ |
+| Create subgroup | | | | ✓ (1) | ✓ |
| Edit group | | | | | ✓ |
-| Create subgroup | | | | | ✓ |
| Manage group members | | | | | ✓ |
| Remove group | | | | | ✓ |
| Delete group epic **(ULTIMATE)** | | | | | ✓ |
| View group Audit Events | | | | | ✓ |
+- (1): Groups can be set to [allow either Owners or Owners and
+ Maintainers to create subgroups](group/subgroups/index.md#creating-a-subgroup)
+
### Subgroup permissions
When you add a member to a subgroup, they inherit the membership and
diff --git a/doc/user/project/badges.md b/doc/user/project/badges.md
index 8849dd2d684..cd3e5f5a63c 100644
--- a/doc/user/project/badges.md
+++ b/doc/user/project/badges.md
@@ -17,10 +17,10 @@ If you find that you have to add the same badges to several projects, you may wa
To add a new badge to a project:
-1. Navigate to your project's **Settings > General > Badges**.
-1. Under "Link", enter the URL that the badges should point to and under
- "Badge image URL" the URL of the image that should be displayed.
-1. Submit the badge by clicking the **Add badge** button.
+1. Navigate to your project's **Settings > General > Badges**.
+1. Under "Link", enter the URL that the badges should point to and under
+ "Badge image URL" the URL of the image that should be displayed.
+1. Submit the badge by clicking the **Add badge** button.
After adding a badge to a project, you can see it in the list below the form.
You can edit it by clicking on the pen icon next to it or to delete it by
@@ -39,10 +39,10 @@ project, consider adding them on the [project level](#project-badges) or use
To add a new badge to a group:
-1. Navigate to your group's **Settings > General > Badges**.
-1. Under "Link", enter the URL that the badges should point to and under
- "Badge image URL" the URL of the image that should be displayed.
-1. Submit the badge by clicking the **Add badge** button.
+1. Navigate to your group's **Settings > General > Badges**.
+1. Under "Link", enter the URL that the badges should point to and under
+ "Badge image URL" the URL of the image that should be displayed.
+1. Submit the badge by clicking the **Add badge** button.
After adding a badge to a group, you can see it in the list below the form.
You can edit the badge by clicking on the pen icon next to it or to delete it
diff --git a/doc/user/project/bulk_editing.md b/doc/user/project/bulk_editing.md
index d0c7daf4692..1783f81df3a 100644
--- a/doc/user/project/bulk_editing.md
+++ b/doc/user/project/bulk_editing.md
@@ -13,13 +13,20 @@ by using the bulk editing feature.
![Bulk editing](img/bulk-editing.png)
NOTE: **Note:**
-Bulk editing of issues and merge requests is only available at the project level.
+Bulk editing of merge requests is only available at the project level.
+For more details, see [bulk editing group issues](../group/bulk_editing/index.md).
-To update multiple project issues or merge requests at the same time, navigate to
-their respective lists and click **Edit issues** or **Edit merge requests** available
-in the tab bar. This will open a sidebar on the right-hand side of your screen
-where editable fields will be displayed. Checkboxes will also appear to the left-hand
-side of eachissue or merge request for you to select the items you want to update.
+To update multiple project issues or merge requests at the same time:
-Once you have selected all relevant items, choose the appropriate fields and their
-values from the sidebar and click **Update all** to apply your changes.
+1. Navigate to their respective list.
+
+1. Click **Edit issues** or **Edit merge requests**.
+
+ - This will open a sidebar on the right-hand side of your screen
+ where editable fields will be displayed.
+
+ - Checkboxes will also appear beside each issue or merge request.
+
+1. Check the checkboxes of each items to be edited.
+1. Choose the appropriate fields and their values from the sidebar.
+1. Click **Update all**.
diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md
index 14ee6303bf9..a32759c7bdc 100644
--- a/doc/user/project/clusters/serverless/index.md
+++ b/doc/user/project/clusters/serverless/index.md
@@ -41,8 +41,7 @@ To run Knative on Gitlab, you will need:
external IP address or hostname for that domain.
1. **`.gitlab-ci.yml`:** GitLab uses [Kaniko](https://github.com/GoogleContainerTools/kaniko)
to build the application. We also use [gitlabktl](https://gitlab.com/gitlab-org/gitlabktl)
- and [TriggerMesh CLI](https://github.com/triggermesh/tm) CLIs to simplify the
- deployment of services and functions to Knative.
+ CLI to simplify the deployment of services and functions to Knative.
1. **`serverless.yml`** (for [functions only](#deploying-functions)): When using serverless to deploy functions, the `serverless.yml` file
will contain the information for all the functions being hosted in the repository as well as a reference to the
runtime being used.
@@ -249,7 +248,7 @@ Explanation of the fields used above:
| Parameter | Description |
|-----------|-------------|
-| `name` | Indicates which provider is used to execute the `serverless.yml` file. In this case, the TriggerMesh `tm` CLI. |
+| `name` | Indicates which provider is used to execute the `serverless.yml` file. In this case, the TriggerMesh middleware. |
| `environment` | Includes the environment variables to be passed as part of function execution for **all** functions in the file, where `FOO` is the variable name and `BAR` are he variable contents. You may replace this with you own variables. |
### `functions`
@@ -343,27 +342,23 @@ Go to the **CI/CD > Pipelines** and click on the pipeline that deployed your app
The output will look like this:
```bash
-Running with gitlab-runner 11.5.0~beta.844.g96d88322 (96d88322)
- on docker-auto-scale 72989761
-Using Docker executor with image gcr.io/triggermesh/tm@sha256:e3ee74db94d215bd297738d93577481f3e4db38013326c90d57f873df7ab41d5 ...
-Pulling docker image gcr.io/triggermesh/tm@sha256:e3ee74db94d215bd297738d93577481f3e4db38013326c90d57f873df7ab41d5 ...
-Using docker image sha256:6b3f6590a9b30bd7aafb9573f047d930c70066e43955b4beb18a1eee175f6de1 for gcr.io/triggermesh/tm@sha256:e3ee74db94d215bd297738d93577481f3e4db38013326c90d57f873df7ab41d5 ...
-Running on runner-72989761-project-4342902-concurrent-0 via runner-72989761-stg-srm-1541795796-27929c96...
-Cloning repository...
-Cloning into '/builds/danielgruesso/knative'...
-Checking out 8671ad20 as master...
-Skipping Git submodules setup
-$ echo "$CI_REGISTRY_IMAGE"
-registry.staging.gitlab.com/danielgruesso/knative
-$ tm -n "$KUBE_NAMESPACE" --config "$KUBECONFIG" deploy service "$CI_PROJECT_NAME" --from-image "$CI_REGISTRY_IMAGE" --wait
-Deployment started. Run "tm -n knative-4342902 describe service knative" to see the details
-Waiting for ready state.......
-Service domain: knative.knative-4342902.example.com
+Running with gitlab-runner 12.1.0-rc1 (6da35412)
+ on prm-com-gitlab-org ae3bfce3
+Using Docker executor with image registry.gitlab.com/gitlab-org/gitlabktl:latest ...
+Running on runner-ae3bfc-concurrent-0 via runner-ae3bfc ...
+Fetching changes...
+Authenticating with credentials from job payload (GitLab Registry)
+$ /usr/bin/gitlabktl application deploy
+Welcome to gitlabktl tool
+time="2019-07-15T10:51:07Z" level=info msg="deploying registry credentials"
+Creating app-hello function
+Waiting for app-hello ready state
+Service app-hello URL: http://app-hello.serverless.example.com
Job succeeded
```
-The second to last line, labeled **Service domain** contains the URL for the deployment. Copy and paste the domain into your
-browser to see the app live.
+The second to last line, labeled **Service domain** contains the URL for the
+deployment. Copy and paste the domain into your browser to see the app live.
![knative app](img/knative-app.png)
diff --git a/doc/user/project/cycle_analytics.md b/doc/user/project/cycle_analytics.md
index 5d36e1d4be3..6707b88c317 100644
--- a/doc/user/project/cycle_analytics.md
+++ b/doc/user/project/cycle_analytics.md
@@ -21,19 +21,19 @@ Analytics** tab.
There are seven stages that are tracked as part of the Cycle Analytics calculations.
- **Issue** (Tracker)
- - Time to schedule an issue (by milestone or by adding it to an issue board)
+ - Time to schedule an issue (by milestone or by adding it to an issue board)
- **Plan** (Board)
- - Time to first commit
+ - Time to first commit
- **Code** (IDE)
- - Time to create a merge request
+ - Time to create a merge request
- **Test** (CI)
- - Time it takes GitLab CI/CD to test your code
+ - Time it takes GitLab CI/CD to test your code
- **Review** (Merge Request/MR)
- - Time spent on code review
+ - Time spent on code review
- **Staging** (Continuous Deployment)
- - Time between merging and deploying to production
+ - Time between merging and deploying to production
- **Production** (Total)
- - Total lifecycle time; i.e. the velocity of the project or team
+ - Total lifecycle time; i.e. the velocity of the project or team
## How the data is measured
diff --git a/doc/user/project/img/autocomplete_characters_example1_v12_0.png b/doc/user/project/img/autocomplete_characters_example1_v12_0.png
index 9c6fa923b80..9c6fa923b80 100755..100644
--- a/doc/user/project/img/autocomplete_characters_example1_v12_0.png
+++ b/doc/user/project/img/autocomplete_characters_example1_v12_0.png
Binary files differ
diff --git a/doc/user/project/img/autocomplete_characters_example2_v12_0.png b/doc/user/project/img/autocomplete_characters_example2_v12_0.png
index b2e8a782a0b..b2e8a782a0b 100755..100644
--- a/doc/user/project/img/autocomplete_characters_example2_v12_0.png
+++ b/doc/user/project/img/autocomplete_characters_example2_v12_0.png
Binary files differ
diff --git a/doc/user/project/import/svn.md b/doc/user/project/import/svn.md
index 7359487e1bf..d175ee87f26 100644
--- a/doc/user/project/import/svn.md
+++ b/doc/user/project/import/svn.md
@@ -9,13 +9,13 @@ between the two, for more information consult your favorite search engine.
There are two approaches to SVN to Git migration:
1. [Git/SVN Mirror](#smooth-migration-with-a-gitsvn-mirror-using-subgit) which:
- - Makes the GitLab repository to mirror the SVN project.
- - Git and SVN repositories are kept in sync; you can use either one.
- - Smoothens the migration process and allows to manage migration risks.
+ - Makes the GitLab repository to mirror the SVN project.
+ - Git and SVN repositories are kept in sync; you can use either one.
+ - Smoothens the migration process and allows to manage migration risks.
1. [Cut over migration](#cut-over-migration-with-svn2git) which:
- - Translates and imports the existing data and history from SVN to Git.
- - Is a fire and forget approach, good for smaller teams.
+ - Translates and imports the existing data and history from SVN to Git.
+ - Is a fire and forget approach, good for smaller teams.
## Smooth migration with a Git/SVN mirror using SubGit
diff --git a/doc/user/project/index.md b/doc/user/project/index.md
index 7307c5b8991..45e96437517 100644
--- a/doc/user/project/index.md
+++ b/doc/user/project/index.md
@@ -66,15 +66,15 @@ When you create a project in GitLab, you'll have access to a large number of
to automatically set up your app's deployment
- [Enable and disable GitLab CI](../../ci/enable_or_disable_ci.md)
- [Pipelines](../../ci/pipelines.md): Configure and visualize
- your GitLab CI/CD pipelines from the UI
- - [Scheduled Pipelines](pipelines/schedules.md): Schedule a pipeline
- to start at a chosen time
- - [Pipeline Graphs](../../ci/pipelines.md#visualizing-pipelines): View your
- entire pipeline from the UI
- - [Job artifacts](pipelines/job_artifacts.md): Define,
- browse, and download job artifacts
- - [Pipeline settings](pipelines/settings.md): Set up Git strategy (choose the default way your repository is fetched from GitLab in a job),
- timeout (defines the maximum amount of time in minutes that a job is able run), custom path for `.gitlab-ci.yml`, test coverage parsing, pipeline's visibility, and much more
+ your GitLab CI/CD pipelines from the UI
+ - [Scheduled Pipelines](pipelines/schedules.md): Schedule a pipeline
+ to start at a chosen time
+ - [Pipeline Graphs](../../ci/pipelines.md#visualizing-pipelines): View your
+ entire pipeline from the UI
+ - [Job artifacts](pipelines/job_artifacts.md): Define,
+ browse, and download job artifacts
+ - [Pipeline settings](pipelines/settings.md): Set up Git strategy (choose the default way your repository is fetched from GitLab in a job),
+ timeout (defines the maximum amount of time in minutes that a job is able run), custom path for `.gitlab-ci.yml`, test coverage parsing, pipeline's visibility, and much more
- [Kubernetes cluster integration](clusters/index.md): Connecting your GitLab project
with a Kubernetes cluster
- [Feature Flags](operations/feature_flags.md): Feature flags allow you to ship a project in
diff --git a/doc/user/project/integrations/img/prometheus_dashboard_area_panel_type.png b/doc/user/project/integrations/img/prometheus_dashboard_area_panel_type.png
new file mode 100644
index 00000000000..7260b11f07b
--- /dev/null
+++ b/doc/user/project/integrations/img/prometheus_dashboard_area_panel_type.png
Binary files differ
diff --git a/doc/user/project/integrations/img/prometheus_dashboard_single_stat_panel_type.png b/doc/user/project/integrations/img/prometheus_dashboard_single_stat_panel_type.png
new file mode 100644
index 00000000000..ce4c54f909d
--- /dev/null
+++ b/doc/user/project/integrations/img/prometheus_dashboard_single_stat_panel_type.png
Binary files differ
diff --git a/doc/user/project/integrations/mock_ci.md b/doc/user/project/integrations/mock_ci.md
index 1c64b275d6e..886094a6531 100644
--- a/doc/user/project/integrations/mock_ci.md
+++ b/doc/user/project/integrations/mock_ci.md
@@ -5,9 +5,9 @@
To set up the mock CI service server, respond to the following endpoints
- `commit_status`: `#{project.namespace.path}/#{project.path}/status/#{sha}.json`
- - Have your service return `200 { status: ['failed'|'canceled'|'running'|'pending'|'success'|'success-with-warnings'|'skipped'|'not_found'] }`
+ - Have your service return `200 { status: ['failed'|'canceled'|'running'|'pending'|'success'|'success-with-warnings'|'skipped'|'not_found'] }`
- If the service returns a 404, it is interpreted as `pending`
- `build_page`: `#{project.namespace.path}/#{project.path}/status/#{sha}`
- - Just where the build is linked to, doesn't matter if implemented
+ - Just where the build is linked to, doesn't matter if implemented
For an example of a mock CI server, see [`gitlab-org/gitlab-mock-ci-service`](https://gitlab.com/gitlab-org/gitlab-mock-ci-service)
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index 72f12972596..61c30e0b0ef 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -191,7 +191,7 @@ The following tables outline the details of expected properties.
| Property | Type | Required | Description |
| ------ | ------ | ------ | ------- |
-| `type` | enum | no, defaults to `area-chart` | Specifies the chart type to use. Only `area-chart` is currently supported. |
+| `type` | enum | no, defaults to `area-chart` | Specifies the chart type to use. |
| `title` | string | yes | Heading for the panel. |
| `y_label` | string | no, but highly encouraged | Y-Axis label for the panel. |
| `weight` | number | no, defaults to order in file | Order to appear within the grouping. Lower number means higher priority, which will be higher on the page. Numbers do not need to be consecutive. |
@@ -207,6 +207,65 @@ The following tables outline the details of expected properties.
| `query` | string | yes if `query_range` is not defined | Defines the Prometheus query to be used to populate the chart/panel. If defined, the `query` endpoint of the [Prometheus API](https://prometheus.io/docs/prometheus/latest/querying/api/) will be utilized. |
| `query_range` | string | yes if `query` is not defined | Defines the Prometheus query to be used to populate the chart/panel. If defined, the `query_range` endpoint of the [Prometheus API](https://prometheus.io/docs/prometheus/latest/querying/api/) will be utilized. |
+#### Panel types for dashboards
+
+The below panel types are supported in monitoring dashboards.
+
+##### Area
+
+To add an area panel type to a dashboard, look at the following sample dashboard file:
+
+```yaml
+dashboard: 'Dashboard Title'
+panel_groups:
+ - group: 'Group Title'
+ panels:
+ - type: area-chart
+ title: "Chart Title"
+ y_label: "Y-Axis"
+ metrics:
+ - id: 10
+ query_range: 'http_requests_total'
+ label: "Metric of Ages"
+ unit: "count"
+```
+
+Note the following properties:
+
+| Property | Type | Required | Description |
+| ------ | ------ | ------ | ------ |
+| type | string | no | Type of panel to be rendered. Optional for area panel types |
+| query_range | yes | required | For area panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
+
+![area panel type](img/prometheus_dashboard_area_panel_type.png)
+
+##### Single Stat
+
+To add a single stat panel type to a dashboard, look at the following sample dashboard file:
+
+```yaml
+dashboard: 'Dashboard Title'
+panel_groups:
+ - group: 'Group Title'
+ panels:
+ - title: "Single Stat"
+ type: "single-stat"
+ metrics:
+ - id: 10
+ query: 'max(go_memstats_alloc_bytes{job="prometheus"})'
+ unit: MB
+ label: "Total"
+```
+
+Note the following properties:
+
+| Property | Type | Required | Description |
+| ------ | ------ | ------ | ------ |
+| type | string | yes | Type of panel to be rendered. For single stat panel types, set to `single-stat` |
+| query | string | yes | For single stat panel types, you must use an [instant query](https://prometheus.io/docs/prometheus/latest/querying/api/#instant-queries) |
+
+![single stat panel type](img/prometheus_dashboard_single_stat_panel_type.png)
+
### Setting up alerts for Prometheus metrics **(ULTIMATE)**
#### Managed Prometheus instances
diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md
index 30940b65454..84adb9637fc 100644
--- a/doc/user/project/integrations/webhooks.md
+++ b/doc/user/project/integrations/webhooks.md
@@ -58,13 +58,13 @@ Navigate to the webhooks page by going to your project's
If you are writing your own endpoint (web server) that will receive
GitLab webhooks keep in mind the following things:
-- Your endpoint should send its HTTP response as fast as possible. If
- you wait too long, GitLab may decide the hook failed and retry it.
-- Your endpoint should ALWAYS return a valid HTTP response. If you do
- not do this then GitLab will think the hook failed and retry it.
- Most HTTP libraries take care of this for you automatically but if
- you are writing a low-level hook this is important to remember.
-- GitLab ignores the HTTP status code returned by your endpoint.
+- Your endpoint should send its HTTP response as fast as possible. If
+ you wait too long, GitLab may decide the hook failed and retry it.
+- Your endpoint should ALWAYS return a valid HTTP response. If you do
+ not do this then GitLab will think the hook failed and retry it.
+ Most HTTP libraries take care of this for you automatically but if
+ you are writing a low-level hook this is important to remember.
+- GitLab ignores the HTTP status code returned by your endpoint.
## Secret token
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index 3eb5581912a..eaca5f8cfb8 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -143,10 +143,10 @@ Create lists for each of your team members and quickly drag-and-drop issues onto
- **Issue Board** - Each board represents a unique view for your issues. It can have multiple lists with each list consisting of issues represented by cards.
- **List** - A column on the issue board that displays issues matching certain attributes. In addition to the default lists of 'Open' and 'Closed' issue, each additional list will show issues matching your chosen label or assignee. On the top of that list you can see the number of issues that belong to it.
- - **Label list**: a list based on a label. It shows all opened issues with that label.
- - **Assignee list**: a list which includes all issues assigned to a user.
- - **Open** (default): shows all open issues that do not belong to one of the other lists. Always appears as the leftmost list.
- - **Closed** (default): shows all closed issues. Always appears as the rightmost list.
+ - **Label list**: a list based on a label. It shows all opened issues with that label.
+ - **Assignee list**: a list which includes all issues assigned to a user.
+ - **Open** (default): shows all open issues that do not belong to one of the other lists. Always appears as the leftmost list.
+ - **Closed** (default): shows all closed issues. Always appears as the rightmost list.
- **Card** - A box in the list that represents an individual issue. The information you can see on a card consists of the issue number, the issue title, the assignee, and the labels associated with the issue. You can drag cards from one list to another to change their label or assignee from that of the source list to that of the destination list.
## Permissions
@@ -386,6 +386,10 @@ a given board inside your GitLab instance, any time those two issues are subsequ
loaded in any board in the same instance (could be a different project board or a different group board, for example),
that ordering will be maintained.
+This ordering also affects [issue lists](issues/sorting_issue_lists.md).
+Changing the order in an issue board changes the ordering in an issue list,
+and vice versa.
+
### Filtering issues
You should be able to use the filters on top of your Issue Board to show only
diff --git a/doc/user/project/issues/confidential_issues.md b/doc/user/project/issues/confidential_issues.md
index 2c755e0fb4d..c10ef564f0d 100644
--- a/doc/user/project/issues/confidential_issues.md
+++ b/doc/user/project/issues/confidential_issues.md
@@ -77,3 +77,49 @@ project's search results respectively.
| Maintainer access | Guest access |
| :-----------: | :----------: |
| ![Confidential issues search master](img/confidential_issues_search_master.png) | ![Confidential issues search guest](img/confidential_issues_search_guest.png) |
+
+## Merge Requests for Confidential Issues
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/58583) in GitLab 12.1.
+
+To help prevent confidential information being leaked from a public project
+in the process of resolving a confidential issue, confidential issues can be
+resolved by creating a merge request from a private fork.
+
+The merge request created will target the default branch of the private fork,
+not the default branch of the public upstream project. This prevents the merge
+request, branch, and commits entering the public repository, and revealing
+confidential information prematurely. When the confidential commits are ready
+to be made public, this can be done by opening a merge request from the private
+fork to the public upstream project.
+
+TIP: **Best practice:**
+If you create a long-lived private fork in the same group or in a sub-group of
+the original upstream, all the users with Developer membership to the public
+project will also have the same permissions in the private project. This way,
+all the Developers, who have access to view confidential issues, will have a
+streamlined workflow for fixing them.
+
+### How it works
+
+On a confidential issue, a **Create confidential merge request** button is
+available. Clicking on it will open a dropdown where you can choose to
+**Create confidential merge request and branch** or **Create branch**:
+
+| Create confidential merge request | Create branch |
+| :-------------------------------: | :-----------: |
+| ![Create Confidential Merge Request Dropdown](img/confidential_mr_dropdown_v12_1.png) | ![Create Confidential Branch Dropdown](img/confidential_mr_branch_dropdown_v12_1.png) |
+
+The **Project** dropdown includes the list of private forks the user is a member
+of as at least a Developer and merge requests are enabled.
+
+Whenever the **Branch name** and **Source (branch or tag)** fields change, the
+availability of the target or source branch will be checked. Both branches should
+be available in the private fork selected.
+
+By clicking the **Create confidential merge request** button, GitLab will create
+the branch and merge request in the private fork. When you choose
+**Create branch**, GitLab will only create the branch.
+
+Once the branch is created in the private fork, developers can now push code to
+that branch to fix the confidential issue.
diff --git a/doc/user/project/issues/img/confidential_mr_branch_dropdown_v12_1.png b/doc/user/project/issues/img/confidential_mr_branch_dropdown_v12_1.png
new file mode 100644
index 00000000000..7c24226a6c4
--- /dev/null
+++ b/doc/user/project/issues/img/confidential_mr_branch_dropdown_v12_1.png
Binary files differ
diff --git a/doc/user/project/issues/img/confidential_mr_dropdown_v12_1.png b/doc/user/project/issues/img/confidential_mr_dropdown_v12_1.png
new file mode 100644
index 00000000000..d6d391c6dd9
--- /dev/null
+++ b/doc/user/project/issues/img/confidential_mr_dropdown_v12_1.png
Binary files differ
diff --git a/doc/user/project/issues/index.md b/doc/user/project/issues/index.md
index e917697e973..6706e1c2e2b 100644
--- a/doc/user/project/issues/index.md
+++ b/doc/user/project/issues/index.md
@@ -89,7 +89,7 @@ Key actions for Issues include:
![Issue view](img/issues_main_view.png)
On an issue's page, you can view [all aspects of the issue](issue_data_and_actions.md),
-and modify them if you you have the necessary [permissions](../../permissions.md).
+and modify them if you have the necessary [permissions](../../permissions.md).
### Issues list
@@ -104,7 +104,8 @@ view, you can also make certain changes [in bulk](../bulk_editing.md) to the dis
For more information, see the [Issue Data and Actions](issue_data_and_actions.md) page
for a rundown of all the fields and information in an issue.
-For sorting by issue priority, see [Label Priority](../labels.md#label-priority).
+You can sort a list of issues several ways, including by issue creation date, milestone due date,
+etc. For more information, see the [Sorting and Ordering Issue Lists](sorting_issue_lists.md) page.
### Issue boards
diff --git a/doc/user/project/issues/managing_issues.md b/doc/user/project/issues/managing_issues.md
index 709588959c1..e9b4e591384 100644
--- a/doc/user/project/issues/managing_issues.md
+++ b/doc/user/project/issues/managing_issues.md
@@ -7,7 +7,9 @@ you can do with issues.
## Create a new Issue
-When you create a new issue, you'll be prompted to fill in the [data and fields of the issue](issue_data_and_actions.md#parts-of-an-issue), as illustrated below.
+When you create a new issue, you'll be prompted to fill in the [data and fields of the issue](issue_data_and_actions.md#parts-of-an-issue), as illustrated below. If you know
+the values you want to assign to an issue, you can use the [Quick actions](../quick_actions.md)
+feature to input values, instead of selecting them from lists.
![New issue from the issues list](img/new_issue.png)
diff --git a/doc/user/project/issues/sorting_issue_lists.md b/doc/user/project/issues/sorting_issue_lists.md
new file mode 100644
index 00000000000..ba120783430
--- /dev/null
+++ b/doc/user/project/issues/sorting_issue_lists.md
@@ -0,0 +1,30 @@
+# Sorting and Ordering Issue Lists
+
+You can sort a list of issues several ways, including by issue creation date, milestone due date,
+etc. The available sorting options can change based on the context of the list.
+For sorting by issue priority, see [Label Priority](../labels.md#label-priority).
+
+In group and project issue lists, it is also possible to order issues manually,
+similar to [issue boards](../issue_board.md#issue-ordering-in-a-list).
+
+## Manual sorting
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/62178) in GitLab 12.1.
+
+When you select **Manual** sorting, you can change
+the order by dragging and dropping the issues. The changed order will persist. Everyone who visits the same list will see the reordered list, with some exceptions.
+
+Each issue is assigned a relative order value, representing its relative
+order with respect to the other issues in the list. When you drag-and-drop reorder
+an issue, its relative order value changes accordingly.
+
+In addition, any time that issue appears in a manually sorted list,
+the updated relative order value will be used for the ordering. This means that
+if issue `A` is drag-and-drop reordered to be above issue `B` by any user in
+a given list inside your GitLab instance, any time those two issues are subsequently
+loaded in any list in the same instance (could be a different project issue list or a
+different group issue list, for example), that ordering will be maintained.
+
+This ordering also affects [issue boards](../issue_board.md#issue-ordering-in-a-list).
+Changing the order in an issue list changes the ordering in an issue board,
+and vice versa.
diff --git a/doc/user/project/merge_requests/blocking_merge_requests.md b/doc/user/project/merge_requests/blocking_merge_requests.md
new file mode 100644
index 00000000000..0506a7cb4a5
--- /dev/null
+++ b/doc/user/project/merge_requests/blocking_merge_requests.md
@@ -0,0 +1,133 @@
+---
+type: reference, concepts
+---
+
+# Blocking merge requests **(PREMIUM)**
+
+> Introduced in GitLab Premium 12.2
+
+Blocking merge requests allow dependencies between MRs to be expressed. If a
+merge request is blocked by another MR, it cannot be merged until that blocking
+MR is itself merged.
+
+NOTE: **Note:**
+Blocking merge requests are a **PREMIUM** feature, but this restriction is only
+enforced for the blocked merge request. A merge request in a **CORE** or
+**STARTER** project can block a **PREMIUM** merge request, but not vice-versa.
+
+## Use cases
+
+* Ensure changes to a library are merged before changes to a project that
+ imports the library
+* Prevent a documentation-only merge request from being merged before the MR
+ implementing the feature to be documented
+* Require an MR updating a permissions matrix to be merged before merging an
+ MR from someone who hasn't yet been granted permissions
+
+It is common for a single logical change to span several merge requests. These
+MRs may all be in a single project, or they may be spread out across multiple
+projects, and the order in which they are merged can be significant.
+
+For example, given a project `mycorp/awesome-project` that imports a library
+at `myfriend/awesome-lib`, adding a feature in `awesome-project` may **also**
+require changes to `awesome-lib`, and so necessitate two merge requests. Merging
+the `awesome-project` MR before the `awesome-lib` one would break the `master`
+branch.
+
+The `awesome-project` MR could be [marked as WIP](work_in_progress_merge_requests.md),
+and the reason for the WIP stated included in the comments. However, this
+requires the state of the `awesome-lib` MR to be manually tracked, and doesn't
+scale well if the `awesome-project` MR depends on changes to **several** other
+projects.
+
+By marking the `awesome-project` MR as blocked on the `awesome-lib` MR instead,
+the status of the dependency is automatically tracked by GitLab, and the WIP
+state can be used to communicate the readiness of the code in each individual
+MR instead.
+
+## Configuration
+
+To continue the above example, you can configure a block when creating the
+new MR in `awesome-project` (or by editing it, if it already exists). The block
+needs to be configured on the MR that will be **blocked**, rather than on the
+**blocking** MR. There is a "Blocking merge requests" section in the form:
+
+![Blocking merge requests form control](img/edit_blocking_merge_requests.png)
+
+Anyone who can edit a merge request can change the list of blocking merge
+requests.
+
+New blocks can be added by reference, by URL, or by using autcompletion. To
+remove a block, press the "X" by its reference.
+
+As blocks can be specified across projects, it's possible that someone else has
+added a block for a merge request in a project you don't have access to. These
+are shown as a simple count:
+
+![Blocking merge requests form control with inaccessible MRs](img/edit_blocking_merge_requests_inaccessible.png)
+
+If necessary, you can remove all the blocks like this by pressing the "X", just
+as you would for a single, visible block.
+
+Once you're finished, press the "Save changes" button to submit the request, or
+"Cancel" to return without making any changes.
+
+The list of configured blocks, and the status of each one, is shown in the merge
+request widget:
+
+![Blocking merge requests in merge request widget](img/show_blocking_merge_requests_in_mr_widget.png)
+
+Until all blocking merge requests have, themselves, been merged, the "Merge"
+button will be disabled. In particular, note that **closed** merge requests
+still block their dependents - it is impossible to automatically determine if
+merge requests that were blocked by that MR when it was open, are still blocked
+when it is closed.
+
+If a merge request has been closed **and** the block is no longer relevant, it
+must be removed as a blocking MR, following the instructions above, before
+merge.
+
+## Limitations
+
+* API support: [gitlab-ee#12551](https://gitlab.com/gitlab-org/gitlab-ee/issues/12551)
+* Blocking relationships are not preserved across project export/import: [gitlab-ee#12549](https://gitlab.com/gitlab-org/gitlab-ee/issues/12549)
+* Complex merge order dependencies are not supported: [gitlab-ee#11393](https://gitlab.com/gitlab-org/gitlab-ee/issues/11393)
+
+The last item merits a little more explanation. Blocking merge requests can be
+described as a graph of dependencies. The simplest possible graph has one
+merge request blocking another:
+
+```mermaid
+graph LR;
+ myfriend/awesome-lib!10-->mycorp/awesome-project!100;
+```
+
+A more complex (and still supported) graph might have several MRs blocking
+another from being merged:
+
+```mermaid
+graph LR;
+ myfriend/awesome-lib!10-->mycorp/awesome-project!100;
+ herfriend/another-lib!1-->mycorp/awesome-project!100;
+```
+
+We also support one MR blocking several others from being merged:
+
+```mermaid
+graph LR;
+ herfriend/another-lib!1-->myfriend/awesome-lib!10;
+ herfriend/another-lib!1-->mycorp/awesome-project!100;
+```
+
+What is **not** supported is a "deep", or "nested" graph of dependencies, e.g.:
+
+```mermaid
+graph LR;
+ herfriend/another-lib!1-->myfriend/awesome-lib!10;
+ myfriend/awesome-lib!10-->mycorp/awesome-project!100;
+```
+
+In this example, `myfriend/awesome-lib!10` would be blocked from being merged by
+`herfriend/another-lib!1`, and would also block `mycorp/awesome-project!100`
+from being merged. This is **not** yet supported.
+
diff --git a/doc/user/project/merge_requests/img/edit_blocking_merge_requests.png b/doc/user/project/merge_requests/img/edit_blocking_merge_requests.png
new file mode 100644
index 00000000000..0fe26d602e3
--- /dev/null
+++ b/doc/user/project/merge_requests/img/edit_blocking_merge_requests.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/edit_blocking_merge_requests_inaccessible.png b/doc/user/project/merge_requests/img/edit_blocking_merge_requests_inaccessible.png
new file mode 100644
index 00000000000..a1003c41c22
--- /dev/null
+++ b/doc/user/project/merge_requests/img/edit_blocking_merge_requests_inaccessible.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/show_blocking_merge_requests_in_mr_widget.png b/doc/user/project/merge_requests/img/show_blocking_merge_requests_in_mr_widget.png
new file mode 100644
index 00000000000..a1241f9e38c
--- /dev/null
+++ b/doc/user/project/merge_requests/img/show_blocking_merge_requests_in_mr_widget.png
Binary files differ
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index f593046fa8b..d5ca853eff5 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -47,6 +47,7 @@ With **[GitLab Enterprise Edition][ee]**, you can also:
- Analyze your dependencies for vulnerabilities with [Dependency Scanning](../../application_security/dependency_scanning/index.md) **(ULTIMATE)**
- Analyze your Docker images for vulnerabilities with [Container Scanning](../../application_security/container_scanning/index.md) **(ULTIMATE)**
- Determine the performance impact of changes with [Browser Performance Testing](#browser-performance-testing-premium) **(PREMIUM)**
+- Specify merge order dependencies with [Blocking Merge Requests](#blocking-merge-requests-premium) **(PREMIUM)**
## Use cases
@@ -285,6 +286,7 @@ as pushing changes:
- Create a new merge request for the pushed branch.
- Set the target of the merge request to a particular branch.
- Set the merge request to merge when its pipeline succeeds.
+- Set the merge request to remove the source branch when it's merged.
### Create a new merge request using git push options
@@ -328,6 +330,19 @@ pipeline succeeds at the same time using a `-o` flag per push option:
git push -o merge_request.create -o merge_request.merge_when_pipeline_succeeds
```
+### Set removing the source branch using git push options
+
+To set an existing merge request to remove the source branch when the
+merge request is merged, the
+`merge_request.remove_source_branch` push option can be used:
+
+```sh
+git push -o merge_request.remove_source_branch
+```
+
+You can also use this push option in addition to the
+`merge_request.create` push option.
+
## Find the merge request that introduced a change
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/2383) in GitLab 10.5.
@@ -365,6 +380,11 @@ have been marked as a **Work In Progress**.
[Learn more about setting a merge request as "Work In Progress".](work_in_progress_merge_requests.md)
+## Merge Requests for Confidential Issues
+
+Create [merge requests to resolve confidential issues](../issues/confidential_issues.md#merge-requests-for-confidential-issues)
+for preventing leakage or early release of sentive data through regular merge requests.
+
## Merge request approvals **(STARTER)**
> Included in [GitLab Starter][products].
@@ -386,6 +406,17 @@ can show the Code Climate report right in the merge request widget area.
[Read more about Code Quality reports.](code_quality.md)
+## Metrics Reports **(PREMIUM)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9788) in [GitLab Premium][products] 11.10.
+Requires GitLab Runner 11.10 and above.
+
+If you are using [GitLab CI][ci], you can configure your job to output custom
+metrics and GitLab will display the Metrics Report on the merge request so
+that it's fast and easy to identify changes to important metrics.
+
+[Read more about Metrics Report](../../../ci/metrics_reports.md).
+
## Browser Performance Testing **(PREMIUM)**
> Introduced in [GitLab Premium][products] 10.3.
@@ -396,6 +427,21 @@ GitLab runs the [Sitespeed.io container][sitespeed-container] and displays the d
[Read more about Browser Performance Testing.](browser_performance_testing.md)
+## Blocking Merge Requests **(PREMIUM)**
+
+> Introduced in [GitLab Premium][products] 12.2.
+
+A single logical change may be split across several merge requests, and perhaps
+even across several projects. When this happens, the order in which MRs are
+merged is important.
+
+GitLab allows you to specify that a merge request is blocked by other MRs. With
+this relationship in place, the merge request cannot be merged until all of its
+blockers have also been merged, helping to maintain the consistency of a single
+logical change.
+
+[Read more about Blocking Merge Requests.](blocking_merge_requests.md)
+
## Security reports **(ULTIMATE)**
GitLab can scan and report any vulnerabilities found in your project.
diff --git a/doc/user/project/packages/npm_registry.md b/doc/user/project/packages/npm_registry.md
index 481b1ce0337..ca0aa9965ef 100644
--- a/doc/user/project/packages/npm_registry.md
+++ b/doc/user/project/packages/npm_registry.md
@@ -49,35 +49,32 @@ Registry.
## Authenticating to the GitLab NPM Registry
If a project is private or you want to upload an NPM package to GitLab,
-credentials will need to be provided for authentication. Support is available
-only for [OAuth tokens](../../../api/oauth2.md#resource-owner-password-credentials-flow).
+credentials will need to be provided for authentication. Support is available for [OAuth tokens](../../../api/oauth2.md#resource-owner-password-credentials-flow) or [personal access tokens](../../profile/personal_access_tokens.md).
-CAUTION: **2FA not supported:**
-Authentication for personal access tokens is not yet supported
-([#9140](https://gitlab.com/gitlab-org/gitlab-ee/issues/9140)). If you have 2FA
-enabled, you won't be able to authenticate to the GitLab NPM Registry.
+CAUTION: **2FA is only supported with personal access tokens:**
+If you have 2FA enabled, you need to use a [personal access token](../../profile/personal_access_tokens.md) with OAuth headers. Standard OAuth tokens won't be able to authenticate to the GitLab NPM Registry.
### Authenticating with an OAuth token
-To authenticate with an [OAuth token](../../../api/oauth2.md#resource-owner-password-credentials-flow),
-add a corresponding section to your `.npmrc` file:
+To authenticate with an [OAuth token](../../../api/oauth2.md#resource-owner-password-credentials-flow)
+or [personal access token](../../profile/personal_access_tokens.md), add a corresponding section to your `.npmrc` file:
```ini
; Set URL for your scoped packages.
; For example package with name `@foo/bar` will use this URL for download
@foo:registry=https://gitlab.com/api/v4/packages/npm/
-; Add the OAuth token for the scoped packages URL. This will allow you to download
+; Add the token for the scoped packages URL. This will allow you to download
; `@foo/` packages from private projects.
-//gitlab.com/api/v4/packages/npm/:_authToken=<your_oauth_token>
+//gitlab.com/api/v4/packages/npm/:_authToken=<your_token>
-; Add OAuth token for uploading to the registry. Replace <your_project_id>
+; Add token for uploading to the registry. Replace <your_project_id>
; with the project you want your package to be uploaded to.
-//gitlab.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken=<your_oauth_token>
+//gitlab.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken=<your_token>
```
Replace `<your_project_id>` with your project ID which can be found on the home page
-of your project and `<your_oauth_token>` with your OAuth token.
+of your project and `<your_token>` with your OAuth or personal access token.
If you have a self-hosted GitLab installation, replace `gitlab.com` with your
domain name.
diff --git a/doc/user/project/pages/custom_domains_ssl_tls_certification/img/lets_encrypt_integration_v12_1.png b/doc/user/project/pages/custom_domains_ssl_tls_certification/img/lets_encrypt_integration_v12_1.png
new file mode 100644
index 00000000000..2e825e84d92
--- /dev/null
+++ b/doc/user/project/pages/custom_domains_ssl_tls_certification/img/lets_encrypt_integration_v12_1.png
Binary files differ
diff --git a/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md b/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md
index 219e141d72e..54ecc42d2b9 100644
--- a/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md
+++ b/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md
@@ -204,6 +204,14 @@ can use the following setup:
Read this document for an [overview on SSL/TLS certification](ssl_tls_concepts.md).
+To secure your custom domain with GitLab Pages you can opt by:
+
+- Using the [Let's Encrypt integration with GitLab Pages](lets_encrypt_integration.md),
+ which automatically obtains and renews SSL certificates
+ for your Pages domains.
+- Manually adding SSL/TLS certificates to GitLab Pages websites
+ by following the steps below.
+
### Requirements
- A GitLab Pages website up and running accessible via a custom domain.
@@ -255,6 +263,7 @@ To enable this setting:
1. Navigate to your project's **Settings > Pages**.
1. Tick the checkbox **Force HTTPS (requires valid certificates)**.
+
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
@@ -265,4 +274,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/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md b/doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md
new file mode 100644
index 00000000000..4588375738e
--- /dev/null
+++ b/doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md
@@ -0,0 +1,87 @@
+---
+type: reference
+description: "Automatic Let's Encrypt SSL certificates for GitLab Pages."
+---
+
+# GitLab Pages integration with Let's Encrypt
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/28996) in GitLab 12.1.
+
+The GitLab Pages integration with Let's Encrypt (LE) allows you
+to use LE certificates for your Pages website with custom domains
+without the hassle of having to issue and update them yourself;
+GitLab does it for you, out-of-the-box.
+
+[Let's Encrypt](https://letsencrypt.org) is a free, automated, and
+open source Certificate Authority.
+
+CAUTION: **Caution:**
+This feature is in **beta** and might present bugs and UX issues
+such as [#64870](https://gitlab.com/gitlab-org/gitlab-ce/issues/64870).
+See all the related issues linked from this [issue's description](https://gitlab.com/gitlab-org/gitlab-ce/issues/28996)
+for more information.
+
+## Requirements
+
+Before you can enable automatic provisioning of a SSL certificate for your domain, make sure you have:
+
+- Created a [project](../getting_started_part_two.md) in GitLab
+ containing your website's source code.
+- Acquired a domain (`example.com`) and added a [DNS entry](index.md)
+ pointing it to your Pages website.
+- [Added your domain to your Pages project](index.md#1-add-a-custom-domain-to-pages)
+ and verified your ownership.
+- Have your website up and running, accessible through your custom domain.
+
+NOTE: **Note:**
+GitLab's Let's Encrypt integration is enabled and available on GitLab.com.
+For **self-managed** GitLab instances, make sure your administrator has
+[enabled it](../../../../administration/pages/index.md#lets-encrypt-integration).
+
+## Enabling Let's Encrypt integration for your custom domain
+
+Once you've met the requirements, to enable Let's Encrypt integration:
+
+1. Navigate to your project's **Settings > Pages**.
+1. Find your domain and click **Details**.
+1. Click **Edit** in the top-right corner.
+1. Enable Let's Encrypt integration by switching **Automatic certificate management using Let's Encrypt**:
+
+ ![Enable Let's Encrypt](img/lets_encrypt_integration_v12_1.png)
+
+1. Click **Save changes**.
+
+Once enabled, GitLab will obtain a LE certificate and add it to the
+associated Pages domain. It will be also renewed automatically by GitLab.
+
+> **Notes:**
+>
+> - Issuing the certificate and updating Pages configuration
+> **can take up to an hour**.
+> - If you already have SSL certificate in domain settings it
+> will continue to work until it will be replaced by Let's Encrypt's certificate.
+
+## Troubleshooting
+
+### Error "Certificate misses intermediates"
+
+If you get an error **Certificate misses intermediates** while trying to enable Let's Encrypt integration for your domain, follow the steps below:
+
+1. Go to your project's **Settings > Pages**.
+1. Turn off **Force HTTPS** if it's turned on.
+1. Click **Details** on your domain.
+1. Click the **Edit** button in the top right corner of domain details page.
+1. Enable Let's Encrypt integration.
+1. Click **Save**.
+1. Go to your project's **Settings > Pages**.
+1. Turn on **Force HTTPS**.
+
+<!-- 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/pages/index.md b/doc/user/project/pages/index.md
index e9d2e9a0059..25944b029d7 100644
--- a/doc/user/project/pages/index.md
+++ b/doc/user/project/pages/index.md
@@ -143,8 +143,8 @@ To learn more about configuration options for GitLab Pages, read the following:
| [Exploring GitLab Pages](introduction.md) | Requirements, technical aspects, specific GitLab CI's configuration options, Access Control, custom 404 pages, limitations, FAQ. |
|---+---|
| [Custom domains and SSL/TLS Certificates](custom_domains_ssl_tls_certification/index.md) | How to add custom domains and subdomains to your website, configure DNS records and SSL/TLS certificates. |
+| [Let's Encrypt integration](custom_domains_ssl_tls_certification/lets_encrypt_integration.md) | Secure your Pages sites with Let's Encrypt certificates automatically obtained and renewed by GitLab. |
| [CloudFlare certificates](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) | Secure your Pages site with CloudFlare certificates. |
-| [Let's Encrypt certificates](lets_encrypt_for_gitlab_pages.md) | Secure your Pages site with Let's Encrypt certificates. |
|---+---|
| [Static vs dynamic websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/) | A conceptual overview on static versus dynamic sites. |
| [Modern static site generators](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/) | A conceptual overview on SSGs. |
diff --git a/doc/user/project/pages/introduction.md b/doc/user/project/pages/introduction.md
index 9451b5349c0..e8984cb8b9f 100644
--- a/doc/user/project/pages/introduction.md
+++ b/doc/user/project/pages/introduction.md
@@ -83,23 +83,23 @@ You can enable Pages access control on your project, so that only
1. Navigate to your project's **Settings > General > Permissions**.
1. Toggle the **Pages** button to enable the access control.
- NOTE: **Note:**
- If you don't see the toggle button, that means that it's not enabled.
- Ask your administrator to [enable it](../../../administration/pages/index.md#access-control).
+ NOTE: **Note:**
+ If you don't see the toggle button, that means that it's not enabled.
+ Ask your administrator to [enable it](../../../administration/pages/index.md#access-control).
1. The Pages access control dropdown allows you to set who can view pages hosted
with GitLab Pages, depending on your project's visibility:
- - If your project is private:
- - **Only project members**: Only project members will be able to browse the website.
- - **Everyone**: Everyone, both logged into and logged out of GitLab, will be able to browse the website, no matter their project membership.
- - If your project is internal:
- - **Only project members**: Only project members will be able to browse the website.
- - **Everyone with access**: Everyone logged into GitLab will be able to browse the website, no matter their project membership.
- - **Everyone**: Everyone, both logged into and logged out of GitLab, will be able to browse the website, no matter their project membership.
- - If your project is public:
- - **Only project members**: Only project members will be able to browse the website.
- - **Everyone with access**: Everyone, both logged into and logged out of GitLab, will be able to browse the website, no matter their project membership.
+ - If your project is private:
+ - **Only project members**: Only project members will be able to browse the website.
+ - **Everyone**: Everyone, both logged into and logged out of GitLab, will be able to browse the website, no matter their project membership.
+ - If your project is internal:
+ - **Only project members**: Only project members will be able to browse the website.
+ - **Everyone with access**: Everyone logged into GitLab will be able to browse the website, no matter their project membership.
+ - **Everyone**: Everyone, both logged into and logged out of GitLab, will be able to browse the website, no matter their project membership.
+ - If your project is public:
+ - **Only project members**: Only project members will be able to browse the website.
+ - **Everyone with access**: Everyone, both logged into and logged out of GitLab, will be able to browse the website, no matter their project membership.
1. Click **Save changes**.
diff --git a/doc/user/project/pages/lets_encrypt_for_gitlab_pages.md b/doc/user/project/pages/lets_encrypt_for_gitlab_pages.md
index cc129f90b7a..1338c7e58f5 100644
--- a/doc/user/project/pages/lets_encrypt_for_gitlab_pages.md
+++ b/doc/user/project/pages/lets_encrypt_for_gitlab_pages.md
@@ -1,10 +1,15 @@
---
-description: "How to secure GitLab Pages websites with Let's Encrypt."
+description: "How to secure GitLab Pages websites with Let's Encrypt (manual process, deprecated)."
type: howto
-last_updated: 2019-06-04
+last_updated: 2019-07-15
---
-# Let's Encrypt for GitLab Pages
+# Let's Encrypt for GitLab Pages (manual process, deprecated)
+
+CAUTION: **Warning:**
+This method is still valid but was **deprecated** in favor of the
+[Let's Encrypt integration](custom_domains_ssl_tls_certification/lets_encrypt_integration.md)
+introduced in GitLab 12.1.
If you have a GitLab Pages website served under your own domain,
you might want to secure it with a SSL/TSL certificate.
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index 948ac91a886..f81669a41c9 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -59,6 +59,12 @@ discussions, and descriptions:
| `/create_merge_request <branch name>` | Create a new merge request starting from the current issue | ✓ | |
| `/relate #issue1 #issue2` | Mark issues as related **(STARTER)** | ✓ | |
+## Autocomplete characters
+
+Many quick actions require a parameter, for example: username, milestone, and
+label. [Autocomplete characters](autocomplete_characters.md) can make it easier
+to enter a parameter, compared to selecting items from a list.
+
## Quick actions for commit messages
The following quick actions are applicable for commit messages:
diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md
index 7241df613eb..35d5320c0b1 100644
--- a/doc/user/project/settings/import_export.md
+++ b/doc/user/project/settings/import_export.md
@@ -116,3 +116,8 @@ For more details on the specific data persisted in a project export, see the
1. Click on **Import project** to begin importing. Your newly imported project
page will appear soon.
+
+NOTE: **Note:**
+If use of the `Internal` visibility level
+[is restricted](../../../public_access/public_access.md#restricting-the-use-of-public-or-internal-projects),
+all imported projects are given the visibility of `Private`.
diff --git a/doc/user/search/advanced_search_syntax.md b/doc/user/search/advanced_search_syntax.md
index ad9f065b19b..c3d22e4fd29 100644
--- a/doc/user/search/advanced_search_syntax.md
+++ b/doc/user/search/advanced_search_syntax.md
@@ -50,9 +50,9 @@ here's a quick guide:
The Advanced Syntax Search also supports the use of filters. The available filters are:
- - filename: Filters by filename. You can use the glob (`*`) operator for fuzzy matching.
- - path: Filters by path. You can use the glob (`*`) operator for fuzzy matching.
- - extension: Filters by extension in the filename. Please write the extension without a leading dot. Exact match only.
+- filename: Filters by filename. You can use the glob (`*`) operator for fuzzy matching.
+- path: Filters by path. You can use the glob (`*`) operator for fuzzy matching.
+- extension: Filters by extension in the filename. Please write the extension without a leading dot. Exact match only.
To use them, simply add them to your query in the format `<filter_name>:<value>` without
any spaces between the colon (`:`) and the value.
diff --git a/doc/user/search/index.md b/doc/user/search/index.md
index c34b9ae3d7e..8d7b4a429aa 100644
--- a/doc/user/search/index.md
+++ b/doc/user/search/index.md
@@ -55,12 +55,12 @@ Selecting **Any** does the opposite. It returns results that have a non-empty va
You can filter issues and merge requests by specific terms included in titles or descriptions.
- Syntax
- - Searches look for all the words in a query, in any order. E.g.: searching
- issues for `display bug` will return all issues matching both those words, in any order.
- - To find the exact term, use double quotes: `"display bug"`
+ - Searches look for all the words in a query, in any order. E.g.: searching
+ issues for `display bug` will return all issues matching both those words, in any order.
+ - To find the exact term, use double quotes: `"display bug"`
- Limitation
- - For performance reasons, terms shorter than 3 chars are ignored. E.g.: searching
- issues for `included in titles` is same as `included titles`
+ - For performance reasons, terms shorter than 3 chars are ignored. E.g.: searching
+ issues for `included in titles` is same as `included titles`
![filter issues by specific terms](img/issue_search_by_term.png)
diff --git a/doc/workflow/forking_workflow.md b/doc/workflow/forking_workflow.md
index 02be0ad191d..869a0a621c3 100644
--- a/doc/workflow/forking_workflow.md
+++ b/doc/workflow/forking_workflow.md
@@ -10,31 +10,25 @@ document more information about using branches to work together.
Forking a project is in most cases a two-step process.
-1. Click on the fork button located in the middle of the page or a project's
- home page right next to the stars button.
+1. Click on the fork button located in the middle of the page or a project's
+ home page right next to the stars button.
- ![Fork button](img/forking_workflow_fork_button.png)
+ ![Fork button](img/forking_workflow_fork_button.png)
- ---
+1. Once you do that, you'll be presented with a screen where you can choose
+ the namespace to fork to. Only namespaces (groups and your own
+ namespace) where you have write access to, will be shown. Click on the
+ namespace to create your fork there.
-1. Once you do that, you'll be presented with a screen where you can choose
- the namespace to fork to. Only namespaces (groups and your own
- namespace) where you have write access to, will be shown. Click on the
- namespace to create your fork there.
+ ![Choose namespace](img/forking_workflow_choose_namespace.png)
- ![Choose namespace](img/forking_workflow_choose_namespace.png)
+ **Note:**
+ If the namespace you chose to fork the project to has another project with
+ the same path name, you will be presented with a warning that the forking
+ could not be completed. Try to resolve the error and repeat the forking
+ process.
- ---
-
- **Note:**
- If the namespace you chose to fork the project to has another project with
- the same path name, you will be presented with a warning that the forking
- could not be completed. Try to resolve the error and repeat the forking
- process.
-
- ![Path taken error](img/forking_workflow_path_taken_error.png)
-
- ---
+ ![Path taken error](img/forking_workflow_path_taken_error.png)
After the forking is done, you can start working on the newly created
repository. There, you will have full [Owner](../user/permissions.md)
diff --git a/doc/workflow/lfs/lfs_administration.md b/doc/workflow/lfs/lfs_administration.md
index 55183408a10..3160b0c4deb 100644
--- a/doc/workflow/lfs/lfs_administration.md
+++ b/doc/workflow/lfs/lfs_administration.md
@@ -91,6 +91,7 @@ Here is a configuration example with S3.
| `aws_access_key_id` | AWS credentials, or compatible | `ABC123DEF456` |
| `aws_secret_access_key` | AWS credentials, or compatible | `ABC123DEF456ABC123DEF456ABC123DEF456` |
| `aws_signature_version` | AWS signature version to use. 2 or 4 are valid options. Digital Ocean Spaces and other providers may need 2. | 4 |
+| `enable_signature_v4_streaming` | Set to true to enable HTTP chunked transfers with AWS v4 signatures (https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html). Oracle Cloud S3 needs this to be false | true
| `region` | AWS region | us-east-1 |
| `host` | S3 compatible host for when not using AWS, e.g. `localhost` or `storage.example.com` | s3.amazonaws.com |
| `endpoint` | Can be used when configuring an S3 compatible service such as [Minio](https://www.minio.io), by entering a URL such as `http://127.0.0.1:9000` | (optional) |
diff --git a/doc/workflow/repository_mirroring.md b/doc/workflow/repository_mirroring.md
index 87ca46e94be..0b8e7d851b0 100644
--- a/doc/workflow/repository_mirroring.md
+++ b/doc/workflow/repository_mirroring.md
@@ -92,9 +92,9 @@ The repository will push soon. To force a push, click the appropriate button.
1. On the destination GitLab instance, create a [personal access token](../user/profile/personal_access_tokens.md) with `API` scope.
1. On the source GitLab instance:
- 1. Fill in the **Git repository URL** field using this format: `https://oauth2@<destination host>/<your_gitlab_group_or_name>/<your_gitlab_project>.git`.
- 1. Fill in **Password** field with the GitLab personal access token created on the destination GitLab instance.
- 1. Click the **Mirror repository** button.
+ 1. Fill in the **Git repository URL** field using this format: `https://oauth2@<destination host>/<your_gitlab_group_or_name>/<your_gitlab_project>.git`.
+ 1. Fill in **Password** field with the GitLab personal access token created on the destination GitLab instance.
+ 1. Click the **Mirror repository** button.
## Pulling from a remote repository **(STARTER)**
@@ -118,9 +118,9 @@ To configure mirror pulling for an existing project:
1. Select **Pull** from the **Mirror direction** dropdown.
1. Select an authentication method from the **Authentication method** dropdown, if necessary.
1. If necessary, check the following boxes:
- - **Overwrite diverged branches**.
- - **Trigger pipelines for mirror updates**.
- - **Only mirror protected branches**.
+ - **Overwrite diverged branches**.
+ - **Trigger pipelines for mirror updates**.
+ - **Only mirror protected branches**.
1. Click the **Mirror repository** button to save the configuration.
![Repository mirroring pull settings screen - upper part](img/repository_mirroring_pull_settings_upper.png)
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index c414ad75d9d..e4f4e79cd46 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -76,7 +76,7 @@ module API
detail 'This feature was introduced in GitLab 8.13'
end
params do
- requires :branch, type: String, desc: 'Name of the branch to commit into. To create a new branch, also provide `start_branch`.', allow_blank: false
+ requires :branch, type: String, desc: 'Name of the branch to commit into. To create a new branch, also provide either `start_branch` or `start_sha`, and optionally `start_project`.', allow_blank: false
requires :commit_message, type: String, desc: 'Commit message'
requires :actions, type: Array, desc: 'Actions to perform in commit' do
requires :action, type: String, desc: 'The action to perform, `create`, `delete`, `move`, `update`, `chmod`', values: %w[create update move delete chmod].freeze
@@ -98,12 +98,16 @@ module API
requires :execute_filemode, type: Boolean, desc: 'When `true/false` enables/disables the execute flag on the file.'
end
end
- optional :start_branch, type: String, desc: 'Name of the branch to start the new commit from'
- optional :start_project, types: [Integer, String], desc: 'The ID or path of the project to start the commit from'
+
+ optional :start_branch, type: String, desc: 'Name of the branch to start the new branch from'
+ optional :start_sha, type: String, desc: 'SHA of the commit to start the new branch from'
+ mutually_exclusive :start_branch, :start_sha
+
+ optional :start_project, types: [Integer, String], desc: 'The ID or path of the project to start the new branch from'
optional :author_email, type: String, desc: 'Author email for commit'
optional :author_name, type: String, desc: 'Author name for commit'
optional :stats, type: Boolean, default: true, desc: 'Include commit stats'
- optional :force, type: Boolean, default: false, desc: 'When `true` overwrites the target branch with a new commit based on the `start_branch`'
+ optional :force, type: Boolean, default: false, desc: 'When `true` overwrites the target branch with a new commit based on the `start_branch` or `start_sha`'
end
post ':id/repository/commits' do
if params[:start_project]
@@ -118,7 +122,7 @@ module API
attrs = declared_params
attrs[:branch_name] = attrs.delete(:branch)
- attrs[:start_branch] ||= attrs[:branch_name]
+ attrs[:start_branch] ||= attrs[:branch_name] unless attrs[:start_sha]
attrs[:start_project] = start_project if start_project
result = ::Files::MultiService.new(user_project, current_user, attrs).execute
@@ -126,7 +130,7 @@ module API
if result[:status] == :success
commit_detail = user_project.repository.commit(result[:result])
- Gitlab::UsageDataCounters::WebIdeCommitsCounter.increment if find_user_from_warden
+ Gitlab::UsageDataCounters::WebIdeCounter.increment_commits_count if find_user_from_warden
present commit_detail, with: Entities::CommitDetail, stats: params[:stats]
else
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index 833e3b9ebaf..51b7cf05c8f 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -71,6 +71,7 @@ module API
:build_timeout,
:builds_access_level,
:ci_config_path,
+ :ci_default_git_depth,
:container_registry_enabled,
:default_branch,
:description,
diff --git a/lib/banzai/filter/ascii_doc_sanitization_filter.rb b/lib/banzai/filter/ascii_doc_sanitization_filter.rb
index d8d63075752..9105e86ad04 100644
--- a/lib/banzai/filter/ascii_doc_sanitization_filter.rb
+++ b/lib/banzai/filter/ascii_doc_sanitization_filter.rb
@@ -8,12 +8,18 @@ module Banzai
class AsciiDocSanitizationFilter < Banzai::Filter::BaseSanitizationFilter
# Section anchor link pattern
SECTION_LINK_REF_PATTERN = /\A#{Gitlab::Asciidoc::DEFAULT_ADOC_ATTRS['idprefix']}(:?[[:alnum:]]|-|_)+\z/.freeze
+ SECTION_HEADINGS = %w(h2 h3 h4 h5 h6).freeze
+
+ # Footnote link patterns
+ FOOTNOTE_LINK_ID_PATTERNS = {
+ a: /\A_footnoteref_\d+\z/,
+ div: /\A_footnotedef_\d+\z/
+ }.freeze
# Classes used by Asciidoctor to style components
ADMONITION_CLASSES = %w(fa icon-note icon-tip icon-warning icon-caution icon-important).freeze
CALLOUT_CLASSES = ['conum'].freeze
CHECKLIST_CLASSES = %w(fa fa-check-square-o fa-square-o).freeze
-
LIST_CLASSES = %w(checklist none no-bullet unnumbered unstyled).freeze
ELEMENT_CLASSES_WHITELIST = {
@@ -26,8 +32,6 @@ module Banzai
a: ['anchor'].freeze
}.freeze
- ALLOWED_HEADERS = %w(h2 h3 h4 h5 h6).freeze
-
def customize_whitelist(whitelist)
# Allow marks
whitelist[:elements].push('mark')
@@ -44,20 +48,39 @@ module Banzai
whitelist[:transformers].push(self.class.remove_element_classes)
# Allow `id` in heading elements for section anchors
- ALLOWED_HEADERS.each do |header|
+ SECTION_HEADINGS.each do |header|
whitelist[:attributes][header] = %w(id)
end
whitelist[:transformers].push(self.class.remove_non_heading_ids)
+ # Allow `id` in footnote elements
+ FOOTNOTE_LINK_ID_PATTERNS.keys.each do |element|
+ whitelist[:attributes][element.to_s].push('id')
+ end
+ whitelist[:transformers].push(self.class.remove_non_footnote_ids)
+
whitelist
end
class << self
+ def remove_non_footnote_ids
+ lambda do |env|
+ node = env[:node]
+
+ return unless (pattern = FOOTNOTE_LINK_ID_PATTERNS[node.name.to_sym])
+ return unless node.has_attribute?('id')
+
+ return if node['id'] =~ pattern
+
+ node.remove_attribute('id')
+ end
+ end
+
def remove_non_heading_ids
lambda do |env|
node = env[:node]
- return unless ALLOWED_HEADERS.any?(node.name)
+ return unless SECTION_HEADINGS.any?(node.name)
return unless node.has_attribute?('id')
return if node['id'] =~ SECTION_LINK_REF_PATTERN
diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb
index 6eb08f674c2..7ef9f7ef630 100644
--- a/lib/gitlab/access.rb
+++ b/lib/gitlab/access.rb
@@ -29,6 +29,10 @@ module Gitlab
MAINTAINER_PROJECT_ACCESS = 1
DEVELOPER_MAINTAINER_PROJECT_ACCESS = 2
+ # Default subgroup creation level
+ OWNER_SUBGROUP_ACCESS = 0
+ MAINTAINER_SUBGROUP_ACCESS = 1
+
class << self
delegate :values, to: :options
@@ -106,6 +110,13 @@ module Gitlab
def project_creation_level_name(name)
project_creation_options.key(name)
end
+
+ def subgroup_creation_options
+ {
+ s_('SubgroupCreationlevel|Owners') => OWNER_SUBGROUP_ACCESS,
+ s_('SubgroupCreationlevel|Maintainers') => MAINTAINER_SUBGROUP_ACCESS
+ }
+ end
end
def human_access
diff --git a/lib/gitlab/auth/user_auth_finders.rb b/lib/gitlab/auth/user_auth_finders.rb
index a5efe33bdc6..bba7e2cbb3c 100644
--- a/lib/gitlab/auth/user_auth_finders.rb
+++ b/lib/gitlab/auth/user_auth_finders.rb
@@ -90,8 +90,8 @@ module Gitlab
def find_personal_access_token
token =
current_request.params[PRIVATE_TOKEN_PARAM].presence ||
- current_request.env[PRIVATE_TOKEN_HEADER].presence
-
+ current_request.env[PRIVATE_TOKEN_HEADER].presence ||
+ parsed_oauth_token
return unless token
# Expiration, revocation and scopes are verified in `validate_access_token!`
@@ -99,9 +99,12 @@ module Gitlab
end
def find_oauth_access_token
- token = Doorkeeper::OAuth::Token.from_request(current_request, *Doorkeeper.configuration.access_token_methods)
+ token = parsed_oauth_token
return unless token
+ # PATs with OAuth headers are not handled by OauthAccessToken
+ return if matches_personal_access_token_length?(token)
+
# Expiration, revocation and scopes are verified in `validate_access_token!`
oauth_token = OauthAccessToken.by_token(token)
raise UnauthorizedError unless oauth_token
@@ -110,6 +113,14 @@ module Gitlab
oauth_token
end
+ def parsed_oauth_token
+ Doorkeeper::OAuth::Token.from_request(current_request, *Doorkeeper.configuration.access_token_methods)
+ end
+
+ def matches_personal_access_token_length?(token)
+ token.length == PersonalAccessToken::TOKEN_LENGTH
+ end
+
# Check if the request is GET/HEAD, or if CSRF token is valid.
def verified_request?
Gitlab::RequestForgeryProtection.verified?(current_request.env)
diff --git a/lib/gitlab/background_migration/fix_pages_access_level.rb b/lib/gitlab/background_migration/fix_pages_access_level.rb
new file mode 100644
index 00000000000..0d49f3dd8c5
--- /dev/null
+++ b/lib/gitlab/background_migration/fix_pages_access_level.rb
@@ -0,0 +1,128 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # corrects stored pages access level on db depending on project visibility
+ class FixPagesAccessLevel
+ # Copy routable here to avoid relying on application logic
+ module Routable
+ def build_full_path
+ if parent && path
+ parent.build_full_path + '/' + path
+ else
+ path
+ end
+ end
+ end
+
+ # Namespace
+ class Namespace < ApplicationRecord
+ self.table_name = 'namespaces'
+ self.inheritance_column = :_type_disabled
+
+ include Routable
+
+ belongs_to :parent, class_name: "Namespace"
+ end
+
+ # Project
+ class Project < ActiveRecord::Base
+ self.table_name = 'projects'
+ self.inheritance_column = :_type_disabled
+
+ include Routable
+
+ belongs_to :namespace
+ alias_method :parent, :namespace
+ alias_attribute :parent_id, :namespace_id
+
+ PRIVATE = 0
+ INTERNAL = 10
+ PUBLIC = 20
+
+ def pages_deployed?
+ Dir.exist?(public_pages_path)
+ end
+
+ def public_pages_path
+ File.join(pages_path, 'public')
+ end
+
+ def pages_path
+ # TODO: when we migrate Pages to work with new storage types, change here to use disk_path
+ File.join(Settings.pages.path, build_full_path)
+ end
+ end
+
+ # ProjectFeature
+ class ProjectFeature < ActiveRecord::Base
+ include ::EachBatch
+
+ self.table_name = 'project_features'
+
+ belongs_to :project
+
+ PRIVATE = 10
+ ENABLED = 20
+ PUBLIC = 30
+ end
+
+ def perform(start_id, stop_id)
+ fix_public_access_level(start_id, stop_id)
+
+ make_internal_projects_public(start_id, stop_id)
+
+ fix_private_access_level(start_id, stop_id)
+ end
+
+ private
+
+ def access_control_is_enabled
+ @access_control_is_enabled = Gitlab.config.pages.access_control
+ end
+
+ # Public projects are allowed to have only enabled pages_access_level
+ # which is equivalent to public
+ def fix_public_access_level(start_id, stop_id)
+ project_features(start_id, stop_id, ProjectFeature::PUBLIC, Project::PUBLIC).each_batch do |features|
+ features.update_all(pages_access_level: ProjectFeature::ENABLED)
+ end
+ end
+
+ # If access control is disabled and project has pages deployed
+ # project will become unavailable when access control will become enabled
+ # we make these projects public to avoid negative surprise to user
+ def make_internal_projects_public(start_id, stop_id)
+ return if access_control_is_enabled
+
+ project_features(start_id, stop_id, ProjectFeature::ENABLED, Project::INTERNAL).find_each do |project_feature|
+ next unless project_feature.project.pages_deployed?
+
+ project_feature.update(pages_access_level: ProjectFeature::PUBLIC)
+ end
+ end
+
+ # Private projects are not allowed to have enabled access level, only `private` and `public`
+ # If access control is enabled, these projects currently behave as if the have `private` pages_access_level
+ # if access control is disabled, these projects currently behave as if the have `public` pages_access_level
+ # so we preserve this behaviour for projects with pages already deployed
+ # for project without pages we always set `private` access_level
+ def fix_private_access_level(start_id, stop_id)
+ project_features(start_id, stop_id, ProjectFeature::ENABLED, Project::PRIVATE).find_each do |project_feature|
+ if access_control_is_enabled
+ project_feature.update!(pages_access_level: ProjectFeature::PRIVATE)
+ else
+ fixed_access_level = project_feature.project.pages_deployed? ? ProjectFeature::PUBLIC : ProjectFeature::PRIVATE
+ project_feature.update!(pages_access_level: fixed_access_level)
+ end
+ end
+ end
+
+ def project_features(start_id, stop_id, pages_access_level, project_visibility_level)
+ ProjectFeature.where(id: start_id..stop_id).joins(:project)
+ .where(pages_access_level: pages_access_level)
+ .where(projects: { visibility_level: project_visibility_level })
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/chaos.rb b/lib/gitlab/chaos.rb
new file mode 100644
index 00000000000..4f47cdef971
--- /dev/null
+++ b/lib/gitlab/chaos.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module Gitlab
+ # Chaos methods for GitLab.
+ # See https://docs.gitlab.com/ee/development/chaos_endpoints.html for more details.
+ class Chaos
+ # leak_mem will retain the specified amount of memory and sleep.
+ # On return, the memory will be released.
+ def self.leak_mem(memory_mb, duration_s)
+ start_time = Time.now
+
+ retainer = []
+ # Add `n` 1mb chunks of memory to the retainer array
+ memory_mb.times { retainer << "x" * 1.megabyte }
+
+ duration_left = [start_time + duration_s - Time.now, 0].max
+ Kernel.sleep(duration_left)
+ end
+
+ # cpu_spin will consume all CPU on a single core for the specified duration
+ def self.cpu_spin(duration_s)
+ expected_end_time = Time.now + duration_s
+
+ rand while Time.now < expected_end_time
+ end
+
+ # db_spin will query the database in a tight loop for the specified duration
+ def self.db_spin(duration_s, interval_s)
+ expected_end_time = Time.now + duration_s
+
+ while Time.now < expected_end_time
+ ActiveRecord::Base.connection.execute("SELECT 1")
+
+ end_interval_time = Time.now + [duration_s, interval_s].min
+ rand while Time.now < end_interval_time
+ end
+ end
+
+ # sleep will sleep for the specified duration
+ def self.sleep(duration_s)
+ Kernel.sleep(duration_s)
+ end
+
+ # Kill will send a SIGKILL signal to the current process
+ def self.kill
+ Process.kill("KILL", Process.pid)
+ end
+ end
+end
diff --git a/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml b/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml
index a3db2705bf6..280e75d46f5 100644
--- a/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml
@@ -8,26 +8,23 @@ stages:
- deploy
.serverless:build:image:
- stage: build
image: registry.gitlab.com/gitlab-org/gitlabktl:latest
+ stage: build
script: /usr/bin/gitlabktl app build
.serverless:deploy:image:
+ image: registry.gitlab.com/gitlab-org/gitlabktl:latest
stage: deploy
- image: gcr.io/triggermesh/tm@sha256:3cfdd470a66b741004fb02354319d79f1598c70117ce79978d2e07e192bfb336 # v0.0.11
environment: development
- script:
- - echo "$CI_REGISTRY_IMAGE"
- - tm -n "$KUBE_NAMESPACE" --config "$KUBECONFIG" deploy service "$CI_PROJECT_NAME" --from-image "$CI_REGISTRY_IMAGE" --wait
+ script: /usr/bin/gitlabktl app deploy
.serverless:build:functions:
- stage: build
- environment: development
image: registry.gitlab.com/gitlab-org/gitlabktl:latest
+ stage: build
script: /usr/bin/gitlabktl serverless build
.serverless:deploy:functions:
+ image: registry.gitlab.com/gitlab-org/gitlabktl:latest
stage: deploy
environment: development
- image: registry.gitlab.com/gitlab-org/gitlabktl:latest
script: /usr/bin/gitlabktl serverless deploy
diff --git a/lib/gitlab/cycle_analytics/base_event_fetcher.rb b/lib/gitlab/cycle_analytics/base_event_fetcher.rb
index 0cacef5b278..96aa799e864 100644
--- a/lib/gitlab/cycle_analytics/base_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/base_event_fetcher.rb
@@ -5,12 +5,11 @@ module Gitlab
class BaseEventFetcher
include BaseQuery
- attr_reader :projections, :query, :stage, :order, :project, :options
+ attr_reader :projections, :query, :stage, :order, :options
MAX_EVENTS = 50
- def initialize(project: nil, stage:, options:)
- @project = project
+ def initialize(stage:, options:)
@stage = stage
@options = options
end
@@ -68,11 +67,23 @@ module Gitlab
end
def allowed_ids_source
- { project_id: project.id }
+ group ? { group_id: group.id, include_subgroups: true } : { project_id: project.id }
+ end
+
+ def serialization_context
+ {}
end
def projects
- [project]
+ group ? Project.inside_path(group.full_path) : [project]
+ end
+
+ def group
+ @group ||= options.fetch(:group, nil)
+ end
+
+ def project
+ @project ||= options.fetch(:project, nil)
end
end
end
diff --git a/lib/gitlab/cycle_analytics/base_query.rb b/lib/gitlab/cycle_analytics/base_query.rb
index 39fc1759cfc..9c98c0bfbf2 100644
--- a/lib/gitlab/cycle_analytics/base_query.rb
+++ b/lib/gitlab/cycle_analytics/base_query.rb
@@ -16,17 +16,25 @@ module Gitlab
def stage_query(project_ids)
query = mr_closing_issues_table.join(issue_table).on(issue_table[:id].eq(mr_closing_issues_table[:issue_id]))
.join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id]))
+ .join(projects_table).on(issue_table[:project_id].eq(projects_table[:id]))
+ .join(routes_table).on(projects_table[:namespace_id].eq(routes_table[:source_id]))
.project(issue_table[:project_id].as("project_id"))
.where(issue_table[:project_id].in(project_ids))
+ .where(routes_table[:source_type].eq('Namespace'))
.where(issue_table[:created_at].gteq(options[:from]))
# Load merge_requests
- query = query.join(mr_table, Arel::Nodes::OuterJoin)
+
+ query = load_merge_requests(query)
+
+ query
+ end
+
+ def load_merge_requests(query)
+ query.join(mr_table, Arel::Nodes::OuterJoin)
.on(mr_table[:id].eq(mr_closing_issues_table[:merge_request_id]))
.join(mr_metrics_table)
.on(mr_table[:id].eq(mr_metrics_table[:merge_request_id]))
-
- query
end
end
end
diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb
index 98b86e54340..678a891e941 100644
--- a/lib/gitlab/cycle_analytics/base_stage.rb
+++ b/lib/gitlab/cycle_analytics/base_stage.rb
@@ -5,10 +5,9 @@ module Gitlab
class BaseStage
include BaseQuery
- attr_reader :project, :options
+ attr_reader :options
- def initialize(project: nil, options:)
- @project = project
+ def initialize(options:)
@options = options
end
@@ -24,7 +23,7 @@ module Gitlab
raise NotImplementedError.new("Expected #{self.name} to implement title")
end
- def median
+ def project_median
return if project.nil?
BatchLoader.for(project.id).batch(key: name) do |project_ids, loader|
@@ -42,6 +41,10 @@ module Gitlab
end
end
+ def group_median
+ median_query(projects.map(&:id))
+ end
+
def median_query(project_ids)
# Build a `SELECT` query. We find the first of the `end_time_attrs` that isn't `NULL` (call this end_time).
# Next, we find the first of the start_time_attrs that isn't `NULL` (call this start_time).
@@ -67,8 +70,7 @@ module Gitlab
private
def event_fetcher
- @event_fetcher ||= Gitlab::CycleAnalytics::EventFetcher[name].new(project: project,
- stage: name,
+ @event_fetcher ||= Gitlab::CycleAnalytics::EventFetcher[name].new(stage: name,
options: event_options)
end
@@ -77,7 +79,15 @@ module Gitlab
end
def projects
- [project]
+ group ? Project.inside_path(group.full_path) : [project]
+ end
+
+ def group
+ @group ||= options.fetch(:group, nil)
+ end
+
+ def project
+ @project ||= options.fetch(:project, nil)
end
end
end
diff --git a/lib/gitlab/cycle_analytics/code_event_fetcher.rb b/lib/gitlab/cycle_analytics/code_event_fetcher.rb
index 9e7ca529579..1e4e9b9e02c 100644
--- a/lib/gitlab/cycle_analytics/code_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/code_event_fetcher.rb
@@ -11,7 +11,9 @@ module Gitlab
mr_table[:id],
mr_table[:created_at],
mr_table[:state],
- mr_table[:author_id]]
+ mr_table[:author_id],
+ projects_table[:name],
+ routes_table[:path]]
@order = mr_table[:created_at]
super(*args)
@@ -20,7 +22,7 @@ module Gitlab
private
def serialize(event)
- AnalyticsMergeRequestSerializer.new(project: project).represent(event)
+ AnalyticsMergeRequestSerializer.new(serialization_context).represent(event)
end
def allowed_ids_finder_class
diff --git a/lib/gitlab/cycle_analytics/group_stage_summary.rb b/lib/gitlab/cycle_analytics/group_stage_summary.rb
new file mode 100644
index 00000000000..7b5c74e1a1b
--- /dev/null
+++ b/lib/gitlab/cycle_analytics/group_stage_summary.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module CycleAnalytics
+ class GroupStageSummary
+ def initialize(group, from:, current_user:)
+ @group = group
+ @from = from
+ @current_user = current_user
+ end
+
+ def data
+ [serialize(Summary::Group::Issue.new(group: @group, from: @from, current_user: @current_user)),
+ serialize(Summary::Group::Deploy.new(group: @group, from: @from))]
+ end
+
+ private
+
+ def serialize(summary_object)
+ AnalyticsSummarySerializer.new.represent(summary_object)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb
index bb3520ae920..2d03e425a6a 100644
--- a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb
@@ -10,7 +10,9 @@ module Gitlab
issue_table[:iid],
issue_table[:id],
issue_table[:created_at],
- issue_table[:author_id]]
+ issue_table[:author_id],
+ projects_table[:name],
+ routes_table[:path]]
super(*args)
end
@@ -18,7 +20,7 @@ module Gitlab
private
def serialize(event)
- AnalyticsIssueSerializer.new(project: project).represent(event)
+ AnalyticsIssueSerializer.new(serialization_context).represent(event)
end
def allowed_ids_finder_class
diff --git a/lib/gitlab/cycle_analytics/issue_helper.rb b/lib/gitlab/cycle_analytics/issue_helper.rb
index ac836b8bf0f..0fc4f1dd41a 100644
--- a/lib/gitlab/cycle_analytics/issue_helper.rb
+++ b/lib/gitlab/cycle_analytics/issue_helper.rb
@@ -5,8 +5,11 @@ module Gitlab
module IssueHelper
def stage_query(project_ids)
query = issue_table.join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id]))
+ .join(projects_table).on(issue_table[:project_id].eq(projects_table[:id]))
+ .join(routes_table).on(projects_table[:namespace_id].eq(routes_table[:source_id]))
.project(issue_table[:project_id].as("project_id"))
.where(issue_table[:project_id].in(project_ids))
+ .where(routes_table[:source_type].eq('Namespace'))
.where(issue_table[:created_at].gteq(options[:from]))
.where(issue_metrics_table[:first_added_to_board_at].not_eq(nil).or(issue_metrics_table[:first_associated_with_milestone_at].not_eq(nil)))
diff --git a/lib/gitlab/cycle_analytics/metrics_tables.rb b/lib/gitlab/cycle_analytics/metrics_tables.rb
index 3e0302d308d..015f7bfde24 100644
--- a/lib/gitlab/cycle_analytics/metrics_tables.rb
+++ b/lib/gitlab/cycle_analytics/metrics_tables.rb
@@ -35,6 +35,14 @@ module Gitlab
User.arel_table
end
+ def projects_table
+ Project.arel_table
+ end
+
+ def routes_table
+ Route.arel_table
+ end
+
def build_table
::CommitStatus.arel_table
end
diff --git a/lib/gitlab/cycle_analytics/permissions.rb b/lib/gitlab/cycle_analytics/permissions.rb
index 03ba98b4dfb..55214e6b896 100644
--- a/lib/gitlab/cycle_analytics/permissions.rb
+++ b/lib/gitlab/cycle_analytics/permissions.rb
@@ -23,7 +23,7 @@ module Gitlab
end
def get
- ::CycleAnalytics::Base::STAGES.each do |stage|
+ ::CycleAnalytics::LevelBase::STAGES.each do |stage|
@stage_permission_hash[stage] = authorized_stage?(stage)
end
diff --git a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb
index 49a6b099f34..77cc358daa9 100644
--- a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb
@@ -10,7 +10,9 @@ module Gitlab
issue_table[:iid],
issue_table[:id],
issue_table[:created_at],
- issue_table[:author_id]]
+ issue_table[:author_id],
+ projects_table[:name],
+ routes_table[:path]]
super(*args)
end
@@ -18,7 +20,7 @@ module Gitlab
private
def serialize(event)
- AnalyticsIssueSerializer.new(project: project).represent(event)
+ AnalyticsIssueSerializer.new(serialization_context).represent(event)
end
def allowed_ids_finder_class
diff --git a/lib/gitlab/cycle_analytics/plan_helper.rb b/lib/gitlab/cycle_analytics/plan_helper.rb
index ae578d45ad5..c3f742503a9 100644
--- a/lib/gitlab/cycle_analytics/plan_helper.rb
+++ b/lib/gitlab/cycle_analytics/plan_helper.rb
@@ -5,14 +5,21 @@ module Gitlab
module PlanHelper
def stage_query(project_ids)
query = issue_table.join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id]))
+ .join(projects_table).on(issue_table[:project_id].eq(projects_table[:id]))
+ .join(routes_table).on(projects_table[:namespace_id].eq(routes_table[:source_id]))
.project(issue_table[:project_id].as("project_id"))
.where(issue_table[:project_id].in(project_ids))
- .where(issue_table[:created_at].gteq(options[:from]))
- .where(issue_metrics_table[:first_added_to_board_at].not_eq(nil).or(issue_metrics_table[:first_associated_with_milestone_at].not_eq(nil)))
- .where(issue_metrics_table[:first_mentioned_in_commit_at].not_eq(nil))
+ .where(routes_table[:source_type].eq('Namespace'))
+ query = add_conditions_to_query(query)
query
end
+
+ def add_conditions_to_query(query)
+ query.where(issue_table[:created_at].gteq(options[:from]))
+ .where(issue_metrics_table[:first_added_to_board_at].not_eq(nil).or(issue_metrics_table[:first_associated_with_milestone_at].not_eq(nil)))
+ .where(issue_metrics_table[:first_mentioned_in_commit_at].not_eq(nil))
+ end
end
end
end
diff --git a/lib/gitlab/cycle_analytics/production_event_fetcher.rb b/lib/gitlab/cycle_analytics/production_event_fetcher.rb
index 949119d69a0..404b2460814 100644
--- a/lib/gitlab/cycle_analytics/production_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/production_event_fetcher.rb
@@ -10,7 +10,9 @@ module Gitlab
issue_table[:iid],
issue_table[:id],
issue_table[:created_at],
- issue_table[:author_id]]
+ issue_table[:author_id],
+ projects_table[:name],
+ routes_table[:path]]
super(*args)
end
@@ -18,7 +20,7 @@ module Gitlab
private
def serialize(event)
- AnalyticsIssueSerializer.new(project: project).represent(event)
+ AnalyticsIssueSerializer.new(serialization_context).represent(event)
end
def allowed_ids_finder_class
diff --git a/lib/gitlab/cycle_analytics/review_event_fetcher.rb b/lib/gitlab/cycle_analytics/review_event_fetcher.rb
index d31736e755d..6acd12517fa 100644
--- a/lib/gitlab/cycle_analytics/review_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/review_event_fetcher.rb
@@ -11,7 +11,9 @@ module Gitlab
mr_table[:id],
mr_table[:created_at],
mr_table[:state],
- mr_table[:author_id]]
+ mr_table[:author_id],
+ projects_table[:name],
+ routes_table[:path]]
super(*args)
end
@@ -19,7 +21,7 @@ module Gitlab
private
def serialize(event)
- AnalyticsMergeRequestSerializer.new(project: project).represent(event)
+ AnalyticsMergeRequestSerializer.new(serialization_context).represent(event)
end
def allowed_ids_finder_class
diff --git a/lib/gitlab/cycle_analytics/summary/group/base.rb b/lib/gitlab/cycle_analytics/summary/group/base.rb
new file mode 100644
index 00000000000..7f18b61d309
--- /dev/null
+++ b/lib/gitlab/cycle_analytics/summary/group/base.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module CycleAnalytics
+ module Summary
+ module Group
+ class Base
+ def initialize(group:, from:)
+ @group = group
+ @from = from
+ end
+
+ def title
+ raise NotImplementedError.new("Expected #{self.name} to implement title")
+ end
+
+ def value
+ raise NotImplementedError.new("Expected #{self.name} to implement value")
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/cycle_analytics/summary/group/deploy.rb b/lib/gitlab/cycle_analytics/summary/group/deploy.rb
new file mode 100644
index 00000000000..d8fcd8f2ce4
--- /dev/null
+++ b/lib/gitlab/cycle_analytics/summary/group/deploy.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module CycleAnalytics
+ module Summary
+ module Group
+ class Deploy < Group::Base
+ def title
+ n_('Deploy', 'Deploys', value)
+ end
+
+ def value
+ @value ||= Deployment.joins(:project)
+ .where(projects: { id: projects })
+ .where("deployments.created_at > ?", @from)
+ .success
+ .count
+ end
+
+ private
+
+ def projects
+ Project.inside_path(@group.full_path).ids
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/cycle_analytics/summary/group/issue.rb b/lib/gitlab/cycle_analytics/summary/group/issue.rb
new file mode 100644
index 00000000000..70073e6d843
--- /dev/null
+++ b/lib/gitlab/cycle_analytics/summary/group/issue.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module CycleAnalytics
+ module Summary
+ module Group
+ class Issue < Group::Base
+ def initialize(group:, from:, current_user:)
+ @group = group
+ @from = from
+ @current_user = current_user
+ end
+
+ def title
+ n_('New Issue', 'New Issues', value)
+ end
+
+ def value
+ @value ||= IssuesFinder.new(@current_user, group_id: @group.id, include_subgroups: true, created_after: @from).execute.count
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/danger/helper.rb b/lib/gitlab/danger/helper.rb
index 0fc145534bf..c0a12318990 100644
--- a/lib/gitlab/danger/helper.rb
+++ b/lib/gitlab/danger/helper.rb
@@ -46,6 +46,16 @@ module Gitlab
ee? ? 'gitlab-ee' : 'gitlab-ce'
end
+ def markdown_list(items)
+ list = items.map { |item| "* `#{item}`" }.join("\n")
+
+ if items.size > 10
+ "\n<details>\n\n#{list}\n\n</details>\n"
+ else
+ list
+ end
+ end
+
# @return [Hash<String,Array<String>>]
def changes_by_category
all_changed_files.each_with_object(Hash.new { |h, k| h[k] = [] }) do |file, hash|
@@ -132,6 +142,22 @@ module Gitlab
def new_teammates(usernames)
usernames.map { |u| Gitlab::Danger::Teammate.new('username' => u) }
end
+
+ def missing_database_labels(current_mr_labels)
+ labels = if has_database_scoped_labels?(current_mr_labels)
+ ['database']
+ else
+ ['database', 'database::review pending']
+ end
+
+ labels - current_mr_labels
+ end
+
+ private
+
+ def has_database_scoped_labels?(current_mr_labels)
+ current_mr_labels.any? { |label| label.start_with?('database::') }
+ end
end
end
end
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index 0c5f33e1b2a..2ae807697bc 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -6,31 +6,45 @@ module Gitlab
BACKGROUND_MIGRATION_BATCH_SIZE = 1000 # Number of rows to process per job
BACKGROUND_MIGRATION_JOB_BUFFER_SIZE = 1000 # Number of jobs to bulk queue at a time
+ PERMITTED_TIMESTAMP_COLUMNS = %i[created_at updated_at deleted_at].to_set.freeze
+ DEFAULT_TIMESTAMP_COLUMNS = %i[created_at updated_at].freeze
+
# Adds `created_at` and `updated_at` columns with timezone information.
#
# This method is an improved version of Rails' built-in method `add_timestamps`.
#
+ # By default, adds `created_at` and `updated_at` columns, but these can be specified as:
+ #
+ # add_timestamps_with_timezone(:my_table, columns: [:created_at, :deleted_at])
+ #
+ # This allows you to create just the timestamps you need, saving space.
+ #
# Available options are:
- # default - The default value for the column.
- # null - When set to `true` the column will allow NULL values.
+ # :default - The default value for the column.
+ # :null - When set to `true` the column will allow NULL values.
# The default is to not allow NULL values.
+ # :columns - the column names to create. Must be one
+ # of `Gitlab::Database::MigrationHelpers::PERMITTED_TIMESTAMP_COLUMNS`.
+ # Default value: `DEFAULT_TIMESTAMP_COLUMNS`
+ #
+ # All options are optional.
def add_timestamps_with_timezone(table_name, options = {})
options[:null] = false if options[:null].nil?
+ columns = options.fetch(:columns, DEFAULT_TIMESTAMP_COLUMNS)
+ default_value = options[:default]
- [:created_at, :updated_at].each do |column_name|
- if options[:default] && transaction_open?
- raise '`add_timestamps_with_timezone` with default value cannot be run inside a transaction. ' \
- 'You can disable transactions by calling `disable_ddl_transaction!` ' \
- 'in the body of your migration class'
- end
+ validate_not_in_transaction!(:add_timestamps_with_timezone, 'with default value') if default_value
+
+ columns.each do |column_name|
+ validate_timestamp_column_name!(column_name)
# If default value is presented, use `add_column_with_default` method instead.
- if options[:default]
+ if default_value
add_column_with_default(
table_name,
column_name,
:datetime_with_timezone,
- default: options[:default],
+ default: default_value,
allow_null: options[:null]
)
else
@@ -39,6 +53,21 @@ module Gitlab
end
end
+ # To be used in the `#down` method of migrations that
+ # use `#add_timestamps_with_timezone`.
+ #
+ # Available options are:
+ # :columns - the column names to remove. Must be one
+ # Default value: `DEFAULT_TIMESTAMP_COLUMNS`
+ #
+ # All options are optional.
+ def remove_timestamps(table_name, options = {})
+ columns = options.fetch(:columns, DEFAULT_TIMESTAMP_COLUMNS)
+ columns.each do |column_name|
+ remove_column(table_name, column_name)
+ end
+ end
+
# Creates a new index, concurrently when supported
#
# On PostgreSQL this method creates an index concurrently, on MySQL this
@@ -1098,6 +1127,28 @@ into similar problems in the future (e.g. when new tables are created).
def mysql_compatible_index_length
Gitlab::Database.mysql? ? 20 : nil
end
+
+ private
+
+ def validate_timestamp_column_name!(column_name)
+ return if PERMITTED_TIMESTAMP_COLUMNS.member?(column_name)
+
+ raise <<~MESSAGE
+ Illegal timestamp column name! Got #{column_name}.
+ Must be one of: #{PERMITTED_TIMESTAMP_COLUMNS.to_a}
+ MESSAGE
+ end
+
+ def validate_not_in_transaction!(method_name, modifier = nil)
+ return unless transaction_open?
+
+ raise <<~ERROR
+ #{["`#{method_name}`", modifier].compact.join(' ')} cannot be run inside a transaction.
+
+ You can disable transactions by calling `disable_ddl_transaction!` in the body of
+ your migration class
+ ERROR
+ end
end
end
end
diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb
index 44a62586a23..df9f33baec2 100644
--- a/lib/gitlab/git.rb
+++ b/lib/gitlab/git.rb
@@ -9,6 +9,7 @@ module Gitlab
# https://github.com/git/git/blob/3ad8b5bf26362ac67c9020bf8c30eee54a84f56d/cache.h#L1011-L1012
EMPTY_TREE_ID = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'.freeze
BLANK_SHA = ('0' * 40).freeze
+ COMMIT_ID = /\A[0-9a-f]{40}\z/.freeze
TAG_REF_PREFIX = "refs/tags/".freeze
BRANCH_REF_PREFIX = "refs/heads/".freeze
@@ -65,6 +66,10 @@ module Gitlab
ref == BLANK_SHA
end
+ def commit_id?(ref)
+ COMMIT_ID.match?(ref)
+ end
+
def version
Gitlab::Git::Version.git_version
end
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index b7b7578cef9..6e8aa5d578e 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -464,6 +464,18 @@ module Gitlab
end
end
+ # Returns path to url mappings for submodules
+ #
+ # Ex.
+ # @repository.submodule_urls_for('master')
+ # # => { 'rack' => 'git@localhost:rack.git' }
+ #
+ def submodule_urls_for(ref)
+ wrapped_gitaly_errors do
+ gitaly_submodule_urls_for(ref)
+ end
+ end
+
# Return total commits count accessible from passed ref
def commit_count(ref)
wrapped_gitaly_errors do
@@ -861,13 +873,13 @@ module Gitlab
def multi_action(
user, branch_name:, message:, actions:,
author_email: nil, author_name: nil,
- start_branch_name: nil, start_repository: self,
+ start_branch_name: nil, start_sha: nil, start_repository: self,
force: false)
wrapped_gitaly_errors do
gitaly_operation_client.user_commit_files(user, branch_name,
message, actions, author_email, author_name,
- start_branch_name, start_repository, force)
+ start_branch_name, start_repository, force, start_sha)
end
end
# rubocop:enable Metrics/ParameterLists
@@ -1059,12 +1071,16 @@ module Gitlab
return unless commit_object && commit_object.type == :COMMIT
+ urls = gitaly_submodule_urls_for(ref)
+ urls && urls[path]
+ end
+
+ def gitaly_submodule_urls_for(ref)
gitmodules = gitaly_commit_client.tree_entry(ref, '.gitmodules', Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
return unless gitmodules
- found_module = GitmodulesParser.new(gitmodules.data).parse[path]
-
- found_module && found_module['url']
+ submodules = GitmodulesParser.new(gitmodules.data).parse
+ submodules.transform_values { |submodule| submodule['url'] }
end
# Returns true if the given ref name exists
diff --git a/lib/gitlab/git/rugged_impl/blob.rb b/lib/gitlab/git/rugged_impl/blob.rb
index 86c9f33d82a..9aea736527b 100644
--- a/lib/gitlab/git/rugged_impl/blob.rb
+++ b/lib/gitlab/git/rugged_impl/blob.rb
@@ -16,7 +16,7 @@ module Gitlab
override :tree_entry
def tree_entry(repository, sha, path, limit)
if use_rugged?(repository, :rugged_tree_entry)
- rugged_tree_entry(repository, sha, path, limit)
+ wrap_rugged_call { rugged_tree_entry(repository, sha, path, limit) }
else
super
end
diff --git a/lib/gitlab/git/rugged_impl/commit.rb b/lib/gitlab/git/rugged_impl/commit.rb
index 971a33b2e99..29ae9bdd851 100644
--- a/lib/gitlab/git/rugged_impl/commit.rb
+++ b/lib/gitlab/git/rugged_impl/commit.rb
@@ -36,7 +36,7 @@ module Gitlab
override :find_commit
def find_commit(repo, commit_id)
if use_rugged?(repo, :rugged_find_commit)
- rugged_find(repo, commit_id)
+ wrap_rugged_call { rugged_find(repo, commit_id) }
else
super
end
@@ -45,7 +45,7 @@ module Gitlab
override :batch_by_oid
def batch_by_oid(repo, oids)
if use_rugged?(repo, :rugged_list_commits_by_oid)
- rugged_batch_by_oid(repo, oids)
+ wrap_rugged_call { rugged_batch_by_oid(repo, oids) }
else
super
end
@@ -68,7 +68,7 @@ module Gitlab
override :commit_tree_entry
def commit_tree_entry(path)
if use_rugged?(@repository, :rugged_commit_tree_entry)
- rugged_tree_entry(path)
+ wrap_rugged_call { rugged_tree_entry(path) }
else
super
end
diff --git a/lib/gitlab/git/rugged_impl/repository.rb b/lib/gitlab/git/rugged_impl/repository.rb
index 9268abdfed9..7bed553393c 100644
--- a/lib/gitlab/git/rugged_impl/repository.rb
+++ b/lib/gitlab/git/rugged_impl/repository.rb
@@ -48,7 +48,7 @@ module Gitlab
override :ancestor?
def ancestor?(from, to)
if use_rugged?(self, :rugged_commit_is_ancestor)
- rugged_is_ancestor?(from, to)
+ wrap_rugged_call { rugged_is_ancestor?(from, to) }
else
super
end
diff --git a/lib/gitlab/git/rugged_impl/tree.rb b/lib/gitlab/git/rugged_impl/tree.rb
index f3721a3f1b7..479c5f9d8b7 100644
--- a/lib/gitlab/git/rugged_impl/tree.rb
+++ b/lib/gitlab/git/rugged_impl/tree.rb
@@ -16,7 +16,7 @@ module Gitlab
override :tree_entries
def tree_entries(repository, sha, path, recursive)
if use_rugged?(repository, :rugged_tree_entries)
- tree_entries_with_flat_path_from_rugged(repository, sha, path, recursive)
+ wrap_rugged_call { tree_entries_with_flat_path_from_rugged(repository, sha, path, recursive) }
else
super
end
diff --git a/lib/gitlab/git/rugged_impl/use_rugged.rb b/lib/gitlab/git/rugged_impl/use_rugged.rb
index 99091b03cd1..badf943e39c 100644
--- a/lib/gitlab/git/rugged_impl/use_rugged.rb
+++ b/lib/gitlab/git/rugged_impl/use_rugged.rb
@@ -10,6 +10,23 @@ module Gitlab
Gitlab::GitalyClient.can_use_disk?(repo.storage)
end
+
+ def wrap_rugged_call(&block)
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ start = Gitlab::Metrics::System.monotonic_time
+
+ result = yield
+
+ duration = Gitlab::Metrics::System.monotonic_time - start
+
+ if Gitlab::RuggedInstrumentation.active?
+ Gitlab::RuggedInstrumentation.increment_query_count
+ Gitlab::RuggedInstrumentation.query_time += duration
+ end
+
+ result
+ end
+ end
end
end
end
diff --git a/lib/gitlab/git_logger.rb b/lib/gitlab/git_logger.rb
index dac4ddd320f..ded5349be01 100644
--- a/lib/gitlab/git_logger.rb
+++ b/lib/gitlab/git_logger.rb
@@ -1,13 +1,9 @@
# frozen_string_literal: true
module Gitlab
- class GitLogger < Gitlab::Logger
+ class GitLogger < JsonLogger
def self.file_name_noext
'githost'
end
-
- def format_message(severity, timestamp, progname, msg)
- "#{timestamp.to_s(:long)} -> #{severity} -> #{msg}\n"
- end
end
end
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index 0b6321c66ab..c98de722fe1 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -387,21 +387,20 @@ module Gitlab
end
def self.can_use_disk?(storage)
- false
- # cached_value = MUTEX.synchronize do
- # @can_use_disk ||= {}
- # @can_use_disk[storage]
- # end
+ cached_value = MUTEX.synchronize do
+ @can_use_disk ||= {}
+ @can_use_disk[storage]
+ end
- # return cached_value unless cached_value.nil?
+ return cached_value unless cached_value.nil?
- # gitaly_filesystem_id = filesystem_id(storage)
- # direct_filesystem_id = filesystem_id_from_disk(storage)
+ gitaly_filesystem_id = filesystem_id(storage)
+ direct_filesystem_id = filesystem_id_from_disk(storage)
- # MUTEX.synchronize do
- # @can_use_disk[storage] = gitaly_filesystem_id.present? &&
- # gitaly_filesystem_id == direct_filesystem_id
- # end
+ MUTEX.synchronize do
+ @can_use_disk[storage] = gitaly_filesystem_id.present? &&
+ gitaly_filesystem_id == direct_filesystem_id
+ end
end
def self.filesystem_id(storage)
@@ -414,7 +413,7 @@ module Gitlab
metadata_file = File.read(storage_metadata_file_path(storage))
metadata_hash = JSON.parse(metadata_file)
metadata_hash['gitaly_filesystem_id']
- rescue Errno::ENOENT, JSON::ParserError
+ rescue Errno::ENOENT, Errno::ACCESS, JSON::ParserError
nil
end
diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb
index 783c2ff0915..33ca428a942 100644
--- a/lib/gitlab/gitaly_client/operation_service.rb
+++ b/lib/gitlab/gitaly_client/operation_service.rb
@@ -325,11 +325,11 @@ module Gitlab
# rubocop:disable Metrics/ParameterLists
def user_commit_files(
user, branch_name, commit_message, actions, author_email, author_name,
- start_branch_name, start_repository, force = false)
+ start_branch_name, start_repository, force = false, start_sha = nil)
req_enum = Enumerator.new do |y|
header = user_commit_files_request_header(user, branch_name,
commit_message, actions, author_email, author_name,
- start_branch_name, start_repository, force)
+ start_branch_name, start_repository, force, start_sha)
y.yield Gitaly::UserCommitFilesRequest.new(header: header)
@@ -445,7 +445,7 @@ module Gitlab
# rubocop:disable Metrics/ParameterLists
def user_commit_files_request_header(
user, branch_name, commit_message, actions, author_email, author_name,
- start_branch_name, start_repository, force)
+ start_branch_name, start_repository, force, start_sha)
Gitaly::UserCommitFilesRequestHeader.new(
repository: @gitaly_repo,
@@ -456,7 +456,8 @@ module Gitlab
commit_author_email: encode_binary(author_email),
start_branch_name: encode_binary(start_branch_name),
start_repository: start_repository.gitaly_repository,
- force: force
+ force: force,
+ start_sha: encode_binary(start_sha)
)
end
# rubocop:enable Metrics/ParameterLists
diff --git a/lib/gitlab/grape_logging/loggers/perf_logger.rb b/lib/gitlab/grape_logging/loggers/perf_logger.rb
index 18ea3a8d2f3..7e86b35a215 100644
--- a/lib/gitlab/grape_logging/loggers/perf_logger.rb
+++ b/lib/gitlab/grape_logging/loggers/perf_logger.rb
@@ -6,11 +6,30 @@ module Gitlab
module Loggers
class PerfLogger < ::GrapeLogging::Loggers::Base
def parameters(_, _)
+ gitaly_data.merge(rugged_data)
+ end
+
+ def gitaly_data
+ gitaly_calls = Gitlab::GitalyClient.get_request_count
+
+ return {} if gitaly_calls.zero?
+
{
gitaly_calls: Gitlab::GitalyClient.get_request_count,
gitaly_duration: Gitlab::GitalyClient.query_time_ms
}
end
+
+ def rugged_data
+ rugged_calls = Gitlab::RuggedInstrumentation.query_count
+
+ return {} if rugged_calls.zero?
+
+ {
+ rugged_calls: rugged_calls,
+ rugged_duration_ms: Gitlab::RuggedInstrumentation.query_time_ms
+ }
+ end
end
end
end
diff --git a/lib/gitlab/graphql/representation/submodule_tree_entry.rb b/lib/gitlab/graphql/representation/submodule_tree_entry.rb
new file mode 100644
index 00000000000..65716dff75d
--- /dev/null
+++ b/lib/gitlab/graphql/representation/submodule_tree_entry.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Graphql
+ module Representation
+ class SubmoduleTreeEntry < SimpleDelegator
+ class << self
+ def decorate(submodules, tree)
+ repository = tree.repository
+ submodule_links = Gitlab::SubmoduleLinks.new(repository)
+
+ submodules.map do |submodule|
+ self.new(submodule, submodule_links.for(submodule, tree.sha))
+ end
+ end
+ end
+
+ def initialize(submodule, submodule_links)
+ @submodule_links = submodule_links
+
+ super(submodule)
+ end
+
+ def web_url
+ @submodule_links.first
+ end
+
+ def tree_url
+ @submodule_links.last
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb
index dec99c23a2d..91fe4e5d074 100644
--- a/lib/gitlab/import_export/project_tree_restorer.rb
+++ b/lib/gitlab/import_export/project_tree_restorer.rb
@@ -130,6 +130,7 @@ module Gitlab
def visibility_level
level = override_params['visibility_level'] || json_params['visibility_level'] || @project.visibility_level
level = @project.group.visibility_level if @project.group && level.to_i > @project.group.visibility_level
+ level = Gitlab::VisibilityLevel::PRIVATE if level == Gitlab::VisibilityLevel::INTERNAL && Gitlab::CurrentSettings.restricted_visibility_levels.include?(level)
{ 'visibility_level' => level }
end
diff --git a/lib/gitlab/metrics/samplers/ruby_sampler.rb b/lib/gitlab/metrics/samplers/ruby_sampler.rb
index eef802caabb..79f756c8f8a 100644
--- a/lib/gitlab/metrics/samplers/ruby_sampler.rb
+++ b/lib/gitlab/metrics/samplers/ruby_sampler.rb
@@ -7,7 +7,7 @@ module Gitlab
module Samplers
class RubySampler < BaseSampler
def initialize(interval)
- metrics[:process_start_time_seconds].set(labels.merge(worker_label), Time.now.to_i)
+ metrics[:process_start_time_seconds].set(labels, Time.now.to_i)
super
end
@@ -50,9 +50,9 @@ module Gitlab
def sample
start_time = System.monotonic_time
- metrics[:file_descriptors].set(labels.merge(worker_label), System.file_descriptor_count)
- metrics[:process_cpu_seconds_total].set(labels.merge(worker_label), ::Gitlab::Metrics::System.cpu_time)
- metrics[:process_max_fds].set(labels.merge(worker_label), ::Gitlab::Metrics::System.max_open_file_descriptors)
+ metrics[:file_descriptors].set(labels, System.file_descriptor_count)
+ metrics[:process_cpu_seconds_total].set(labels, ::Gitlab::Metrics::System.cpu_time)
+ metrics[:process_max_fds].set(labels, ::Gitlab::Metrics::System.max_open_file_descriptors)
set_memory_usage_metrics
sample_gc
@@ -75,22 +75,9 @@ module Gitlab
def set_memory_usage_metrics
memory_usage = System.memory_usage
- memory_labels = labels.merge(worker_label)
- metrics[:memory_bytes].set(memory_labels, memory_usage)
- metrics[:process_resident_memory_bytes].set(memory_labels, memory_usage)
- 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
- { worker: 'master' }
- end
+ metrics[:memory_bytes].set(labels, memory_usage)
+ metrics[:process_resident_memory_bytes].set(labels, memory_usage)
end
end
end
diff --git a/lib/gitlab/metrics/subscribers/rails_cache.rb b/lib/gitlab/metrics/subscribers/rails_cache.rb
index 01db507761b..2ee7144fe2f 100644
--- a/lib/gitlab/metrics/subscribers/rails_cache.rb
+++ b/lib/gitlab/metrics/subscribers/rails_cache.rb
@@ -50,7 +50,8 @@ module Gitlab
def observe(key, duration)
return unless current_transaction
- metric_cache_operation_duration_seconds.observe(current_transaction.labels.merge({ operation: key }), duration / 1000.0)
+ metric_cache_operations_total.increment(current_transaction.labels.merge({ operation: key }))
+ metric_cache_operation_duration_seconds.observe({ operation: key }, duration / 1000.0)
current_transaction.increment(:cache_duration, duration, false)
current_transaction.increment(:cache_count, 1, false)
current_transaction.increment("cache_#{key}_duration".to_sym, duration, false)
@@ -63,12 +64,20 @@ module Gitlab
Transaction.current
end
+ def metric_cache_operations_total
+ @metric_cache_operations_total ||= ::Gitlab::Metrics.counter(
+ :gitlab_cache_operations_total,
+ 'Cache operations',
+ Transaction::BASE_LABELS
+ )
+ end
+
def metric_cache_operation_duration_seconds
@metric_cache_operation_duration_seconds ||= ::Gitlab::Metrics.histogram(
:gitlab_cache_operation_duration_seconds,
'Cache access time',
- Transaction::BASE_LABELS.merge({ action: nil }),
- [0.001, 0.01, 0.1, 1, 10]
+ {},
+ [0.00001, 0.0001, 0.001, 0.01, 0.1, 1.0]
)
end
diff --git a/lib/gitlab/push_options.rb b/lib/gitlab/push_options.rb
index 3137676ba4b..b96590af08e 100644
--- a/lib/gitlab/push_options.rb
+++ b/lib/gitlab/push_options.rb
@@ -4,7 +4,12 @@ module Gitlab
class PushOptions
VALID_OPTIONS = HashWithIndifferentAccess.new({
merge_request: {
- keys: [:create, :merge_when_pipeline_succeeds, :target]
+ keys: [
+ :create,
+ :merge_when_pipeline_succeeds,
+ :remove_source_branch,
+ :target
+ ]
},
ci: {
keys: [:skip]
diff --git a/lib/gitlab/request_profiler.rb b/lib/gitlab/request_profiler.rb
index 64593153686..033e451dbee 100644
--- a/lib/gitlab/request_profiler.rb
+++ b/lib/gitlab/request_profiler.rb
@@ -6,6 +6,21 @@ module Gitlab
module RequestProfiler
PROFILES_DIR = "#{Gitlab.config.shared.path}/tmp/requests_profiles".freeze
+ def all
+ Dir["#{PROFILES_DIR}/*.{html,txt}"].map do |path|
+ Profile.new(File.basename(path))
+ end.select(&:valid?)
+ end
+ module_function :all
+
+ def find(name)
+ file_path = File.join(PROFILES_DIR, name)
+ return unless File.exist?(file_path)
+
+ Profile.new(name)
+ end
+ module_function :find
+
def profile_token
Rails.cache.fetch('profile-token') do
Devise.friendly_token
diff --git a/lib/gitlab/request_profiler/middleware.rb b/lib/gitlab/request_profiler/middleware.rb
index 7615f6f443b..99958d7a211 100644
--- a/lib/gitlab/request_profiler/middleware.rb
+++ b/lib/gitlab/request_profiler/middleware.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'ruby-prof'
+require 'memory_profiler'
module Gitlab
module RequestProfiler
@@ -28,22 +29,73 @@ module Gitlab
end
def call_with_profiling(env)
+ case env['HTTP_X_PROFILE_MODE']
+ when 'execution', nil
+ call_with_call_stack_profiling(env)
+ when 'memory'
+ call_with_memory_profiling(env)
+ else
+ raise ActionController::BadRequest, invalid_profile_mode(env)
+ end
+ end
+
+ def invalid_profile_mode(env)
+ <<~HEREDOC
+ Invalid X-Profile-Mode: #{env['HTTP_X_PROFILE_MODE']}.
+ Supported profile mode request header:
+ - X-Profile-Mode: execution
+ - X-Profile-Mode: memory
+ HEREDOC
+ end
+
+ def call_with_call_stack_profiling(env)
ret = nil
- result = RubyProf::Profile.profile do
+ report = RubyProf::Profile.profile do
ret = catch(:warden) do
@app.call(env)
end
end
- printer = RubyProf::CallStackPrinter.new(result)
- file_name = "#{env['PATH_INFO'].tr('/', '|')}_#{Time.current.to_i}.html"
+ generate_report(env, 'execution', 'html') do |file|
+ printer = RubyProf::CallStackPrinter.new(report)
+ printer.print(file)
+ end
+
+ handle_request_ret(ret)
+ end
+
+ def call_with_memory_profiling(env)
+ ret = nil
+ report = MemoryProfiler.report do
+ ret = catch(:warden) do
+ @app.call(env)
+ end
+ end
+
+ generate_report(env, 'memory', 'txt') do |file|
+ report.pretty_print(to_file: file)
+ end
+
+ handle_request_ret(ret)
+ end
+
+ def generate_report(env, report_type, extension)
+ file_name = "#{env['PATH_INFO'].tr('/', '|')}_#{Time.current.to_i}"\
+ "_#{report_type}.#{extension}"
file_path = "#{PROFILES_DIR}/#{file_name}"
FileUtils.mkdir_p(PROFILES_DIR)
- File.open(file_path, 'wb') do |file|
- printer.print(file)
+
+ begin
+ File.open(file_path, 'wb') do |file|
+ yield(file)
+ end
+ rescue
+ FileUtils.rm(file_path)
end
+ end
+ def handle_request_ret(ret)
if ret.is_a?(Array)
ret
else
diff --git a/lib/gitlab/request_profiler/profile.rb b/lib/gitlab/request_profiler/profile.rb
index 46996ef8c51..76c675658b1 100644
--- a/lib/gitlab/request_profiler/profile.rb
+++ b/lib/gitlab/request_profiler/profile.rb
@@ -3,42 +3,40 @@
module Gitlab
module RequestProfiler
class Profile
- attr_reader :name, :time, :request_path
+ attr_reader :name, :time, :file_path, :request_path, :profile_mode, :type
alias_method :to_param, :name
- def self.all
- Dir["#{PROFILES_DIR}/*.html"].map do |path|
- new(File.basename(path))
- end
- end
-
- def self.find(name)
- name_dup = name.dup
- name_dup << '.html' unless name.end_with?('.html')
-
- file_path = "#{PROFILES_DIR}/#{name_dup}"
- return unless File.exist?(file_path)
-
- new(name_dup)
- end
-
def initialize(name)
@name = name
+ @file_path = File.join(PROFILES_DIR, name)
set_attributes
end
- def content
- File.read("#{PROFILES_DIR}/#{name}")
+ def valid?
+ @request_path.present?
+ end
+
+ def content_type
+ case type
+ when 'html'
+ 'text/html'
+ when 'txt'
+ 'text/plain'
+ end
end
private
def set_attributes
- _, path, timestamp = name.split(/(.*)_(\d+)\.html$/)
- @request_path = path.tr('|', '/')
- @time = Time.at(timestamp.to_i).utc
+ matches = name.match(/^(?<path>.*)_(?<timestamp>\d+)(_(?<profile_mode>\w+))?\.(?<type>html|txt)$/)
+ return unless matches
+
+ @request_path = matches[:path].tr('|', '/')
+ @time = Time.at(matches[:timestamp].to_i).utc
+ @profile_mode = matches[:profile_mode] || 'unknown'
+ @type = matches[:type]
end
end
end
diff --git a/lib/gitlab/rugged_instrumentation.rb b/lib/gitlab/rugged_instrumentation.rb
new file mode 100644
index 00000000000..70c06e8b308
--- /dev/null
+++ b/lib/gitlab/rugged_instrumentation.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module RuggedInstrumentation
+ def self.query_time
+ SafeRequestStore[:rugged_query_time] ||= 0
+ end
+
+ def self.query_time=(duration)
+ SafeRequestStore[:rugged_query_time] = duration
+ end
+
+ def self.query_time_ms
+ (self.query_time * 1000).round(2)
+ end
+
+ def self.query_count
+ SafeRequestStore[:rugged_call_count] ||= 0
+ end
+
+ def self.increment_query_count
+ SafeRequestStore[:rugged_call_count] ||= 0
+ SafeRequestStore[:rugged_call_count] += 1
+ end
+
+ def self.active?
+ Gitlab::SafeRequestStore.active?
+ end
+ end
+end
diff --git a/lib/gitlab/slug/environment.rb b/lib/gitlab/slug/environment.rb
new file mode 100644
index 00000000000..1b87d3bb626
--- /dev/null
+++ b/lib/gitlab/slug/environment.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+# An environment name is not necessarily suitable for use in URLs, DNS
+# or other third-party contexts, so provide a slugified version. A slug has
+# the following properties:
+# * contains only lowercase letters (a-z), numbers (0-9), and '-'
+# * begins with a letter
+# * has a maximum length of 24 bytes (OpenShift limitation)
+# * cannot end with `-`
+module Gitlab
+ module Slug
+ class Environment
+ attr_reader :name
+
+ def initialize(name)
+ @name = name
+ end
+
+ def generate
+ # Lowercase letters and numbers only
+ slugified = name.to_s.downcase.gsub(/[^a-z0-9]/, '-')
+
+ # Must start with a letter
+ slugified = 'env-' + slugified unless slugified.match?(/^[a-z]/)
+
+ # Repeated dashes are invalid (OpenShift limitation)
+ slugified.squeeze!('-')
+
+ slugified =
+ if slugified.size > 24 || slugified != name
+ # Maximum length: 24 characters (OpenShift limitation)
+ shorten_and_add_suffix(slugified)
+ else
+ # Cannot end with a dash (Kubernetes label limitation)
+ slugified.chomp('-')
+ end
+
+ slugified
+ end
+
+ private
+
+ def shorten_and_add_suffix(slug)
+ slug = slug[0..16]
+ slug << '-' unless slug.ends_with?('-')
+ slug << suffix
+ end
+
+ # Slugifying a name may remove the uniqueness guarantee afforded by it being
+ # based on name (which must be unique). To compensate, we add a predictable
+ # 6-byte suffix in those circumstances. This is not *guaranteed* uniqueness,
+ # but the chance of collisions is vanishingly small
+ def suffix
+ Digest::SHA2.hexdigest(name.to_s).to_i(16).to_s(36).last(6)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/submodule_links.rb b/lib/gitlab/submodule_links.rb
new file mode 100644
index 00000000000..a6c0369d864
--- /dev/null
+++ b/lib/gitlab/submodule_links.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Gitlab
+ class SubmoduleLinks
+ include Gitlab::Utils::StrongMemoize
+
+ def initialize(repository)
+ @repository = repository
+ end
+
+ def for(submodule, sha)
+ submodule_url = submodule_url_for(sha)[submodule.path]
+ SubmoduleHelper.submodule_links_for_url(submodule.id, submodule_url, repository)
+ end
+
+ private
+
+ attr_reader :repository
+
+ def submodule_url_for(sha)
+ strong_memoize(:"submodule_links_for_#{sha}") do
+ repository.submodule_urls_for(sha)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index 055e01a9399..db1086c9cae 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -6,7 +6,9 @@ module Gitlab
class << self
def data(force_refresh: false)
- Rails.cache.fetch('usage_data', force: force_refresh, expires_in: 2.weeks) { uncached_data }
+ Rails.cache.fetch('usage_data', force: force_refresh, expires_in: 2.weeks) do
+ uncached_data
+ end
end
def uncached_data
@@ -128,10 +130,15 @@ module Gitlab
}
end
+ # @return [Hash<Symbol, Integer>]
def usage_counters
- {
- web_ide_commits: Gitlab::UsageDataCounters::WebIdeCommitsCounter.total_count
- }
+ usage_data_counters.map(&:totals).reduce({}) { |a, b| a.merge(b) }
+ end
+
+ # @return [Array<#totals>] An array of objects that respond to `#totals`
+ def usage_data_counters
+ [Gitlab::UsageDataCounters::WikiPageCounter,
+ Gitlab::UsageDataCounters::WebIdeCounter]
end
def components_usage_data
diff --git a/lib/gitlab/usage_data_counters/redis_counter.rb b/lib/gitlab/usage_data_counters/redis_counter.rb
index 123b8e1bef1..75d5a75e3a4 100644
--- a/lib/gitlab/usage_data_counters/redis_counter.rb
+++ b/lib/gitlab/usage_data_counters/redis_counter.rb
@@ -3,17 +3,15 @@
module Gitlab
module UsageDataCounters
module RedisCounter
- def increment
+ def increment(redis_counter_key)
+ return unless Gitlab::CurrentSettings.usage_ping_enabled
+
Gitlab::Redis::SharedState.with { |redis| redis.incr(redis_counter_key) }
end
- def total_count
+ def total_count(redis_counter_key)
Gitlab::Redis::SharedState.with { |redis| redis.get(redis_counter_key).to_i }
end
-
- def redis_counter_key
- raise NotImplementedError
- end
end
end
end
diff --git a/lib/gitlab/usage_data_counters/web_ide_commits_counter.rb b/lib/gitlab/usage_data_counters/web_ide_commits_counter.rb
deleted file mode 100644
index 62236fa07a3..00000000000
--- a/lib/gitlab/usage_data_counters/web_ide_commits_counter.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module UsageDataCounters
- class WebIdeCommitsCounter
- extend RedisCounter
-
- def self.redis_counter_key
- 'WEB_IDE_COMMITS_COUNT'
- end
- end
- end
-end
diff --git a/lib/gitlab/usage_data_counters/web_ide_counter.rb b/lib/gitlab/usage_data_counters/web_ide_counter.rb
new file mode 100644
index 00000000000..0718c1dd761
--- /dev/null
+++ b/lib/gitlab/usage_data_counters/web_ide_counter.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module UsageDataCounters
+ class WebIdeCounter
+ extend RedisCounter
+
+ COMMITS_COUNT_KEY = 'WEB_IDE_COMMITS_COUNT'
+ MERGE_REQUEST_COUNT_KEY = 'WEB_IDE_MERGE_REQUESTS_COUNT'
+ VIEWS_COUNT_KEY = 'WEB_IDE_VIEWS_COUNT'
+
+ class << self
+ def increment_commits_count
+ increment(COMMITS_COUNT_KEY)
+ end
+
+ def total_commits_count
+ total_count(COMMITS_COUNT_KEY)
+ end
+
+ def increment_merge_requests_count
+ increment(MERGE_REQUEST_COUNT_KEY)
+ end
+
+ def total_merge_requests_count
+ total_count(MERGE_REQUEST_COUNT_KEY)
+ end
+
+ def increment_views_count
+ increment(VIEWS_COUNT_KEY)
+ end
+
+ def total_views_count
+ total_count(VIEWS_COUNT_KEY)
+ end
+
+ def totals
+ {
+ web_ide_commits: total_commits_count,
+ web_ide_views: total_views_count,
+ web_ide_merge_requests: total_merge_requests_count
+ }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage_data_counters/wiki_page_counter.rb b/lib/gitlab/usage_data_counters/wiki_page_counter.rb
new file mode 100644
index 00000000000..c8b59a3160c
--- /dev/null
+++ b/lib/gitlab/usage_data_counters/wiki_page_counter.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Gitlab::UsageDataCounters
+ class WikiPageCounter
+ extend RedisCounter
+
+ KNOWN_EVENTS = %w[create update delete].map(&:freeze).freeze
+
+ UnknownEvent = Class.new(StandardError)
+
+ class << self
+ # Each event gets a unique Redis key
+ def redis_key(event)
+ raise UnknownEvent, event unless KNOWN_EVENTS.include?(event.to_s)
+
+ "USAGE_WIKI_PAGES_#{event}".upcase
+ end
+
+ def count(event)
+ increment(redis_key event)
+ end
+
+ def read(event)
+ total_count(redis_key event)
+ end
+
+ def totals
+ KNOWN_EVENTS.map { |e| ["wiki_pages_#{e}".to_sym, read(e)] }.to_h
+ end
+ end
+ end
+end
diff --git a/lib/peek/views/redis.rb b/lib/peek/views/redis_detailed.rb
index 73de8672fa4..12760c9b75e 100644
--- a/lib/peek/views/redis.rb
+++ b/lib/peek/views/redis_detailed.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'redis'
-require 'peek-redis'
module Gitlab
module Peek
@@ -36,11 +35,42 @@ end
module Peek
module Views
- module RedisDetailed
+ class RedisDetailed < View
REDACTED_MARKER = "<redacted>"
+ def key
+ 'redis'
+ end
+
def results
- super.merge(details: details)
+ {
+ calls: calls,
+ duration: formatted_duration,
+ details: details
+ }
+ end
+
+ def detail_store
+ ::Gitlab::SafeRequestStore['redis_call_details'] ||= []
+ end
+
+ private
+
+ def formatted_duration
+ ms = duration * 1000
+ if ms >= 1000
+ "%.2fms" % ms
+ else
+ "%.0fms" % ms
+ end
+ end
+
+ def duration
+ detail_store.map { |entry| entry[:duration] }.sum # rubocop:disable CodeReuse/ActiveRecord
+ end
+
+ def calls
+ detail_store.count
end
def details
@@ -49,10 +79,6 @@ module Peek
.map(&method(:format_call_details))
end
- def detail_store
- ::Gitlab::SafeRequestStore['redis_call_details'] ||= []
- end
-
def format_call_details(call)
call.merge(cmd: format_command(call[:cmd]),
duration: (call[:duration] * 1000).round(3))
@@ -76,11 +102,3 @@ end
class Redis::Client
prepend Gitlab::Peek::RedisInstrumented
end
-
-module Peek
- module Views
- class Redis < View
- prepend Peek::Views::RedisDetailed
- end
- end
-end
diff --git a/lib/prometheus/pid_provider.rb b/lib/prometheus/pid_provider.rb
new file mode 100644
index 00000000000..c92522c73c5
--- /dev/null
+++ b/lib/prometheus/pid_provider.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'prometheus/client/support/unicorn'
+
+module Prometheus
+ module PidProvider
+ extend self
+
+ def worker_id
+ if Sidekiq.server?
+ 'sidekiq'
+ elsif defined?(Unicorn::Worker)
+ "unicorn_#{unicorn_worker_id}"
+ elsif defined?(::Puma)
+ "puma_#{puma_worker_id}"
+ else
+ "process_#{Process.pid}"
+ end
+ end
+
+ private
+
+ # This is not fully accurate as we don't really know if the nil returned
+ # is actually means we're on master or not.
+ # Follow up issue was created to address this problem and
+ # to introduce more structrured approach to a current process discovery:
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/64740
+ def unicorn_worker_id
+ ::Prometheus::Client::Support::Unicorn.worker_id || 'master'
+ end
+
+ # See the comment for #unicorn_worker_id
+ def puma_worker_id
+ match = process_name.match(/cluster worker ([0-9]+):/)
+ match ? match[1] : 'master'
+ end
+
+ def process_name
+ $0
+ end
+ end
+end
diff --git a/lib/tasks/frontend.rake b/lib/tasks/frontend.rake
new file mode 100644
index 00000000000..1cac7520227
--- /dev/null
+++ b/lib/tasks/frontend.rake
@@ -0,0 +1,21 @@
+unless Rails.env.production?
+ namespace :frontend do
+ desc 'GitLab | Frontend | Generate fixtures for JavaScript tests'
+ RSpec::Core::RakeTask.new(:fixtures, [:pattern]) do |t, args|
+ args.with_defaults(pattern: '{spec,ee/spec}/frontend/fixtures/*.rb')
+ ENV['NO_KNAPSACK'] = 'true'
+ t.pattern = args[:pattern]
+ t.rspec_opts = '--format documentation'
+ end
+
+ desc 'GitLab | Frontend | Run JavaScript tests'
+ task tests: ['yarn:check'] do
+ sh "yarn test" do |ok, res|
+ abort('rake frontend:tests failed') unless ok
+ end
+ end
+ end
+
+ desc 'GitLab | Frontend | Shortcut for frontend:fixtures and frontend:tests'
+ task frontend: ['frontend:fixtures', 'frontend:tests']
+end
diff --git a/lib/tasks/karma.rake b/lib/tasks/karma.rake
index 2dc14183fa3..36590010406 100644
--- a/lib/tasks/karma.rake
+++ b/lib/tasks/karma.rake
@@ -1,15 +1,8 @@
unless Rails.env.production?
namespace :karma do
+ # alias exists for legacy reasons
desc 'GitLab | Karma | Generate fixtures for JavaScript tests'
- task fixtures: ['karma:rspec_fixtures']
-
- desc 'GitLab | Karma | Generate fixtures using RSpec'
- RSpec::Core::RakeTask.new(:rspec_fixtures, [:pattern]) do |t, args|
- args.with_defaults(pattern: '{spec,ee/spec}/javascripts/fixtures/*.rb')
- ENV['NO_KNAPSACK'] = 'true'
- t.pattern = args[:pattern]
- t.rspec_opts = '--format documentation'
- end
+ task fixtures: ['frontend:fixtures']
desc 'GitLab | Karma | Run JavaScript tests'
task tests: ['yarn:check'] do
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 5e8d1ac206a..bd26ca6714d 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -5324,6 +5324,9 @@ msgstr ""
msgid "ID"
msgstr ""
+msgid "ID:"
+msgstr ""
+
msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
msgstr ""
@@ -5717,6 +5720,21 @@ msgstr ""
msgid "IssueBoards|Boards"
msgstr ""
+msgid "IssueBoards|Create new board"
+msgstr ""
+
+msgid "IssueBoards|Delete board"
+msgstr ""
+
+msgid "IssueBoards|No matching boards found"
+msgstr ""
+
+msgid "IssueBoards|Some of your boards are hidden, activate a license to see them again."
+msgstr ""
+
+msgid "IssueBoards|Switch board"
+msgstr ""
+
msgid "IssueTracker|Bugzilla issue tracker"
msgstr ""
@@ -8698,6 +8716,9 @@ msgstr ""
msgid "Receive notifications about your own activity"
msgstr ""
+msgid "Recent"
+msgstr ""
+
msgid "Recent Project Activity"
msgstr ""
@@ -10117,6 +10138,18 @@ msgstr ""
msgid "StorageSize|Unknown"
msgstr ""
+msgid "SubgroupCreationLevel|Allowed to create subgroups"
+msgstr ""
+
+msgid "SubgroupCreationlevel|Allowed to create subgroups"
+msgstr ""
+
+msgid "SubgroupCreationlevel|Maintainers"
+msgstr ""
+
+msgid "SubgroupCreationlevel|Owners"
+msgstr ""
+
msgid "Subgroups"
msgstr ""
diff --git a/package.json b/package.json
index 44aa850860e..ef88d9c658c 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,7 @@
"prettier-staged-save": "node ./scripts/frontend/prettier.js save",
"prettier-all": "node ./scripts/frontend/prettier.js check-all",
"prettier-all-save": "node ./scripts/frontend/prettier.js save-all",
- "stylelint": "node node_modules/stylelint/bin/stylelint.js app/assets/stylesheets/**/*.* ee/app/assets/stylesheets/**/*.* !**/vendors/** --custom-formatter node_modules/stylelint-error-string-formatter",
+ "stylelint": "node node_modules/stylelint/bin/stylelint.js app/assets/stylesheets/**/*.* ee/app/assets/stylesheets/**/*.* !**/vendors/**",
"stylelint-file": "node node_modules/stylelint/bin/stylelint.js",
"stylelint-create-utility-map": "node scripts/frontend/stylelint/stylelint-utility-map.js",
"test": "node scripts/frontend/test",
@@ -38,7 +38,7 @@
"@babel/preset-env": "^7.4.4",
"@gitlab/csslab": "^1.9.0",
"@gitlab/svgs": "^1.67.0",
- "@gitlab/ui": "^5.6.0",
+ "@gitlab/ui": "^5.7.0",
"apollo-cache-inmemory": "^1.5.1",
"apollo-client": "^2.5.1",
"apollo-link": "^1.2.11",
@@ -117,7 +117,6 @@
"sql.js": "^0.4.0",
"stickyfilljs": "^2.0.5",
"style-loader": "^0.23.1",
- "stylelint-error-string-formatter": "1.0.2",
"svg4everybody": "2.1.9",
"three": "^0.84.0",
"three-orbit-controls": "^82.1.0",
@@ -138,7 +137,7 @@
"vue-virtual-scroll-list": "^1.3.1",
"vuex": "^3.1.0",
"webpack": "^4.29.0",
- "webpack-bundle-analyzer": "^3.0.3",
+ "webpack-bundle-analyzer": "^3.3.2",
"webpack-cli": "^3.2.1",
"webpack-stats-plugin": "^0.2.1",
"worker-loader": "^2.0.0",
@@ -191,9 +190,10 @@
"pixelmatch": "^4.0.2",
"postcss": "^7.0.14",
"prettier": "1.18.2",
- "stylelint": "^9.10.1",
- "stylelint-config-recommended": "^2.1.0",
- "stylelint-scss": "^3.5.4",
+ "readdir-enhanced": "^2.2.4",
+ "stylelint": "^10.1.0",
+ "stylelint-config-recommended": "^2.2.0",
+ "stylelint-scss": "^3.9.2",
"vue-jest": "^4.0.0-beta.2",
"webpack-dev-server": "^3.1.14",
"yarn-deduplicate": "^1.1.1"
diff --git a/qa/Gemfile b/qa/Gemfile
index c46be8a0362..53e7cc497e2 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -10,6 +10,7 @@ gem 'selenium-webdriver', '~> 3.12'
gem 'airborne', '~> 0.2.13'
gem 'nokogiri', '~> 1.10.3'
gem 'rspec-retry', '~> 0.6.1'
+gem 'rspec_junit_formatter', '~> 0.4.1'
gem 'faker', '~> 1.6', '>= 1.6.6'
gem 'knapsack', '~> 1.17'
gem 'parallel_tests', '~> 2.29'
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 73aabf2c6ad..7d19366f83b 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -87,6 +87,8 @@ GEM
rspec-retry (0.6.1)
rspec-core (> 3.3)
rspec-support (3.7.0)
+ rspec_junit_formatter (0.4.1)
+ rspec-core (>= 2, < 4, != 2.12.0)
rubyzip (1.2.2)
selenium-webdriver (3.141.0)
childprocess (~> 0.5)
@@ -116,6 +118,7 @@ DEPENDENCIES
rake (~> 12.3.0)
rspec (~> 3.7)
rspec-retry (~> 0.6.1)
+ rspec_junit_formatter (~> 0.4.1)
selenium-webdriver (~> 3.12)
BUNDLED WITH
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index 80ce23d5c38..45496c6b245 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -181,11 +181,11 @@ module QA
return ["Page class does not have views / elements defined!"]
end
- views.map(&:errors).flatten
+ views.flat_map(&:errors)
end
def self.elements
- views.map(&:elements).flatten
+ views.flat_map(&:elements)
end
def send_keys_to_element(name, keys)
diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb
index 8970eeb6678..c2b0482d789 100644
--- a/qa/qa/page/main/login.rb
+++ b/qa/qa/page/main/login.rb
@@ -6,7 +6,7 @@ module QA
class Login < Page::Base
view 'app/views/devise/passwords/edit.html.haml' do
element :password_field
- element :password_confirmation
+ element :password_confirmation_field
element :change_password_button
end
@@ -44,7 +44,7 @@ module QA
def sign_in_using_credentials(user = nil)
# Don't try to log-in if we're already logged-in
- return if Page::Main::Menu.act { has_personal_area?(wait: 0) }
+ return if Page::Main::Menu.perform { |menu| menu.has_personal_area?(wait: 0) }
using_wait_time 0 do
set_initial_password_if_present
@@ -58,7 +58,7 @@ module QA
end
end
- Page::Main::Menu.act { has_personal_area? }
+ Page::Main::Menu.perform(&:has_personal_area?)
end
def sign_in_using_admin_credentials
@@ -73,7 +73,7 @@ module QA
sign_in_using_gitlab_credentials(admin)
end
- Page::Main::Menu.act { has_personal_area? }
+ Page::Main::Menu.perform(&:has_personal_area?)
end
def self.path
@@ -154,7 +154,7 @@ module QA
return unless has_content?('Change your password')
fill_element :password_field, Runtime::User.password
- fill_element :password_confirmation, Runtime::User.password
+ fill_element :password_confirmation_field, Runtime::User.password
click_element :change_password_button
end
end
diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb
index c98d85d7911..d86d554356e 100644
--- a/qa/qa/page/main/menu.rb
+++ b/qa/qa/page/main/menu.rb
@@ -6,7 +6,7 @@ module QA
class Menu < Page::Base
view 'app/views/layouts/header/_current_user_dropdown.html.haml' do
element :sign_out_link
- element :settings_link, 'link_to s_("CurrentUser|Settings")' # rubocop:disable QA/ElementWithPattern
+ element :settings_link
end
view 'app/views/layouts/header/_default.html.haml' do
diff --git a/qa/qa/page/validator.rb b/qa/qa/page/validator.rb
index edd12665f1e..9b2d0a1a41d 100644
--- a/qa/qa/page/validator.rb
+++ b/qa/qa/page/validator.rb
@@ -22,16 +22,14 @@ module QA
end
def descendants
- @descendants ||= constants.map do |const|
+ @descendants ||= constants.flat_map do |const|
case const
when Class
const if const < Page::Base
when Module
Page::Validator.new(const).descendants
end
- end
-
- @descendants.flatten.compact
+ end.compact
end
def errors
diff --git a/qa/qa/scenario/test/sanity/selectors.rb b/qa/qa/scenario/test/sanity/selectors.rb
index e05269e8d55..b4d70fc191a 100644
--- a/qa/qa/scenario/test/sanity/selectors.rb
+++ b/qa/qa/scenario/test/sanity/selectors.rb
@@ -14,7 +14,7 @@ module QA
Page::Validator.new(pages)
end
- validators.map(&:errors).flatten.tap do |errors|
+ validators.flat_map(&:errors).tap do |errors|
break if errors.none?
warn <<~EOS
diff --git a/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb
index 2363836d5e3..c9acd7df776 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb
@@ -5,7 +5,7 @@ module QA
describe 'Project transfer between groups' do
it 'user transfers a project between groups' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
+ Page::Main::Login.perform(&:sign_in_using_credentials)
source_group = Resource::Group.fabricate_via_api! do |group|
group.path = 'source-group'
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb
index 09d1d3fe76e..6556c28ccab 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb
@@ -5,7 +5,7 @@ module QA
describe 'basic user login' do
it 'user logs in using basic credentials and logs out' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
+ Page::Main::Login.perform(&:sign_in_using_credentials)
Page::Main::Menu.perform do |menu|
expect(menu).to have_personal_area
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb
index 72dde4e5bd8..10cd8470a8f 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb
@@ -5,7 +5,7 @@ module QA
describe 'LDAP login' do
it 'user logs into GitLab using LDAP credentials' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
+ Page::Main::Login.perform(&:sign_in_using_credentials)
Page::Main::Menu.perform do |menu|
expect(menu).to have_personal_area
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb
index 87f0e9030d2..101143399f6 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb
@@ -6,9 +6,9 @@ module QA
it 'User logs in to gitlab with SAML SSO' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_with_saml }
+ Page::Main::Login.perform(&:sign_in_with_saml)
- Vendor::SAMLIdp::Page::Login.act { login }
+ Vendor::SAMLIdp::Page::Login.perform(&:login)
expect(page).to have_content('Welcome to GitLab')
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb
index 6632c2977ef..fbe857dc2a5 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb
@@ -5,7 +5,7 @@ module QA
describe 'Project creation' do
it 'user creates a new project' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
+ Page::Main::Login.perform(&:sign_in_using_credentials)
created_project = Resource::Project.fabricate_via_browser_ui! do |project|
project.name = 'awesome-project'
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb
index a9eafd61a91..4f8c46cbd5f 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb
@@ -24,16 +24,16 @@ module QA
it 'user imports a GitHub repo' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
+ Page::Main::Login.perform(&:sign_in_using_credentials)
imported_project # import the project
- Page::Main::Menu.act { go_to_projects }
+ Page::Main::Menu.perform(&:go_to_projects)
Page::Dashboard::Projects.perform do |dashboard|
dashboard.go_to_project(imported_project.name)
end
- Page::Project::Show.act { wait_for_import }
+ Page::Project::Show.perform(&:wait_for_import)
verify_repository_import
verify_issues_import
@@ -50,7 +50,7 @@ module QA
def verify_issues_import
QA::Support::Retrier.retry_on_exception do
- Page::Project::Menu.act { click_issues }
+ Page::Project::Menu.perform(&:click_issues)
expect(page).to have_content('This is a sample issue')
click_link 'This is a sample issue'
@@ -73,7 +73,7 @@ module QA
end
def verify_merge_requests_import
- Page::Project::Menu.act { click_merge_requests }
+ Page::Project::Menu.perform(&:click_merge_requests)
expect(page).to have_content('Improve README.md')
click_link 'Improve README.md'
@@ -108,7 +108,7 @@ module QA
end
def verify_wiki_import
- Page::Project::Menu.act { click_wiki }
+ Page::Project::Menu.perform(&:click_wiki)
expect(page).to have_content('Welcome to the test-project wiki!')
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb
index 5eceeb9661c..425fb861456 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb
@@ -3,17 +3,25 @@
module QA
context 'Plan' do
describe 'check xss occurence in @mentions in issues' do
- let(:issue_title) { 'issue title' }
+ before do
+ QA::Runtime::Env.personal_access_token = QA::Runtime::Env.admin_personal_access_token
- it 'user mentions a user in comment' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
+ unless QA::Runtime::Env.personal_access_token
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.perform(&:sign_in_using_admin_credentials)
+ end
user = Resource::User.fabricate_via_api! do |user|
user.name = "eve <img src=x onerror=alert(2)&lt;img src=x onerror=alert(1)&gt;"
user.password = "test1234"
end
+ QA::Runtime::Env.personal_access_token = nil
+
+ Page::Main::Menu.perform(&:sign_out) if Page::Main::Menu.perform { |p| p.has_personal_area?(wait: 0) }
+
+ Page::Main::Login.perform(&:sign_in_using_credentials)
+
project = Resource::Project.fabricate_via_api! do |resource|
resource.name = 'xss-test-for-mentions-project'
end
@@ -25,17 +33,19 @@ module QA
end
issue = Resource::Issue.fabricate_via_api! do |issue|
- issue.title = issue_title
+ issue.title = 'issue title'
issue.project = project
end
issue.visit!
+ end
- Page::Project::Issue::Show.perform do |show_page|
- show_page.select_all_activities_filter
- show_page.comment('cc-ing you here @eve')
+ it 'user mentions a user in comment' do
+ Page::Project::Issue::Show.perform do |show|
+ show.select_all_activities_filter
+ show.comment('cc-ing you here @eve')
expect do
- expect(show_page).to have_content("cc-ing you here")
+ expect(show).to have_content("cc-ing you here")
end.not_to raise_error # Selenium::WebDriver::Error::UnhandledAlertError
end
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb
index 2101311f065..ad70f6813fb 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb
@@ -3,39 +3,40 @@
module QA
context 'Plan' do
describe 'collapse comments in issue discussions' do
- let(:issue_title) { 'issue title' }
+ let(:my_first_reply) { 'My first reply' }
- it 'user collapses reply for comments in an issue' do
+ before do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
issue = Resource::Issue.fabricate_via_api! do |issue|
- issue.title = issue_title
+ issue.title = 'issue title'
end
issue.visit!
- expect(page).to have_content(issue_title)
+ Page::Project::Issue::Show.perform do |show|
+ my_first_discussion = 'My first discussion'
- Page::Project::Issue::Show.perform do |show_page|
- my_first_discussion = "My first discussion"
- my_first_reply = "My First Reply"
- one_reply = "1 reply"
-
- show_page.select_all_activities_filter
- show_page.start_discussion(my_first_discussion)
- expect(show_page).to have_content(my_first_discussion)
+ show.select_all_activities_filter
+ show.start_discussion(my_first_discussion)
+ page.assert_text(my_first_discussion)
+ show.reply_to_discussion(my_first_reply)
+ page.assert_text(my_first_reply)
+ end
+ end
- show_page.reply_to_discussion(my_first_reply)
- expect(show_page).to have_content(my_first_reply)
+ it 'user collapses and expands reply for comments in an issue' do
+ Page::Project::Issue::Show.perform do |show|
+ one_reply = "1 reply"
- show_page.collapse_replies
- expect(show_page).to have_content(one_reply)
- expect(show_page).not_to have_content(my_first_reply)
+ show.collapse_replies
+ expect(show).to have_content(one_reply)
+ expect(show).not_to have_content(my_first_reply)
- show_page.expand_replies
- expect(show_page).to have_content(my_first_reply)
- expect(show_page).not_to have_content(one_reply)
+ show.expand_replies
+ expect(show).to have_content(my_first_reply)
+ expect(show).not_to have_content(one_reply)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb
index a62a51b11f4..0b1bd00ac8d 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb
@@ -3,27 +3,29 @@
module QA
context 'Plan' do
describe 'Issue comments' do
- it 'user comments on an issue and edits the comment' do
+ before do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
+ Page::Main::Login.perform(&:sign_in_using_credentials)
issue = Resource::Issue.fabricate_via_api! do |issue|
issue.title = 'issue title'
end
issue.visit!
+ end
- Page::Project::Issue::Show.perform do |issue_show_page|
+ it 'user comments on an issue and edits the comment' do
+ Page::Project::Issue::Show.perform do |show|
first_version_of_comment = 'First version of the comment'
second_version_of_comment = 'Second version of the comment'
- issue_show_page.comment(first_version_of_comment)
+ show.comment(first_version_of_comment)
- expect(issue_show_page).to have_content(first_version_of_comment)
+ expect(show).to have_content(first_version_of_comment)
- issue_show_page.edit_comment(second_version_of_comment)
+ show.edit_comment(second_version_of_comment)
- expect(issue_show_page).to have_content(second_version_of_comment)
- expect(issue_show_page).not_to have_content(first_version_of_comment)
+ expect(show).to have_content(second_version_of_comment)
+ expect(show).not_to have_content(first_version_of_comment)
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 1eea3efec7f..04ae4963d3a 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
@@ -5,23 +5,35 @@ module QA
describe 'Issue creation' do
let(:issue_title) { 'issue title' }
+ before do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.perform(&:sign_in_using_credentials)
+ end
+
it 'user creates an issue' do
- create_issue
+ Resource::Issue.fabricate_via_browser_ui! do |issue|
+ issue.title = issue_title
+ end
- Page::Project::Menu.act { click_issues }
+ Page::Project::Menu.perform(&:click_issues)
expect(page).to have_content(issue_title)
end
- # Failure issue: https://gitlab.com/gitlab-org/quality/nightly/issues/101
- context 'when using attachments in comments', :object_storage, :quarantine do
+ context 'when using attachments in comments', :object_storage do
let(:file_to_attach) do
File.absolute_path(File.join('spec', 'fixtures', 'banana_sample.gif'))
end
- it 'user comments on an issue with an attachment' do
- create_issue
+ before do
+ issue = Resource::Issue.fabricate_via_api! do |issue|
+ issue.title = issue_title
+ end
+
+ issue.visit!
+ end
+ it 'user comments on an issue with an attachment' do
Page::Project::Issue::Show.perform do |show|
show.comment('See attached banana for scale', attachment: file_to_attach)
@@ -37,15 +49,6 @@ module QA
end
end
end
-
- def create_issue
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- Resource::Issue.fabricate_via_browser_ui! do |issue|
- issue.title = issue_title
- end
- end
end
end
end
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 301836f5ce8..317e31feea8 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
@@ -5,37 +5,37 @@ module QA
describe 'filter issue comments activities' do
let(:issue_title) { 'issue title' }
- it 'user filters comments and activities in an issue' do
+ before do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
+ Page::Main::Login.perform(&:sign_in_using_credentials)
issue = Resource::Issue.fabricate_via_api! do |issue|
issue.title = issue_title
end
issue.visit!
+ end
- expect(page).to have_content(issue_title)
-
- Page::Project::Issue::Show.perform do |show_page|
+ it 'user filters comments and activities in an issue' do
+ Page::Project::Issue::Show.perform do |show|
my_own_comment = "My own comment"
made_the_issue_confidential = "made the issue confidential"
- show_page.comment('/confidential', filter: :comments_only)
- show_page.comment(my_own_comment, filter: :comments_only)
+ show.comment('/confidential', filter: :comments_only)
+ show.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)
+ expect(show).not_to have_content(made_the_issue_confidential)
+ expect(show).to have_content(my_own_comment)
- show_page.select_all_activities_filter
+ show.select_all_activities_filter
- expect(show_page).to have_content(made_the_issue_confidential)
- expect(show_page).to have_content(my_own_comment)
+ expect(show).to have_content(made_the_issue_confidential)
+ expect(show).to have_content(my_own_comment)
- show_page.select_history_only_filter
+ show.select_history_only_filter
- expect(show_page).to have_content(made_the_issue_confidential)
- expect(show_page).not_to have_content(my_own_comment)
+ expect(show).to have_content(made_the_issue_confidential)
+ expect(show).not_to have_content(my_own_comment)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb
index 24dcb32f63f..c42c2cedde0 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb
@@ -5,7 +5,7 @@ module QA
describe 'issue suggestions' do
let(:issue_title) { 'Issue Lists are awesome' }
- it 'user sees issue suggestions when creating a new issue' do
+ before do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
@@ -20,14 +20,16 @@ module QA
end
project.visit!
+ end
+ it 'user sees issue suggestions when creating a new issue' do
Page::Project::Show.perform(&:go_to_new_issue)
- Page::Project::Issue::New.perform do |new_issue_page|
- new_issue_page.add_title("issue")
- expect(new_issue_page).to have_content(issue_title)
+ Page::Project::Issue::New.perform do |new|
+ new.add_title("issue")
+ expect(new).to have_content(issue_title)
- new_issue_page.add_title("Issue Board")
- expect(new_issue_page).not_to have_content(issue_title)
+ new.add_title("Issue Board")
+ expect(new).not_to have_content(issue_title)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
index 7a36f9ea420..3ce291bf8bc 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
module QA
- context 'Create' do
+ # Failure issue: https://gitlab.com/gitlab-org/quality/staging/issues/66
+ context 'Create', :quarantine do
describe 'Merge request rebasing' do
it 'user rebases source branch of merge request' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb
index f41240b7605..56a7a04e840 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb
@@ -7,7 +7,7 @@ module QA
it 'user adds and then removes an SSH key', :smoke do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
+ Page::Main::Login.perform(&:sign_in_using_credentials)
key = Resource::SSHKey.fabricate! do |resource|
resource.title = key_title
@@ -16,8 +16,8 @@ module QA
expect(page).to have_content("Title: #{key_title}")
expect(page).to have_content(key.fingerprint)
- Page::Main::Menu.act { click_settings_link }
- Page::Profile::Menu.act { click_ssh_keys }
+ Page::Main::Menu.perform(&:click_settings_link)
+ Page::Profile::Menu.perform(&:click_ssh_keys)
Page::Profile::SSHKeys.perform do |ssh_keys|
ssh_keys.remove_key(key_title)
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb
index d345fbfe995..51a1c19f0f7 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb
@@ -5,7 +5,7 @@ module QA
describe 'Files management' do
it 'user creates, edits and deletes a file via the Web' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
+ Page::Main::Login.perform(&:sign_in_using_credentials)
# Create
file_name = 'QA Test - File name'
@@ -27,7 +27,7 @@ module QA
updated_file_content = 'QA Test - Updated file content'
commit_message_for_update = 'QA Test - Update file'
- Page::File::Show.act { click_edit }
+ Page::File::Show.perform(&:click_edit)
Page::File::Form.act do
remove_content
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb
index 6aebd04af03..e159e517cbb 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb
@@ -13,7 +13,7 @@ module QA
before do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
+ Page::Main::Login.perform(&:sign_in_using_credentials)
end
after do
diff --git a/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb
index 3af7db751e7..33744236dd4 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb
@@ -11,7 +11,7 @@ module QA
it 'user registers a new specific runner' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
+ Page::Main::Login.perform(&:sign_in_using_credentials)
Resource::Runner.fabricate! do |runner|
runner.name = executor
diff --git a/qa/qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb b/qa/qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb
index 791dc62e32f..ec0c45652fd 100644
--- a/qa/qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb
+++ b/qa/qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb
@@ -5,7 +5,7 @@ module QA
describe 'Deploy token creation' do
it 'user adds a deploy token' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
+ Page::Main::Login.perform(&:sign_in_using_credentials)
deploy_token_name = 'deploy token name'
one_week_from_now = Date.today + 7
diff --git a/qa/qa/specs/features/browser_ui/7_configure/mattermost/create_group_with_mattermost_team_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/mattermost/create_group_with_mattermost_team_spec.rb
index 8383dcdb983..94d20106de4 100644
--- a/qa/qa/specs/features/browser_ui/7_configure/mattermost/create_group_with_mattermost_team_spec.rb
+++ b/qa/qa/specs/features/browser_ui/7_configure/mattermost/create_group_with_mattermost_team_spec.rb
@@ -5,8 +5,8 @@ module QA
describe 'Mattermost support' do
it 'user creates a group with a mattermost team' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
- Page::Main::Menu.act { go_to_groups }
+ Page::Main::Login.perform(&:sign_in_using_credentials)
+ Page::Main::Menu.perform(&:go_to_groups)
Page::Dashboard::Groups.perform do |page|
page.click_new_group
diff --git a/qa/spec/specs/runner_spec.rb b/qa/spec/specs/runner_spec.rb
index 6c533c6dc7d..3d98f03a982 100644
--- a/qa/spec/specs/runner_spec.rb
+++ b/qa/spec/specs/runner_spec.rb
@@ -125,9 +125,9 @@ describe QA::Specs::Runner do
end
def excluded_feature_tags_except(tag)
- QA::Runtime::Env.supported_features.except(tag).map do |tag, _|
+ QA::Runtime::Env.supported_features.except(tag).flat_map do |tag, _|
['--tag', "~requires_#{tag}"]
- end.flatten
+ end
end
def expect_rspec_runner_arguments(arguments)
diff --git a/scripts/lint-rugged b/scripts/lint-rugged
index 9466c62a415..e1605b4501b 100755
--- a/scripts/lint-rugged
+++ b/scripts/lint-rugged
@@ -10,7 +10,12 @@ ALLOWED = [
# Reverted Rugged calls due to Gitaly atop NFS performance
# See https://docs.gitlab.com/ee/development/gitaly.html#legacy-rugged-code.
'lib/gitlab/git/rugged_impl/',
- 'lib/gitlab/gitaly_client/storage_settings.rb'
+ 'lib/gitlab/gitaly_client/storage_settings.rb',
+
+ # Needed for logging
+ 'config/initializers/lograge.rb',
+ 'lib/gitlab/grape_logging/loggers/perf_logger.rb',
+ 'lib/gitlab/rugged_instrumentation.rb'
].freeze
rugged_lines = IO.popen(%w[git grep -i -n rugged -- app config lib], &:read).lines
diff --git a/scripts/merge-html-reports b/scripts/merge-html-reports
new file mode 100755
index 00000000000..7d1e15186c8
--- /dev/null
+++ b/scripts/merge-html-reports
@@ -0,0 +1,84 @@
+#!/usr/bin/env ruby
+
+require 'nokogiri'
+
+main_report_file = ARGV.shift
+unless main_report_file
+ puts 'usage: merge-html-reports <main-report> <base-artifact-url> [parallel reports...]'
+ exit 1
+end
+
+base_artifact_url = ARGV.shift
+unless base_artifact_url
+ puts 'usage: merge-html-reports <main-report> <base-artifact-url> [parallel reports...]'
+ exit 1
+end
+
+# Create the base report with empty body tag
+new_report = Nokogiri::HTML.parse(File.read(ARGV[0]))
+new_report.at_css('body').remove
+empty_body = Nokogiri::XML::Node.new('body', new_report)
+new_report.at_css('head').add_next_sibling(empty_body)
+
+ARGV.each do |report_file|
+ report = Nokogiri::HTML.parse(File.read(report_file))
+
+ report.css('a').each do |link|
+ link_suffix = link['href'].slice(19..-1)
+ link['href'] = base_artifact_url + link_suffix
+ end
+
+ header = report.css('div #rspec-header')
+ tests = report.css('dt[id^="example_group_"]')
+
+ tests.each do |test|
+ title = test.parent
+ group = title.parent
+ script = title.css('script')
+
+ if script.inner_html.include? 'makeYellow'
+ test.remove_class('passed')
+ test.add_class('not_implemented')
+
+ group.remove_class('passed')
+ group.add_class('not_implemented')
+ header.add_class('not_implemented')
+
+ script.remove
+ test.next_sibling.remove
+ test.next_sibling.remove
+
+ elsif script.inner_html.include? 'makeRed'
+ test.remove_class('passed')
+ test.add_class('failed')
+
+ group.remove_class('passed')
+ group.add_class('failed')
+ header.add_class('failed')
+
+ script.remove
+ test.next_sibling.remove
+ test.next_sibling.remove
+ end
+ end
+
+ duration = report.at_css('p#duration')
+ totals = report.at_css('p#totals')
+
+ duration_script = report.css('div.results script')[-2]
+ totals_script = report.css('div.results script')[-1]
+
+ duration_text = duration_script.text.slice(49..-3)
+ totals_text = totals_script.text.slice(47..-3)
+
+ duration.inner_html = duration_text
+ totals.inner_html = totals_text
+
+ duration_script.remove
+ totals_script.remove
+
+ # Add the new result after the last one to keep the test order
+ new_report.css('body')[-1].add_next_sibling(report.at_css('body'))
+end
+
+File.write(main_report_file, new_report)
diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb
index 509d8944e3a..1123563c1e3 100644
--- a/spec/controllers/admin/groups_controller_spec.rb
+++ b/spec/controllers/admin/groups_controller_spec.rb
@@ -68,5 +68,13 @@ describe Admin::GroupsController do
post :update, params: { id: group.to_param, group: { project_creation_level: ::Gitlab::Access::NO_ONE_PROJECT_ACCESS } }
end.to change { group.reload.project_creation_level }.to(::Gitlab::Access::NO_ONE_PROJECT_ACCESS)
end
+
+ it 'updates the subgroup_creation_level successfully' do
+ expect do
+ post :update,
+ params: { id: group.to_param,
+ group: { subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS } }
+ end.to change { group.reload.subgroup_creation_level }.to(::Gitlab::Access::OWNER_SUBGROUP_ACCESS)
+ end
end
end
diff --git a/spec/controllers/admin/requests_profiles_controller_spec.rb b/spec/controllers/admin/requests_profiles_controller_spec.rb
index 10850cb4603..345f7720c25 100644
--- a/spec/controllers/admin/requests_profiles_controller_spec.rb
+++ b/spec/controllers/admin/requests_profiles_controller_spec.rb
@@ -10,38 +10,63 @@ describe Admin::RequestsProfilesController do
end
describe '#show' do
- let(:basename) { "profile_#{Time.now.to_i}.html" }
let(:tmpdir) { Dir.mktmpdir('profiler-test') }
let(:test_file) { File.join(tmpdir, basename) }
- let(:profile) { Gitlab::RequestProfiler::Profile.new(basename) }
- let(:sample_data) do
- <<~HTML
- <!DOCTYPE html>
- <html>
- <body>
- <h1>My First Heading</h1>
- <p>My first paragraph.</p>
- </body>
- </html>
- HTML
+
+ subject do
+ get :show, params: { name: basename }
end
before do
stub_const('Gitlab::RequestProfiler::PROFILES_DIR', tmpdir)
- output = File.open(test_file, 'w')
- output.write(sample_data)
- output.close
+ File.write(test_file, sample_data)
end
after do
- File.unlink(test_file)
+ FileUtils.rm_rf(tmpdir)
end
- it 'loads an HTML profile' do
- get :show, params: { name: basename }
+ context 'when loading HTML profile' do
+ let(:basename) { "profile_#{Time.now.to_i}_execution.html" }
+
+ let(:sample_data) do
+ '<html> <body> <h1>Heading</h1> <p>paragraph.</p> </body> </html>'
+ end
+
+ it 'renders the data' do
+ subject
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.body).to eq(sample_data)
+ end
+ end
+
+ context 'when loading TXT profile' do
+ let(:basename) { "profile_#{Time.now.to_i}_memory.txt" }
+
+ let(:sample_data) do
+ <<~TXT
+ Total allocated: 112096396 bytes (1080431 objects)
+ Total retained: 10312598 bytes (53567 objects)
+ TXT
+ end
+
+ it 'renders the data' do
+ subject
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.body).to eq(sample_data)
+ end
+ end
+
+ context 'when loading PDF profile' do
+ let(:basename) { "profile_#{Time.now.to_i}_anything.pdf" }
+
+ let(:sample_data) { 'mocked pdf content' }
- expect(response).to have_gitlab_http_status(200)
- expect(response.body).to eq(sample_data)
+ it 'fails to render the data' do
+ expect { subject }.to raise_error(ActionController::UrlGenerationError, /No route matches.*unmatched constraints:/)
+ end
end
end
end
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
index 3f1c0ae8ac4..eaa5d6cd073 100644
--- a/spec/controllers/autocomplete_controller_spec.rb
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -295,28 +295,6 @@ describe AutocompleteController do
end
end
- context 'authorized projects with offset' do
- before do
- authorized_project2 = create(:project)
- authorized_project3 = create(:project)
-
- authorized_project.add_maintainer(user)
- authorized_project2.add_maintainer(user)
- authorized_project3.add_maintainer(user)
- end
-
- describe 'GET #projects with project ID and offset_id' do
- before do
- get(:projects, params: { project_id: project.id, offset_id: authorized_project.id })
- end
-
- it 'returns projects' do
- expect(json_response).to be_kind_of(Array)
- expect(json_response.size).to eq 2 # Of a total of 3
- end
- end
- end
-
context 'authorized projects without admin_issue ability' do
before do
authorized_project.add_guest(user)
diff --git a/spec/controllers/chaos_controller_spec.rb b/spec/controllers/chaos_controller_spec.rb
new file mode 100644
index 00000000000..bafd4a70862
--- /dev/null
+++ b/spec/controllers/chaos_controller_spec.rb
@@ -0,0 +1,127 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ChaosController do
+ describe '#leakmem' do
+ it 'calls synchronously' do
+ expect(Gitlab::Chaos).to receive(:leak_mem).with(100, 30.seconds)
+
+ get :leakmem
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'call synchronously with params' do
+ expect(Gitlab::Chaos).to receive(:leak_mem).with(1, 2.seconds)
+
+ get :leakmem, params: { memory_mb: 1, duration_s: 2 }
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'calls asynchronously' do
+ expect(Chaos::LeakMemWorker).to receive(:perform_async).with(100, 30.seconds)
+
+ get :leakmem, params: { async: 1 }
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+
+ describe '#cpu_spin' do
+ it 'calls synchronously' do
+ expect(Gitlab::Chaos).to receive(:cpu_spin).with(30.seconds)
+
+ get :cpu_spin
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'calls synchronously with params' do
+ expect(Gitlab::Chaos).to receive(:cpu_spin).with(3.seconds)
+
+ get :cpu_spin, params: { duration_s: 3 }
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'calls asynchronously' do
+ expect(Chaos::CpuSpinWorker).to receive(:perform_async).with(30.seconds)
+
+ get :cpu_spin, params: { async: 1 }
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+
+ describe '#db_spin' do
+ it 'calls synchronously' do
+ expect(Gitlab::Chaos).to receive(:db_spin).with(30.seconds, 1.second)
+
+ get :db_spin
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'calls synchronously with params' do
+ expect(Gitlab::Chaos).to receive(:db_spin).with(4.seconds, 5.seconds)
+
+ get :db_spin, params: { duration_s: 4, interval_s: 5 }
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'calls asynchronously' do
+ expect(Chaos::DbSpinWorker).to receive(:perform_async).with(30.seconds, 1.second)
+
+ get :db_spin, params: { async: 1 }
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+
+ describe '#sleep' do
+ it 'calls synchronously' do
+ expect(Gitlab::Chaos).to receive(:sleep).with(30.seconds)
+
+ get :sleep
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'calls synchronously with params' do
+ expect(Gitlab::Chaos).to receive(:sleep).with(5.seconds)
+
+ get :sleep, params: { duration_s: 5 }
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'calls asynchronously' do
+ expect(Chaos::SleepWorker).to receive(:perform_async).with(30.seconds)
+
+ get :sleep, params: { async: 1 }
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+
+ describe '#kill' do
+ it 'calls synchronously' do
+ expect(Gitlab::Chaos).to receive(:kill).with(no_args)
+
+ get :kill
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'calls asynchronously' do
+ expect(Chaos::KillWorker).to receive(:perform_async).with(no_args)
+
+ get :kill, params: { async: 1 }
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+end
diff --git a/spec/controllers/dashboard/milestones_controller_spec.rb b/spec/controllers/dashboard/milestones_controller_spec.rb
index 4de537ae6f8..67939aa4e6a 100644
--- a/spec/controllers/dashboard/milestones_controller_spec.rb
+++ b/spec/controllers/dashboard/milestones_controller_spec.rb
@@ -47,6 +47,8 @@ describe Dashboard::MilestonesController do
describe "#index" do
let(:public_group) { create(:group, :public) }
let!(:public_milestone) { create(:milestone, group: public_group) }
+ let!(:closed_group_milestone) { create(:milestone, group: group, state: 'closed') }
+ let!(:closed_project_milestone) { create(:milestone, project: project, state: 'closed') }
render_views
@@ -59,6 +61,15 @@ describe Dashboard::MilestonesController do
expect(json_response.map { |i| i["group_name"] }.compact).to match_array(group.name)
end
+ it 'returns closed group and project milestones to which the user belongs' do
+ get :index, params: { state: 'closed' }, format: :json
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response.size).to eq(2)
+ expect(json_response.map { |i| i["name"] }).to match_array([closed_group_milestone.name, closed_project_milestone.name])
+ expect(json_response.map { |i| i["group_name"] }.compact).to match_array(group.name)
+ end
+
it 'searches legacy project milestones by title when search_title is given' do
project_milestone = create(:milestone, title: 'Project milestone title', project: project)
@@ -77,11 +88,11 @@ describe Dashboard::MilestonesController do
expect(response.body).not_to include(project_milestone.title)
end
- it 'shows counts of group and project milestones to which the user belongs to' do
+ it 'shows counts of open and closed group and project milestones to which the user belongs to' do
get :index
expect(response.body).to include("Open\n<span class=\"badge badge-pill\">2</span>")
- expect(response.body).to include("Closed\n<span class=\"badge badge-pill\">0</span>")
+ expect(response.body).to include("Closed\n<span class=\"badge badge-pill\">2</span>")
end
context 'external authorization' do
diff --git a/spec/controllers/ide_controller_spec.rb b/spec/controllers/ide_controller_spec.rb
new file mode 100644
index 00000000000..0462f9520d5
--- /dev/null
+++ b/spec/controllers/ide_controller_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe IdeController do
+ let(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ end
+
+ it 'increases the views counter' do
+ expect(Gitlab::UsageDataCounters::WebIdeCounter).to receive(:increment_views_count)
+
+ get :index
+ end
+end
diff --git a/spec/controllers/projects/cycle_analytics/events_controller_spec.rb b/spec/controllers/projects/cycle_analytics/events_controller_spec.rb
new file mode 100644
index 00000000000..8fc3ae0aa32
--- /dev/null
+++ b/spec/controllers/projects/cycle_analytics/events_controller_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Projects::CycleAnalytics::EventsController do
+ let(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ project.add_maintainer(user)
+ end
+
+ describe 'cycle analytics not set up flag' do
+ context 'with no data' do
+ it 'is empty' do
+ get_issue
+
+ expect(response).to be_success
+ expect(JSON.parse(response.body)['events']).to be_empty
+ end
+ end
+
+ context 'with data' do
+ let(:milestone) { create(:milestone, project: project, created_at: 10.days.ago) }
+ let(:issue) { create(:issue, project: project, created_at: 9.days.ago) }
+
+ before do
+ issue.update(milestone: milestone)
+ end
+
+ it 'is not empty' do
+ get_issue
+
+ expect(response).to be_success
+ end
+
+ it 'contains event detais' do
+ get_issue
+
+ events = JSON.parse(response.body)['events']
+
+ expect(events).not_to be_empty
+ expect(events.first).to include('title', 'author', 'iid', 'total_time', 'created_at', 'url')
+ expect(events.first['title']).to eq(issue.title)
+ end
+
+ context 'with data older than start date' do
+ it 'is empty' do
+ get_issue(additional_params: { cycle_analytics: { start_date: 7 } })
+
+ expect(response).to be_success
+
+ expect(JSON.parse(response.body)['events']).to be_empty
+ end
+ end
+ end
+ end
+
+ def get_issue(additional_params: {})
+ params = additional_params.merge(namespace_id: project.namespace, project_id: project)
+ get(:issue, params: params, format: :json)
+ end
+end
diff --git a/spec/controllers/projects/merge_requests/creations_controller_spec.rb b/spec/controllers/projects/merge_requests/creations_controller_spec.rb
index 5fefad86ef3..3816e1c7a31 100644
--- a/spec/controllers/projects/merge_requests/creations_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/creations_controller_spec.rb
@@ -212,4 +212,46 @@ describe Projects::MergeRequests::CreationsController do
expect(response).to have_gitlab_http_status(200)
end
end
+
+ describe 'POST create' do
+ let(:params) do
+ {
+ namespace_id: fork_project.namespace.to_param,
+ project_id: fork_project,
+ merge_request: {
+ title: 'Test merge request',
+ source_branch: 'remove-submodule',
+ target_branch: 'master'
+ }
+ }
+ end
+
+ it 'creates merge request' do
+ expect do
+ post_request(params)
+ end.to change { MergeRequest.count }.by(1)
+ end
+
+ context 'when the merge request is not created from the web ide' do
+ it 'counter is not increased' do
+ expect(Gitlab::UsageDataCounters::WebIdeCounter).not_to receive(:increment_merge_requests_count)
+
+ post_request(params)
+ end
+ end
+
+ context 'when the merge request is created from the web ide' do
+ let(:nav_source) { { nav_source: 'webide' } }
+
+ it 'counter is increased' do
+ expect(Gitlab::UsageDataCounters::WebIdeCounter).to receive(:increment_merge_requests_count)
+
+ post_request(params.merge(nav_source))
+ end
+ end
+
+ def post_request(merge_request_params)
+ post :create, params: merge_request_params
+ end
+ end
end
diff --git a/spec/factories/clusters/clusters.rb b/spec/factories/clusters/clusters.rb
index ab332fc238b..171f5256d2b 100644
--- a/spec/factories/clusters/clusters.rb
+++ b/spec/factories/clusters/clusters.rb
@@ -5,6 +5,8 @@ FactoryBot.define do
cluster_type :project_type
managed true
+ factory :cluster_for_group, traits: [:provided_by_gcp, :group]
+
trait :instance do
cluster_type { Clusters::Cluster.cluster_types[:instance_type] }
end
diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb
index 18a0c2ec731..b67ab955ffc 100644
--- a/spec/factories/groups.rb
+++ b/spec/factories/groups.rb
@@ -45,5 +45,9 @@ FactoryBot.define do
trait :auto_devops_disabled do
auto_devops_enabled false
end
+
+ trait :owner_subgroup_creation_only do
+ subgroup_creation_level ::Gitlab::Access::OWNER_SUBGROUP_ACCESS
+ end
end
end
diff --git a/spec/factories/pages_domains.rb b/spec/factories/pages_domains.rb
index 3e0baab04ce..e441dfcf229 100644
--- a/spec/factories/pages_domains.rb
+++ b/spec/factories/pages_domains.rb
@@ -166,6 +166,90 @@ pu/xO28QOG8=
-----END CERTIFICATE-----'
end
+ trait :with_trusted_expired_chain do
+ # This contains
+ # Let's Encrypt Authority X3
+ # DST Root CA X3
+ certificate '-----BEGIN CERTIFICATE-----
+MIIFSjCCBDKgAwIBAgISAw24xGWrFotvTBa6AZI/pzq1MA0GCSqGSIb3DQEBCwUA
+MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
+ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTAzMDcxNzU5NTZaFw0x
+OTA2MDUxNzU5NTZaMBQxEjAQBgNVBAMTCXN5dHNlLmNvbTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALtIpQuqeZN6OgEE+y2UoGC/31Vt9NAeQWvTuWWO
+nMn/MvDJiw8731Dx4DDbMjhF50UBE20a9iAu2nhlxcsuuIITk2MXKMEgPtqSbwM7
+Mg0/WvgrBOWnF9CpdD3qcsjtstT6Djij06VfMfUrRZzMkGgbGzudR0cShKPmkBVU
+LgB6crFmSQ/qHt5PzBivdexCUpz5WzSKU5UWYFx2UnkSLykvEJuUr3Nn4/o9oyKw
+Qoiq354S262mFuMW+s6wQdMNNkwj41OqCwAGbqq7YUYLDc8OQiRC2LcqSO5yYnnA
+0lNfbEatZ1BzHiDjTH7wMUtwcLGHsZ1C5ZmORD2s2gtGiRkCAwEAAaOCAl4wggJa
+MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
+DAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUAMn3t1s4zXdOQbJFOP1riSwjuGkwHwYD
+VR0jBBgwFoAUqEpqYwR93brm0Tm3pkVl7/Oo7KEwbwYIKwYBBQUHAQEEYzBhMC4G
+CCsGAQUFBzABhiJodHRwOi8vb2NzcC5pbnQteDMubGV0c2VuY3J5cHQub3JnMC8G
+CCsGAQUFBzAChiNodHRwOi8vY2VydC5pbnQteDMubGV0c2VuY3J5cHQub3JnLzAU
+BgNVHREEDTALgglzeXRzZS5jb20wTAYDVR0gBEUwQzAIBgZngQwBAgEwNwYLKwYB
+BAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5sZXRzZW5jcnlwdC5v
+cmcwggEEBgorBgEEAdZ5AgQCBIH1BIHyAPAAdQB0ftqDMa0zEJEhnM4lT0Jwwr/9
+XkIgCMY3NXnmEHvMVgAAAWlZhr4pAAAEAwBGMEQCIBEA+3oiM1UJKY1kajBO5Aoz
+9AZMMlImaR1X5hFIPr95AiBXGIACuXUDLchB0kT8VIG/jM4f9iuXMoYCoKNJggNM
+/gB3ACk8UZZUyDlluqpQ/FgH1Ldvv1h6KXLcpMMM9OVFR/R4AAABaVmGv/AAAAQD
+AEgwRgIhANeTA7H51SZUmcT2ldtumFYX6/OkOr0fdvze72U0j9U9AiEAjSOSVQmi
+ZdYK6u3JYkDVOWsEzyKwjPWod8UN5K3ej0EwDQYJKoZIhvcNAQELBQADggEBAJev
+ArtxZVVTmLghV0O7471J1mN1fVC2p6b3AsK/TqrI7aiq8XuQq76KmUsB+U05MTXH
+3sYiHm+/RJ7+ljiKVIC8ZfbQsHo5I+F1CNMo6JB6z8Z+bOeRkoves5FNYmiJnUjO
+uoGzt//CyldbX1dEPVNuU7P0s2wZ6Bubump2LoapGIiGxQJfeb0vj0TQzfRacTIZ
+x9U5E/D0y0iewX4kPHK17QDBsSL9WlqsRzFAkQjJ9XWUVn3BO7JG3WU47iOuykby
+y2HmOWUxjv1Yf/H/OYRBiuSCR4LhrE5Ze4tTo2AByrXQ5h7ezjDJQqnKBP5NuwIq
+7NuX+D2esUNos/D6uJg=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
+MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
+DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
+SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
+GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
+q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
+SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
+Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
+a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
+/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
+AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
+CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
+bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
+c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
+VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
+ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
+MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
+Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
+AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
+uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
+wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
+X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
+PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
+KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
+MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
+DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
+PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
+Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
+rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
+OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
+xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
+7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
+aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
+HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
+SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
+ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
+AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
+R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
+JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
+Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
+-----END CERTIFICATE-----'
+ end
+
trait :with_expired_certificate do
certificate '-----BEGIN CERTIFICATE-----
MIIBsDCCARmgAwIBAgIBATANBgkqhkiG9w0BAQUFADAeMRwwGgYDVQQDExNleHBp
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 7d7738a30c8..0e8810b73a1 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -25,7 +25,9 @@ FactoryBot.define do
issues_access_level ProjectFeature::ENABLED
merge_requests_access_level ProjectFeature::ENABLED
repository_access_level ProjectFeature::ENABLED
- pages_access_level ProjectFeature::ENABLED
+ pages_access_level do
+ visibility_level == Gitlab::VisibilityLevel::PUBLIC ? ProjectFeature::ENABLED : ProjectFeature::PRIVATE
+ end
# we can't assign the delegated `#ci_cd_settings` attributes directly, as the
# `#ci_cd_settings` relation needs to be created first
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index 735ca60f7da..a08902c30be 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -90,6 +90,7 @@ describe 'Admin Groups' do
visit admin_group_path(group)
expect(page).to have_content("Group: #{group.name}")
+ expect(page).to have_content("ID: #{group.id}")
end
end
@@ -102,6 +103,14 @@ describe 'Admin Groups' do
expect_selected_visibility(group.visibility_level)
end
+ it 'shows the subgroup creation level dropdown populated with the group subgroup_creation_level value' do
+ group = create(:group, :private, :owner_subgroup_creation_only)
+
+ visit admin_group_edit_path(group)
+
+ expect(page).to have_content('Allowed to create subgroups')
+ end
+
it 'edit group path does not change group name', :js do
group = create(:group, :private)
diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb
index 2b6bfa40beb..1c1ca41f633 100644
--- a/spec/features/admin/admin_projects_spec.rb
+++ b/spec/features/admin/admin_projects_spec.rb
@@ -60,6 +60,7 @@ describe "Admin::Projects" do
expect(page).to have_content(project.name)
expect(page).to have_content(project.full_name)
expect(page).to have_content(project.creator.name)
+ expect(page).to have_content(project.id)
end
end
diff --git a/spec/features/admin/admin_requests_profiles_spec.rb b/spec/features/admin/admin_requests_profiles_spec.rb
index 2503fc9067d..e8764d0a79c 100644
--- a/spec/features/admin/admin_requests_profiles_spec.rb
+++ b/spec/features/admin/admin_requests_profiles_spec.rb
@@ -1,13 +1,15 @@
require 'spec_helper'
describe 'Admin::RequestsProfilesController' do
+ let(:tmpdir) { Dir.mktmpdir('profiler-test') }
+
before do
- FileUtils.mkdir_p(Gitlab::RequestProfiler::PROFILES_DIR)
+ stub_const('Gitlab::RequestProfiler::PROFILES_DIR', tmpdir)
sign_in(create(:admin))
end
after do
- Gitlab::RequestProfiler.remove_all_profiles
+ FileUtils.rm_rf(tmpdir)
end
describe 'GET /admin/requests_profiles' do
@@ -19,42 +21,103 @@ describe 'Admin::RequestsProfilesController' do
expect(page).to have_content("X-Profile-Token: #{Gitlab::RequestProfiler.profile_token}")
end
- it 'lists all available profiles' do
- time1 = 1.hour.ago
- time2 = 2.hours.ago
- time3 = 3.hours.ago
- profile1 = "|gitlab-org|gitlab-ce_#{time1.to_i}.html"
- profile2 = "|gitlab-org|gitlab-ce_#{time2.to_i}.html"
- profile3 = "|gitlab-com|infrastructure_#{time3.to_i}.html"
-
- FileUtils.touch("#{Gitlab::RequestProfiler::PROFILES_DIR}/#{profile1}")
- FileUtils.touch("#{Gitlab::RequestProfiler::PROFILES_DIR}/#{profile2}")
- FileUtils.touch("#{Gitlab::RequestProfiler::PROFILES_DIR}/#{profile3}")
-
- visit admin_requests_profiles_path
+ context 'when having multiple profiles' do
+ let(:time1) { 1.hour.ago }
+ let(:time2) { 2.hours.ago }
+
+ let(:profiles) do
+ [
+ {
+ request_path: '/gitlab-org/gitlab-ce',
+ name: "|gitlab-org|gitlab-ce_#{time1.to_i}_execution.html",
+ created: time1,
+ profile_mode: 'Execution'
+ },
+ {
+ request_path: '/gitlab-org/gitlab-ce',
+ name: "|gitlab-org|gitlab-ce_#{time2.to_i}_execution.html",
+ created: time2,
+ profile_mode: 'Execution'
+ },
+ {
+ request_path: '/gitlab-org/gitlab-ce',
+ name: "|gitlab-org|gitlab-ce_#{time1.to_i}_memory.html",
+ created: time1,
+ profile_mode: 'Memory'
+ },
+ {
+ request_path: '/gitlab-org/gitlab-ce',
+ name: "|gitlab-org|gitlab-ce_#{time2.to_i}_memory.html",
+ created: time2,
+ profile_mode: 'Memory'
+ },
+ {
+ request_path: '/gitlab-org/infrastructure',
+ name: "|gitlab-org|infrastructure_#{time1.to_i}_execution.html",
+ created: time1,
+ profile_mode: 'Execution'
+ },
+ {
+ request_path: '/gitlab-org/infrastructure',
+ name: "|gitlab-org|infrastructure_#{time2.to_i}_memory.html",
+ created: time2,
+ profile_mode: 'Memory'
+ },
+ {
+ request_path: '/gitlab-org/infrastructure',
+ name: "|gitlab-org|infrastructure_#{time2.to_i}.html",
+ created: time2,
+ profile_mode: 'Unknown'
+ }
+ ]
+ end
- within('.card', text: '/gitlab-org/gitlab-ce') do
- expect(page).to have_selector("a[href='#{admin_requests_profile_path(profile1)}']", text: time1.to_s(:long))
- expect(page).to have_selector("a[href='#{admin_requests_profile_path(profile2)}']", text: time2.to_s(:long))
+ before do
+ profiles.each do |profile|
+ FileUtils.touch(File.join(Gitlab::RequestProfiler::PROFILES_DIR, profile[:name]))
+ end
end
- within('.card', text: '/gitlab-com/infrastructure') do
- expect(page).to have_selector("a[href='#{admin_requests_profile_path(profile3)}']", text: time3.to_s(:long))
+ it 'lists all available profiles' do
+ visit admin_requests_profiles_path
+
+ profiles.each do |profile|
+ within('.card', text: profile[:request_path]) do
+ expect(page).to have_selector(
+ "a[href='#{admin_requests_profile_path(profile[:name])}']",
+ text: "#{profile[:created].to_s(:long)} #{profile[:profile_mode]}")
+ end
+ end
end
end
end
describe 'GET /admin/requests_profiles/:profile' do
context 'when a profile exists' do
- it 'displays the content of the profile' do
- content = 'This is a request profile'
- profile = "|gitlab-org|gitlab-ce_#{Time.now.to_i}.html"
-
+ before do
File.write("#{Gitlab::RequestProfiler::PROFILES_DIR}/#{profile}", content)
+ end
+
+ context 'when is valid call stack profile' do
+ let(:content) { 'This is a call stack request profile' }
+ let(:profile) { "|gitlab-org|gitlab-ce_#{Time.now.to_i}_execution.html" }
+
+ it 'displays the content' do
+ visit admin_requests_profile_path(profile)
+
+ expect(page).to have_content(content)
+ end
+ end
+
+ context 'when is valid memory profile' do
+ let(:content) { 'This is a memory request profile' }
+ let(:profile) { "|gitlab-org|gitlab-ce_#{Time.now.to_i}_memory.txt" }
- visit admin_requests_profile_path(profile)
+ it 'displays the content' do
+ visit admin_requests_profile_path(profile)
- expect(page).to have_content(content)
+ expect(page).to have_content(content)
+ end
end
end
diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb
index 5cef5f0521f..676769c25fe 100644
--- a/spec/features/groups/group_settings_spec.rb
+++ b/spec/features/groups/group_settings_spec.rb
@@ -85,6 +85,14 @@ describe 'Edit group settings' do
end
end
+ describe 'subgroup creation level menu' do
+ it 'shows the selection menu' do
+ visit edit_group_path(group)
+
+ expect(page).to have_content('Allowed to create subgroups')
+ end
+ end
+
describe 'edit group avatar' do
before do
visit edit_group_path(group)
diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb
index 9671a4d8c49..bed998a0859 100644
--- a/spec/features/groups/show_spec.rb
+++ b/spec/features/groups/show_spec.rb
@@ -56,32 +56,103 @@ describe 'Group show page' do
end
context 'subgroup support' do
- let(:user) { create(:user) }
+ let(:restricted_group) do
+ create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS)
+ end
- before do
- group.add_owner(user)
- sign_in(user)
+ let(:relaxed_group) do
+ create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS)
end
- context 'when subgroups are supported', :js, :nested_groups do
+ let(:owner) { create(:user) }
+ let(:maintainer) { create(:user) }
+
+ context 'for owners' do
+ let(:path) { group_path(restricted_group) }
+
before do
- allow(Group).to receive(:supports_nested_objects?) { true }
- visit path
+ restricted_group.add_owner(owner)
+ sign_in(owner)
end
- it 'allows creating subgroups' do
- expect(page).to have_css("li[data-text='New subgroup']", visible: false)
+ context 'when subgroups are supported', :nested_groups do
+ before do
+ allow(Group).to receive(:supports_nested_objects?) { true }
+ end
+
+ it 'allows creating subgroups' do
+ visit path
+
+ expect(page)
+ .to have_css("li[data-text='New subgroup']", visible: false)
+ end
+ end
+
+ context 'when subgroups are not supported' do
+ before do
+ allow(Group).to receive(:supports_nested_objects?) { false }
+ end
+
+ it 'does not allow creating subgroups' do
+ visit path
+
+ expect(page)
+ .not_to have_selector("li[data-text='New subgroup']", visible: false)
+ end
end
end
- context 'when subgroups are not supported' do
+ context 'for maintainers' do
before do
- allow(Group).to receive(:supports_nested_objects?) { false }
- visit path
+ sign_in(maintainer)
end
- it 'allows creating subgroups' do
- expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false)
+ context 'when subgroups are supported', :nested_groups do
+ before do
+ allow(Group).to receive(:supports_nested_objects?) { true }
+ end
+
+ context 'when subgroup_creation_level is set to maintainers' do
+ before do
+ relaxed_group.add_maintainer(maintainer)
+ end
+
+ it 'allows creating subgroups' do
+ path = group_path(relaxed_group)
+ visit path
+
+ expect(page)
+ .to have_css("li[data-text='New subgroup']", visible: false)
+ end
+ end
+
+ context 'when subgroup_creation_level is set to owners' do
+ before do
+ restricted_group.add_maintainer(maintainer)
+ end
+
+ it 'does not allow creating subgroups' do
+ path = group_path(restricted_group)
+ visit path
+
+ expect(page)
+ .not_to have_selector("li[data-text='New subgroup']",
+ visible: false)
+ end
+ end
+ end
+
+ context 'when subgroups are not supported' do
+ before do
+ allow(Group).to receive(:supports_nested_objects?) { false }
+ end
+
+ it 'does not allow creating subgroups' do
+ visit path
+
+ expect(page)
+ .not_to have_selector("li[data-text='New subgroup']", visible: false)
+ end
end
end
end
diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb
index 06cb2e36334..7be5961af09 100644
--- a/spec/features/issues/bulk_assignment_labels_spec.rb
+++ b/spec/features/issues/bulk_assignment_labels_spec.rb
@@ -381,7 +381,7 @@ describe 'Issues > Labels bulk assignment' do
if unmark
items.map do |item|
# Make sure we are unmarking the item no matter the state it has currently
- click_link item until find('a', text: item)[:class] == 'label-item'
+ click_link item until find('a', text: item)[:class].include? 'label-item'
end
end
end
diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
index 10fe60cb075..3e72b2ce71f 100644
--- a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
+++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
@@ -362,14 +362,14 @@ describe 'Merge request > User resolves diff notes and threads', :js do
end
end
- it 'shows jump to next thread button except on last thread' do
+ it 'shows jump to next discussion button on all discussions' do
wait_for_requests
all_discussion_replies = page.all('.discussion-reply-holder')
expect(all_discussion_replies.count).to eq(2)
- expect(all_discussion_replies.first.all('.discussion-next-btn').count).to eq(2)
- expect(all_discussion_replies.last.all('.discussion-next-btn').count).to eq(2)
+ expect(all_discussion_replies.first.all('.discussion-next-btn').count).to eq(1)
+ expect(all_discussion_replies.last.all('.discussion-next-btn').count).to eq(1)
end
it 'displays next thread even if hidden' do
diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb
index 2cb2a23b7be..8585e24bc35 100644
--- a/spec/features/projects/tree/create_directory_spec.rb
+++ b/spec/features/projects/tree/create_directory_spec.rb
@@ -47,7 +47,9 @@ describe 'Multi-file editor new directory', :js do
fill_in('commit-message', with: 'commit message ide')
- click_button('Commit')
+ page.within '.multi-file-commit-form' do
+ click_button('Commit')
+ end
find('.js-ide-edit-mode').click
diff --git a/spec/features/projects/tree/create_file_spec.rb b/spec/features/projects/tree/create_file_spec.rb
index 9f5524da8e9..8623b10562d 100644
--- a/spec/features/projects/tree/create_file_spec.rb
+++ b/spec/features/projects/tree/create_file_spec.rb
@@ -39,7 +39,9 @@ describe 'Multi-file editor new file', :js do
fill_in('commit-message', with: 'commit message ide')
- click_button('Commit')
+ page.within '.multi-file-commit-form' do
+ click_button('Commit')
+ end
expect(page).to have_content('file name')
end
diff --git a/spec/finders/autocomplete/move_to_project_finder_spec.rb b/spec/finders/autocomplete/move_to_project_finder_spec.rb
index c3bc410a7f6..4a87b47bd08 100644
--- a/spec/finders/autocomplete/move_to_project_finder_spec.rb
+++ b/spec/finders/autocomplete/move_to_project_finder_spec.rb
@@ -6,9 +6,9 @@ describe Autocomplete::MoveToProjectFinder do
let(:no_access_project) { create(:project) }
let(:guest_project) { create(:project) }
- let(:reporter_project) { create(:project) }
- let(:developer_project) { create(:project) }
- let(:maintainer_project) { create(:project) }
+ let(:reporter_project) { create(:project, name: 'name') }
+ let(:developer_project) { create(:project, name: 'name2') }
+ let(:maintainer_project) { create(:project, name: 'name3') }
describe '#execute' do
context 'filter' do
@@ -20,14 +20,14 @@ describe Autocomplete::MoveToProjectFinder do
expect(finder.execute).to be_empty
end
- it 'returns projects equal or above Gitlab::Access::REPORTER ordered by id in descending order' do
+ it 'returns projects equal or above Gitlab::Access::REPORTER ordered by name' do
reporter_project.add_reporter(user)
developer_project.add_developer(user)
maintainer_project.add_maintainer(user)
finder = described_class.new(user, project_id: project.id)
- expect(finder.execute.to_a).to eq([maintainer_project, developer_project, reporter_project])
+ expect(finder.execute.to_a).to eq([reporter_project, developer_project, maintainer_project])
end
it 'does not include the source project' do
@@ -60,46 +60,32 @@ describe Autocomplete::MoveToProjectFinder do
expect(finder.execute.to_a).to eq([other_reporter_project])
end
- it 'returns a page of projects ordered by id in descending order' do
- allow(Kaminari.config).to receive(:default_per_page).and_return(2)
+ it 'returns a page of projects ordered by name' do
+ stub_const('Autocomplete::MoveToProjectFinder::LIMIT', 2)
- projects = create_list(:project, 2) do |project|
+ projects = create_list(:project, 3) do |project|
project.add_developer(user)
end
finder = described_class.new(user, project_id: project.id)
page = finder.execute.to_a
- expect(page.length).to eq(Kaminari.config.default_per_page)
- expect(page[0]).to eq(projects.last)
- end
-
- it 'returns projects after the given offset id' do
- reporter_project.add_reporter(user)
- developer_project.add_developer(user)
- maintainer_project.add_maintainer(user)
-
- expect(described_class.new(user, project_id: project.id, offset_id: maintainer_project.id).execute.to_a)
- .to eq([developer_project, reporter_project])
-
- expect(described_class.new(user, project_id: project.id, offset_id: developer_project.id).execute.to_a)
- .to eq([reporter_project])
-
- expect(described_class.new(user, project_id: project.id, offset_id: reporter_project.id).execute.to_a)
- .to be_empty
+ expected_projects = projects.sort_by(&:name).first(2)
+ expect(page.length).to eq(2)
+ expect(page).to eq(expected_projects)
end
end
context 'search' do
it 'returns projects matching a search query' do
- foo_project = create(:project)
+ foo_project = create(:project, name: 'foo')
foo_project.add_maintainer(user)
wadus_project = create(:project, name: 'wadus')
wadus_project.add_maintainer(user)
expect(described_class.new(user, project_id: project.id).execute.to_a)
- .to eq([wadus_project, foo_project])
+ .to eq([foo_project, wadus_project])
expect(described_class.new(user, project_id: project.id, search: 'wadus').execute.to_a)
.to eq([wadus_project])
diff --git a/spec/fixtures/security-reports/deprecated/gl-sast-report.json b/spec/fixtures/security-reports/deprecated/gl-sast-report.json
index a85b9be8b5f..2f7e47281e2 100644
--- a/spec/fixtures/security-reports/deprecated/gl-sast-report.json
+++ b/spec/fixtures/security-reports/deprecated/gl-sast-report.json
@@ -838,6 +838,11 @@
},
"identifiers": [
{
+ "type": "flawfinder_func_name",
+ "name": "Flawfinder - char",
+ "value": "char"
+ },
+ {
"type": "cwe",
"name": "CWE-119",
"value": "119",
@@ -870,6 +875,11 @@
},
"identifiers": [
{
+ "type": "flawfinder_func_name",
+ "name": "Flawfinder - fopen",
+ "value": "fopen"
+ },
+ {
"type": "cwe",
"name": "CWE-362",
"value": "362",
@@ -897,6 +907,11 @@
},
"identifiers": [
{
+ "type": "flawfinder_func_name",
+ "name": "Flawfinder - char",
+ "value": "char"
+ },
+ {
"type": "cwe",
"name": "CWE-119",
"value": "119",
@@ -930,6 +945,11 @@
},
"identifiers": [
{
+ "type": "flawfinder_func_name",
+ "name": "Flawfinder - strcpy",
+ "value": "strcpy"
+ },
+ {
"type": "cwe",
"name": "CWE-120",
"value": "120",
diff --git a/spec/fixtures/security-reports/master/gl-sast-report.json b/spec/fixtures/security-reports/master/gl-sast-report.json
index 4bef3d22f70..345e1e9f83a 100644
--- a/spec/fixtures/security-reports/master/gl-sast-report.json
+++ b/spec/fixtures/security-reports/master/gl-sast-report.json
@@ -840,6 +840,11 @@
},
"identifiers": [
{
+ "type": "flawfinder_func_name",
+ "name": "Flawfinder - char",
+ "value": "char"
+ },
+ {
"type": "cwe",
"name": "CWE-119",
"value": "119",
@@ -872,6 +877,11 @@
},
"identifiers": [
{
+ "type": "flawfinder_func_name",
+ "name": "Flawfinder - fopen",
+ "value": "fopen"
+ },
+ {
"type": "cwe",
"name": "CWE-362",
"value": "362",
@@ -899,6 +909,11 @@
},
"identifiers": [
{
+ "type": "flawfinder_func_name",
+ "name": "Flawfinder - char",
+ "value": "char"
+ },
+ {
"type": "cwe",
"name": "CWE-119",
"value": "119",
@@ -932,6 +947,11 @@
},
"identifiers": [
{
+ "type": "flawfinder_func_name",
+ "name": "Flawfinder - strcpy",
+ "value": "strcpy"
+ },
+ {
"type": "cwe",
"name": "CWE-120",
"value": "120",
diff --git a/spec/frontend/behaviors/markdown/render_metrics_spec.js b/spec/frontend/behaviors/markdown/render_metrics_spec.js
new file mode 100644
index 00000000000..6db0eabc16b
--- /dev/null
+++ b/spec/frontend/behaviors/markdown/render_metrics_spec.js
@@ -0,0 +1,37 @@
+import Vue from 'vue';
+import renderMetrics from '~/behaviors/markdown/render_metrics';
+import { TEST_HOST } from 'helpers/test_constants';
+
+const originalExtend = Vue.extend;
+
+describe('Render metrics for Gitlab Flavoured Markdown', () => {
+ const container = {
+ Metrics() {},
+ };
+
+ let spyExtend;
+
+ beforeEach(() => {
+ Vue.extend = () => container.Metrics;
+ spyExtend = jest.spyOn(Vue, 'extend');
+ });
+
+ afterEach(() => {
+ Vue.extend = originalExtend;
+ });
+
+ it('does nothing when no elements are found', () => {
+ renderMetrics([]);
+
+ expect(spyExtend).not.toHaveBeenCalled();
+ });
+
+ it('renders a vue component when elements are found', () => {
+ const element = document.createElement('div');
+ element.setAttribute('data-dashboard-url', TEST_HOST);
+
+ renderMetrics([element]);
+
+ expect(spyExtend).toHaveBeenCalled();
+ });
+});
diff --git a/spec/frontend/environment.js b/spec/frontend/environment.js
index a8c8688441d..290c0e797cb 100644
--- a/spec/frontend/environment.js
+++ b/spec/frontend/environment.js
@@ -1,8 +1,11 @@
/* eslint-disable import/no-commonjs */
+const path = require('path');
const { ErrorWithStack } = require('jest-util');
const JSDOMEnvironment = require('jest-environment-jsdom');
+const ROOT_PATH = path.resolve(__dirname, '../..');
+
class CustomEnvironment extends JSDOMEnvironment {
constructor(config, context) {
super(config, context);
@@ -35,9 +38,8 @@ class CustomEnvironment extends JSDOMEnvironment {
this.rejectedPromises.push(error);
};
- this.global.fixturesBasePath = `${process.cwd()}/${
- IS_EE ? 'ee/' : ''
- }spec/javascripts/fixtures`;
+ this.global.fixturesBasePath = `${ROOT_PATH}/tmp/tests/frontend/fixtures${IS_EE ? '-ee' : ''}`;
+ this.global.staticFixturesBasePath = `${ROOT_PATH}/spec/frontend/fixtures`;
// Not yet supported by JSDOM: https://github.com/jsdom/jsdom/issues/317
this.global.document.createRange = () => ({
diff --git a/spec/javascripts/fixtures/abuse_reports.rb b/spec/frontend/fixtures/abuse_reports.rb
index e0aaecf626a..21356390cae 100644
--- a/spec/javascripts/fixtures/abuse_reports.rb
+++ b/spec/frontend/fixtures/abuse_reports.rb
@@ -21,6 +21,6 @@ describe Admin::AbuseReportsController, '(JavaScript fixtures)', type: :controll
it 'abuse_reports/abuse_reports_list.html' do
get :index
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/admin_users.rb b/spec/frontend/fixtures/admin_users.rb
index 22a5de66577..0209594dadc 100644
--- a/spec/javascripts/fixtures/admin_users.rb
+++ b/spec/frontend/fixtures/admin_users.rb
@@ -23,6 +23,6 @@ describe Admin::UsersController, '(JavaScript fixtures)', type: :controller do
get :new
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/application_settings.rb b/spec/frontend/fixtures/application_settings.rb
index d4651fa6ece..38a060580c1 100644
--- a/spec/javascripts/fixtures/application_settings.rb
+++ b/spec/frontend/fixtures/application_settings.rb
@@ -28,6 +28,6 @@ describe Admin::ApplicationSettingsController, '(JavaScript fixtures)', type: :c
get :show
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/autocomplete_sources.rb b/spec/frontend/fixtures/autocomplete_sources.rb
index b20a0159d7d..9e04328e2b9 100644
--- a/spec/javascripts/fixtures/autocomplete_sources.rb
+++ b/spec/frontend/fixtures/autocomplete_sources.rb
@@ -34,6 +34,6 @@ describe Projects::AutocompleteSourcesController, '(JavaScript fixtures)', type:
type_id: issue.id
}
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/blob.rb b/spec/frontend/fixtures/blob.rb
index 07670552cd5..ce5030efbf8 100644
--- a/spec/javascripts/fixtures/blob.rb
+++ b/spec/frontend/fixtures/blob.rb
@@ -29,6 +29,6 @@ describe Projects::BlobController, '(JavaScript fixtures)', type: :controller do
id: 'add-ipython-files/files/ipython/basic.ipynb'
})
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/boards.rb b/spec/frontend/fixtures/boards.rb
index 5835721d3d5..f257d80390f 100644
--- a/spec/javascripts/fixtures/boards.rb
+++ b/spec/frontend/fixtures/boards.rb
@@ -23,6 +23,6 @@ describe Projects::BoardsController, '(JavaScript fixtures)', type: :controller
project_id: project
})
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/branches.rb b/spec/frontend/fixtures/branches.rb
index 204aa9b7c7a..197fe42c52a 100644
--- a/spec/javascripts/fixtures/branches.rb
+++ b/spec/frontend/fixtures/branches.rb
@@ -27,6 +27,6 @@ describe Projects::BranchesController, '(JavaScript fixtures)', type: :controlle
project_id: project
}
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/clusters.rb b/spec/frontend/fixtures/clusters.rb
index 1076404e0e3..f15ef010807 100644
--- a/spec/javascripts/fixtures/clusters.rb
+++ b/spec/frontend/fixtures/clusters.rb
@@ -29,6 +29,6 @@ describe Projects::ClustersController, '(JavaScript fixtures)', type: :controlle
id: cluster
}
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/commit.rb b/spec/frontend/fixtures/commit.rb
index ff9a4bc1adc..a328c455356 100644
--- a/spec/javascripts/fixtures/commit.rb
+++ b/spec/frontend/fixtures/commit.rb
@@ -28,6 +28,6 @@ describe Projects::CommitController, '(JavaScript fixtures)', type: :controller
get :show, params: params
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/deploy_keys.rb b/spec/frontend/fixtures/deploy_keys.rb
index 38eab853da2..fca233c6f59 100644
--- a/spec/javascripts/fixtures/deploy_keys.rb
+++ b/spec/frontend/fixtures/deploy_keys.rb
@@ -38,6 +38,6 @@ describe Projects::DeployKeysController, '(JavaScript fixtures)', type: :control
project_id: project
}, format: :json
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/groups.rb b/spec/frontend/fixtures/groups.rb
index 4d0afc3ce1a..c1bb2d43332 100644
--- a/spec/javascripts/fixtures/groups.rb
+++ b/spec/frontend/fixtures/groups.rb
@@ -21,7 +21,7 @@ describe 'Groups (JavaScript fixtures)', type: :controller do
it 'groups/edit.html' do
get :edit, params: { id: group }
- expect(response).to be_success
+ expect(response).to be_successful
end
end
@@ -29,7 +29,7 @@ describe 'Groups (JavaScript fixtures)', type: :controller do
it 'groups/ci_cd_settings.html' do
get :show, params: { group_id: group }
- expect(response).to be_success
+ expect(response).to be_successful
end
end
end
diff --git a/spec/javascripts/fixtures/issues.rb b/spec/frontend/fixtures/issues.rb
index d8d77f767de..b5eb38e0023 100644
--- a/spec/javascripts/fixtures/issues.rb
+++ b/spec/frontend/fixtures/issues.rb
@@ -48,7 +48,7 @@ describe Projects::IssuesController, '(JavaScript fixtures)', type: :controller
project_id: project
}
- expect(response).to be_success
+ expect(response).to be_successful
end
private
@@ -60,7 +60,7 @@ describe Projects::IssuesController, '(JavaScript fixtures)', type: :controller
id: issue.to_param
}
- expect(response).to be_success
+ expect(response).to be_successful
end
end
@@ -117,6 +117,6 @@ describe API::Issues, '(JavaScript fixtures)', type: :request do
get_related_merge_requests(project.id, issue.iid, user)
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/jobs.rb b/spec/frontend/fixtures/jobs.rb
index 46ccd6f8c8a..a3a7759c85b 100644
--- a/spec/javascripts/fixtures/jobs.rb
+++ b/spec/frontend/fixtures/jobs.rb
@@ -39,7 +39,7 @@ describe Projects::JobsController, '(JavaScript fixtures)', type: :controller do
id: build_with_artifacts.to_param
}
- expect(response).to be_success
+ expect(response).to be_successful
end
it 'jobs/delayed.json' do
@@ -49,6 +49,6 @@ describe Projects::JobsController, '(JavaScript fixtures)', type: :controller do
id: delayed_job.to_param
}, format: :json
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/labels.rb b/spec/frontend/fixtures/labels.rb
index 4d1b7317274..a312287970f 100644
--- a/spec/javascripts/fixtures/labels.rb
+++ b/spec/frontend/fixtures/labels.rb
@@ -35,7 +35,7 @@ describe 'Labels (JavaScript fixtures)' do
group_id: group
}, format: 'json'
- expect(response).to be_success
+ expect(response).to be_successful
end
end
@@ -52,7 +52,7 @@ describe 'Labels (JavaScript fixtures)' do
project_id: project
}, format: 'json'
- expect(response).to be_success
+ expect(response).to be_successful
end
end
end
diff --git a/spec/javascripts/fixtures/merge_requests.rb b/spec/frontend/fixtures/merge_requests.rb
index 05860be2291..88706e96676 100644
--- a/spec/javascripts/fixtures/merge_requests.rb
+++ b/spec/frontend/fixtures/merge_requests.rb
@@ -129,6 +129,6 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont
id: merge_request.to_param
}, format: :html
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/merge_requests_diffs.rb b/spec/frontend/fixtures/merge_requests_diffs.rb
index 03b9b713fd8..b633a0495a6 100644
--- a/spec/javascripts/fixtures/merge_requests_diffs.rb
+++ b/spec/frontend/fixtures/merge_requests_diffs.rb
@@ -1,4 +1,3 @@
-
require 'spec_helper'
describe Projects::MergeRequests::DiffsController, '(JavaScript fixtures)', type: :controller do
@@ -65,6 +64,6 @@ describe Projects::MergeRequests::DiffsController, '(JavaScript fixtures)', type
**extra_params
}, format: :json
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/pipeline_schedules.rb b/spec/frontend/fixtures/pipeline_schedules.rb
index aecd56e6198..a70091a3919 100644
--- a/spec/javascripts/fixtures/pipeline_schedules.rb
+++ b/spec/frontend/fixtures/pipeline_schedules.rb
@@ -28,7 +28,7 @@ describe Projects::PipelineSchedulesController, '(JavaScript fixtures)', type: :
id: pipeline_schedule.id
}
- expect(response).to be_success
+ expect(response).to be_successful
end
it 'pipeline_schedules/edit_with_variables.html' do
@@ -38,6 +38,6 @@ describe Projects::PipelineSchedulesController, '(JavaScript fixtures)', type: :
id: pipeline_schedule_populated.id
}
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/pipelines.rb b/spec/frontend/fixtures/pipelines.rb
index 6b6b0eefab9..ed57eb0aa80 100644
--- a/spec/javascripts/fixtures/pipelines.rb
+++ b/spec/frontend/fixtures/pipelines.rb
@@ -29,6 +29,6 @@ describe Projects::PipelinesController, '(JavaScript fixtures)', type: :controll
project_id: project
}, format: :json
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/projects.rb b/spec/frontend/fixtures/projects.rb
index 94c59207898..b6c29003e57 100644
--- a/spec/javascripts/fixtures/projects.rb
+++ b/spec/frontend/fixtures/projects.rb
@@ -18,6 +18,8 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
end
before do
+ stub_licensed_features(variable_environment_scope: true)
+
project.add_maintainer(admin)
sign_in(admin)
allow(SecureRandom).to receive(:hex).and_return('securerandomhex:thereisnospoon')
@@ -34,7 +36,7 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
id: project
}
- expect(response).to be_success
+ expect(response).to be_successful
end
it 'projects/overview.html' do
@@ -43,7 +45,7 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
id: project_with_repo
}
- expect(response).to be_success
+ expect(response).to be_successful
end
it 'projects/edit.html' do
@@ -52,7 +54,7 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
id: project
}
- expect(response).to be_success
+ expect(response).to be_successful
end
end
@@ -63,7 +65,7 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
project_id: project
}
- expect(response).to be_success
+ expect(response).to be_successful
end
it 'projects/ci_cd_settings_with_variables.html' do
@@ -75,7 +77,7 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
project_id: project_variable_populated
}
- expect(response).to be_success
+ expect(response).to be_successful
end
end
end
diff --git a/spec/javascripts/fixtures/prometheus_service.rb b/spec/frontend/fixtures/prometheus_service.rb
index f3171fdd97b..93ee81120d7 100644
--- a/spec/javascripts/fixtures/prometheus_service.rb
+++ b/spec/frontend/fixtures/prometheus_service.rb
@@ -29,6 +29,6 @@ describe Projects::ServicesController, '(JavaScript fixtures)', type: :controlle
id: service.to_param
}
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/raw.rb b/spec/frontend/fixtures/raw.rb
index 801c80a0112..801c80a0112 100644
--- a/spec/javascripts/fixtures/raw.rb
+++ b/spec/frontend/fixtures/raw.rb
diff --git a/spec/javascripts/fixtures/search.rb b/spec/frontend/fixtures/search.rb
index 22fc546d761..c26c6998ae9 100644
--- a/spec/javascripts/fixtures/search.rb
+++ b/spec/frontend/fixtures/search.rb
@@ -12,6 +12,6 @@ describe SearchController, '(JavaScript fixtures)', type: :controller do
it 'search/show.html' do
get :show
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/services.rb b/spec/frontend/fixtures/services.rb
index 2237702ccca..ee1e088f158 100644
--- a/spec/javascripts/fixtures/services.rb
+++ b/spec/frontend/fixtures/services.rb
@@ -29,6 +29,6 @@ describe Projects::ServicesController, '(JavaScript fixtures)', type: :controlle
id: service.to_param
}
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/javascripts/fixtures/sessions.rb b/spec/frontend/fixtures/sessions.rb
index 92b74c01c89..18574ea06b5 100644
--- a/spec/javascripts/fixtures/sessions.rb
+++ b/spec/frontend/fixtures/sessions.rb
@@ -19,7 +19,7 @@ describe 'Sessions (JavaScript fixtures)' do
it 'sessions/new.html' do
get :new
- expect(response).to be_success
+ expect(response).to be_successful
end
end
end
diff --git a/spec/javascripts/fixtures/snippet.rb b/spec/frontend/fixtures/snippet.rb
index ace84b14eb7..23bcdb47ac6 100644
--- a/spec/javascripts/fixtures/snippet.rb
+++ b/spec/frontend/fixtures/snippet.rb
@@ -28,6 +28,6 @@ describe SnippetsController, '(JavaScript fixtures)', type: :controller do
get(:show, params: { id: snippet.to_param })
- expect(response).to be_success
+ expect(response).to be_successful
end
end
diff --git a/spec/frontend/fixtures/static/README.md b/spec/frontend/fixtures/static/README.md
new file mode 100644
index 00000000000..011601d0df8
--- /dev/null
+++ b/spec/frontend/fixtures/static/README.md
@@ -0,0 +1,3 @@
+# Please do not add new files here!
+
+Instead use a Ruby file in the fixtures root directory (`spec/frontend/fixtures/`).
diff --git a/spec/javascripts/fixtures/static/ajax_loading_spinner.html b/spec/frontend/fixtures/static/ajax_loading_spinner.html
index 0e1ebb32b1c..0e1ebb32b1c 100644
--- a/spec/javascripts/fixtures/static/ajax_loading_spinner.html
+++ b/spec/frontend/fixtures/static/ajax_loading_spinner.html
diff --git a/spec/javascripts/fixtures/static/balsamiq_viewer.html b/spec/frontend/fixtures/static/balsamiq_viewer.html
index cdd723d1a84..cdd723d1a84 100644
--- a/spec/javascripts/fixtures/static/balsamiq_viewer.html
+++ b/spec/frontend/fixtures/static/balsamiq_viewer.html
diff --git a/spec/javascripts/fixtures/static/create_item_dropdown.html b/spec/frontend/fixtures/static/create_item_dropdown.html
index d2d38370092..d2d38370092 100644
--- a/spec/javascripts/fixtures/static/create_item_dropdown.html
+++ b/spec/frontend/fixtures/static/create_item_dropdown.html
diff --git a/spec/javascripts/fixtures/static/environments/table.html b/spec/frontend/fixtures/static/environments/table.html
index 417af564ff1..417af564ff1 100644
--- a/spec/javascripts/fixtures/static/environments/table.html
+++ b/spec/frontend/fixtures/static/environments/table.html
diff --git a/spec/frontend/fixtures/static/environments_logs.html b/spec/frontend/fixtures/static/environments_logs.html
new file mode 100644
index 00000000000..6179d3dbc23
--- /dev/null
+++ b/spec/frontend/fixtures/static/environments_logs.html
@@ -0,0 +1,29 @@
+<div class="js-kubernetes-logs" data-logs-path="/root/kubernetes-app/environments/1/logs">
+ <div class="build-page">
+ <div class="build-trace-container prepend-top-default">
+ <div class="top-bar js-top-bar">
+ <div class="truncated-info hidden-xs pull-left"></div>
+ <div class="dropdown prepend-left-10 js-pod-dropdown">
+ <button aria-expanded="false" class="dropdown-menu-toggle" data-toggle="dropdown" type="button">
+ <i class="fa fa-chevron-down"></i>
+ </button>
+ <div class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up"></div>
+ </div>
+ <div class="controllers pull-right">
+ <div class="has-tooltip controllers-buttons" data-container="body" data-placement="top" title="Scroll to top">
+ <button class="js-scroll-up btn-scroll btn-transparent btn-blank" disabled type="button"></button>
+ </div>
+ <div class="has-tooltip controllers-buttons" data-container="body" data-placement="top" title="Scroll to bottom">
+ <button class="js-scroll-down btn-scroll btn-transparent btn-blank" disabled type="button"></button>
+ </div>
+ <div class="refresh-control pull-right">
+ <div class="has-tooltip controllers-buttons" data-container="body" data-placement="top" title="Refresh">
+ <button class="js-refresh-log btn-default btn-refresh" disabled type="button"></button>
+ </div>
+ </div>
+ </div>
+ </div>
+ <pre class="build-trace" id="build-trace"><code class="bash js-build-output"><div class="build-loader-animation js-build-refresh"></div></code></pre>
+ </div>
+ </div>
+</div>
diff --git a/spec/javascripts/fixtures/static/event_filter.html b/spec/frontend/fixtures/static/event_filter.html
index 8e9b6fb1b5c..8e9b6fb1b5c 100644
--- a/spec/javascripts/fixtures/static/event_filter.html
+++ b/spec/frontend/fixtures/static/event_filter.html
diff --git a/spec/javascripts/fixtures/static/gl_dropdown.html b/spec/frontend/fixtures/static/gl_dropdown.html
index 08f6738414e..08f6738414e 100644
--- a/spec/javascripts/fixtures/static/gl_dropdown.html
+++ b/spec/frontend/fixtures/static/gl_dropdown.html
diff --git a/spec/javascripts/fixtures/static/gl_field_errors.html b/spec/frontend/fixtures/static/gl_field_errors.html
index f8470e02b7c..f8470e02b7c 100644
--- a/spec/javascripts/fixtures/static/gl_field_errors.html
+++ b/spec/frontend/fixtures/static/gl_field_errors.html
diff --git a/spec/javascripts/fixtures/static/images/green_box.png b/spec/frontend/fixtures/static/images/green_box.png
index cd1ff9f9ade..cd1ff9f9ade 100644
--- a/spec/javascripts/fixtures/static/images/green_box.png
+++ b/spec/frontend/fixtures/static/images/green_box.png
Binary files differ
diff --git a/spec/javascripts/fixtures/static/images/one_white_pixel.png b/spec/frontend/fixtures/static/images/one_white_pixel.png
index 073fcf40a18..073fcf40a18 100644
--- a/spec/javascripts/fixtures/static/images/one_white_pixel.png
+++ b/spec/frontend/fixtures/static/images/one_white_pixel.png
Binary files differ
diff --git a/spec/javascripts/fixtures/static/images/red_box.png b/spec/frontend/fixtures/static/images/red_box.png
index 73b2927da0f..73b2927da0f 100644
--- a/spec/javascripts/fixtures/static/images/red_box.png
+++ b/spec/frontend/fixtures/static/images/red_box.png
Binary files differ
diff --git a/spec/javascripts/fixtures/static/issuable_filter.html b/spec/frontend/fixtures/static/issuable_filter.html
index 06b70fb43f1..06b70fb43f1 100644
--- a/spec/javascripts/fixtures/static/issuable_filter.html
+++ b/spec/frontend/fixtures/static/issuable_filter.html
diff --git a/spec/javascripts/fixtures/static/issue_sidebar_label.html b/spec/frontend/fixtures/static/issue_sidebar_label.html
index ec8fb30f219..ec8fb30f219 100644
--- a/spec/javascripts/fixtures/static/issue_sidebar_label.html
+++ b/spec/frontend/fixtures/static/issue_sidebar_label.html
diff --git a/spec/javascripts/fixtures/static/line_highlighter.html b/spec/frontend/fixtures/static/line_highlighter.html
index 897a25d6760..897a25d6760 100644
--- a/spec/javascripts/fixtures/static/line_highlighter.html
+++ b/spec/frontend/fixtures/static/line_highlighter.html
diff --git a/spec/javascripts/fixtures/static/linked_tabs.html b/spec/frontend/fixtures/static/linked_tabs.html
index c25463bf1db..c25463bf1db 100644
--- a/spec/javascripts/fixtures/static/linked_tabs.html
+++ b/spec/frontend/fixtures/static/linked_tabs.html
diff --git a/spec/javascripts/fixtures/static/merge_requests_show.html b/spec/frontend/fixtures/static/merge_requests_show.html
index 87e36c9f315..87e36c9f315 100644
--- a/spec/javascripts/fixtures/static/merge_requests_show.html
+++ b/spec/frontend/fixtures/static/merge_requests_show.html
diff --git a/spec/javascripts/fixtures/static/mini_dropdown_graph.html b/spec/frontend/fixtures/static/mini_dropdown_graph.html
index cd0b8dec3fc..cd0b8dec3fc 100644
--- a/spec/javascripts/fixtures/static/mini_dropdown_graph.html
+++ b/spec/frontend/fixtures/static/mini_dropdown_graph.html
diff --git a/spec/javascripts/fixtures/static/notebook_viewer.html b/spec/frontend/fixtures/static/notebook_viewer.html
index 4bbb7bf1094..4bbb7bf1094 100644
--- a/spec/javascripts/fixtures/static/notebook_viewer.html
+++ b/spec/frontend/fixtures/static/notebook_viewer.html
diff --git a/spec/javascripts/fixtures/static/oauth_remember_me.html b/spec/frontend/fixtures/static/oauth_remember_me.html
index 9ba1ffc72fe..9ba1ffc72fe 100644
--- a/spec/javascripts/fixtures/static/oauth_remember_me.html
+++ b/spec/frontend/fixtures/static/oauth_remember_me.html
diff --git a/spec/javascripts/fixtures/static/pdf_viewer.html b/spec/frontend/fixtures/static/pdf_viewer.html
index 350d35a262f..350d35a262f 100644
--- a/spec/javascripts/fixtures/static/pdf_viewer.html
+++ b/spec/frontend/fixtures/static/pdf_viewer.html
diff --git a/spec/javascripts/fixtures/static/pipeline_graph.html b/spec/frontend/fixtures/static/pipeline_graph.html
index 422372bb7d5..422372bb7d5 100644
--- a/spec/javascripts/fixtures/static/pipeline_graph.html
+++ b/spec/frontend/fixtures/static/pipeline_graph.html
diff --git a/spec/javascripts/fixtures/static/pipelines.html b/spec/frontend/fixtures/static/pipelines.html
index 42333f94f2f..42333f94f2f 100644
--- a/spec/javascripts/fixtures/static/pipelines.html
+++ b/spec/frontend/fixtures/static/pipelines.html
diff --git a/spec/javascripts/fixtures/static/project_select_combo_button.html b/spec/frontend/fixtures/static/project_select_combo_button.html
index 50c826051c0..50c826051c0 100644
--- a/spec/javascripts/fixtures/static/project_select_combo_button.html
+++ b/spec/frontend/fixtures/static/project_select_combo_button.html
diff --git a/spec/javascripts/fixtures/static/projects.json b/spec/frontend/fixtures/static/projects.json
index 68a150f602a..68a150f602a 100644
--- a/spec/javascripts/fixtures/static/projects.json
+++ b/spec/frontend/fixtures/static/projects.json
diff --git a/spec/javascripts/fixtures/static/search_autocomplete.html b/spec/frontend/fixtures/static/search_autocomplete.html
index 29db9020424..29db9020424 100644
--- a/spec/javascripts/fixtures/static/search_autocomplete.html
+++ b/spec/frontend/fixtures/static/search_autocomplete.html
diff --git a/spec/javascripts/fixtures/static/signin_tabs.html b/spec/frontend/fixtures/static/signin_tabs.html
index 7e66ab9394b..7e66ab9394b 100644
--- a/spec/javascripts/fixtures/static/signin_tabs.html
+++ b/spec/frontend/fixtures/static/signin_tabs.html
diff --git a/spec/javascripts/fixtures/static/sketch_viewer.html b/spec/frontend/fixtures/static/sketch_viewer.html
index e25e554e568..e25e554e568 100644
--- a/spec/javascripts/fixtures/static/sketch_viewer.html
+++ b/spec/frontend/fixtures/static/sketch_viewer.html
diff --git a/spec/javascripts/fixtures/todos.rb b/spec/frontend/fixtures/todos.rb
index d0c8a6eca01..a7c183d2414 100644
--- a/spec/javascripts/fixtures/todos.rb
+++ b/spec/frontend/fixtures/todos.rb
@@ -29,7 +29,7 @@ describe 'Todos (JavaScript fixtures)' do
it 'todos/todos.html' do
get :index
- expect(response).to be_success
+ expect(response).to be_successful
end
end
@@ -48,7 +48,7 @@ describe 'Todos (JavaScript fixtures)' do
issuable_id: issue_2.id
}, format: 'json'
- expect(response).to be_success
+ expect(response).to be_successful
end
end
end
diff --git a/spec/javascripts/fixtures/u2f.rb b/spec/frontend/fixtures/u2f.rb
index f52832b6efb..8ecbc0390cd 100644
--- a/spec/javascripts/fixtures/u2f.rb
+++ b/spec/frontend/fixtures/u2f.rb
@@ -23,7 +23,7 @@ context 'U2F' do
post :create, params: { user: { login: user.username, password: user.password } }
- expect(response).to be_success
+ expect(response).to be_successful
end
end
@@ -38,7 +38,7 @@ context 'U2F' do
it 'u2f/register.html' do
get :show
- expect(response).to be_success
+ expect(response).to be_successful
end
end
end
diff --git a/spec/frontend/helpers/fixtures.js b/spec/frontend/helpers/fixtures.js
index b77bcd6266e..778196843db 100644
--- a/spec/frontend/helpers/fixtures.js
+++ b/spec/frontend/helpers/fixtures.js
@@ -4,12 +4,15 @@ import path from 'path';
import { ErrorWithStack } from 'jest-util';
export function getFixture(relativePath) {
- const absolutePath = path.join(global.fixturesBasePath, relativePath);
+ const basePath = relativePath.startsWith('static/')
+ ? global.staticFixturesBasePath
+ : global.fixturesBasePath;
+ const absolutePath = path.join(basePath, relativePath);
if (!fs.existsSync(absolutePath)) {
throw new ErrorWithStack(
`Fixture file ${relativePath} does not exist.
-Did you run bin/rake karma:fixtures?`,
+Did you run bin/rake frontend:fixtures?`,
getFixture,
);
}
diff --git a/spec/frontend/ide/services/index_spec.js b/spec/frontend/ide/services/index_spec.js
index 499fa8fc012..3d5ed4b5c0c 100644
--- a/spec/frontend/ide/services/index_spec.js
+++ b/spec/frontend/ide/services/index_spec.js
@@ -16,40 +16,16 @@ describe('IDE services', () => {
branch: TEST_BRANCH,
commit_message: 'Hello world',
actions: [],
- start_sha: undefined,
+ start_sha: TEST_COMMIT_SHA,
};
- Api.createBranch.mockReturnValue(Promise.resolve());
Api.commitMultiple.mockReturnValue(Promise.resolve());
});
- describe.each`
- startSha | shouldCreateBranch
- ${undefined} | ${false}
- ${TEST_COMMIT_SHA} | ${true}
- `('when start_sha is $startSha', ({ startSha, shouldCreateBranch }) => {
- beforeEach(() => {
- payload.start_sha = startSha;
+ it('should commit', () => {
+ services.commit(TEST_PROJECT_ID, payload);
- return services.commit(TEST_PROJECT_ID, payload);
- });
-
- if (shouldCreateBranch) {
- it('should create branch', () => {
- expect(Api.createBranch).toHaveBeenCalledWith(TEST_PROJECT_ID, {
- ref: TEST_COMMIT_SHA,
- branch: TEST_BRANCH,
- });
- });
- } else {
- it('should not create branch', () => {
- expect(Api.createBranch).not.toHaveBeenCalled();
- });
- }
-
- it('should commit', () => {
- expect(Api.commitMultiple).toHaveBeenCalledWith(TEST_PROJECT_ID, payload);
- });
+ expect(Api.commitMultiple).toHaveBeenCalledWith(TEST_PROJECT_ID, payload);
});
});
});
diff --git a/spec/frontend/mocks/ce/lib/utils/axios_utils.js b/spec/frontend/mocks/ce/lib/utils/axios_utils.js
new file mode 100644
index 00000000000..b4065626b09
--- /dev/null
+++ b/spec/frontend/mocks/ce/lib/utils/axios_utils.js
@@ -0,0 +1,15 @@
+const axios = jest.requireActual('~/lib/utils/axios_utils').default;
+
+axios.isMock = true;
+
+// Fail tests for unmocked requests
+axios.defaults.adapter = config => {
+ const message =
+ `Unexpected unmocked request: ${JSON.stringify(config, null, 2)}\n` +
+ 'Consider using the `axios-mock-adapter` in tests.';
+ const error = new Error(message);
+ error.config = config;
+ throw error;
+};
+
+export default axios;
diff --git a/spec/frontend/mocks/mocks_helper.js b/spec/frontend/mocks/mocks_helper.js
new file mode 100644
index 00000000000..21c032cd3c9
--- /dev/null
+++ b/spec/frontend/mocks/mocks_helper.js
@@ -0,0 +1,60 @@
+/**
+ * @module
+ *
+ * This module implements auto-injected manual mocks that are cleaner than Jest's approach.
+ *
+ * See https://docs.gitlab.com/ee/development/testing_guide/frontend_testing.html
+ */
+
+import fs from 'fs';
+import path from 'path';
+
+import readdir from 'readdir-enhanced';
+
+const MAX_DEPTH = 20;
+const prefixMap = [
+ // E.g. the mock ce/foo/bar maps to require path ~/foo/bar
+ { mocksRoot: 'ce', requirePrefix: '~' },
+ // { mocksRoot: 'ee', requirePrefix: 'ee' }, // We'll deal with EE-specific mocks later
+ { mocksRoot: 'node', requirePrefix: '' },
+ // { mocksRoot: 'virtual', requirePrefix: '' }, // We'll deal with virtual mocks later
+];
+
+const mockFileFilter = stats => stats.isFile() && stats.path.endsWith('.js');
+
+const getMockFiles = root => readdir.sync(root, { deep: MAX_DEPTH, filter: mockFileFilter });
+
+// Function that performs setting a mock. This has to be overridden by the unit test, because
+// jest.setMock can't be overwritten across files.
+// Use require() because jest.setMock expects the CommonJS exports object
+const defaultSetMock = (srcPath, mockPath) =>
+ jest.mock(srcPath, () => jest.requireActual(mockPath));
+
+// eslint-disable-next-line import/prefer-default-export
+export const setupManualMocks = function setupManualMocks(setMock = defaultSetMock) {
+ prefixMap.forEach(({ mocksRoot, requirePrefix }) => {
+ const mocksRootAbsolute = path.join(__dirname, mocksRoot);
+ if (!fs.existsSync(mocksRootAbsolute)) {
+ return;
+ }
+
+ getMockFiles(path.join(__dirname, mocksRoot)).forEach(mockPath => {
+ const mockPathNoExt = mockPath.substring(0, mockPath.length - path.extname(mockPath).length);
+ const sourcePath = path.join(requirePrefix, mockPathNoExt);
+ const mockPathRelative = `./${path.join(mocksRoot, mockPathNoExt)}`;
+
+ try {
+ setMock(sourcePath, mockPathRelative);
+ } catch (e) {
+ if (e.message.includes('Could not locate module')) {
+ // The corresponding mocked module doesn't exist. Raise a better error.
+ // Eventualy, we may support virtual mocks (mocks whose path doesn't directly correspond
+ // to a module, like with the `ee_else_ce` prefix).
+ throw new Error(
+ `A manual mock was defined for module ${sourcePath}, but the module doesn't exist!`,
+ );
+ }
+ }
+ });
+ });
+};
diff --git a/spec/frontend/mocks/mocks_helper_spec.js b/spec/frontend/mocks/mocks_helper_spec.js
new file mode 100644
index 00000000000..34be110a7e3
--- /dev/null
+++ b/spec/frontend/mocks/mocks_helper_spec.js
@@ -0,0 +1,147 @@
+/* eslint-disable global-require, promise/catch-or-return */
+
+import path from 'path';
+
+import axios from '~/lib/utils/axios_utils';
+
+const absPath = path.join.bind(null, __dirname);
+
+jest.mock('fs');
+jest.mock('readdir-enhanced');
+
+describe('mocks_helper.js', () => {
+ let setupManualMocks;
+ const setMock = jest.fn().mockName('setMock');
+ let fs;
+ let readdir;
+
+ beforeAll(() => {
+ jest.resetModules();
+ jest.setMock = jest.fn().mockName('jest.setMock');
+ fs = require('fs');
+ readdir = require('readdir-enhanced');
+
+ // We need to provide setupManualMocks with a mock function that pretends to do the setup of
+ // the mock. This is because we can't mock jest.setMock across files.
+ setupManualMocks = () => require('./mocks_helper').setupManualMocks(setMock);
+ });
+
+ afterEach(() => {
+ fs.existsSync.mockReset();
+ readdir.sync.mockReset();
+ setMock.mockReset();
+ });
+
+ it('enumerates through mock file roots', () => {
+ setupManualMocks();
+ expect(fs.existsSync).toHaveBeenCalledTimes(2);
+ expect(fs.existsSync).toHaveBeenNthCalledWith(1, absPath('ce'));
+ expect(fs.existsSync).toHaveBeenNthCalledWith(2, absPath('node'));
+
+ expect(readdir.sync).toHaveBeenCalledTimes(0);
+ });
+
+ it("doesn't traverse the directory tree infinitely", () => {
+ fs.existsSync.mockReturnValue(true);
+ readdir.sync.mockReturnValue([]);
+ setupManualMocks();
+
+ readdir.mock.calls.forEach(call => {
+ expect(call[1].deep).toBeLessThan(100);
+ });
+ });
+
+ it('sets up mocks for CE (the ~/ prefix)', () => {
+ fs.existsSync.mockImplementation(root => root.endsWith('ce'));
+ readdir.sync.mockReturnValue(['root.js', 'lib/utils/util.js']);
+ setupManualMocks();
+
+ expect(readdir.sync).toHaveBeenCalledTimes(1);
+ expect(readdir.sync.mock.calls[0][0]).toBe(absPath('ce'));
+
+ expect(setMock).toHaveBeenCalledTimes(2);
+ expect(setMock).toHaveBeenNthCalledWith(1, '~/root', './ce/root');
+ expect(setMock).toHaveBeenNthCalledWith(2, '~/lib/utils/util', './ce/lib/utils/util');
+ });
+
+ it('sets up mocks for node_modules', () => {
+ fs.existsSync.mockImplementation(root => root.endsWith('node'));
+ readdir.sync.mockReturnValue(['jquery', '@babel/core']);
+ setupManualMocks();
+
+ expect(readdir.sync).toHaveBeenCalledTimes(1);
+ expect(readdir.sync.mock.calls[0][0]).toBe(absPath('node'));
+
+ expect(setMock).toHaveBeenCalledTimes(2);
+ expect(setMock).toHaveBeenNthCalledWith(1, 'jquery', './node/jquery');
+ expect(setMock).toHaveBeenNthCalledWith(2, '@babel/core', './node/@babel/core');
+ });
+
+ it('sets up mocks for all roots', () => {
+ const files = {
+ [absPath('ce')]: ['root', 'lib/utils/util'],
+ [absPath('node')]: ['jquery', '@babel/core'],
+ };
+
+ fs.existsSync.mockReturnValue(true);
+ readdir.sync.mockImplementation(root => files[root]);
+ setupManualMocks();
+
+ expect(readdir.sync).toHaveBeenCalledTimes(2);
+ expect(readdir.sync.mock.calls[0][0]).toBe(absPath('ce'));
+ expect(readdir.sync.mock.calls[1][0]).toBe(absPath('node'));
+
+ expect(setMock).toHaveBeenCalledTimes(4);
+ expect(setMock).toHaveBeenNthCalledWith(1, '~/root', './ce/root');
+ expect(setMock).toHaveBeenNthCalledWith(2, '~/lib/utils/util', './ce/lib/utils/util');
+ expect(setMock).toHaveBeenNthCalledWith(3, 'jquery', './node/jquery');
+ expect(setMock).toHaveBeenNthCalledWith(4, '@babel/core', './node/@babel/core');
+ });
+
+ it('fails when given a virtual mock', () => {
+ fs.existsSync.mockImplementation(p => p.endsWith('ce'));
+ readdir.sync.mockReturnValue(['virtual', 'shouldntBeImported']);
+ setMock.mockImplementation(() => {
+ throw new Error('Could not locate module');
+ });
+
+ expect(setupManualMocks).toThrow(
+ new Error("A manual mock was defined for module ~/virtual, but the module doesn't exist!"),
+ );
+
+ expect(readdir.sync).toHaveBeenCalledTimes(1);
+ expect(readdir.sync.mock.calls[0][0]).toBe(absPath('ce'));
+ });
+
+ describe('auto-injection', () => {
+ it('handles ambiguous paths', () => {
+ jest.isolateModules(() => {
+ const axios2 = require('../../../app/assets/javascripts/lib/utils/axios_utils').default;
+ expect(axios2.isMock).toBe(true);
+ });
+ });
+
+ it('survives jest.isolateModules()', done => {
+ jest.isolateModules(() => {
+ const axios2 = require('~/lib/utils/axios_utils').default;
+ expect(axios2.get('http://gitlab.com'))
+ .rejects.toThrow('Unexpected unmocked request')
+ .then(done);
+ });
+ });
+
+ it('can be unmocked and remocked', () => {
+ jest.dontMock('~/lib/utils/axios_utils');
+ jest.resetModules();
+ const axios2 = require('~/lib/utils/axios_utils').default;
+ expect(axios2).not.toBe(axios);
+ expect(axios2.isMock).toBeUndefined();
+
+ jest.doMock('~/lib/utils/axios_utils');
+ jest.resetModules();
+ const axios3 = require('~/lib/utils/axios_utils').default;
+ expect(axios3).not.toBe(axios2);
+ expect(axios3.isMock).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/mocks/node/jquery.js b/spec/frontend/mocks/node/jquery.js
new file mode 100644
index 00000000000..34a25772f67
--- /dev/null
+++ b/spec/frontend/mocks/node/jquery.js
@@ -0,0 +1,13 @@
+/* eslint-disable import/no-commonjs */
+
+const $ = jest.requireActual('jquery');
+
+// Fail tests for unmocked requests
+$.ajax = () => {
+ throw new Error(
+ 'Unexpected unmocked jQuery.ajax() call! Make sure to mock jQuery.ajax() in tests.',
+ );
+};
+
+// jquery is not an ES6 module
+module.exports = $;
diff --git a/spec/frontend/mocks_spec.js b/spec/frontend/mocks_spec.js
new file mode 100644
index 00000000000..2d2324120fd
--- /dev/null
+++ b/spec/frontend/mocks_spec.js
@@ -0,0 +1,13 @@
+import $ from 'jquery';
+import axios from '~/lib/utils/axios_utils';
+
+describe('Mock auto-injection', () => {
+ describe('mocks', () => {
+ it('~/lib/utils/axios_utils', () =>
+ expect(axios.get('http://gitlab.com')).rejects.toThrow('Unexpected unmocked request'));
+
+ it('jQuery.ajax()', () => {
+ expect($.ajax).toThrow('Unexpected unmocked');
+ });
+ });
+});
diff --git a/spec/frontend/monitoring/embed/embed_spec.js b/spec/frontend/monitoring/embed/embed_spec.js
new file mode 100644
index 00000000000..3b18a0f77c7
--- /dev/null
+++ b/spec/frontend/monitoring/embed/embed_spec.js
@@ -0,0 +1,78 @@
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import Vuex from 'vuex';
+import Embed from '~/monitoring/components/embed.vue';
+import MonitorAreaChart from '~/monitoring/components/charts/area.vue';
+import { TEST_HOST } from 'helpers/test_constants';
+import { groups, initialState, metricsData, metricsWithData } from './mock_data';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('Embed', () => {
+ let wrapper;
+ let store;
+ let actions;
+
+ function mountComponent() {
+ wrapper = shallowMount(Embed, {
+ localVue,
+ store,
+ propsData: {
+ dashboardUrl: TEST_HOST,
+ },
+ });
+ }
+
+ beforeEach(() => {
+ actions = {
+ setFeatureFlags: () => {},
+ setShowErrorBanner: () => {},
+ setEndpoints: () => {},
+ fetchMetricsData: () => {},
+ };
+
+ store = new Vuex.Store({
+ modules: {
+ monitoringDashboard: {
+ namespaced: true,
+ actions,
+ state: initialState,
+ },
+ },
+ });
+ });
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ }
+ });
+
+ describe('no metrics are available yet', () => {
+ beforeEach(() => {
+ mountComponent();
+ });
+
+ it('shows an empty state when no metrics are present', () => {
+ expect(wrapper.find('.metrics-embed').exists()).toBe(true);
+ expect(wrapper.find(MonitorAreaChart).exists()).toBe(false);
+ });
+ });
+
+ describe('metrics are available', () => {
+ beforeEach(() => {
+ store.state.monitoringDashboard.groups = groups;
+ store.state.monitoringDashboard.groups[0].metrics = metricsData;
+ store.state.monitoringDashboard.metricsWithData = metricsWithData;
+
+ mountComponent();
+ });
+
+ it('shows a chart when metrics are present', () => {
+ wrapper.setProps({});
+ expect(wrapper.find('.metrics-embed').exists()).toBe(true);
+ expect(wrapper.find(MonitorAreaChart).exists()).toBe(true);
+ expect(wrapper.findAll(MonitorAreaChart).length).toBe(2);
+ });
+ });
+});
diff --git a/spec/frontend/monitoring/embed/mock_data.js b/spec/frontend/monitoring/embed/mock_data.js
new file mode 100644
index 00000000000..df4acb82e95
--- /dev/null
+++ b/spec/frontend/monitoring/embed/mock_data.js
@@ -0,0 +1,87 @@
+export const metricsWithData = [15, 16];
+
+export const groups = [
+ {
+ panels: [
+ {
+ title: 'Memory Usage (Total)',
+ type: 'area-chart',
+ y_label: 'Total Memory Used',
+ weight: 4,
+ metrics: [
+ {
+ id: 'system_metrics_kubernetes_container_memory_total',
+ metric_id: 15,
+ },
+ ],
+ },
+ {
+ title: 'Core Usage (Total)',
+ type: 'area-chart',
+ y_label: 'Total Cores',
+ weight: 3,
+ metrics: [
+ {
+ id: 'system_metrics_kubernetes_container_cores_total',
+ metric_id: 16,
+ },
+ ],
+ },
+ ],
+ },
+];
+
+export const metrics = [
+ {
+ id: 'system_metrics_kubernetes_container_memory_total',
+ metric_id: 15,
+ },
+ {
+ id: 'system_metrics_kubernetes_container_cores_total',
+ metric_id: 16,
+ },
+];
+
+const queries = [
+ {
+ result: [
+ {
+ values: [
+ ['Mon', 1220],
+ ['Tue', 932],
+ ['Wed', 901],
+ ['Thu', 934],
+ ['Fri', 1290],
+ ['Sat', 1330],
+ ['Sun', 1320],
+ ],
+ },
+ ],
+ },
+];
+
+export const metricsData = [
+ {
+ queries,
+ metrics: [
+ {
+ metric_id: 15,
+ },
+ ],
+ },
+ {
+ queries,
+ metrics: [
+ {
+ metric_id: 16,
+ },
+ ],
+ },
+];
+
+export const initialState = {
+ monitoringDashboard: {},
+ groups: [],
+ metricsWithData: [],
+ useDashboardEndpoint: true,
+};
diff --git a/spec/frontend/notes/components/discussion_jump_to_next_button_spec.js b/spec/frontend/notes/components/discussion_jump_to_next_button_spec.js
index 989b0458481..fd439ba46bd 100644
--- a/spec/frontend/notes/components/discussion_jump_to_next_button_spec.js
+++ b/spec/frontend/notes/components/discussion_jump_to_next_button_spec.js
@@ -23,8 +23,7 @@ describe('JumpToNextDiscussionButton', () => {
button.trigger('click');
- expect(wrapper.emitted()).toEqual({
- onClick: [[]],
- });
+ expect(wrapper.emitted().onClick).toBeTruthy();
+ expect(wrapper.emitted().onClick.length).toBe(1);
});
});
diff --git a/spec/frontend/operation_settings/components/external_dashboard_spec.js b/spec/frontend/operation_settings/components/external_dashboard_spec.js
index a881de8fbfe..39d7c19e731 100644
--- a/spec/frontend/operation_settings/components/external_dashboard_spec.js
+++ b/spec/frontend/operation_settings/components/external_dashboard_spec.js
@@ -7,7 +7,6 @@ import { refreshCurrentPage } from '~/lib/utils/url_utility';
import createFlash from '~/flash';
import { TEST_HOST } from 'helpers/test_constants';
-jest.mock('~/lib/utils/axios_utils');
jest.mock('~/lib/utils/url_utility');
jest.mock('~/flash');
@@ -32,6 +31,10 @@ describe('operation settings external dashboard component', () => {
wrapper = shallow ? shallowMount(...config) : mount(...config);
};
+ beforeEach(() => {
+ jest.spyOn(axios, 'patch').mockImplementation();
+ });
+
afterEach(() => {
if (wrapper.destroy) {
wrapper.destroy();
diff --git a/spec/frontend/repository/components/table/row_spec.js b/spec/frontend/repository/components/table/row_spec.js
index c566057ad3f..e539c560975 100644
--- a/spec/frontend/repository/components/table/row_spec.js
+++ b/spec/frontend/repository/components/table/row_spec.js
@@ -1,5 +1,5 @@
import { shallowMount, RouterLinkStub } from '@vue/test-utils';
-import { GlBadge } from '@gitlab/ui';
+import { GlBadge, GlLink } from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
import TableRow from '~/repository/components/table/row.vue';
@@ -142,4 +142,18 @@ describe('Repository table row component', () => {
expect(vm.find(GlBadge).exists()).toBe(true);
});
+
+ it('renders commit and web links with href for submodule', () => {
+ factory({
+ id: '1',
+ path: 'test',
+ type: 'commit',
+ url: 'https://test.com',
+ submoduleTreeUrl: 'https://test.com/commit',
+ currentPath: '/',
+ });
+
+ expect(vm.find('a').attributes('href')).toEqual('https://test.com');
+ expect(vm.find(GlLink).attributes('href')).toEqual('https://test.com/commit');
+ });
});
diff --git a/spec/frontend/test_setup.js b/spec/frontend/test_setup.js
index 15cf18700ed..e4d62b044ca 100644
--- a/spec/frontend/test_setup.js
+++ b/spec/frontend/test_setup.js
@@ -2,10 +2,10 @@ import Vue from 'vue';
import * as jqueryMatchers from 'custom-jquery-matchers';
import $ from 'jquery';
import Translate from '~/vue_shared/translate';
-import axios from '~/lib/utils/axios_utils';
import { config as testUtilsConfig } from '@vue/test-utils';
import { initializeTestTimeout } from './helpers/timeout';
import { loadHTMLFixture, setHTMLFixture } from './helpers/fixtures';
+import { setupManualMocks } from './mocks/mocks_helper';
// Expose jQuery so specs using jQuery plugins can be imported nicely.
// Here is an issue to explore better alternatives:
@@ -14,6 +14,8 @@ window.jQuery = $;
process.on('unhandledRejection', global.promiseRejectionHandler);
+setupManualMocks();
+
afterEach(() =>
// give Promises a bit more time so they fail the right test
new Promise(setImmediate).then(() => {
@@ -24,18 +26,6 @@ afterEach(() =>
initializeTestTimeout(process.env.CI ? 5000 : 500);
-// fail tests for unmocked requests
-beforeEach(done => {
- axios.defaults.adapter = config => {
- const error = new Error(`Unexpected unmocked request: ${JSON.stringify(config, null, 2)}`);
- error.config = config;
- done.fail(error);
- return Promise.reject(error);
- };
-
- done();
-});
-
Vue.config.devtools = false;
Vue.config.productionTip = false;
@@ -79,3 +69,9 @@ Object.entries(jqueryMatchers).forEach(([matcherName, matcherFactory]) => {
// Tech debt issue TBD
testUtilsConfig.logModifiedComponents = false;
+
+// Basic stub for MutationObserver
+global.MutationObserver = () => ({
+ disconnect: () => {},
+ observe: () => {},
+});
diff --git a/spec/frontend/vue_shared/components/markdown/suggestion_diff_header_spec.js b/spec/frontend/vue_shared/components/markdown/suggestion_diff_header_spec.js
index 3b6f67457ad..d69b4c7c162 100644
--- a/spec/frontend/vue_shared/components/markdown/suggestion_diff_header_spec.js
+++ b/spec/frontend/vue_shared/components/markdown/suggestion_diff_header_spec.js
@@ -73,7 +73,10 @@ describe('Suggestion Diff component', () => {
});
it('emits apply', () => {
- expect(wrapper.emittedByOrder()).toEqual([{ name: 'apply', args: [expect.any(Function)] }]);
+ expect(wrapper.emittedByOrder()).toContainEqual({
+ name: 'apply',
+ args: [expect.any(Function)],
+ });
});
it('hides apply button', () => {
diff --git a/spec/graphql/types/tree/submodule_type_spec.rb b/spec/graphql/types/tree/submodule_type_spec.rb
index bdb3149b41c..768eccba68c 100644
--- a/spec/graphql/types/tree/submodule_type_spec.rb
+++ b/spec/graphql/types/tree/submodule_type_spec.rb
@@ -5,5 +5,5 @@ require 'spec_helper'
describe Types::Tree::SubmoduleType do
it { expect(described_class.graphql_name).to eq('Submodule') }
- it { expect(described_class).to have_graphql_fields(:id, :name, :type, :path, :flat_path) }
+ it { expect(described_class).to have_graphql_fields(:id, :name, :type, :path, :flat_path, :web_url, :tree_url) }
end
diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb
index ea48c69e0ae..b922b910c89 100644
--- a/spec/helpers/submodule_helper_spec.rb
+++ b/spec/helpers/submodule_helper_spec.rb
@@ -3,15 +3,11 @@ require 'spec_helper'
describe SubmoduleHelper do
include RepoHelpers
- describe 'submodule links' do
- let(:submodule_item) { double(id: 'hash', path: 'rack') }
- let(:config) { Gitlab.config.gitlab }
- let(:repo) { double }
-
- before do
- self.instance_variable_set(:@repository, repo)
- end
+ let(:submodule_item) { double(id: 'hash', path: 'rack') }
+ let(:config) { Gitlab.config.gitlab }
+ let(:repo) { double }
+ shared_examples 'submodule_links' do
context 'submodule on self' do
before do
allow(Gitlab.config.gitlab).to receive(:protocol).and_return('http') # set this just to be sure
@@ -21,28 +17,28 @@ describe SubmoduleHelper do
allow(Gitlab.config.gitlab_shell).to receive(:ssh_port).and_return(22) # set this just to be sure
allow(Gitlab.config.gitlab_shell).to receive(:ssh_path_prefix).and_return(Settings.send(:build_gitlab_shell_ssh_path_prefix))
stub_url([config.user, '@', config.host, ':gitlab-org/gitlab-ce.git'].join(''))
- expect(submodule_links(submodule_item)).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')])
+ expect(subject).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')])
end
it 'detects ssh on non-standard port' do
allow(Gitlab.config.gitlab_shell).to receive(:ssh_port).and_return(2222)
allow(Gitlab.config.gitlab_shell).to receive(:ssh_path_prefix).and_return(Settings.send(:build_gitlab_shell_ssh_path_prefix))
stub_url(['ssh://', config.user, '@', config.host, ':2222/gitlab-org/gitlab-ce.git'].join(''))
- expect(submodule_links(submodule_item)).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')])
+ expect(subject).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')])
end
it 'detects http on standard port' do
allow(Gitlab.config.gitlab).to receive(:port).and_return(80)
allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url))
stub_url(['http://', config.host, '/gitlab-org/gitlab-ce.git'].join(''))
- expect(submodule_links(submodule_item)).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')])
+ expect(subject).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')])
end
it 'detects http on non-standard port' do
allow(Gitlab.config.gitlab).to receive(:port).and_return(3000)
allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url))
stub_url(['http://', config.host, ':3000/gitlab-org/gitlab-ce.git'].join(''))
- expect(submodule_links(submodule_item)).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')])
+ expect(subject).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')])
end
it 'works with relative_url_root' do
@@ -50,7 +46,7 @@ describe SubmoduleHelper do
allow(Gitlab.config.gitlab).to receive(:relative_url_root).and_return('/gitlab/root')
allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url))
stub_url(['http://', config.host, '/gitlab/root/gitlab-org/gitlab-ce.git'].join(''))
- expect(submodule_links(submodule_item)).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')])
+ expect(subject).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')])
end
it 'works with subgroups' do
@@ -58,34 +54,34 @@ describe SubmoduleHelper do
allow(Gitlab.config.gitlab).to receive(:relative_url_root).and_return('/gitlab/root')
allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url))
stub_url(['http://', config.host, '/gitlab/root/gitlab-org/sub/gitlab-ce.git'].join(''))
- expect(submodule_links(submodule_item)).to eq([namespace_project_path('gitlab-org/sub', 'gitlab-ce'), namespace_project_tree_path('gitlab-org/sub', 'gitlab-ce', 'hash')])
+ expect(subject).to eq([namespace_project_path('gitlab-org/sub', 'gitlab-ce'), namespace_project_tree_path('gitlab-org/sub', 'gitlab-ce', 'hash')])
end
end
context 'submodule on github.com' do
it 'detects ssh' do
stub_url('git@github.com:gitlab-org/gitlab-ce.git')
- expect(submodule_links(submodule_item)).to eq(['https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash'])
+ expect(subject).to eq(['https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash'])
end
it 'detects http' do
stub_url('http://github.com/gitlab-org/gitlab-ce.git')
- expect(submodule_links(submodule_item)).to eq(['https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash'])
+ expect(subject).to eq(['https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash'])
end
it 'detects https' do
stub_url('https://github.com/gitlab-org/gitlab-ce.git')
- expect(submodule_links(submodule_item)).to eq(['https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash'])
+ expect(subject).to eq(['https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash'])
end
it 'handles urls with no .git on the end' do
stub_url('http://github.com/gitlab-org/gitlab-ce')
- expect(submodule_links(submodule_item)).to eq(['https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash'])
+ expect(subject).to eq(['https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash'])
end
it 'returns original with non-standard url' do
stub_url('http://github.com/another/gitlab-org/gitlab-ce.git')
- expect(submodule_links(submodule_item)).to eq([repo.submodule_url_for, nil])
+ expect(subject).to eq([repo.submodule_url_for, nil])
end
end
@@ -97,39 +93,39 @@ describe SubmoduleHelper do
allow(repo).to receive(:project).and_return(project)
stub_url('./')
- expect(submodule_links(submodule_item)).to eq(["/master-project/#{project.path}", "/master-project/#{project.path}/tree/hash"])
+ expect(subject).to eq(["/master-project/#{project.path}", "/master-project/#{project.path}/tree/hash"])
end
end
context 'submodule on gitlab.com' do
it 'detects ssh' do
stub_url('git@gitlab.com:gitlab-org/gitlab-ce.git')
- expect(submodule_links(submodule_item)).to eq(['https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash'])
+ expect(subject).to eq(['https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash'])
end
it 'detects http' do
stub_url('http://gitlab.com/gitlab-org/gitlab-ce.git')
- expect(submodule_links(submodule_item)).to eq(['https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash'])
+ expect(subject).to eq(['https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash'])
end
it 'detects https' do
stub_url('https://gitlab.com/gitlab-org/gitlab-ce.git')
- expect(submodule_links(submodule_item)).to eq(['https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash'])
+ expect(subject).to eq(['https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash'])
end
it 'handles urls with no .git on the end' do
stub_url('http://gitlab.com/gitlab-org/gitlab-ce')
- expect(submodule_links(submodule_item)).to eq(['https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash'])
+ expect(subject).to eq(['https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash'])
end
it 'handles urls with trailing whitespace' do
stub_url('http://gitlab.com/gitlab-org/gitlab-ce.git ')
- expect(submodule_links(submodule_item)).to eq(['https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash'])
+ expect(subject).to eq(['https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash'])
end
it 'returns original with non-standard url' do
stub_url('http://gitlab.com/another/gitlab-org/gitlab-ce.git')
- expect(submodule_links(submodule_item)).to eq([repo.submodule_url_for, nil])
+ expect(subject).to eq([repo.submodule_url_for, nil])
end
end
@@ -137,27 +133,25 @@ describe SubmoduleHelper do
it 'sanitizes unsupported protocols' do
stub_url('javascript:alert("XSS");')
- expect(helper.submodule_links(submodule_item)).to eq([nil, nil])
+ expect(subject).to eq([nil, nil])
end
it 'sanitizes unsupported protocols disguised as a repository URL' do
stub_url('javascript:alert("XSS");foo/bar.git')
- expect(helper.submodule_links(submodule_item)).to eq([nil, nil])
+ expect(subject).to eq([nil, nil])
end
it 'sanitizes invalid URL with extended ASCII' do
stub_url('é')
- expect(helper.submodule_links(submodule_item)).to eq([nil, nil])
+ expect(subject).to eq([nil, nil])
end
it 'returns original' do
stub_url('http://mygitserver.com/gitlab-org/gitlab-ce')
- expect(submodule_links(submodule_item)).to eq([repo.submodule_url_for, nil])
- stub_url('http://mygitserver.com/gitlab-org/gitlab-ce.git')
- expect(submodule_links(submodule_item)).to eq([repo.submodule_url_for, nil])
+ expect(subject).to eq([repo.submodule_url_for, nil])
end
end
@@ -168,8 +162,7 @@ describe SubmoduleHelper do
def expect_relative_link_to_resolve_to(relative_path, expected_path)
allow(repo).to receive(:submodule_url_for).and_return(relative_path)
-
- result = submodule_links(submodule_item)
+ result = subject
expect(result).to eq([expected_path, "#{expected_path}/tree/#{submodule_item.id}"])
end
@@ -190,7 +183,7 @@ describe SubmoduleHelper do
it 'returns nil' do
allow(repo).to receive(:submodule_url_for).and_return('../../test.git')
- result = submodule_links(submodule_item)
+ result = subject
expect(result).to eq([nil, nil])
end
@@ -200,7 +193,7 @@ describe SubmoduleHelper do
it 'returns nil because it is not possible to have repo nested under another repo' do
allow(repo).to receive(:submodule_url_for).and_return('./test.git')
- result = submodule_links(submodule_item)
+ result = subject
expect(result).to eq([nil, nil])
end
@@ -238,6 +231,22 @@ describe SubmoduleHelper do
end
end
+ context 'as view helpers in view context' do
+ subject { helper.submodule_links(submodule_item) }
+
+ before do
+ self.instance_variable_set(:@repository, repo)
+ end
+
+ it_behaves_like 'submodule_links'
+ end
+
+ context 'as stand-alone module' do
+ subject { described_class.submodule_links(submodule_item, nil, repo) }
+
+ it_behaves_like 'submodule_links'
+ end
+
def stub_url(url)
allow(repo).to receive(:submodule_url_for).and_return(url)
end
diff --git a/spec/javascripts/boards/board_blank_state_spec.js b/spec/javascripts/boards/board_blank_state_spec.js
index 8e2a947b0dd..b64859ec32a 100644
--- a/spec/javascripts/boards/board_blank_state_spec.js
+++ b/spec/javascripts/boards/board_blank_state_spec.js
@@ -1,7 +1,6 @@
import Vue from 'vue';
import boardsStore from '~/boards/stores/boards_store';
import BoardBlankState from '~/boards/components/board_blank_state.vue';
-import { mockBoardService } from './mock_data';
describe('Boards blank state', () => {
let vm;
@@ -11,9 +10,10 @@ describe('Boards blank state', () => {
const Comp = Vue.extend(BoardBlankState);
boardsStore.create();
- gl.boardService = mockBoardService();
- spyOn(gl.boardService, 'generateDefaultLists').and.callFake(
+ spyOn(boardsStore, 'addList').and.stub();
+ spyOn(boardsStore, 'removeList').and.stub();
+ spyOn(boardsStore, 'generateDefaultLists').and.callFake(
() =>
new Promise((resolve, reject) => {
if (fail) {
@@ -71,9 +71,14 @@ describe('Boards blank state', () => {
vm.$el.querySelector('.btn-success').click();
setTimeout(() => {
- expect(boardsStore.state.lists.length).toBe(2);
- expect(boardsStore.state.lists[0].title).toEqual('To Do');
- expect(boardsStore.state.lists[1].title).toEqual('Doing');
+ expect(boardsStore.addList).toHaveBeenCalledTimes(2);
+ expect(boardsStore.addList).toHaveBeenCalledWith(
+ jasmine.objectContaining({ title: 'To Do' }),
+ );
+
+ expect(boardsStore.addList).toHaveBeenCalledWith(
+ jasmine.objectContaining({ title: 'Doing' }),
+ );
done();
});
@@ -86,7 +91,7 @@ describe('Boards blank state', () => {
setTimeout(() => {
expect(boardsStore.welcomeIsHidden()).toBeFalsy();
- expect(boardsStore.state.lists.length).toBe(1);
+ expect(boardsStore.removeList).toHaveBeenCalledWith(undefined, 'label');
done();
});
diff --git a/spec/javascripts/boards/components/board_form_spec.js b/spec/javascripts/boards/components/board_form_spec.js
new file mode 100644
index 00000000000..e9014156a98
--- /dev/null
+++ b/spec/javascripts/boards/components/board_form_spec.js
@@ -0,0 +1,56 @@
+import $ from 'jquery';
+import Vue from 'vue';
+import boardsStore from '~/boards/stores/boards_store';
+import boardForm from '~/boards/components/board_form.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+describe('board_form.vue', () => {
+ const props = {
+ canAdminBoard: false,
+ labelsPath: `${gl.TEST_HOST}/labels/path`,
+ milestonePath: `${gl.TEST_HOST}/milestone/path`,
+ };
+ let vm;
+
+ beforeEach(() => {
+ spyOn($, 'ajax');
+ boardsStore.state.currentPage = 'edit';
+ const Component = Vue.extend(boardForm);
+ vm = mountComponent(Component, props);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('methods', () => {
+ describe('cancel', () => {
+ it('resets currentPage', done => {
+ vm.cancel();
+
+ Vue.nextTick()
+ .then(() => {
+ expect(boardsStore.state.currentPage).toBe('');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+
+ describe('buttons', () => {
+ it('cancel button triggers cancel()', done => {
+ spyOn(vm, 'cancel');
+
+ Vue.nextTick()
+ .then(() => {
+ const cancelButton = vm.$el.querySelector('button[data-dismiss="modal"]');
+ cancelButton.click();
+
+ expect(vm.cancel).toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+});
diff --git a/spec/javascripts/boards/components/boards_selector_spec.js b/spec/javascripts/boards/components/boards_selector_spec.js
new file mode 100644
index 00000000000..504bc51778c
--- /dev/null
+++ b/spec/javascripts/boards/components/boards_selector_spec.js
@@ -0,0 +1,205 @@
+import Vue from 'vue';
+import BoardService from '~/boards/services/board_service';
+import BoardsSelector from '~/boards/components/boards_selector.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { TEST_HOST } from 'spec/test_constants';
+import boardsStore from '~/boards/stores/boards_store';
+
+const throttleDuration = 1;
+
+function boardGenerator(n) {
+ return new Array(n).fill().map((board, id) => {
+ const name = `board${id}`;
+
+ return {
+ id,
+ name,
+ };
+ });
+}
+
+describe('BoardsSelector', () => {
+ let vm;
+ let allBoardsResponse;
+ let recentBoardsResponse;
+ let fillSearchBox;
+ const boards = boardGenerator(20);
+ const recentBoards = boardGenerator(5);
+
+ beforeEach(done => {
+ setFixtures('<div class="js-boards-selector"></div>');
+ window.gl = window.gl || {};
+
+ boardsStore.setEndpoints({
+ boardsEndpoint: '',
+ recentBoardsEndpoint: '',
+ listsEndpoint: '',
+ bulkUpdatePath: '',
+ boardId: '',
+ });
+ window.gl.boardService = new BoardService();
+
+ allBoardsResponse = Promise.resolve({
+ data: boards,
+ });
+ recentBoardsResponse = Promise.resolve({
+ data: recentBoards,
+ });
+
+ spyOn(BoardService.prototype, 'allBoards').and.returnValue(allBoardsResponse);
+ spyOn(BoardService.prototype, 'recentBoards').and.returnValue(recentBoardsResponse);
+
+ const Component = Vue.extend(BoardsSelector);
+ vm = mountComponent(
+ Component,
+ {
+ throttleDuration,
+ currentBoard: {
+ id: 1,
+ name: 'Development',
+ milestone_id: null,
+ weight: null,
+ assignee_id: null,
+ labels: [],
+ },
+ milestonePath: `${TEST_HOST}/milestone/path`,
+ boardBaseUrl: `${TEST_HOST}/board/base/url`,
+ hasMissingBoards: false,
+ canAdminBoard: true,
+ multipleIssueBoardsAvailable: true,
+ labelsPath: `${TEST_HOST}/labels/path`,
+ projectId: 42,
+ groupId: 19,
+ scopedIssueBoardFeatureEnabled: true,
+ weights: [],
+ },
+ document.querySelector('.js-boards-selector'),
+ );
+
+ vm.$el.querySelector('.js-dropdown-toggle').click();
+
+ Promise.all([allBoardsResponse, recentBoardsResponse])
+ .then(() => vm.$nextTick())
+ .then(done)
+ .catch(done.fail);
+
+ fillSearchBox = filterTerm => {
+ const { searchBox } = vm.$refs;
+ const searchBoxInput = searchBox.$el.querySelector('input');
+ searchBoxInput.value = filterTerm;
+ searchBoxInput.dispatchEvent(new Event('input'));
+ };
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ window.gl.boardService = undefined;
+ });
+
+ describe('filtering', () => {
+ it('shows all boards without filtering', done => {
+ vm.$nextTick()
+ .then(() => {
+ const dropdownItem = vm.$el.querySelectorAll('.js-dropdown-item');
+
+ expect(dropdownItem.length).toBe(boards.length + recentBoards.length);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('shows only matching boards when filtering', done => {
+ const filterTerm = 'board1';
+ const expectedCount = boards.filter(board => board.name.includes(filterTerm)).length;
+
+ fillSearchBox(filterTerm);
+
+ vm.$nextTick()
+ .then(() => {
+ const dropdownItems = vm.$el.querySelectorAll('.js-dropdown-item');
+
+ expect(dropdownItems.length).toBe(expectedCount);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('shows message if there are no matching boards', done => {
+ fillSearchBox('does not exist');
+
+ vm.$nextTick()
+ .then(() => {
+ const dropdownItems = vm.$el.querySelectorAll('.js-dropdown-item');
+
+ expect(dropdownItems.length).toBe(0);
+ expect(vm.$el).toContainText('No matching boards found');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('recent boards section', () => {
+ it('shows only when boards are greater than 10', done => {
+ vm.$nextTick()
+ .then(() => {
+ const headerEls = vm.$el.querySelectorAll('.dropdown-bold-header');
+
+ const expectedCount = 2; // Recent + All
+
+ expect(expectedCount).toBe(headerEls.length);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('does not show when boards are less than 10', done => {
+ spyOn(vm, 'initScrollFade');
+ spyOn(vm, 'setScrollFade');
+
+ vm.$nextTick()
+ .then(() => {
+ vm.boards = vm.boards.slice(0, 5);
+ })
+ .then(vm.$nextTick)
+ .then(() => {
+ const headerEls = vm.$el.querySelectorAll('.dropdown-bold-header');
+ const expectedCount = 0;
+
+ expect(expectedCount).toBe(headerEls.length);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('does not show when recentBoards api returns empty array', done => {
+ vm.$nextTick()
+ .then(() => {
+ vm.recentBoards = [];
+ })
+ .then(vm.$nextTick)
+ .then(() => {
+ const headerEls = vm.$el.querySelectorAll('.dropdown-bold-header');
+ const expectedCount = 0;
+
+ expect(expectedCount).toBe(headerEls.length);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('does not show when search is active', done => {
+ fillSearchBox('Random string');
+
+ vm.$nextTick()
+ .then(() => {
+ const headerEls = vm.$el.querySelectorAll('.dropdown-bold-header');
+ const expectedCount = 0;
+
+ expect(expectedCount).toBe(headerEls.length);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+});
diff --git a/spec/javascripts/fixtures/.gitignore b/spec/javascripts/fixtures/.gitignore
index bed020f5b0a..d6b7ef32c84 100644
--- a/spec/javascripts/fixtures/.gitignore
+++ b/spec/javascripts/fixtures/.gitignore
@@ -1,5 +1,2 @@
-*.html.raw
-*.html
-*.json
-*.pdf
-*.bmpr
+*
+!.gitignore
diff --git a/spec/javascripts/fixtures/static/README.md b/spec/javascripts/fixtures/static/README.md
deleted file mode 100644
index b5c2f8233bf..00000000000
--- a/spec/javascripts/fixtures/static/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Please do not add new files here!
-
-Instead use a Ruby file in the fixtures root directory (`spec/javascripts/fixtures/`).
diff --git a/spec/javascripts/helpers/vue_test_utils_helper.js b/spec/javascripts/helpers/vue_test_utils_helper.js
index 121e99c9783..68326e37ae7 100644
--- a/spec/javascripts/helpers/vue_test_utils_helper.js
+++ b/spec/javascripts/helpers/vue_test_utils_helper.js
@@ -1,5 +1,3 @@
-/* eslint-disable import/prefer-default-export */
-
const vNodeContainsText = (vnode, text) =>
(vnode.text && vnode.text.includes(text)) ||
(vnode.children && vnode.children.filter(child => vNodeContainsText(child, text)).length);
@@ -19,3 +17,19 @@ export const shallowWrapperContainsSlotText = (shallowWrapper, slotName, text) =
Boolean(
shallowWrapper.vm.$slots[slotName].filter(vnode => vNodeContainsText(vnode, text)).length,
);
+
+/**
+ * Returns a promise that waits for a mutation to be fired before resolving
+ * NOTE: There's no reject action here so it will hang if it waits for a mutation that won't happen.
+ * @param {Object} store - The Vue store that contains the mutations
+ * @param {String} expectedMutationType - The Mutation to wait for
+ */
+export const waitForMutation = (store, expectedMutationType) =>
+ new Promise(resolve => {
+ const unsubscribe = store.subscribe(mutation => {
+ if (mutation.type === expectedMutationType) {
+ unsubscribe();
+ resolve();
+ }
+ });
+ });
diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
index 8a3c132972e..14d861f21d2 100644
--- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
@@ -245,7 +245,7 @@ describe('IDE commit module actions', () => {
master: {
workingReference: '1',
commit: {
- short_id: TEST_COMMIT_SHA,
+ id: TEST_COMMIT_SHA,
},
},
},
@@ -411,7 +411,7 @@ describe('IDE commit module actions', () => {
expect(visitUrl).toHaveBeenCalledWith(
`webUrl/merge_requests/new?merge_request[source_branch]=${
store.getters['commit/placeholderBranchName']
- }&merge_request[target_branch]=master`,
+ }&merge_request[target_branch]=master&nav_source=webide`,
);
done();
diff --git a/spec/javascripts/ide/stores/mutations/file_spec.js b/spec/javascripts/ide/stores/mutations/file_spec.js
index 7714f66c9a4..1b41c3c2548 100644
--- a/spec/javascripts/ide/stores/mutations/file_spec.js
+++ b/spec/javascripts/ide/stores/mutations/file_spec.js
@@ -103,6 +103,43 @@ describe('IDE store file mutations', () => {
expect(localState.openFiles[0].rawPath).toEqual(rawPath);
expect(localFile.rawPath).toEqual(rawPath);
});
+
+ it('does not mutate certain props on the file', () => {
+ const path = 'New Path';
+ const name = 'New Name';
+ localFile.path = path;
+ localFile.name = name;
+
+ localState.stagedFiles = [localFile];
+ localState.changedFiles = [localFile];
+ localState.openFiles = [localFile];
+
+ mutations.SET_FILE_DATA(localState, {
+ data: {
+ path: 'Old Path',
+ name: 'Old Name',
+ raw: 'Old Raw',
+ base_raw: 'Old Base Raw',
+ },
+ file: localFile,
+ });
+
+ [
+ localState.stagedFiles[0],
+ localState.changedFiles[0],
+ localState.openFiles[0],
+ localFile,
+ ].forEach(f => {
+ expect(f).toEqual(
+ jasmine.objectContaining({
+ path,
+ name,
+ raw: null,
+ baseRaw: null,
+ }),
+ );
+ });
+ });
});
describe('SET_FILE_RAW_DATA', () => {
diff --git a/spec/javascripts/notes/components/note_actions/reply_button_spec.js b/spec/javascripts/notes/components/note_actions/reply_button_spec.js
index 11fb89808d9..003773d07ea 100644
--- a/spec/javascripts/notes/components/note_actions/reply_button_spec.js
+++ b/spec/javascripts/notes/components/note_actions/reply_button_spec.js
@@ -25,8 +25,7 @@ describe('ReplyButton', () => {
button.trigger('click');
- expect(wrapper.emitted()).toEqual({
- startReplying: [[]],
- });
+ expect(wrapper.emitted().startReplying).toBeTruthy();
+ expect(wrapper.emitted().startReplying.length).toBe(1);
});
});
diff --git a/spec/javascripts/test_constants.js b/spec/javascripts/test_constants.js
index 77c206585fe..c97d47a6406 100644
--- a/spec/javascripts/test_constants.js
+++ b/spec/javascripts/test_constants.js
@@ -1,6 +1,4 @@
-export const FIXTURES_PATH = `/base/${
- process.env.IS_GITLAB_EE ? 'ee/' : ''
-}spec/javascripts/fixtures`;
+export const FIXTURES_PATH = `/fixtures`;
export const TEST_HOST = 'http://test.host';
export const DUMMY_IMAGE_URL = `${FIXTURES_PATH}/static/images/one_white_pixel.png`;
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index 5293732ead1..cbd4a509a55 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -110,6 +110,56 @@ module Gitlab
expect(render(input, context)).to include(output.strip)
end
+
+ it 'removes non footnote def ids' do
+ input = <<~ADOC
+ ++++
+ <div id="def">Footnote definition</div>
+ ++++
+ ADOC
+
+ output = <<~HTML
+ <div>Footnote definition</div>
+ HTML
+
+ expect(render(input, context)).to include(output.strip)
+ end
+
+ it 'removes non footnote ref ids' do
+ input = <<~ADOC
+ ++++
+ <a id="ref">Footnote reference</a>
+ ++++
+ ADOC
+
+ output = <<~HTML
+ <a>Footnote reference</a>
+ HTML
+
+ expect(render(input, context)).to include(output.strip)
+ end
+ end
+
+ context 'with footnotes' do
+ it 'preserves ids and links' do
+ input = <<~ADOC
+ This paragraph has a footnote.footnote:[This is the text of the footnote.]
+ ADOC
+
+ output = <<~HTML
+ <div>
+ <p>This paragraph has a footnote.<sup>[<a id="_footnoteref_1" href="#_footnotedef_1" title="View footnote.">1</a>]</sup></p>
+ </div>
+ <div>
+ <hr>
+ <div id="_footnotedef_1">
+ <a href="#_footnoteref_1">1</a>. This is the text of the footnote.
+ </div>
+ </div>
+ HTML
+
+ expect(render(input, context)).to include(output.strip)
+ end
end
context 'with section anchors' do
diff --git a/spec/lib/gitlab/auth/user_auth_finders_spec.rb b/spec/lib/gitlab/auth/user_auth_finders_spec.rb
index 1e2aebdc84b..4751f880cee 100644
--- a/spec/lib/gitlab/auth/user_auth_finders_spec.rb
+++ b/spec/lib/gitlab/auth/user_auth_finders_spec.rb
@@ -138,6 +138,20 @@ describe Gitlab::Auth::UserAuthFinders do
expect { find_user_from_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
end
end
+
+ context 'with OAuth headers' do
+ it 'returns user' do
+ env['HTTP_AUTHORIZATION'] = "Bearer #{personal_access_token.token}"
+
+ expect(find_user_from_access_token).to eq user
+ end
+
+ it 'returns exception if invalid personal_access_token' do
+ env['HTTP_AUTHORIZATION'] = 'Bearer invalid_20byte_token'
+
+ expect { find_personal_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
+ end
+ end
end
describe '#find_user_from_web_access_token' do
diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb
index d9c73cff01e..0403830f700 100644
--- a/spec/lib/gitlab/auth_spec.rb
+++ b/spec/lib/gitlab/auth_spec.rb
@@ -297,6 +297,70 @@ describe Gitlab::Auth do
let(:project) { create(:project) }
let(:auth_failure) { Gitlab::Auth::Result.new(nil, nil) }
+ context 'when deploy token and user have the same username' do
+ let(:username) { 'normal_user' }
+ let(:user) { create(:user, username: username, password: 'my-secret') }
+ let(:deploy_token) { create(:deploy_token, username: username, read_registry: false, projects: [project]) }
+
+ before do
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: username)
+ end
+
+ it 'succeeds for the token' do
+ auth_success = Gitlab::Auth::Result.new(deploy_token, project, :deploy_token, [:download_code])
+
+ expect(gl_auth.find_for_git_client(username, deploy_token.token, project: project, ip: 'ip'))
+ .to eq(auth_success)
+ end
+
+ it 'succeeds for the user' do
+ auth_success = Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, full_authentication_abilities)
+
+ expect(gl_auth.find_for_git_client(username, 'my-secret', project: project, ip: 'ip'))
+ .to eq(auth_success)
+ end
+ end
+
+ context 'when deploy tokens have the same username' do
+ context 'and belong to the same project' do
+ let!(:read_registry) { create(:deploy_token, username: 'deployer', read_repository: false, projects: [project]) }
+ let!(:read_repository) { create(:deploy_token, username: read_registry.username, read_registry: false, projects: [project]) }
+
+ it 'succeeds for the right token' do
+ auth_success = Gitlab::Auth::Result.new(read_repository, project, :deploy_token, [:download_code])
+
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: 'deployer')
+ expect(gl_auth.find_for_git_client('deployer', read_repository.token, project: project, ip: 'ip'))
+ .to eq(auth_success)
+ end
+
+ it 'fails for the wrong token' do
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'deployer')
+ expect(gl_auth.find_for_git_client('deployer', read_registry.token, project: project, ip: 'ip'))
+ .to eq(auth_failure)
+ end
+ end
+
+ context 'and belong to different projects' do
+ let!(:read_registry) { create(:deploy_token, username: 'deployer', read_repository: false, projects: [create(:project)]) }
+ let!(:read_repository) { create(:deploy_token, username: read_registry.username, read_registry: false, projects: [project]) }
+
+ it 'succeeds for the right token' do
+ auth_success = Gitlab::Auth::Result.new(read_repository, project, :deploy_token, [:download_code])
+
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: 'deployer')
+ expect(gl_auth.find_for_git_client('deployer', read_repository.token, project: project, ip: 'ip'))
+ .to eq(auth_success)
+ end
+
+ it 'fails for the wrong token' do
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'deployer')
+ expect(gl_auth.find_for_git_client('deployer', read_registry.token, project: project, ip: 'ip'))
+ .to eq(auth_failure)
+ end
+ end
+ end
+
context 'when the deploy token has read_repository as scope' do
let(:deploy_token) { create(:deploy_token, read_registry: false, projects: [project]) }
let(:login) { deploy_token.username }
diff --git a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
index 972dd7e0d2b..483c5ea9cff 100644
--- a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
+++ b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
@@ -26,7 +26,6 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :clean_gitlab_redis_cache do
end
it 'loads 10 projects without hitting Gitaly call limit', :request_store do
- allow(Gitlab::GitalyClient).to receive(:can_access_disk?).and_return(false)
projects = Gitlab::GitalyClient.allow_n_plus_1_calls do
(1..10).map { create(:project, :repository) }
end
diff --git a/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb b/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb
index 8b07da11c5d..b7a64adc2ff 100644
--- a/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb
@@ -9,12 +9,12 @@ describe Gitlab::CycleAnalytics::BaseEventFetcher do
let(:options) do
{ start_time_attrs: start_time_attrs,
end_time_attrs: end_time_attrs,
- from: 30.days.ago }
+ from: 30.days.ago,
+ project: project }
end
subject do
- described_class.new(project: project,
- stage: :issue,
+ described_class.new(stage: :issue,
options: options).fetch
end
diff --git a/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb
index c738cc49c1f..933f3c7896e 100644
--- a/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb
@@ -5,40 +5,114 @@ describe Gitlab::CycleAnalytics::CodeStage do
let(:stage_name) { :code }
let(:project) { create(:project) }
- let!(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) }
- let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) }
- let!(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) }
- let!(:mr_1) { create(:merge_request, source_project: project, created_at: 15.minutes.ago) }
- let!(:mr_2) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'A') }
- let!(:mr_3) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') }
- let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) }
+ let(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) }
+ let(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) }
+ let(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) }
+ let(:mr_1) { create(:merge_request, source_project: project, created_at: 15.minutes.ago) }
+ let(:mr_2) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'A') }
+ let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: project.creator, project: project }) }
before do
issue_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 45.minutes.ago)
issue_2.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago)
issue_3.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago)
+ create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B')
create(:merge_requests_closing_issues, merge_request: mr_1, issue: issue_1)
create(:merge_requests_closing_issues, merge_request: mr_2, issue: issue_2)
end
it_behaves_like 'base stage'
- describe '#median' do
+ describe '#project_median' do
around do |example|
Timecop.freeze { example.run }
end
it 'counts median from issues with metrics' do
- expect(stage.median).to eq(ISSUES_MEDIAN)
+ expect(stage.project_median).to eq(ISSUES_MEDIAN)
end
end
describe '#events' do
+ subject { stage.events }
+
it 'exposes merge requests that closes issues' do
- result = stage.events
+ expect(subject.count).to eq(2)
+ expect(subject.map { |event| event[:title] }).to contain_exactly(mr_1.title, mr_2.title)
+ end
+ end
+
+ context 'when group is given' do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:project_2) { create(:project, group: group) }
+ let(:project_3) { create(:project, group: group) }
+ let(:issue_2_1) { create(:issue, project: project_2, created_at: 90.minutes.ago) }
+ let(:issue_2_2) { create(:issue, project: project_3, created_at: 60.minutes.ago) }
+ let(:issue_2_3) { create(:issue, project: project_2, created_at: 60.minutes.ago) }
+ let(:mr_2_1) { create(:merge_request, source_project: project_2, created_at: 15.minutes.ago) }
+ let(:mr_2_2) { create(:merge_request, source_project: project_3, created_at: 10.minutes.ago, source_branch: 'A') }
+ let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: user, group: group }) }
+
+ before do
+ group.add_owner(user)
+ issue_2_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 45.minutes.ago)
+ issue_2_2.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago)
+ issue_2_3.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago)
+ create(:merge_requests_closing_issues, merge_request: mr_2_1, issue: issue_2_1)
+ create(:merge_requests_closing_issues, merge_request: mr_2_2, issue: issue_2_2)
+ end
+
+ describe '#group_median' do
+ around do |example|
+ Timecop.freeze { example.run }
+ end
+
+ it 'counts median from issues with metrics' do
+ expect(stage.group_median).to eq(ISSUES_MEDIAN)
+ end
+ end
+
+ describe '#events' do
+ subject { stage.events }
+
+ it 'exposes merge requests that close issues' do
+ expect(subject.count).to eq(2)
+ expect(subject.map { |event| event[:title] }).to contain_exactly(mr_2_1.title, mr_2_2.title)
+ end
+ end
+
+ context 'when subgroup is given' do
+ let(:subgroup) { create(:group, parent: group) }
+ let(:project_4) { create(:project, group: subgroup) }
+ let(:project_5) { create(:project, group: subgroup) }
+ let(:issue_3_1) { create(:issue, project: project_4, created_at: 90.minutes.ago) }
+ let(:issue_3_2) { create(:issue, project: project_5, created_at: 60.minutes.ago) }
+ let(:issue_3_3) { create(:issue, project: project_5, created_at: 60.minutes.ago) }
+ let(:mr_3_1) { create(:merge_request, source_project: project_4, created_at: 15.minutes.ago) }
+ let(:mr_3_2) { create(:merge_request, source_project: project_5, created_at: 10.minutes.ago, source_branch: 'A') }
+
+ before do
+ issue_3_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 45.minutes.ago)
+ issue_3_2.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago)
+ issue_3_3.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago)
+ create(:merge_requests_closing_issues, merge_request: mr_3_1, issue: issue_3_1)
+ create(:merge_requests_closing_issues, merge_request: mr_3_2, issue: issue_3_2)
+ end
+
+ describe '#events' do
+ subject { stage.events }
+
+ it 'exposes merge requests that close issues' do
+ expect(subject.count).to eq(4)
+ expect(subject.map { |event| event[:title] }).to contain_exactly(mr_2_1.title, mr_2_2.title, mr_3_1.title, mr_3_2.title)
+ end
- expect(result.count).to eq(2)
- expect(result.map { |event| event[:title] }).to contain_exactly(mr_1.title, mr_2.title)
+ it 'exposes merge requests that close issues with full path for subgroup' do
+ expect(subject.count).to eq(4)
+ expect(subject.find { |event| event[:title] == mr_3_1.title }[:url]).to include("#{subgroup.full_path}")
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb b/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb
new file mode 100644
index 00000000000..eea4f33ccb8
--- /dev/null
+++ b/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Gitlab::CycleAnalytics::GroupStageSummary do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, :repository, namespace: group) }
+ let(:project_2) { create(:project, :repository, namespace: group) }
+ let(:from) { 1.day.ago }
+ let(:user) { create(:user, :admin) }
+
+ subject { described_class.new(group, from: Time.now, current_user: user).data }
+
+ describe "#new_issues" do
+ context 'with from date' do
+ before do
+ Timecop.freeze(5.days.ago) { create(:issue, project: project) }
+ Timecop.freeze(5.days.ago) { create(:issue, project: project_2) }
+ Timecop.freeze(5.days.from_now) { create(:issue, project: project) }
+ Timecop.freeze(5.days.from_now) { create(:issue, project: project_2) }
+ end
+
+ it "finds the number of issues created after it" do
+ expect(subject.first[:value]).to eq(2)
+ end
+
+ context 'with subgroups' do
+ before do
+ Timecop.freeze(5.days.from_now) { create(:issue, project: create(:project, namespace: create(:group, parent: group))) }
+ end
+
+ it "finds issues from them" do
+ expect(subject.first[:value]).to eq(3)
+ end
+ end
+ end
+
+ context 'with other projects' do
+ before do
+ Timecop.freeze(5.days.from_now) { create(:issue, project: create(:project, namespace: create(:group))) }
+ Timecop.freeze(5.days.from_now) { create(:issue, project: project) }
+ Timecop.freeze(5.days.from_now) { create(:issue, project: project_2) }
+ end
+
+ it "doesn't find issues from them" do
+ expect(subject.first[:value]).to eq(2)
+ end
+ end
+ end
+
+ describe "#deploys" do
+ context 'with from date' do
+ before do
+ Timecop.freeze(5.days.ago) { create(:deployment, :success, project: project) }
+ Timecop.freeze(5.days.from_now) { create(:deployment, :success, project: project) }
+ Timecop.freeze(5.days.ago) { create(:deployment, :success, project: project_2) }
+ Timecop.freeze(5.days.from_now) { create(:deployment, :success, project: project_2) }
+ end
+
+ it "finds the number of deploys made created after it" do
+ expect(subject.second[:value]).to eq(2)
+ end
+
+ context 'with subgroups' do
+ before do
+ Timecop.freeze(5.days.from_now) do
+ create(:deployment, :success, project: create(:project, :repository, namespace: create(:group, parent: group)))
+ end
+ end
+
+ it "finds deploys from them" do
+ expect(subject.second[:value]).to eq(3)
+ end
+ end
+ end
+
+ context 'with other projects' do
+ before do
+ Timecop.freeze(5.days.from_now) do
+ create(:deployment, :success, project: create(:project, :repository, namespace: create(:group)))
+ end
+ end
+
+ it "doesn't find deploys from them" do
+ expect(subject.second[:value]).to eq(0)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb
index 3b6af9cbaed..ffd0b84cb57 100644
--- a/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb
@@ -4,11 +4,11 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec'
describe Gitlab::CycleAnalytics::IssueStage do
let(:stage_name) { :issue }
let(:project) { create(:project) }
- let!(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) }
- let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) }
- let!(:issue_3) { create(:issue, project: project, created_at: 30.minutes.ago) }
+ let(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) }
+ let(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) }
+ let(:issue_3) { create(:issue, project: project, created_at: 30.minutes.ago) }
let!(:issue_without_milestone) { create(:issue, project: project, created_at: 1.minute.ago) }
- let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) }
+ let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: project.creator, project: project }) }
before do
issue_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago )
@@ -24,7 +24,7 @@ describe Gitlab::CycleAnalytics::IssueStage do
end
it 'counts median from issues with metrics' do
- expect(stage.median).to eq(ISSUES_MEDIAN)
+ expect(stage.project_median).to eq(ISSUES_MEDIAN)
end
end
@@ -36,4 +36,67 @@ describe Gitlab::CycleAnalytics::IssueStage do
expect(result.map { |event| event[:title] }).to contain_exactly(issue_1.title, issue_2.title, issue_3.title)
end
end
+ context 'when group is given' do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:project_2) { create(:project, group: group) }
+ let(:project_3) { create(:project, group: group) }
+ let(:issue_2_1) { create(:issue, project: project_2, created_at: 90.minutes.ago) }
+ let(:issue_2_2) { create(:issue, project: project_3, created_at: 60.minutes.ago) }
+ let(:issue_2_3) { create(:issue, project: project_2, created_at: 60.minutes.ago) }
+ let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: user, group: group }) }
+
+ before do
+ group.add_owner(user)
+ issue_2_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago)
+ issue_2_2.metrics.update!(first_added_to_board_at: 30.minutes.ago)
+ end
+
+ describe '#group_median' do
+ around do |example|
+ Timecop.freeze { example.run }
+ end
+
+ it 'counts median from issues with metrics' do
+ expect(stage.group_median).to eq(ISSUES_MEDIAN)
+ end
+ end
+
+ describe '#events' do
+ subject { stage.events }
+
+ it 'exposes merge requests that close issues' do
+ expect(subject.count).to eq(2)
+ expect(subject.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title)
+ end
+ end
+
+ context 'when subgroup is given' do
+ let(:subgroup) { create(:group, parent: group) }
+ let(:project_4) { create(:project, group: subgroup) }
+ let(:project_5) { create(:project, group: subgroup) }
+ let(:issue_3_1) { create(:issue, project: project_4, created_at: 90.minutes.ago) }
+ let(:issue_3_2) { create(:issue, project: project_5, created_at: 60.minutes.ago) }
+ let(:issue_3_3) { create(:issue, project: project_5, created_at: 60.minutes.ago) }
+
+ before do
+ issue_3_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago)
+ issue_3_2.metrics.update!(first_added_to_board_at: 30.minutes.ago)
+ end
+
+ describe '#events' do
+ subject { stage.events }
+
+ it 'exposes merge requests that close issues' do
+ expect(subject.count).to eq(4)
+ expect(subject.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title, issue_3_1.title, issue_3_2.title)
+ end
+
+ it 'exposes merge requests that close issues with full path for subgroup' do
+ expect(subject.count).to eq(4)
+ expect(subject.find { |event| event[:title] == issue_3_1.title }[:url]).to include("#{subgroup.full_path}")
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb
index 506a8160412..3cd1320ca9c 100644
--- a/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::CycleAnalytics::PlanStage do
let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) }
let!(:issue_3) { create(:issue, project: project, created_at: 30.minutes.ago) }
let!(:issue_without_milestone) { create(:issue, project: project, created_at: 1.minute.ago) }
- let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) }
+ let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: project.creator, project: project }) }
before do
issue_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 10.minutes.ago)
@@ -18,22 +18,88 @@ describe Gitlab::CycleAnalytics::PlanStage do
it_behaves_like 'base stage'
- describe '#median' do
+ describe '#project_median' do
around do |example|
Timecop.freeze { example.run }
end
it 'counts median from issues with metrics' do
- expect(stage.median).to eq(ISSUES_MEDIAN)
+ expect(stage.project_median).to eq(ISSUES_MEDIAN)
end
end
describe '#events' do
+ subject { stage.events }
+
it 'exposes issues with metrics' do
- result = stage.events
+ expect(subject.count).to eq(2)
+ expect(subject.map { |event| event[:title] }).to contain_exactly(issue_1.title, issue_2.title)
+ end
+ end
+
+ context 'when group is given' do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:project_2) { create(:project, group: group) }
+ let(:project_3) { create(:project, group: group) }
+ let(:issue_2_1) { create(:issue, project: project_2, created_at: 90.minutes.ago) }
+ let(:issue_2_2) { create(:issue, project: project_3, created_at: 60.minutes.ago) }
+ let(:issue_2_3) { create(:issue, project: project_2, created_at: 60.minutes.ago) }
+ let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: user, group: group }) }
+
+ before do
+ group.add_owner(user)
+ issue_2_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 10.minutes.ago)
+ issue_2_2.metrics.update!(first_added_to_board_at: 30.minutes.ago, first_mentioned_in_commit_at: 20.minutes.ago)
+ issue_2_3.metrics.update!(first_added_to_board_at: 15.minutes.ago)
+ end
+
+ describe '#group_median' do
+ around do |example|
+ Timecop.freeze { example.run }
+ end
+
+ it 'counts median from issues with metrics' do
+ expect(stage.group_median).to eq(ISSUES_MEDIAN)
+ end
+ end
+
+ describe '#events' do
+ subject { stage.events }
+
+ it 'exposes merge requests that close issues' do
+ expect(subject.count).to eq(2)
+ expect(subject.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title)
+ end
+ end
+
+ context 'when subgroup is given' do
+ let(:subgroup) { create(:group, parent: group) }
+ let(:project_4) { create(:project, group: subgroup) }
+ let(:project_5) { create(:project, group: subgroup) }
+ let(:issue_3_1) { create(:issue, project: project_4, created_at: 90.minutes.ago) }
+ let(:issue_3_2) { create(:issue, project: project_5, created_at: 60.minutes.ago) }
+ let(:issue_3_3) { create(:issue, project: project_5, created_at: 60.minutes.ago) }
+
+ before do
+ issue_3_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 10.minutes.ago)
+ issue_3_2.metrics.update!(first_added_to_board_at: 30.minutes.ago, first_mentioned_in_commit_at: 20.minutes.ago)
+ issue_3_3.metrics.update!(first_added_to_board_at: 15.minutes.ago)
+ end
+
+ describe '#events' do
+ subject { stage.events }
+
+ it 'exposes merge requests that close issues' do
+ expect(subject.count).to eq(4)
+ expect(subject.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title, issue_3_1.title, issue_3_2.title)
+ end
- expect(result.count).to eq(2)
- expect(result.map { |event| event[:title] }).to contain_exactly(issue_1.title, issue_2.title)
+ it 'exposes merge requests that close issues with full path for subgroup' do
+ expect(subject.count).to eq(4)
+ expect(subject.find { |event| event[:title] == issue_3_1.title }[:url]).to include("#{subgroup.full_path}")
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb
index f072a9644e8..6d14973c711 100644
--- a/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb
@@ -4,14 +4,14 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec'
describe Gitlab::CycleAnalytics::ReviewStage do
let(:stage_name) { :review }
let(:project) { create(:project) }
- let!(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) }
- let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) }
- let!(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) }
- let!(:mr_1) { create(:merge_request, :closed, source_project: project, created_at: 60.minutes.ago) }
- let!(:mr_2) { create(:merge_request, :closed, source_project: project, created_at: 40.minutes.ago, source_branch: 'A') }
- let!(:mr_3) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') }
+ let(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) }
+ let(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) }
+ let(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) }
+ let(:mr_1) { create(:merge_request, :closed, source_project: project, created_at: 60.minutes.ago) }
+ let(:mr_2) { create(:merge_request, :closed, source_project: project, created_at: 40.minutes.ago, source_branch: 'A') }
+ let(:mr_3) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') }
let!(:mr_4) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'C') }
- let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) }
+ let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: project.creator, project: project }) }
before do
mr_1.metrics.update!(merged_at: 30.minutes.ago)
@@ -24,22 +24,66 @@ describe Gitlab::CycleAnalytics::ReviewStage do
it_behaves_like 'base stage'
- describe '#median' do
+ describe '#project_median' do
around do |example|
Timecop.freeze { example.run }
end
it 'counts median from issues with metrics' do
- expect(stage.median).to eq(ISSUES_MEDIAN)
+ expect(stage.project_median).to eq(ISSUES_MEDIAN)
end
end
describe '#events' do
+ subject { stage.events }
+
it 'exposes merge requests that close issues' do
- result = stage.events
+ expect(subject.count).to eq(2)
+ expect(subject.map { |event| event[:title] }).to contain_exactly(mr_1.title, mr_2.title)
+ end
+ end
+
+ context 'when group is given' do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:project_2) { create(:project, group: group) }
+ let(:project_3) { create(:project, group: group) }
+ let(:issue_2_1) { create(:issue, project: project_2, created_at: 90.minutes.ago) }
+ let(:issue_2_2) { create(:issue, project: project_3, created_at: 60.minutes.ago) }
+ let(:issue_2_3) { create(:issue, project: project_2, created_at: 60.minutes.ago) }
+ let(:mr_2_1) { create(:merge_request, :closed, source_project: project_2, created_at: 60.minutes.ago) }
+ let(:mr_2_2) { create(:merge_request, :closed, source_project: project_3, created_at: 40.minutes.ago, source_branch: 'A') }
+ let(:mr_2_3) { create(:merge_request, source_project: project_2, created_at: 10.minutes.ago, source_branch: 'B') }
+ let!(:mr_2_4) { create(:merge_request, source_project: project_3, created_at: 10.minutes.ago, source_branch: 'C') }
+ let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: user, group: group }) }
+
+ before do
+ group.add_owner(user)
+ mr_2_1.metrics.update!(merged_at: 30.minutes.ago)
+ mr_2_2.metrics.update!(merged_at: 10.minutes.ago)
+
+ create(:merge_requests_closing_issues, merge_request: mr_2_1, issue: issue_2_1)
+ create(:merge_requests_closing_issues, merge_request: mr_2_2, issue: issue_2_2)
+ create(:merge_requests_closing_issues, merge_request: mr_2_3, issue: issue_2_3)
+ end
+
+ describe '#group_median' do
+ around do |example|
+ Timecop.freeze { example.run }
+ end
+
+ it 'counts median from issues with metrics' do
+ expect(stage.group_median).to eq(ISSUES_MEDIAN)
+ end
+ end
+
+ describe '#events' do
+ subject { stage.events }
- expect(result.count).to eq(2)
- expect(result.map { |event| event[:title] }).to contain_exactly(mr_1.title, mr_2.title)
+ it 'exposes merge requests that close issues' do
+ expect(subject.count).to eq(2)
+ expect(subject.map { |event| event[:title] }).to contain_exactly(mr_2_1.title, mr_2_2.title)
+ end
end
end
end
diff --git a/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb b/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb
index c22d27f60d6..b001a46001e 100644
--- a/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
shared_examples 'default query config' do
let(:project) { create(:project) }
- let(:event) { described_class.new(project: project, stage: stage_name, options: { from: 1.day.ago }) }
+ let(:event) { described_class.new(stage: stage_name, options: { from: 1.day.ago, project: project }) }
it 'has the stage attribute' do
expect(event.stage).not_to be_nil
diff --git a/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb
index 1a4b572cc11..c146146723f 100644
--- a/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb
@@ -3,10 +3,10 @@ require 'spec_helper'
shared_examples 'base stage' do
ISSUES_MEDIAN = 30.minutes.to_i
- let(:stage) { described_class.new(project: double, options: {}) }
+ let(:stage) { described_class.new(options: { project: double }) }
before do
- allow(stage).to receive(:median).and_return(1.12)
+ allow(stage).to receive(:project_median).and_return(1.12)
allow_any_instance_of(Gitlab::CycleAnalytics::BaseEventFetcher).to receive(:event_result).and_return({})
end
diff --git a/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb
index 17d5fbb9733..9ca12cc448c 100644
--- a/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb
@@ -5,16 +5,16 @@ describe Gitlab::CycleAnalytics::StagingStage do
let(:stage_name) { :staging }
let(:project) { create(:project) }
- let!(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) }
- let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) }
- let!(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) }
- let!(:mr_1) { create(:merge_request, :closed, source_project: project, created_at: 60.minutes.ago) }
- let!(:mr_2) { create(:merge_request, :closed, source_project: project, created_at: 40.minutes.ago, source_branch: 'A') }
- let!(:mr_3) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') }
+ let(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) }
+ let(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) }
+ let(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) }
+ let(:mr_1) { create(:merge_request, :closed, source_project: project, created_at: 60.minutes.ago) }
+ let(:mr_2) { create(:merge_request, :closed, source_project: project, created_at: 40.minutes.ago, source_branch: 'A') }
+ let(:mr_3) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') }
let(:build_1) { create(:ci_build, project: project) }
let(:build_2) { create(:ci_build, project: project) }
- let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) }
+ let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: project.creator, project: project }) }
before do
mr_1.metrics.update!(merged_at: 80.minutes.ago, first_deployed_to_production_at: 50.minutes.ago, pipeline_id: build_1.commit_id)
@@ -28,22 +28,68 @@ describe Gitlab::CycleAnalytics::StagingStage do
it_behaves_like 'base stage'
- describe '#median' do
+ describe '#project_median' do
around do |example|
Timecop.freeze { example.run }
end
it 'counts median from issues with metrics' do
- expect(stage.median).to eq(ISSUES_MEDIAN)
+ expect(stage.project_median).to eq(ISSUES_MEDIAN)
end
end
describe '#events' do
+ subject { stage.events }
+
it 'exposes builds connected to merge request' do
- result = stage.events
+ expect(subject.count).to eq(2)
+ expect(subject.map { |event| event[:name] }).to contain_exactly(build_1.name, build_2.name)
+ end
+ end
+
+ context 'when group is given' do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:project_2) { create(:project, group: group) }
+ let(:project_3) { create(:project, group: group) }
+ let(:issue_2_1) { create(:issue, project: project_2, created_at: 90.minutes.ago) }
+ let(:issue_2_2) { create(:issue, project: project_3, created_at: 60.minutes.ago) }
+ let(:issue_2_3) { create(:issue, project: project_2, created_at: 60.minutes.ago) }
+ let(:mr_1) { create(:merge_request, :closed, source_project: project_2, created_at: 60.minutes.ago) }
+ let(:mr_2) { create(:merge_request, :closed, source_project: project_3, created_at: 40.minutes.ago, source_branch: 'A') }
+ let(:mr_3) { create(:merge_request, source_project: project_2, created_at: 10.minutes.ago, source_branch: 'B') }
+ let(:build_1) { create(:ci_build, project: project_2) }
+ let(:build_2) { create(:ci_build, project: project_3) }
+ let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: user, group: group }) }
+
+ before do
+ group.add_owner(user)
+ mr_1.metrics.update!(merged_at: 80.minutes.ago, first_deployed_to_production_at: 50.minutes.ago, pipeline_id: build_1.commit_id)
+ mr_2.metrics.update!(merged_at: 60.minutes.ago, first_deployed_to_production_at: 30.minutes.ago, pipeline_id: build_2.commit_id)
+ mr_3.metrics.update!(merged_at: 10.minutes.ago, first_deployed_to_production_at: 3.days.ago, pipeline_id: create(:ci_build, project: project_2).commit_id)
+
+ create(:merge_requests_closing_issues, merge_request: mr_1, issue: issue_2_1)
+ create(:merge_requests_closing_issues, merge_request: mr_2, issue: issue_2_2)
+ create(:merge_requests_closing_issues, merge_request: mr_3, issue: issue_2_3)
+ end
+
+ describe '#group_median' do
+ around do |example|
+ Timecop.freeze { example.run }
+ end
+
+ it 'counts median from issues with metrics' do
+ expect(stage.group_median).to eq(ISSUES_MEDIAN)
+ end
+ end
+
+ describe '#events' do
+ subject { stage.events }
- expect(result.count).to eq(2)
- expect(result.map { |event| event[:name] }).to contain_exactly(build_1.name, build_2.name)
+ it 'exposes merge requests that close issues' do
+ expect(subject.count).to eq(2)
+ expect(subject.map { |event| event[:name] }).to contain_exactly(build_1.name, build_2.name)
+ end
end
end
end
diff --git a/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb
index 8633a63849f..41028c44a00 100644
--- a/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb
@@ -4,7 +4,7 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec'
describe Gitlab::CycleAnalytics::TestStage do
let(:stage_name) { :test }
let(:project) { create(:project) }
- let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) }
+ let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: project.creator, project: project }) }
it_behaves_like 'base stage'
@@ -36,7 +36,7 @@ describe Gitlab::CycleAnalytics::TestStage do
end
it 'counts median from issues with metrics' do
- expect(stage.median).to eq(ISSUES_MEDIAN)
+ expect(stage.project_median).to eq(ISSUES_MEDIAN)
end
end
end
diff --git a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb b/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb
index 8122e85a981..ad61bdeace7 100644
--- a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb
@@ -34,7 +34,7 @@ describe Gitlab::CycleAnalytics::UsageData do
expect(result).to have_key(:avg_cycle_analytics)
- CycleAnalytics::Base::STAGES.each do |stage|
+ CycleAnalytics::LevelBase::STAGES.each do |stage|
expect(result[:avg_cycle_analytics]).to have_key(stage)
stage_values = result[:avg_cycle_analytics][stage]
diff --git a/spec/lib/gitlab/danger/helper_spec.rb b/spec/lib/gitlab/danger/helper_spec.rb
index 92d90ac2fef..f11f68ab3c2 100644
--- a/spec/lib/gitlab/danger/helper_spec.rb
+++ b/spec/lib/gitlab/danger/helper_spec.rb
@@ -85,6 +85,20 @@ describe Gitlab::Danger::Helper do
end
end
+ describe '#markdown_list' do
+ it 'creates a markdown list of items' do
+ items = %w[a b]
+
+ expect(helper.markdown_list(items)).to eq("* `a`\n* `b`")
+ end
+
+ it 'wraps items in <details> when there are more than 10 items' do
+ items = ('a'..'k').to_a
+
+ expect(helper.markdown_list(items)).to match(%r{<details>[^<]+</details>})
+ end
+ end
+
describe '#changes_by_category' do
it 'categorizes changed files' do
expect(fake_git).to receive(:added_files) { %w[foo foo.md foo.rb foo.js db/foo lib/gitlab/database/foo.rb qa/foo ee/changelogs/foo.yml] }
@@ -224,4 +238,20 @@ describe Gitlab::Danger::Helper do
expect(teammates.map(&:username)).to eq(usernames)
end
end
+
+ describe '#missing_database_labels' do
+ subject { helper.missing_database_labels(current_mr_labels) }
+
+ context 'when current merge request has ~database::review pending' do
+ let(:current_mr_labels) { ['database::review pending', 'feature'] }
+
+ it { is_expected.to match_array(['database']) }
+ end
+
+ context 'when current merge request does not have ~database::review pending' do
+ let(:current_mr_labels) { ['feature'] }
+
+ it { is_expected.to match_array(['database', 'database::review pending']) }
+ end
+ end
end
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 7409572288c..dd0033bbc14 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -9,9 +9,27 @@ describe Gitlab::Database::MigrationHelpers do
allow(model).to receive(:puts)
end
+ describe '#remove_timestamps' do
+ it 'can remove the default timestamps' do
+ Gitlab::Database::MigrationHelpers::DEFAULT_TIMESTAMP_COLUMNS.each do |column_name|
+ expect(model).to receive(:remove_column).with(:foo, column_name)
+ end
+
+ model.remove_timestamps(:foo)
+ end
+
+ it 'can remove custom timestamps' do
+ expect(model).to receive(:remove_column).with(:foo, :bar)
+
+ model.remove_timestamps(:foo, columns: [:bar])
+ end
+ end
+
describe '#add_timestamps_with_timezone' do
+ let(:in_transaction) { false }
+
before do
- allow(model).to receive(:transaction_open?).and_return(false)
+ allow(model).to receive(:transaction_open?).and_return(in_transaction)
end
context 'using PostgreSQL' do
@@ -21,11 +39,64 @@ describe Gitlab::Database::MigrationHelpers do
end
it 'adds "created_at" and "updated_at" fields with the "datetime_with_timezone" data type' do
- expect(model).to receive(:add_column).with(:foo, :created_at, :datetime_with_timezone, { null: false })
- expect(model).to receive(:add_column).with(:foo, :updated_at, :datetime_with_timezone, { null: false })
+ Gitlab::Database::MigrationHelpers::DEFAULT_TIMESTAMP_COLUMNS.each do |column_name|
+ expect(model).to receive(:add_column).with(:foo, column_name, :datetime_with_timezone, { null: false })
+ end
model.add_timestamps_with_timezone(:foo)
end
+
+ it 'can disable the NOT NULL constraint' do
+ Gitlab::Database::MigrationHelpers::DEFAULT_TIMESTAMP_COLUMNS.each do |column_name|
+ expect(model).to receive(:add_column).with(:foo, column_name, :datetime_with_timezone, { null: true })
+ end
+
+ model.add_timestamps_with_timezone(:foo, null: true)
+ end
+
+ it 'can add just one column' do
+ expect(model).to receive(:add_column).with(:foo, :created_at, :datetime_with_timezone, anything)
+ expect(model).not_to receive(:add_column).with(:foo, :updated_at, :datetime_with_timezone, anything)
+
+ model.add_timestamps_with_timezone(:foo, columns: [:created_at])
+ end
+
+ it 'can add choice of acceptable columns' do
+ expect(model).to receive(:add_column).with(:foo, :created_at, :datetime_with_timezone, anything)
+ expect(model).to receive(:add_column).with(:foo, :deleted_at, :datetime_with_timezone, anything)
+ expect(model).not_to receive(:add_column).with(:foo, :updated_at, :datetime_with_timezone, anything)
+
+ model.add_timestamps_with_timezone(:foo, columns: [:created_at, :deleted_at])
+ end
+
+ it 'cannot add unacceptable column names' do
+ expect do
+ model.add_timestamps_with_timezone(:foo, columns: [:bar])
+ end.to raise_error %r/Illegal timestamp column name/
+ end
+
+ context 'in a transaction' do
+ let(:in_transaction) { true }
+
+ before do
+ allow(model).to receive(:add_column).with(any_args).and_call_original
+ allow(model).to receive(:add_column)
+ .with(:foo, anything, :datetime_with_timezone, anything)
+ .and_return(nil)
+ end
+
+ it 'cannot add a default value' do
+ expect do
+ model.add_timestamps_with_timezone(:foo, default: :i_cause_an_error)
+ end.to raise_error %r/add_timestamps_with_timezone/
+ end
+
+ it 'can add columns without defaults' do
+ expect do
+ model.add_timestamps_with_timezone(:foo)
+ end.not_to raise_error
+ end
+ end
end
context 'using MySQL' do
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index a28b95e5bff..41b898df112 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -256,6 +256,22 @@ describe Gitlab::Git::Repository, :seed_helper do
end
end
+ describe '#submodule_urls_for' do
+ let(:ref) { 'master' }
+
+ it 'returns url mappings for submodules' do
+ urls = repository.submodule_urls_for(ref)
+
+ expect(urls).to eq({
+ "deeper/nested/six" => "git://github.com/randx/six.git",
+ "gitlab-grack" => "https://gitlab.com/gitlab-org/gitlab-grack.git",
+ "gitlab-shell" => "https://github.com/gitlabhq/gitlab-shell.git",
+ "nested/six" => "git://github.com/randx/six.git",
+ "six" => "git://github.com/randx/six.git"
+ })
+ end
+ end
+
describe '#commit_count' do
it { expect(repository.commit_count("master")).to eq(25) }
it { expect(repository.commit_count("feature")).to eq(9) }
diff --git a/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb b/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb
index e7ef9d08f80..e437647c258 100644
--- a/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb
+++ b/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb
@@ -21,6 +21,7 @@ describe Gitlab::Git::RuggedImpl::UseRugged, :seed_helper do
end
before do
+ allow(Gitlab::GitalyClient).to receive(:can_use_disk?).and_call_original
Gitlab::GitalyClient.instance_variable_set(:@can_use_disk, {})
end
@@ -30,7 +31,6 @@ describe Gitlab::Git::RuggedImpl::UseRugged, :seed_helper do
end
it 'returns true when gitaly matches disk' do
- pending('temporary disabled because of https://gitlab.com/gitlab-org/gitlab-ce/issues/64338')
expect(subject.use_rugged?(repository, feature_flag_name)).to be true
end
@@ -49,7 +49,6 @@ describe Gitlab::Git::RuggedImpl::UseRugged, :seed_helper do
end
it "doesn't lead to a second rpc call because gitaly client should use the cached value" do
- pending('temporary disabled because of https://gitlab.com/gitlab-org/gitlab-ce/issues/64338')
expect(subject.use_rugged?(repository, feature_flag_name)).to be true
expect(Gitlab::GitalyClient).not_to receive(:filesystem_id)
diff --git a/spec/lib/gitlab/git_spec.rb b/spec/lib/gitlab/git_spec.rb
index ce15057dd7d..6515be85ae3 100644
--- a/spec/lib/gitlab/git_spec.rb
+++ b/spec/lib/gitlab/git_spec.rb
@@ -39,6 +39,26 @@ describe Gitlab::Git do
end
end
+ describe '.commit_id?' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:sha, :result) do
+ '' | false
+ 'foobar' | false
+ '4b825dc' | false
+ 'zzz25dc642cb6eb9a060e54bf8d69288fbee4904' | false
+
+ '4b825dc642cb6eb9a060e54bf8d69288fbee4904' | true
+ Gitlab::Git::BLANK_SHA | true
+ end
+
+ with_them do
+ it 'returns the expected result' do
+ expect(described_class.commit_id?(sha)).to eq(result)
+ end
+ end
+ end
+
describe '.shas_eql?' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb
index b8debee3b58..e1d24ae8977 100644
--- a/spec/lib/gitlab/gitaly_client_spec.rb
+++ b/spec/lib/gitlab/gitaly_client_spec.rb
@@ -119,6 +119,19 @@ describe Gitlab::GitalyClient do
end
end
+ describe '.can_use_disk?' do
+ it 'properly caches a false result' do
+ # spec_helper stubs this globally
+ allow(described_class).to receive(:can_use_disk?).and_call_original
+ expect(described_class).to receive(:filesystem_id).once
+ expect(described_class).to receive(:filesystem_id_from_disk).once
+
+ 2.times do
+ described_class.can_use_disk?('unknown')
+ end
+ end
+ end
+
describe '.connection_data' do
it 'returns connection data' do
address = 'tcp://localhost:9876'
diff --git a/spec/lib/gitlab/graphql/representation/submodule_tree_entry_spec.rb b/spec/lib/gitlab/graphql/representation/submodule_tree_entry_spec.rb
new file mode 100644
index 00000000000..28056a6085d
--- /dev/null
+++ b/spec/lib/gitlab/graphql/representation/submodule_tree_entry_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Graphql::Representation::SubmoduleTreeEntry do
+ let(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
+
+ describe '.decorate' do
+ let(:submodules) { repository.tree.submodules }
+
+ it 'returns array of SubmoduleTreeEntry' do
+ entries = described_class.decorate(submodules, repository.tree)
+
+ expect(entries.first).to be_a(described_class)
+
+ expect(entries.map(&:web_url)).to contain_exactly(
+ "https://gitlab.com/gitlab-org/gitlab-grack",
+ "https://github.com/gitlabhq/gitlab-shell",
+ "https://github.com/randx/six"
+ )
+
+ expect(entries.map(&:tree_url)).to contain_exactly(
+ "https://gitlab.com/gitlab-org/gitlab-grack/tree/645f6c4c82fd3f5e06f67134450a570b795e55a6",
+ "https://github.com/gitlabhq/gitlab-shell/tree/79bceae69cb5750d6567b223597999bfa91cb3b9",
+ "https://github.com/randx/six/tree/409f37c4f05865e4fb208c771485f211a22c4c2d"
+ )
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 7baa52ffb4f..929b6222900 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -343,7 +343,6 @@ project:
- fork_network_member
- fork_network
- custom_attributes
-- prometheus_metrics
- lfs_file_locks
- project_badges
- source_of_merge_requests
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index c0b97486eeb..9e54ca28e58 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -2775,7 +2775,8 @@
"action": 1,
"author_id": 1
}
- ]
+ ],
+ "approvals_before_merge": 1
},
{
"id": 26,
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index e6ce3f1bcea..3b7de185cf1 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -496,6 +496,18 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
end
end
+ context 'with restricted internal visibility' do
+ describe 'internal project' do
+ let(:visibility) { Gitlab::VisibilityLevel::INTERNAL }
+
+ it 'uses private visibility' do
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::INTERNAL])
+
+ expect(restorer.restored_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+ end
+ end
+
context 'with group visibility' do
before do
group = create(:group, visibility_level: group_visibility)
@@ -528,6 +540,14 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
it 'uses the group visibility' do
expect(restorer.restored_project.visibility_level).to eq(group_visibility)
end
+
+ context 'with restricted internal visibility' do
+ it 'sets private visibility' do
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::INTERNAL])
+
+ expect(restorer.restored_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
index bc4f867e891..5f56c30c7e0 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -42,6 +42,10 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
expect(saved_project_json).to include({ 'description' => 'description', 'visibility_level' => 20 })
end
+ it 'has approvals_before_merge set' do
+ expect(saved_project_json['approvals_before_merge']).to eq(1)
+ end
+
it 'has milestones' do
expect(saved_project_json['milestones']).not_to be_empty
end
@@ -287,7 +291,8 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
issues: [issue],
snippets: [snippet],
releases: [release],
- group: group
+ group: group,
+ approvals_before_merge: 1
)
project_label = create(:label, project: project)
group_label = create(:group_label, group: group)
diff --git a/spec/lib/gitlab/import_export/relation_rename_service_spec.rb b/spec/lib/gitlab/import_export/relation_rename_service_spec.rb
index a20a844a492..15748407f0c 100644
--- a/spec/lib/gitlab/import_export/relation_rename_service_spec.rb
+++ b/spec/lib/gitlab/import_export/relation_rename_service_spec.rb
@@ -28,6 +28,7 @@ describe Gitlab::ImportExport::RelationRenameService do
before do
allow(shared).to receive(:export_path).and_return(import_path)
+ allow(ActiveSupport::JSON).to receive(:decode).and_call_original
allow(ActiveSupport::JSON).to receive(:decode).with(file_content).and_return(json_file)
end
diff --git a/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb b/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
index 6795c1ab56b..e04056b3450 100644
--- a/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
@@ -201,7 +201,15 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
it 'observes cache metric' do
expect(subscriber.send(:metric_cache_operation_duration_seconds))
.to receive(:observe)
- .with(transaction.labels.merge(operation: :delete), event.duration / 1000.0)
+ .with({ operation: :delete }, event.duration / 1000.0)
+
+ subscriber.observe(:delete, event.duration)
+ end
+
+ it 'increments the operations total' do
+ expect(subscriber.send(:metric_cache_operations_total))
+ .to receive(:increment)
+ .with(transaction.labels.merge(operation: :delete))
subscriber.observe(:delete, event.duration)
end
diff --git a/spec/lib/gitlab/request_profiler/profile_spec.rb b/spec/lib/gitlab/request_profiler/profile_spec.rb
new file mode 100644
index 00000000000..b37ee558e1a
--- /dev/null
+++ b/spec/lib/gitlab/request_profiler/profile_spec.rb
@@ -0,0 +1,59 @@
+require 'fast_spec_helper'
+
+describe Gitlab::RequestProfiler::Profile do
+ let(:profile) { described_class.new(filename) }
+
+ describe '.new' do
+ context 'using old filename' do
+ let(:filename) { '|api|v4|version.txt_1562854738.html' }
+
+ it 'returns valid data' do
+ expect(profile).to be_valid
+ expect(profile.request_path).to eq('/api/v4/version.txt')
+ expect(profile.time).to eq(Time.at(1562854738).utc)
+ expect(profile.type).to eq('html')
+ end
+ end
+
+ context 'using new filename' do
+ let(:filename) { '|api|v4|version.txt_1563547949_execution.html' }
+
+ it 'returns valid data' do
+ expect(profile).to be_valid
+ expect(profile.request_path).to eq('/api/v4/version.txt')
+ expect(profile.profile_mode).to eq('execution')
+ expect(profile.time).to eq(Time.at(1563547949).utc)
+ expect(profile.type).to eq('html')
+ end
+ end
+ end
+
+ describe '#content_type' do
+ context 'when using html file' do
+ let(:filename) { '|api|v4|version.txt_1562854738_memory.html' }
+
+ it 'returns valid data' do
+ expect(profile).to be_valid
+ expect(profile.content_type).to eq('text/html')
+ end
+ end
+
+ context 'when using text file' do
+ let(:filename) { '|api|v4|version.txt_1562854738_memory.txt' }
+
+ it 'returns valid data' do
+ expect(profile).to be_valid
+ expect(profile.content_type).to eq('text/plain')
+ end
+ end
+
+ context 'when file is unknown' do
+ let(:filename) { '|api|v4|version.txt_1562854738_memory.xxx' }
+
+ it 'returns valid data' do
+ expect(profile).not_to be_valid
+ expect(profile.content_type).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/request_profiler_spec.rb b/spec/lib/gitlab/request_profiler_spec.rb
index fd8cbf39bce..498c045b6cd 100644
--- a/spec/lib/gitlab/request_profiler_spec.rb
+++ b/spec/lib/gitlab/request_profiler_spec.rb
@@ -13,15 +13,42 @@ describe Gitlab::RequestProfiler do
end
end
- describe '.remove_all_profiles' do
- it 'removes Gitlab::RequestProfiler::PROFILES_DIR directory' do
- dir = described_class::PROFILES_DIR
- FileUtils.mkdir_p(dir)
+ context 'with temporary PROFILES_DIR' do
+ let(:tmpdir) { Dir.mktmpdir('profiler-test') }
+ let(:profile_name) { '|api|v4|version.txt_1562854738_memory.html' }
+ let(:profile_path) { File.join(tmpdir, profile_name) }
- expect(Dir.exist?(dir)).to be true
+ before do
+ stub_const('Gitlab::RequestProfiler::PROFILES_DIR', tmpdir)
+ FileUtils.touch(profile_path)
+ end
+
+ after do
+ FileUtils.rm_rf(tmpdir)
+ end
+
+ describe '.remove_all_profiles' do
+ it 'removes Gitlab::RequestProfiler::PROFILES_DIR directory' do
+ described_class.remove_all_profiles
+
+ expect(Dir.exist?(tmpdir)).to be false
+ end
+ end
+
+ describe '.all' do
+ subject { described_class.all }
+
+ it 'returns all profiles' do
+ expect(subject.map(&:name)).to contain_exactly(profile_name)
+ end
+ end
+
+ describe '.find' do
+ subject { described_class.find(profile_name) }
- described_class.remove_all_profiles
- expect(Dir.exist?(dir)).to be false
+ it 'returns all profiles' do
+ expect(subject.name).to eq(profile_name)
+ end
end
end
end
diff --git a/spec/lib/gitlab/rugged_instrumentation_spec.rb b/spec/lib/gitlab/rugged_instrumentation_spec.rb
new file mode 100644
index 00000000000..4dcc8ae514a
--- /dev/null
+++ b/spec/lib/gitlab/rugged_instrumentation_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::RuggedInstrumentation, :request_store do
+ subject { described_class }
+
+ describe '.query_time' do
+ it 'increments query times' do
+ subject.query_time += 0.451
+ subject.query_time += 0.322
+
+ expect(subject.query_time).to be_within(0.001).of(0.773)
+ expect(subject.query_time_ms).to eq(773.0)
+ end
+ end
+
+ context '.increment_query_count' do
+ it 'tracks query counts' do
+ expect(subject.query_count).to eq(0)
+
+ 2.times { subject.increment_query_count }
+
+ expect(subject.query_count).to eq(2)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/slug/environment_spec.rb b/spec/lib/gitlab/slug/environment_spec.rb
new file mode 100644
index 00000000000..7dc583a94b8
--- /dev/null
+++ b/spec/lib/gitlab/slug/environment_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Slug::Environment do
+ describe '#generate' do
+ {
+ "staging-12345678901234567" => "staging-123456789-q517sa",
+ "9-staging-123456789012345" => "env-9-staging-123-q517sa",
+ "staging-1234567890123456" => "staging-1234567890123456",
+ "staging-1234567890123456-" => "staging-123456789-q517sa",
+ "production" => "production",
+ "PRODUCTION" => "production-q517sa",
+ "review/1-foo" => "review-1-foo-q517sa",
+ "1-foo" => "env-1-foo-q517sa",
+ "1/foo" => "env-1-foo-q517sa",
+ "foo-" => "foo",
+ "foo--bar" => "foo-bar-q517sa",
+ "foo**bar" => "foo-bar-q517sa",
+ "*-foo" => "env-foo-q517sa",
+ "staging-12345678-" => "staging-12345678",
+ "staging-12345678-01234567" => "staging-12345678-q517sa",
+ "" => "env-q517sa",
+ nil => "env-q517sa"
+ }.each do |name, matcher|
+ before do
+ # ('a' * 64).to_i(16).to_s(36).last(6) gives 'q517sa'
+ allow(Digest::SHA2).to receive(:hexdigest).with(name).and_return('a' * 64)
+ end
+
+ it "returns a slug matching #{matcher}, given #{name}" do
+ slug = described_class.new(name).generate
+
+ expect(slug).to match(/\A#{matcher}\z/)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/usage_data_counters/redis_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/redis_counter_spec.rb
index 38b4c22e186..c34ac7867ab 100644
--- a/spec/lib/gitlab/usage_data_counters/redis_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/redis_counter_spec.rb
@@ -3,52 +3,31 @@
require 'spec_helper'
describe Gitlab::UsageDataCounters::RedisCounter, :clean_gitlab_redis_shared_state do
- context 'when redis_key is not defined' do
- subject do
- Class.new.extend(described_class)
- end
+ let(:redis_key) { 'foobar' }
- describe '.increment' do
- it 'raises a NotImplementedError exception' do
- expect { subject.increment}.to raise_error(NotImplementedError)
- end
- end
+ subject { Class.new.extend(described_class) }
- describe '.total_count' do
- it 'raises a NotImplementedError exception' do
- expect { subject.total_count}.to raise_error(NotImplementedError)
- end
- end
+ before do
+ stub_application_setting(usage_ping_enabled: setting_value)
end
- context 'when redis_key is defined' do
- subject do
- counter_module = described_class
-
- Class.new do
- extend counter_module
+ context 'when usage_ping is disabled' do
+ let(:setting_value) { false }
- def self.redis_counter_key
- 'foo_redis_key'
- end
- end
- end
-
- describe '.increment' do
- it 'increments the web ide commits counter by 1' do
- expect do
- subject.increment
- end.to change { subject.total_count }.from(0).to(1)
- end
+ it 'counter is not increased' do
+ expect do
+ subject.increment(redis_key)
+ end.not_to change { subject.total_count(redis_key) }
end
+ end
- describe '.total_count' do
- it 'returns the total amount of web ide commits' do
- subject.increment
- subject.increment
+ context 'when usage_ping is enabled' do
+ let(:setting_value) { true }
- expect(subject.total_count).to eq(2)
- end
+ it 'counter is increased' do
+ expect do
+ subject.increment(redis_key)
+ end.to change { subject.total_count(redis_key) }.by(1)
end
end
end
diff --git a/spec/lib/gitlab/usage_data_counters/web_ide_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/web_ide_counter_spec.rb
new file mode 100644
index 00000000000..7a01f7d1de8
--- /dev/null
+++ b/spec/lib/gitlab/usage_data_counters/web_ide_counter_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::UsageDataCounters::WebIdeCounter, :clean_gitlab_redis_shared_state do
+ shared_examples 'counter examples' do
+ it 'increments counter and return the total count' do
+ expect(described_class.public_send(total_counter_method)).to eq(0)
+
+ 2.times { described_class.public_send(increment_counter_method) }
+
+ expect(described_class.public_send(total_counter_method)).to eq(2)
+ end
+ end
+
+ describe 'commits counter' do
+ let(:increment_counter_method) { :increment_commits_count }
+ let(:total_counter_method) { :total_commits_count }
+
+ it_behaves_like 'counter examples'
+ end
+
+ describe 'merge requests counter' do
+ let(:increment_counter_method) { :increment_merge_requests_count }
+ let(:total_counter_method) { :total_merge_requests_count }
+
+ it_behaves_like 'counter examples'
+ end
+
+ describe 'views counter' do
+ let(:increment_counter_method) { :increment_views_count }
+ let(:total_counter_method) { :total_views_count }
+
+ it_behaves_like 'counter examples'
+ end
+
+ describe '.totals' do
+ commits = 5
+ merge_requests = 3
+ views = 2
+
+ before do
+ commits.times { described_class.increment_commits_count }
+ merge_requests.times { described_class.increment_merge_requests_count }
+ views.times { described_class.increment_views_count }
+ end
+
+ it 'can report all totals' do
+ expect(described_class.totals).to include(
+ web_ide_commits: commits,
+ web_ide_views: views,
+ web_ide_merge_requests: merge_requests
+ )
+ end
+ end
+end
diff --git a/spec/lib/gitlab/usage_data_counters/wiki_page_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/wiki_page_counter_spec.rb
new file mode 100644
index 00000000000..41afbbb191c
--- /dev/null
+++ b/spec/lib/gitlab/usage_data_counters/wiki_page_counter_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::UsageDataCounters::WikiPageCounter, :clean_gitlab_redis_shared_state do
+ shared_examples :wiki_page_event do |event|
+ describe ".count(#{event})" do
+ it "increments the wiki page #{event} counter by 1" do
+ expect do
+ described_class.count(event)
+ end.to change { described_class.read(event) }.by 1
+ end
+ end
+
+ describe ".read(#{event})" do
+ event_count = 5
+
+ it "returns the total number of #{event} events" do
+ event_count.times do
+ described_class.count(event)
+ end
+
+ expect(described_class.read(event)).to eq(event_count)
+ end
+ end
+ end
+
+ include_examples :wiki_page_event, :create
+ include_examples :wiki_page_event, :update
+ include_examples :wiki_page_event, :delete
+
+ describe 'totals' do
+ creations = 5
+ edits = 3
+ deletions = 2
+
+ before do
+ creations.times do
+ described_class.count(:create)
+ end
+ edits.times do
+ described_class.count(:update)
+ end
+ deletions.times do
+ described_class.count(:delete)
+ end
+ end
+
+ it 'can report all totals' do
+ expect(described_class.totals).to include(
+ wiki_pages_update: edits,
+ wiki_pages_create: creations,
+ wiki_pages_delete: deletions
+ )
+ end
+ end
+
+ describe 'unknown events' do
+ error = described_class::UnknownEvent
+
+ it 'cannot increment' do
+ expect { described_class.count(:wibble) }.to raise_error error
+ end
+
+ it 'cannot read' do
+ expect { described_class.read(:wibble) }.to raise_error error
+ end
+ end
+end
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 90a534de202..2289d906944 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -57,10 +57,18 @@ describe Gitlab::UsageData do
gitaly
database
avg_cycle_analytics
- web_ide_commits
influxdb_metrics_enabled
prometheus_metrics_enabled
))
+
+ expect(subject).to include(
+ wiki_pages_create: a_kind_of(Integer),
+ wiki_pages_update: a_kind_of(Integer),
+ wiki_pages_delete: a_kind_of(Integer),
+ web_ide_views: a_kind_of(Integer),
+ web_ide_commits: a_kind_of(Integer),
+ web_ide_merge_requests: a_kind_of(Integer)
+ )
end
it "gathers usage counts" do
@@ -182,6 +190,28 @@ describe Gitlab::UsageData do
end
end
+ describe '#usage_data_counters' do
+ subject { described_class.usage_data_counters }
+
+ it { is_expected.to all(respond_to :totals) }
+
+ describe 'the results of calling #totals on all objects in the array' do
+ subject { described_class.usage_data_counters.map(&:totals) }
+
+ it do
+ is_expected
+ .to all(be_a Hash)
+ .and all(have_attributes(keys: all(be_a Symbol), values: all(be_a Integer)))
+ end
+ end
+
+ it 'does not have any conflicts' do
+ all_keys = subject.flat_map { |counter| counter.totals.keys }
+
+ expect(all_keys.size).to eq all_keys.to_set.size
+ end
+ end
+
describe '#features_usage_data_ce' do
subject { described_class.features_usage_data_ce }
diff --git a/spec/lib/peek/views/redis_detailed_spec.rb b/spec/lib/peek/views/redis_detailed_spec.rb
index da13b6df53b..61096e6c69e 100644
--- a/spec/lib/peek/views/redis_detailed_spec.rb
+++ b/spec/lib/peek/views/redis_detailed_spec.rb
@@ -2,14 +2,8 @@
require 'spec_helper'
-describe Peek::Views::RedisDetailed do
- let(:redis_detailed_class) do
- Class.new do
- include Peek::Views::RedisDetailed
- end
- end
-
- subject { redis_detailed_class.new }
+describe Peek::Views::RedisDetailed, :request_store do
+ subject { described_class.new }
using RSpec::Parameterized::TableSyntax
@@ -22,15 +16,24 @@ describe Peek::Views::RedisDetailed do
end
with_them do
- it 'scrubs Redis commands', :request_store do
+ it 'scrubs Redis commands' do
subject.detail_store << { cmd: cmd, duration: 1.second }
- expect(subject.details.count).to eq(1)
- expect(subject.details.first)
+ expect(subject.results[:details].count).to eq(1)
+ expect(subject.results[:details].first)
.to eq({
cmd: expected,
duration: 1000
})
end
end
+
+ it 'returns aggregated results' do
+ subject.detail_store << { cmd: [:get, 'test'], duration: 0.001 }
+ subject.detail_store << { cmd: [:get, 'test'], duration: 1.second }
+
+ expect(subject.results[:calls]).to eq(2)
+ expect(subject.results[:duration]).to eq('1001.00ms')
+ expect(subject.results[:details].count).to eq(2)
+ end
end
diff --git a/spec/lib/prometheus/pid_provider_spec.rb b/spec/lib/prometheus/pid_provider_spec.rb
new file mode 100644
index 00000000000..e7d500612b1
--- /dev/null
+++ b/spec/lib/prometheus/pid_provider_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+describe Prometheus::PidProvider do
+ describe '.worker_id' do
+ subject { described_class.worker_id }
+
+ let(:sidekiq_module) { Module.new }
+
+ before do
+ allow(sidekiq_module).to receive(:server?).and_return(false)
+ stub_const('Sidekiq', sidekiq_module)
+ end
+
+ context 'when running in Sidekiq server mode' do
+ before do
+ expect(Sidekiq).to receive(:server?).and_return(true)
+ end
+
+ it { is_expected.to eq 'sidekiq' }
+ end
+
+ context 'when running in Unicorn mode' do
+ before do
+ stub_const('Unicorn::Worker', Class.new)
+ hide_const('Puma')
+ end
+
+ context 'when `Prometheus::Client::Support::Unicorn` provides worker_id' do
+ before do
+ expect(::Prometheus::Client::Support::Unicorn).to receive(:worker_id).and_return(1)
+ end
+
+ it { is_expected.to eq 'unicorn_1' }
+ end
+
+ context 'when no worker_id is provided from `Prometheus::Client::Support::Unicorn`' do
+ before do
+ expect(::Prometheus::Client::Support::Unicorn).to receive(:worker_id).and_return(nil)
+ end
+
+ it { is_expected.to eq 'unicorn_master' }
+ end
+ end
+
+ context 'when running in Puma mode' do
+ before do
+ stub_const('Puma', Module.new)
+ hide_const('Unicorn::Worker')
+ end
+
+ context 'when cluster worker id is specified in process name' do
+ before do
+ expect(described_class).to receive(:process_name).and_return('puma: cluster worker 1: 17483 [gitlab-puma-worker]')
+ end
+
+ it { is_expected.to eq 'puma_1' }
+ end
+
+ context 'when no worker id is specified in process name' do
+ before do
+ expect(described_class).to receive(:process_name).and_return('bin/puma')
+ end
+
+ it { is_expected.to eq 'puma_master' }
+ end
+ end
+
+ context 'when running in unknown mode' do
+ before do
+ hide_const('Puma')
+ hide_const('Unicorn::Worker')
+ end
+
+ it { is_expected.to eq "process_#{Process.pid}" }
+ end
+ end
+end
diff --git a/spec/migrations/fix_wrong_pages_access_level_spec.rb b/spec/migrations/fix_wrong_pages_access_level_spec.rb
new file mode 100644
index 00000000000..75ac5d919b2
--- /dev/null
+++ b/spec/migrations/fix_wrong_pages_access_level_spec.rb
@@ -0,0 +1,97 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20190703185326_fix_wrong_pages_access_level.rb')
+
+describe FixWrongPagesAccessLevel, :migration, :sidekiq, schema: 20190628185004 do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:migration_class) { described_class::MIGRATION }
+ let(:migration_name) { migration_class.to_s.demodulize }
+
+ project_class = ::Gitlab::BackgroundMigration::FixPagesAccessLevel::Project
+ feature_class = ::Gitlab::BackgroundMigration::FixPagesAccessLevel::ProjectFeature
+
+ let(:namespaces_table) { table(:namespaces) }
+ let(:projects_table) { table(:projects) }
+ let(:features_table) { table(:project_features) }
+
+ let(:subgroup) do
+ root_group = namespaces_table.create(path: "group", name: "group")
+ namespaces_table.create!(path: "subgroup", name: "group", parent_id: root_group.id)
+ end
+
+ def create_project_feature(path, project_visibility, pages_access_level)
+ project = projects_table.create!(path: path, visibility_level: project_visibility,
+ namespace_id: subgroup.id)
+ features_table.create!(project_id: project.id, pages_access_level: pages_access_level)
+ end
+
+ it 'correctly schedules background migrations' do
+ Sidekiq::Testing.fake! do
+ Timecop.freeze do
+ first_id = create_project_feature("project1", project_class::PRIVATE, feature_class::PRIVATE).id
+ last_id = create_project_feature("project2", project_class::PRIVATE, feature_class::PUBLIC).id
+
+ migrate!
+
+ expect(migration_name).to be_scheduled_delayed_migration(2.minutes, first_id, last_id)
+ expect(BackgroundMigrationWorker.jobs.size).to eq(1)
+ end
+ end
+ end
+
+ def expect_migration
+ expect do
+ perform_enqueued_jobs do
+ migrate!
+ end
+ end
+ end
+
+ where(:project_visibility, :pages_access_level, :access_control_is_enabled,
+ :pages_deployed, :resulting_pages_access_level) do
+ # update settings for public projects regardless of access_control being enabled
+ project_class::PUBLIC | feature_class::PUBLIC | true | true | feature_class::ENABLED
+ project_class::PUBLIC | feature_class::PUBLIC | false | true | feature_class::ENABLED
+ # don't update public level for private and internal projects
+ project_class::PRIVATE | feature_class::PUBLIC | true | true | feature_class::PUBLIC
+ project_class::INTERNAL | feature_class::PUBLIC | true | true | feature_class::PUBLIC
+
+ # if access control is disabled but pages are deployed we make them public
+ project_class::INTERNAL | feature_class::ENABLED | false | true | feature_class::PUBLIC
+ # don't change anything if one of the conditions is not satisfied
+ project_class::INTERNAL | feature_class::ENABLED | true | true | feature_class::ENABLED
+ project_class::INTERNAL | feature_class::ENABLED | true | false | feature_class::ENABLED
+
+ # private projects
+ # if access control is enabled update pages_access_level to private regardless of deployment
+ project_class::PRIVATE | feature_class::ENABLED | true | true | feature_class::PRIVATE
+ project_class::PRIVATE | feature_class::ENABLED | true | false | feature_class::PRIVATE
+ # if access control is disabled and pages are deployed update pages_access_level to public
+ project_class::PRIVATE | feature_class::ENABLED | false | true | feature_class::PUBLIC
+ # if access control is disabled but pages aren't deployed update pages_access_level to private
+ project_class::PRIVATE | feature_class::ENABLED | false | false | feature_class::PRIVATE
+ end
+
+ with_them do
+ let!(:project_feature) do
+ create_project_feature("projectpath", project_visibility, pages_access_level)
+ end
+
+ before do
+ tested_path = File.join(Settings.pages.path, "group/subgroup/projectpath", "public")
+ allow(Dir).to receive(:exist?).with(tested_path).and_return(pages_deployed)
+
+ stub_pages_setting(access_control: access_control_is_enabled)
+ end
+
+ it "sets proper pages_access_level" do
+ expect(project_feature.reload.pages_access_level).to eq(pages_access_level)
+
+ perform_enqueued_jobs do
+ migrate!
+ end
+
+ expect(project_feature.reload.pages_access_level).to eq(resulting_pages_access_level)
+ end
+ end
+end
diff --git a/spec/models/active_session_spec.rb b/spec/models/active_session_spec.rb
index 2762eaeccd3..09c2878663a 100644
--- a/spec/models/active_session_spec.rb
+++ b/spec/models/active_session_spec.rb
@@ -132,6 +132,19 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
expect(ActiveSession.sessions_from_ids([])).to eq([])
end
+
+ it 'uses redis lookup in batches' do
+ stub_const('ActiveSession::SESSION_BATCH_SIZE', 1)
+
+ redis = double(:redis)
+ expect(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis)
+
+ sessions = ['session-a', 'session-b']
+ mget_responses = sessions.map { |session| [Marshal.dump(session)]}
+ expect(redis).to receive(:mget).twice.and_return(*mget_responses)
+
+ expect(ActiveSession.sessions_from_ids([1, 2])).to eql(sessions)
+ end
end
describe '.set' do
diff --git a/spec/models/concerns/deployment_platform_spec.rb b/spec/models/concerns/deployment_platform_spec.rb
index e2fc8a5d127..c4f9f62ece5 100644
--- a/spec/models/concerns/deployment_platform_spec.rb
+++ b/spec/models/concerns/deployment_platform_spec.rb
@@ -5,7 +5,7 @@ require 'rails_helper'
describe DeploymentPlatform do
let(:project) { create(:project) }
- shared_examples '#deployment_platform' do
+ describe '#deployment_platform' do
subject { project.deployment_platform }
context 'with no Kubernetes configuration on CI/CD, no Kubernetes Service' do
@@ -46,12 +46,11 @@ describe DeploymentPlatform do
end
context 'when child group has configured kubernetes cluster', :nested_groups do
- let!(:child_group1_cluster) { create(:cluster, :provided_by_gcp, :group) }
- let(:child_group1) { child_group1_cluster.group }
+ let(:child_group1) { create(:group, parent: group) }
+ let!(:child_group1_cluster) { create(:cluster_for_group, groups: [child_group1]) }
before do
project.update!(group: child_group1)
- child_group1.update!(parent: group)
end
it 'returns the Kubernetes platform for the child group' do
@@ -59,11 +58,10 @@ describe DeploymentPlatform do
end
context 'deeply nested group' do
- let!(:child_group2_cluster) { create(:cluster, :provided_by_gcp, :group) }
- let(:child_group2) { child_group2_cluster.group }
+ let(:child_group2) { create(:group, parent: child_group1) }
+ let!(:child_group2_cluster) { create(:cluster_for_group, groups: [child_group2]) }
before do
- child_group2.update!(parent: child_group1)
project.update!(group: child_group2)
end
@@ -84,20 +82,4 @@ describe DeploymentPlatform do
end
end
end
-
- context 'legacy implementation' do
- before do
- stub_feature_flags(clusters_cte: false)
- end
-
- include_examples '#deployment_platform'
- end
-
- context 'CTE implementation' do
- before do
- stub_feature_flags(clusters_cte: true)
- end
-
- include_examples '#deployment_platform'
- end
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 68224a56515..e19da41c3fe 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -774,4 +774,25 @@ describe Issuable do
end
end
end
+
+ describe '#supports_milestone?' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, group: group) }
+
+ context "for issues" do
+ let(:issue) { build(:issue, project: project) }
+
+ it 'returns true' do
+ expect(issue.supports_milestone?).to be_truthy
+ end
+ end
+
+ context "for merge requests" do
+ let(:merge_request) { build(:merge_request, target_project: project, source_project: project) }
+
+ it 'returns true' do
+ expect(merge_request.supports_milestone?).to be_truthy
+ end
+ end
+ end
end
diff --git a/spec/models/concerns/project_api_compatibility_spec.rb b/spec/models/concerns/project_api_compatibility_spec.rb
index 8cecd4fe7bc..f5722f88aac 100644
--- a/spec/models/concerns/project_api_compatibility_spec.rb
+++ b/spec/models/concerns/project_api_compatibility_spec.rb
@@ -16,23 +16,44 @@ describe ProjectAPICompatibility do
expect(project.build_allow_git_fetch).to eq(false)
end
- # auto_devops_enabled
- it "converts auto_devops_enabled=false to auto_devops_enabled?=false" do
- expect(project.auto_devops_enabled?).to eq(true)
- project.update!(auto_devops_enabled: false)
- expect(project.auto_devops_enabled?).to eq(false)
- end
+ describe '#auto_devops_enabled' do
+ where(
+ initial: [:missing, nil, false, true],
+ final: [nil, false, true]
+ )
+
+ with_them do
+ before do
+ project.build_auto_devops(enabled: initial) unless initial == :missing
+ end
+
+ # Implicit auto devops when enabled is nil
+ let(:expected) { final.nil? ? true : final }
+
+ it 'sets the correct value' do
+ project.update!(auto_devops_enabled: final)
- it "converts auto_devops_enabled=true to auto_devops_enabled?=true" do
- expect(project.auto_devops_enabled?).to eq(true)
- project.update!(auto_devops_enabled: true)
- expect(project.auto_devops_enabled?).to eq(true)
+ expect(project.auto_devops_enabled?).to eq(expected)
+ end
+ end
end
- # auto_devops_deploy_strategy
- it "converts auto_devops_deploy_strategy=timed_incremental to auto_devops.deploy_strategy=timed_incremental" do
- expect(project.auto_devops).to be_nil
- project.update!(auto_devops_deploy_strategy: 'timed_incremental')
- expect(project.auto_devops.deploy_strategy).to eq('timed_incremental')
+ describe '#auto_devops_deploy_strategy' do
+ where(
+ initial: [:missing, *ProjectAutoDevops.deploy_strategies.keys],
+ final: ProjectAutoDevops.deploy_strategies.keys
+ )
+
+ with_them do
+ before do
+ project.build_auto_devops(deploy_strategy: initial) unless initial == :missing
+ end
+
+ it 'sets the correct value' do
+ project.update!(auto_devops_deploy_strategy: final)
+
+ expect(project.auto_devops.deploy_strategy).to eq(final)
+ end
+ end
end
end
diff --git a/spec/models/cycle_analytics/code_spec.rb b/spec/models/cycle_analytics/code_spec.rb
index db6e70973ae..808659552ff 100644
--- a/spec/models/cycle_analytics/code_spec.rb
+++ b/spec/models/cycle_analytics/code_spec.rb
@@ -38,7 +38,7 @@ describe 'CycleAnalytics#code' do
merge_merge_requests_closing_issue(user, project, issue)
deploy_master(user, project)
- expect(subject[:code].median).to be_nil
+ expect(subject[:code].project_median).to be_nil
end
end
end
@@ -68,7 +68,7 @@ describe 'CycleAnalytics#code' do
merge_merge_requests_closing_issue(user, project, issue)
- expect(subject[:code].median).to be_nil
+ expect(subject[:code].project_median).to be_nil
end
end
end
diff --git a/spec/models/cycle_analytics/group_level_spec.rb b/spec/models/cycle_analytics/group_level_spec.rb
new file mode 100644
index 00000000000..154c1b9c0f8
--- /dev/null
+++ b/spec/models/cycle_analytics/group_level_spec.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe CycleAnalytics::GroupLevel do
+ let(:group) { create(:group)}
+ let(:project) { create(:project, :repository, namespace: group) }
+ let(:from_date) { 10.days.ago }
+ let(:user) { create(:user, :admin) }
+ let(:issue) { create(:issue, project: project, created_at: 2.days.ago) }
+ let(:milestone) { create(:milestone, project: project) }
+ let(:mr) { create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}") }
+ let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha, head_pipeline_of: mr) }
+
+ subject { described_class.new(group: group, options: { from: from_date, current_user: user }) }
+
+ describe '#permissions' do
+ it 'returns true for all stages' do
+ expect(subject.permissions.values.uniq).to eq([true])
+ end
+ end
+
+ describe '#stats' do
+ before do
+ allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue])
+
+ create_cycle(user, project, issue, mr, milestone, pipeline)
+ deploy_master(user, project)
+ end
+
+ it 'returns medians for each stage for a specific group' do
+ expect(subject.no_stats?).to eq(false)
+ end
+ end
+
+ describe '#summary' do
+ before do
+ create_cycle(user, project, issue, mr, milestone, pipeline)
+ deploy_master(user, project)
+ end
+
+ it 'returns medians for each stage for a specific group' do
+ expect(subject.summary.map { |summary| summary[:value] }).to contain_exactly(1, 1)
+ end
+ end
+end
diff --git a/spec/models/cycle_analytics/issue_spec.rb b/spec/models/cycle_analytics/issue_spec.rb
index 4ccbdf29df6..8cdf83b1292 100644
--- a/spec/models/cycle_analytics/issue_spec.rb
+++ b/spec/models/cycle_analytics/issue_spec.rb
@@ -43,7 +43,7 @@ describe 'CycleAnalytics#issue' do
create_merge_request_closing_issue(user, project, issue)
merge_merge_requests_closing_issue(user, project, issue)
- expect(subject[:issue].median).to be_nil
+ expect(subject[:issue].project_median).to be_nil
end
end
end
diff --git a/spec/models/cycle_analytics/plan_spec.rb b/spec/models/cycle_analytics/plan_spec.rb
index c99c38e9cf3..28ad9bd194d 100644
--- a/spec/models/cycle_analytics/plan_spec.rb
+++ b/spec/models/cycle_analytics/plan_spec.rb
@@ -47,7 +47,7 @@ describe 'CycleAnalytics#plan' do
create_merge_request_closing_issue(user, project, issue, source_branch: branch_name)
merge_merge_requests_closing_issue(user, project, issue)
- expect(subject[:issue].median).to be_nil
+ expect(subject[:issue].project_median).to be_nil
end
end
end
diff --git a/spec/models/cycle_analytics/production_spec.rb b/spec/models/cycle_analytics/production_spec.rb
index ddd199362d1..613c1786540 100644
--- a/spec/models/cycle_analytics/production_spec.rb
+++ b/spec/models/cycle_analytics/production_spec.rb
@@ -41,7 +41,7 @@ describe 'CycleAnalytics#production' do
MergeRequests::MergeService.new(project, user).execute(merge_request)
deploy_master(user, project)
- expect(subject[:production].median).to be_nil
+ expect(subject[:production].project_median).to be_nil
end
end
@@ -52,7 +52,7 @@ describe 'CycleAnalytics#production' do
MergeRequests::MergeService.new(project, user).execute(merge_request)
deploy_master(user, project, environment: 'staging')
- expect(subject[:production].median).to be_nil
+ expect(subject[:production].project_median).to be_nil
end
end
end
diff --git a/spec/models/cycle_analytics/project_level_spec.rb b/spec/models/cycle_analytics/project_level_spec.rb
index 77bd0bfeb9c..4de01b1c679 100644
--- a/spec/models/cycle_analytics/project_level_spec.rb
+++ b/spec/models/cycle_analytics/project_level_spec.rb
@@ -23,7 +23,7 @@ describe CycleAnalytics::ProjectLevel do
it 'returns every median for each stage for a specific project' do
values = described_class::STAGES.each_with_object({}) do |stage_name, hsh|
- hsh[stage_name] = subject[stage_name].median.presence
+ hsh[stage_name] = subject[stage_name].project_median.presence
end
expect(subject.all_medians_by_stage).to eq(values)
diff --git a/spec/models/cycle_analytics/review_spec.rb b/spec/models/cycle_analytics/review_spec.rb
index 63c481ed465..ef88fd86340 100644
--- a/spec/models/cycle_analytics/review_spec.rb
+++ b/spec/models/cycle_analytics/review_spec.rb
@@ -28,7 +28,7 @@ describe 'CycleAnalytics#review' do
it "returns nil" do
MergeRequests::MergeService.new(project, user).execute(create(:merge_request))
- expect(subject[:review].median).to be_nil
+ expect(subject[:review].project_median).to be_nil
end
end
end
diff --git a/spec/models/cycle_analytics/staging_spec.rb b/spec/models/cycle_analytics/staging_spec.rb
index c134b97553f..571792559d8 100644
--- a/spec/models/cycle_analytics/staging_spec.rb
+++ b/spec/models/cycle_analytics/staging_spec.rb
@@ -45,7 +45,7 @@ describe 'CycleAnalytics#staging' do
MergeRequests::MergeService.new(project, user).execute(merge_request)
deploy_master(user, project)
- expect(subject[:staging].median).to be_nil
+ expect(subject[:staging].project_median).to be_nil
end
end
@@ -56,7 +56,7 @@ describe 'CycleAnalytics#staging' do
MergeRequests::MergeService.new(project, user).execute(merge_request)
deploy_master(user, project, environment: 'staging')
- expect(subject[:staging].median).to be_nil
+ expect(subject[:staging].project_median).to be_nil
end
end
end
diff --git a/spec/models/cycle_analytics/test_spec.rb b/spec/models/cycle_analytics/test_spec.rb
index a6ea73b2699..7b3001d2bd8 100644
--- a/spec/models/cycle_analytics/test_spec.rb
+++ b/spec/models/cycle_analytics/test_spec.rb
@@ -36,7 +36,7 @@ describe 'CycleAnalytics#test' do
merge_merge_requests_closing_issue(user, project, issue)
- expect(subject[:test].median).to be_nil
+ expect(subject[:test].project_median).to be_nil
end
end
@@ -47,7 +47,7 @@ describe 'CycleAnalytics#test' do
pipeline.run!
pipeline.succeed!
- expect(subject[:test].median).to be_nil
+ expect(subject[:test].project_median).to be_nil
end
end
@@ -62,7 +62,7 @@ describe 'CycleAnalytics#test' do
merge_merge_requests_closing_issue(user, project, issue)
- expect(subject[:test].median).to be_nil
+ expect(subject[:test].project_median).to be_nil
end
end
@@ -77,7 +77,7 @@ describe 'CycleAnalytics#test' do
merge_merge_requests_closing_issue(user, project, issue)
- expect(subject[:test].median).to be_nil
+ expect(subject[:test].project_median).to be_nil
end
end
end
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index fe4d64818b4..ce0681c4331 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -762,32 +762,6 @@ describe Environment, :use_clean_rails_memory_store_caching do
end
end
- describe '#generate_slug' do
- SUFFIX = "-[a-z0-9]{6}".freeze
- {
- "staging-12345678901234567" => "staging-123456789" + SUFFIX,
- "9-staging-123456789012345" => "env-9-staging-123" + SUFFIX,
- "staging-1234567890123456" => "staging-1234567890123456",
- "production" => "production",
- "PRODUCTION" => "production" + SUFFIX,
- "review/1-foo" => "review-1-foo" + SUFFIX,
- "1-foo" => "env-1-foo" + SUFFIX,
- "1/foo" => "env-1-foo" + SUFFIX,
- "foo-" => "foo" + SUFFIX,
- "foo--bar" => "foo-bar" + SUFFIX,
- "foo**bar" => "foo-bar" + SUFFIX,
- "*-foo" => "env-foo" + SUFFIX,
- "staging-12345678-" => "staging-12345678" + SUFFIX,
- "staging-12345678-01234567" => "staging-12345678" + SUFFIX
- }.each do |name, matcher|
- it "returns a slug matching #{matcher}, given #{name}" do
- slug = described_class.new(name: name).generate_slug
-
- expect(slug).to match(/\A#{matcher}\z/)
- end
- end
- end
-
describe '#ref_path' do
subject(:environment) do
create(:environment, name: 'staging / review-1')
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 470ce65707d..c7fb0f51075 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -994,4 +994,11 @@ describe Group do
expect(group.project_creation_level).to eq(Gitlab::CurrentSettings.default_project_creation)
end
end
+
+ describe 'subgroup_creation_level' do
+ it 'defaults to maintainers' do
+ expect(group.subgroup_creation_level)
+ .to eq(Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS)
+ end
+ end
end
diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb
index 973c67937b7..519c519fbcf 100644
--- a/spec/models/pages_domain_spec.rb
+++ b/spec/models/pages_domain_spec.rb
@@ -127,6 +127,30 @@ describe PagesDomain do
it { is_expected.not_to be_valid }
end
+
+ context 'when certificate is expired' do
+ let(:domain) do
+ build(:pages_domain, :with_trusted_expired_chain)
+ end
+
+ context 'when certificate is being changed' do
+ it "adds error to certificate" do
+ domain.valid?
+
+ expect(domain.errors.keys).to contain_exactly(:key, :certificate)
+ end
+ end
+
+ context 'when certificate is already saved' do
+ it "doesn't add error to certificate" do
+ domain.save(validate: false)
+
+ domain.valid?
+
+ expect(domain.errors.keys).to contain_exactly(:key)
+ end
+ end
+ end
end
describe 'validations' do
diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb
index 50c9d5968ac..31e55bf6be6 100644
--- a/spec/models/project_feature_spec.rb
+++ b/spec/models/project_feature_spec.rb
@@ -150,4 +150,32 @@ describe ProjectFeature do
end
end
end
+
+ describe 'default pages access level' do
+ subject { project.project_feature.pages_access_level }
+
+ before do
+ # project factory overrides all values in project_feature after creation
+ project.project_feature.destroy!
+ project.build_project_feature.save!
+ end
+
+ context 'when new project is private' do
+ let(:project) { create(:project, :private) }
+
+ it { is_expected.to eq(ProjectFeature::PRIVATE) }
+ end
+
+ context 'when new project is internal' do
+ let(:project) { create(:project, :internal) }
+
+ it { is_expected.to eq(ProjectFeature::PRIVATE) }
+ end
+
+ context 'when new project is public' do
+ let(:project) { create(:project, :public) }
+
+ it { is_expected.to eq(ProjectFeature::ENABLED) }
+ end
+ end
end
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index 235cf314af5..02060699e9a 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -236,7 +236,7 @@ describe JiraService do
allow(JIRA::Resource::Remotelink).to receive(:all).and_return(nil)
expect { @jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project)) }
- .not_to raise_error(NoMethodError)
+ .not_to raise_error
end
# Check https://developer.atlassian.com/jiradev/jira-platform/guides/other/guide-jira-remote-issue-links/fields-in-remote-issue-links
@@ -606,6 +606,12 @@ describe JiraService do
expect(service.properties['api_url']).to eq('http://jira.sample/api')
end
end
+
+ it 'removes trailing slashes from url' do
+ service = described_class.new(url: 'http://jira.test.com/path/')
+
+ expect(service.url).to eq('http://jira.test.com/path')
+ end
end
describe 'favicon urls', :request_store do
@@ -621,4 +627,20 @@ describe JiraService do
expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/uploads/-/system/appearance/favicon/\d+/dk.png$}
end
end
+
+ context 'generating external URLs' do
+ let(:service) { described_class.new(url: 'http://jira.test.com/path/') }
+
+ describe '#issues_url' do
+ it 'handles trailing slashes' do
+ expect(service.issues_url).to eq('http://jira.test.com/path/browse/:id')
+ end
+ end
+
+ describe '#new_issue_url' do
+ it 'handles trailing slashes' do
+ expect(service.new_issue_url).to eq('http://jira.test.com/path/secure/CreateIssue.jspa')
+ end
+ end
+ end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 927c072be10..bcb2da7eed2 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1675,26 +1675,6 @@ describe Project do
end
end
- describe '.paginate_in_descending_order_using_id' do
- let!(:project1) { create(:project) }
- let!(:project2) { create(:project) }
-
- it 'orders the relation in descending order' do
- expect(described_class.paginate_in_descending_order_using_id)
- .to eq([project2, project1])
- end
-
- it 'applies a limit to the relation' do
- expect(described_class.paginate_in_descending_order_using_id(limit: 1))
- .to eq([project2])
- end
-
- it 'limits projects by and ID when given' do
- expect(described_class.paginate_in_descending_order_using_id(before: project2.id))
- .to eq([project1])
- end
- end
-
describe '.including_namespace_and_owner' do
it 'eager loads the namespace and namespace owner' do
create(:project)
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index 59f3a961d50..dc3675a7b9e 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -98,12 +98,38 @@ describe GroupPolicy do
context 'maintainer' do
let(:current_user) { maintainer }
- it do
- expect_allowed(*guest_permissions)
- expect_allowed(*reporter_permissions)
- expect_allowed(*developer_permissions)
- expect_allowed(*maintainer_permissions)
- expect_disallowed(*owner_permissions)
+ context 'with subgroup_creation level set to maintainer' do
+ let(:group) do
+ create(:group, :private, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS)
+ end
+
+ it 'allows every maintainer permission plus creating subgroups' do
+ allow(Group).to receive(:supports_nested_objects?).and_return(true)
+
+ create_subgroup_permission = [:create_subgroup]
+ updated_maintainer_permissions =
+ maintainer_permissions + create_subgroup_permission
+ updated_owner_permissions =
+ owner_permissions - create_subgroup_permission
+
+ expect_allowed(*guest_permissions)
+ expect_allowed(*reporter_permissions)
+ expect_allowed(*developer_permissions)
+ expect_allowed(*updated_maintainer_permissions)
+ expect_disallowed(*updated_owner_permissions)
+ end
+ end
+
+ context 'with subgroup_creation_level set to owner' do
+ it 'allows every maintainer permission' do
+ allow(Group).to receive(:supports_nested_objects?).and_return(true)
+
+ expect_allowed(*guest_permissions)
+ expect_allowed(*reporter_permissions)
+ expect_allowed(*developer_permissions)
+ expect_allowed(*maintainer_permissions)
+ expect_disallowed(*owner_permissions)
+ end
end
end
@@ -145,7 +171,8 @@ describe GroupPolicy do
it 'allows every owner permission except creating subgroups' do
create_subgroup_permission = [:create_subgroup]
- updated_owner_permissions = owner_permissions - create_subgroup_permission
+ updated_owner_permissions =
+ owner_permissions - create_subgroup_permission
expect_disallowed(*create_subgroup_permission)
expect_allowed(*updated_owner_permissions)
@@ -157,16 +184,32 @@ describe GroupPolicy do
it 'allows every owner permission except creating subgroups' do
create_subgroup_permission = [:create_subgroup]
- updated_owner_permissions = owner_permissions - create_subgroup_permission
+ updated_owner_permissions =
+ owner_permissions - create_subgroup_permission
expect_disallowed(*create_subgroup_permission)
expect_allowed(*updated_owner_permissions)
end
end
+
+ context 'maintainer' do
+ let(:current_user) { maintainer }
+
+ it 'allows every maintainer permission except creating subgroups' do
+ create_subgroup_permission = [:create_subgroup]
+ updated_maintainer_permissions =
+ maintainer_permissions - create_subgroup_permission
+
+ expect_disallowed(*create_subgroup_permission)
+ expect_allowed(*updated_maintainer_permissions)
+ end
+ end
end
describe 'private nested group use the highest access level from the group and inherited permissions', :nested_groups do
- let(:nested_group) { create(:group, :private, parent: group) }
+ let(:nested_group) do
+ create(:group, :private, :owner_subgroup_creation_only, parent: group)
+ end
before do
nested_group.add_guest(guest)
@@ -461,6 +504,72 @@ describe GroupPolicy do
end
end
+ context "create_subgroup" do
+ context 'when group has subgroup creation level set to owner' do
+ let(:group) do
+ create(
+ :group,
+ subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS)
+ end
+
+ context 'reporter' do
+ let(:current_user) { reporter }
+
+ it { is_expected.to be_disallowed(:create_subgroup) }
+ end
+
+ context 'developer' do
+ let(:current_user) { developer }
+
+ it { is_expected.to be_disallowed(:create_subgroup) }
+ end
+
+ context 'maintainer' do
+ let(:current_user) { maintainer }
+
+ it { is_expected.to be_disallowed(:create_subgroup) }
+ end
+
+ context 'owner' do
+ let(:current_user) { owner }
+
+ it { is_expected.to be_allowed(:create_subgroup) }
+ end
+ end
+
+ context 'when group has subgroup creation level set to maintainer' do
+ let(:group) do
+ create(
+ :group,
+ subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS)
+ end
+
+ context 'reporter' do
+ let(:current_user) { reporter }
+
+ it { is_expected.to be_disallowed(:create_subgroup) }
+ end
+
+ context 'developer' do
+ let(:current_user) { developer }
+
+ it { is_expected.to be_disallowed(:create_subgroup) }
+ end
+
+ context 'maintainer' do
+ let(:current_user) { maintainer }
+
+ it { is_expected.to be_allowed(:create_subgroup) }
+ end
+
+ context 'owner' do
+ let(:current_user) { owner }
+
+ it { is_expected.to be_allowed(:create_subgroup) }
+ end
+ end
+ end
+
it_behaves_like 'clusterable policies' do
let(:clusterable) { create(:group) }
let(:cluster) do
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 204e378f7be..e8e17228523 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -281,7 +281,7 @@ describe API::Commits do
end
it 'does not increment the usage counters using access token authentication' do
- expect(::Gitlab::UsageDataCounters::WebIdeCommitsCounter).not_to receive(:increment)
+ expect(::Gitlab::UsageDataCounters::WebIdeCounter).not_to receive(:increment_commits_count)
post api(url, user), params: valid_c_params
end
@@ -320,67 +320,132 @@ describe API::Commits do
end
end
- context 'when the API user is a guest' do
+ context 'when committing to a new branch' do
def last_commit_id(project, branch_name)
project.repository.find_branch(branch_name)&.dereferenced_target&.id
end
- let(:public_project) { create(:project, :public, :repository) }
- let!(:url) { "/projects/#{public_project.id}/repository/commits" }
- let(:guest) { create(:user).tap { |u| public_project.add_guest(u) } }
+ before do
+ valid_c_params[:start_branch] = 'master'
+ valid_c_params[:branch] = 'patch'
+ end
- it 'returns a 403' do
- post api(url, guest), params: valid_c_params
+ context 'when the API user is a guest' do
+ let(:public_project) { create(:project, :public, :repository) }
+ let(:url) { "/projects/#{public_project.id}/repository/commits" }
+ let(:guest) { create(:user).tap { |u| public_project.add_guest(u) } }
- expect(response).to have_gitlab_http_status(403)
- end
+ it 'returns a 403' do
+ post api(url, guest), params: valid_c_params
- context 'when start_project is provided' do
- context 'when posting to a forked project the user owns' do
- let!(:forked_project) { fork_project(public_project, guest, namespace: guest.namespace, repository: true) }
- let!(:url) { "/projects/#{forked_project.id}/repository/commits" }
+ expect(response).to have_gitlab_http_status(403)
+ end
- before do
- valid_c_params[:start_branch] = "master"
- valid_c_params[:branch] = "patch"
- end
+ context 'when start_project is provided' do
+ context 'when posting to a forked project the user owns' do
+ let(:forked_project) { fork_project(public_project, guest, namespace: guest.namespace, repository: true) }
+ let(:url) { "/projects/#{forked_project.id}/repository/commits" }
+
+ context 'identified by Integer (id)' do
+ before do
+ valid_c_params[:start_project] = public_project.id
+ end
+
+ it 'adds a new commit to forked_project and returns a 201' do
+ expect_request_with_status(201) { post api(url, guest), params: valid_c_params }
+ .to change { last_commit_id(forked_project, valid_c_params[:branch]) }
+ .and not_change { last_commit_id(public_project, valid_c_params[:start_branch]) }
+ end
+ end
- context 'identified by Integer (id)' do
- before do
- valid_c_params[:start_project] = public_project.id
+ context 'identified by String (full_path)' do
+ before do
+ valid_c_params[:start_project] = public_project.full_path
+ end
+
+ it 'adds a new commit to forked_project and returns a 201' do
+ expect_request_with_status(201) { post api(url, guest), params: valid_c_params }
+ .to change { last_commit_id(forked_project, valid_c_params[:branch]) }
+ .and not_change { last_commit_id(public_project, valid_c_params[:start_branch]) }
+ end
end
- it 'adds a new commit to forked_project and returns a 201' do
- expect { post api(url, guest), params: valid_c_params }
- .to change { last_commit_id(forked_project, valid_c_params[:branch]) }
- .and not_change { last_commit_id(public_project, valid_c_params[:start_branch]) }
+ context 'when branch already exists' do
+ before do
+ valid_c_params.delete(:start_branch)
+ valid_c_params[:branch] = 'master'
+ valid_c_params[:start_project] = public_project.id
+ end
+
+ it 'returns a 400' do
+ post api(url, guest), params: valid_c_params
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']).to eq("A branch called 'master' already exists. Switch to that branch in order to make changes")
+ end
+
+ context 'when force is set to true' do
+ before do
+ valid_c_params[:force] = true
+ end
+
+ it 'adds a new commit to forked_project and returns a 201' do
+ expect_request_with_status(201) { post api(url, guest), params: valid_c_params }
+ .to change { last_commit_id(forked_project, valid_c_params[:branch]) }
+ .and not_change { last_commit_id(public_project, valid_c_params[:branch]) }
+ end
+ end
+ end
+
+ context 'when start_sha is also provided' do
+ let(:forked_project) { fork_project(public_project, guest, namespace: guest.namespace, repository: false) }
+ let(:start_sha) { public_project.repository.commit.parent.sha }
+
+ before do
+ # initialize an empty repository to force fetching from the original project
+ forked_project.repository.create_if_not_exists
- expect(response).to have_gitlab_http_status(201)
+ valid_c_params[:start_project] = public_project.id
+ valid_c_params[:start_sha] = start_sha
+ valid_c_params.delete(:start_branch)
+ end
+
+ it 'fetches the start_sha from the original project to use as parent commit and returns a 201' do
+ expect_request_with_status(201) { post api(url, guest), params: valid_c_params }
+ .to change { last_commit_id(forked_project, valid_c_params[:branch]) }
+ .and not_change { last_commit_id(forked_project, 'master') }
+
+ last_commit = forked_project.repository.find_branch(valid_c_params[:branch]).dereferenced_target
+ expect(last_commit.parent_id).to eq(start_sha)
+ end
end
end
- context 'identified by String (full_path)' do
+ context 'when the target project is not part of the fork network of start_project' do
+ let(:unrelated_project) { create(:project, :public, :repository, creator: guest) }
+ let(:url) { "/projects/#{unrelated_project.id}/repository/commits" }
+
before do
- valid_c_params[:start_project] = public_project.full_path
+ valid_c_params[:start_branch] = 'master'
+ valid_c_params[:branch] = 'patch'
+ valid_c_params[:start_project] = public_project.id
end
- it 'adds a new commit to forked_project and returns a 201' do
- expect { post api(url, guest), params: valid_c_params }
- .to change { last_commit_id(forked_project, valid_c_params[:branch]) }
- .and not_change { last_commit_id(public_project, valid_c_params[:start_branch]) }
+ it 'returns a 403' do
+ post api(url, guest), params: valid_c_params
- expect(response).to have_gitlab_http_status(201)
+ expect(response).to have_gitlab_http_status(403)
end
end
end
- context 'when the target project is not part of the fork network of start_project' do
- let(:unrelated_project) { create(:project, :public, :repository, creator: guest) }
- let!(:url) { "/projects/#{unrelated_project.id}/repository/commits" }
+ context 'when posting to a forked project the user does not have write access' do
+ let(:forked_project) { fork_project(public_project, user, namespace: user.namespace, repository: true) }
+ let(:url) { "/projects/#{forked_project.id}/repository/commits" }
before do
- valid_c_params[:start_branch] = "master"
- valid_c_params[:branch] = "patch"
+ valid_c_params[:start_branch] = 'master'
+ valid_c_params[:branch] = 'patch'
valid_c_params[:start_project] = public_project.id
end
@@ -392,20 +457,68 @@ describe API::Commits do
end
end
- context 'when posting to a forked project the user does not have write access' do
- let!(:forked_project) { fork_project(public_project, user, namespace: user.namespace, repository: true) }
- let!(:url) { "/projects/#{forked_project.id}/repository/commits" }
+ context 'when start_sha is provided' do
+ let(:start_sha) { project.repository.commit.parent.sha }
before do
- valid_c_params[:start_branch] = "master"
- valid_c_params[:branch] = "patch"
- valid_c_params[:start_project] = public_project.id
+ valid_c_params[:start_sha] = start_sha
+ valid_c_params.delete(:start_branch)
end
- it 'returns a 403' do
- post api(url, guest), params: valid_c_params
+ it 'returns a 400 if start_branch is also provided' do
+ valid_c_params[:start_branch] = 'master'
+ post api(url, user), params: valid_c_params
- expect(response).to have_gitlab_http_status(403)
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['error']).to eq('start_branch, start_sha are mutually exclusive')
+ end
+
+ it 'returns a 400 if branch already exists' do
+ valid_c_params[:branch] = 'master'
+ post api(url, user), params: valid_c_params
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']).to eq("A branch called 'master' already exists. Switch to that branch in order to make changes")
+ end
+
+ it 'returns a 400 if start_sha does not exist' do
+ valid_c_params[:start_sha] = '1' * 40
+ post api(url, user), params: valid_c_params
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']).to eq("Cannot find start_sha '#{valid_c_params[:start_sha]}'")
+ end
+
+ it 'returns a 400 if start_sha is not a full SHA' do
+ valid_c_params[:start_sha] = start_sha.slice(0, 7)
+ post api(url, user), params: valid_c_params
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']).to eq("Invalid start_sha '#{valid_c_params[:start_sha]}'")
+ end
+
+ it 'uses the start_sha as parent commit and returns a 201' do
+ expect_request_with_status(201) { post api(url, user), params: valid_c_params }
+ .to change { last_commit_id(project, valid_c_params[:branch]) }
+ .and not_change { last_commit_id(project, 'master') }
+
+ last_commit = project.repository.find_branch(valid_c_params[:branch]).dereferenced_target
+ expect(last_commit.parent_id).to eq(start_sha)
+ end
+
+ context 'when force is set to true and branch already exists' do
+ before do
+ valid_c_params[:force] = true
+ valid_c_params[:branch] = 'master'
+ end
+
+ it 'uses the start_sha as parent commit and returns a 201' do
+ expect_request_with_status(201) { post api(url, user), params: valid_c_params }
+ .to change { last_commit_id(project, valid_c_params[:branch]) }
+
+ last_commit = project.repository.find_branch(valid_c_params[:branch]).dereferenced_target
+ expect(last_commit.parent_id).to eq(start_sha)
+ end
end
end
end
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index c41408fba65..52d926d5484 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -803,10 +803,10 @@ describe API::Groups do
group2.add_maintainer(user1)
end
- it 'cannot create subgroups' do
+ it 'can create subgroups' do
post api("/groups", user1), params: { parent_id: group2.id, name: 'foo', path: 'foo' }
- expect(response).to have_gitlab_http_status(403)
+ expect(response).to have_gitlab_http_status(201)
end
end
end
diff --git a/spec/requests/request_profiler_spec.rb b/spec/requests/request_profiler_spec.rb
index 75b22b1879b..851affbcf88 100644
--- a/spec/requests/request_profiler_spec.rb
+++ b/spec/requests/request_profiler_spec.rb
@@ -3,13 +3,18 @@ require 'spec_helper'
describe 'Request Profiler' do
let(:user) { create(:user) }
- shared_examples 'profiling a request' do
+ shared_examples 'profiling a request' do |profile_type, extension|
before do
allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::MemoryStore.new)
allow(RubyProf::Profile).to receive(:profile) do |&blk|
blk.call
RubyProf::Profile.new
end
+ allow(MemoryProfiler).to receive(:report) do |&blk|
+ blk.call
+ MemoryProfiler.start
+ MemoryProfiler.stop
+ end
end
it 'creates a profile of the request' do
@@ -18,10 +23,11 @@ describe 'Request Profiler' do
path = "/#{project.full_path}"
Timecop.freeze(time) do
- get path, params: {}, headers: { 'X-Profile-Token' => Gitlab::RequestProfiler.profile_token }
+ get path, params: {}, headers: { 'X-Profile-Token' => Gitlab::RequestProfiler.profile_token, 'X-Profile-Mode' => profile_type }
end
- profile_path = "#{Gitlab.config.shared.path}/tmp/requests_profiles/#{path.tr('/', '|')}_#{time.to_i}.html"
+ profile_type = 'execution' if profile_type.nil?
+ profile_path = "#{Gitlab.config.shared.path}/tmp/requests_profiles/#{path.tr('/', '|')}_#{time.to_i}_#{profile_type}.#{extension}"
expect(File.exist?(profile_path)).to be true
end
@@ -35,10 +41,14 @@ describe 'Request Profiler' do
login_as(user)
end
- include_examples 'profiling a request'
+ include_examples 'profiling a request', 'execution', 'html'
+ include_examples 'profiling a request', nil, 'html'
+ include_examples 'profiling a request', 'memory', 'txt'
end
context "when user is not logged-in" do
- include_examples 'profiling a request'
+ include_examples 'profiling a request', 'execution', 'html'
+ include_examples 'profiling a request', nil, 'html'
+ include_examples 'profiling a request', 'memory', 'txt'
end
end
diff --git a/spec/serializers/analytics_issue_entity_spec.rb b/spec/serializers/analytics_issue_entity_spec.rb
index 89588b4df2b..dd5e43a4b62 100644
--- a/spec/serializers/analytics_issue_entity_spec.rb
+++ b/spec/serializers/analytics_issue_entity_spec.rb
@@ -9,12 +9,14 @@ describe AnalyticsIssueEntity do
iid: "1",
id: "1",
created_at: "2016-11-12 15:04:02.948604",
- author: user
+ author: user,
+ name: project.name,
+ path: project.namespace
}
end
let(:project) { create(:project) }
- let(:request) { EntityRequest.new(project: project, entity: :merge_request) }
+ let(:request) { EntityRequest.new(entity: :merge_request) }
let(:entity) do
described_class.new(entity_hash, request: request, project: project)
diff --git a/spec/serializers/analytics_issue_serializer_spec.rb b/spec/serializers/analytics_issue_serializer_spec.rb
index 5befc28f4fa..c9ffe1c5dad 100644
--- a/spec/serializers/analytics_issue_serializer_spec.rb
+++ b/spec/serializers/analytics_issue_serializer_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe AnalyticsIssueSerializer do
subject do
described_class
- .new(project: project, entity: :merge_request)
+ .new(entity: :merge_request)
.represent(resource)
end
@@ -16,7 +16,9 @@ describe AnalyticsIssueSerializer do
iid: "1",
id: "1",
created_at: "2016-11-12 15:04:02.948604",
- author: user
+ author: user,
+ name: project.name,
+ path: project.namespace
}
end
diff --git a/spec/serializers/analytics_merge_request_serializer_spec.rb b/spec/serializers/analytics_merge_request_serializer_spec.rb
index 62067cc0ef2..123d7d795ce 100644
--- a/spec/serializers/analytics_merge_request_serializer_spec.rb
+++ b/spec/serializers/analytics_merge_request_serializer_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe AnalyticsMergeRequestSerializer do
subject do
described_class
- .new(project: project, entity: :merge_request)
+ .new(entity: :merge_request)
.represent(resource)
end
@@ -17,7 +17,9 @@ describe AnalyticsMergeRequestSerializer do
id: "1",
state: 'open',
created_at: "2016-11-12 15:04:02.948604",
- author: user
+ author: user,
+ name: project.name,
+ path: project.namespace
}
end
diff --git a/spec/serializers/analytics_stage_serializer_spec.rb b/spec/serializers/analytics_stage_serializer_spec.rb
index 5b05c2f2ef3..86a796a2d94 100644
--- a/spec/serializers/analytics_stage_serializer_spec.rb
+++ b/spec/serializers/analytics_stage_serializer_spec.rb
@@ -6,11 +6,11 @@ describe AnalyticsStageSerializer do
end
let(:resource) do
- Gitlab::CycleAnalytics::CodeStage.new(project: double, options: {})
+ Gitlab::CycleAnalytics::CodeStage.new(options: { project: double })
end
before do
- allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:median).and_return(1.12)
+ allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:project_median).and_return(1.12)
allow_any_instance_of(Gitlab::CycleAnalytics::BaseEventFetcher).to receive(:event_result).and_return({})
end
@@ -24,7 +24,7 @@ describe AnalyticsStageSerializer do
context 'when median is equal 0' do
before do
- allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:median).and_return(0)
+ allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:project_median).and_return(0)
end
it 'sets the value to nil' do
@@ -34,7 +34,7 @@ describe AnalyticsStageSerializer do
context 'when median is below 1' do
before do
- allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:median).and_return(0.12)
+ allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:project_median).and_return(0.12)
end
it 'sets the value to equal to median' do
@@ -44,7 +44,7 @@ describe AnalyticsStageSerializer do
context 'when median is above 1' do
before do
- allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:median).and_return(60.12)
+ allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:project_median).and_return(60.12)
end
it 'sets the value to equal to median' do
diff --git a/spec/serializers/diff_file_base_entity_spec.rb b/spec/serializers/diff_file_base_entity_spec.rb
new file mode 100644
index 00000000000..68c5c665ed6
--- /dev/null
+++ b/spec/serializers/diff_file_base_entity_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe DiffFileBaseEntity do
+ let(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
+
+ context 'diff for a changed submodule' do
+ let(:commit_sha_with_changed_submodule) do
+ "cfe32cf61b73a0d5e9f13e774abde7ff789b1660"
+ end
+ let(:commit) { project.commit(commit_sha_with_changed_submodule) }
+ let(:diff_file) { commit.diffs.diff_files.to_a.last }
+ let(:options) { { request: {}, submodule_links: Gitlab::SubmoduleLinks.new(repository) } }
+ let(:entity) { described_class.new(diff_file, options).as_json }
+
+ it do
+ expect(entity[:submodule]).to eq(true)
+ expect(entity[:submodule_link]).to eq("https://github.com/randx/six")
+ expect(entity[:submodule_tree_url]).to eq(
+ "https://github.com/randx/six/tree/409f37c4f05865e4fb208c771485f211a22c4c2d"
+ )
+ end
+ end
+end
diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb
index c5ff6cdbacd..a7c95428485 100644
--- a/spec/services/groups/create_service_spec.rb
+++ b/spec/services/groups/create_service_spec.rb
@@ -87,6 +87,14 @@ describe Groups::CreateService, '#execute' do
it { is_expected.to be_persisted }
end
+
+ context 'as maintainer' do
+ before do
+ group.add_maintainer(user)
+ end
+
+ it { is_expected.to be_persisted }
+ end
end
end
diff --git a/spec/services/merge_requests/push_options_handler_service_spec.rb b/spec/services/merge_requests/push_options_handler_service_spec.rb
index 54b9c6dae38..ac40cf02c48 100644
--- a/spec/services/merge_requests/push_options_handler_service_spec.rb
+++ b/spec/services/merge_requests/push_options_handler_service_spec.rb
@@ -90,6 +90,16 @@ describe MergeRequests::PushOptionsHandlerService do
end
end
+ shared_examples_for 'a service that can remove the source branch when it is merged' do
+ subject(:last_mr) { MergeRequest.last }
+
+ it 'returns true to force_remove_source_branch?' do
+ service.execute
+
+ expect(last_mr.force_remove_source_branch?).to eq(true)
+ end
+ end
+
shared_examples_for 'a service that does not create a merge request' do
it do
expect { service.execute }.not_to change { MergeRequest.count }
@@ -208,6 +218,72 @@ describe MergeRequests::PushOptionsHandlerService do
end
end
+ describe '`remove_source_branch` push option' do
+ let(:push_options) { { remove_source_branch: true } }
+
+ context 'with a new branch' do
+ let(:changes) { new_branch_changes }
+
+ it_behaves_like 'a service that does not create a merge request'
+
+ it 'adds an error to the service' do
+ error = "A merge_request.create push option is required to create a merge request for branch #{source_branch}"
+
+ service.execute
+
+ expect(service.errors).to include(error)
+ end
+
+ context 'when coupled with the `create` push option' do
+ let(:push_options) { { create: true, remove_source_branch: true } }
+
+ it_behaves_like 'a service that can create a merge request'
+ it_behaves_like 'a service that can remove the source branch when it is merged'
+ end
+ end
+
+ context 'with an existing branch but no open MR' do
+ let(:changes) { existing_branch_changes }
+
+ it_behaves_like 'a service that does not create a merge request'
+
+ it 'adds an error to the service' do
+ error = "A merge_request.create push option is required to create a merge request for branch #{source_branch}"
+
+ service.execute
+
+ expect(service.errors).to include(error)
+ end
+
+ context 'when coupled with the `create` push option' do
+ let(:push_options) { { create: true, remove_source_branch: true } }
+
+ it_behaves_like 'a service that can create a merge request'
+ it_behaves_like 'a service that can remove the source branch when it is merged'
+ end
+ end
+
+ context 'with an existing branch that has a merge request open' do
+ let(:changes) { existing_branch_changes }
+ let!(:merge_request) { create(:merge_request, source_project: project, source_branch: source_branch)}
+
+ it_behaves_like 'a service that does not create a merge request'
+ it_behaves_like 'a service that can remove the source branch when it is merged'
+ end
+
+ context 'with a deleted branch' do
+ let(:changes) { deleted_branch_changes }
+
+ it_behaves_like 'a service that does nothing'
+ end
+
+ context 'with the project default branch' do
+ let(:changes) { default_branch_changes }
+
+ it_behaves_like 'a service that does nothing'
+ end
+ end
+
describe '`target` push option' do
let(:push_options) { { target: target_branch } }
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index 1dcfb739eb6..6bbaa410d56 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -347,13 +347,13 @@ describe Projects::UpdateService do
context 'when updating #pages_access_level' do
subject(:call_service) do
- update_project(project, admin, project_feature_attributes: { pages_access_level: ProjectFeature::PRIVATE })
+ update_project(project, admin, project_feature_attributes: { pages_access_level: ProjectFeature::ENABLED })
end
it 'updates the attribute' do
expect { call_service }
.to change { project.project_feature.pages_access_level }
- .to(ProjectFeature::PRIVATE)
+ .to(ProjectFeature::ENABLED)
end
it 'calls Projects::UpdatePagesConfigurationService' do
diff --git a/spec/services/submodules/update_service_spec.rb b/spec/services/submodules/update_service_spec.rb
index cf92350c1b2..47b31d4bcbf 100644
--- a/spec/services/submodules/update_service_spec.rb
+++ b/spec/services/submodules/update_service_spec.rb
@@ -142,7 +142,7 @@ describe Submodules::UpdateService do
let(:branch_name) { nil }
it_behaves_like 'returns error result' do
- let(:error_message) { 'You can only create or edit files when you are on a branch' }
+ let(:error_message) { 'Invalid parameters' }
end
end
diff --git a/spec/services/task_list_toggle_service_spec.rb b/spec/services/task_list_toggle_service_spec.rb
index 9adaee6481b..a309951bbcb 100644
--- a/spec/services/task_list_toggle_service_spec.rb
+++ b/spec/services/task_list_toggle_service_spec.rb
@@ -114,6 +114,23 @@ describe TaskListToggleService do
expect(toggler.execute).to be_falsey
end
+ it 'properly handles tasks in a blockquote' do
+ markdown =
+ <<-EOT.strip_heredoc
+ > > * [ ] Task 1
+ > * [x] Task 2
+ EOT
+
+ markdown_html = Banzai::Pipeline::FullPipeline.call(markdown, project: nil)[:output].to_html
+ toggler = described_class.new(markdown, markdown_html,
+ toggle_as_checked: true,
+ line_source: '> > * [ ] Task 1', line_number: 1)
+
+ expect(toggler.execute).to be_truthy
+ expect(toggler.updated_markdown.lines[0]).to eq "> > * [x] Task 1\n"
+ expect(toggler.updated_markdown_html).to include('disabled checked> Task 1')
+ end
+
it 'properly handles a GitLab blockquote' do
markdown =
<<-EOT.strip_heredoc
diff --git a/spec/services/wiki_pages/base_service_spec.rb b/spec/services/wiki_pages/base_service_spec.rb
new file mode 100644
index 00000000000..2e70246c6f2
--- /dev/null
+++ b/spec/services/wiki_pages/base_service_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe WikiPages::BaseService do
+ let(:project) { double('project') }
+ let(:user) { double('user') }
+
+ subject(:service) { described_class.new(project, user, {}) }
+
+ describe '#increment_usage' do
+ counter = Gitlab::UsageDataCounters::WikiPageCounter
+ error = counter::UnknownEvent
+
+ it 'raises an error on unknown events' do
+ expect { subject.send(:increment_usage, :bad_event) }.to raise_error error
+ end
+
+ context 'the event is valid' do
+ counter::KNOWN_EVENTS.each do |e|
+ it "updates the #{e} counter" do
+ expect { subject.send(:increment_usage, e) }.to change { counter.read(e) }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/wiki_pages/create_service_spec.rb b/spec/services/wiki_pages/create_service_spec.rb
index 84510dcf700..ef03a2e9788 100644
--- a/spec/services/wiki_pages/create_service_spec.rb
+++ b/spec/services/wiki_pages/create_service_spec.rb
@@ -14,6 +14,10 @@ describe WikiPages::CreateService do
}
end
+ let(:bad_opts) do
+ { title: '' }
+ end
+
subject(:service) { described_class.new(project, user, opts) }
before do
@@ -36,5 +40,26 @@ describe WikiPages::CreateService do
service.execute
end
+
+ it 'counts wiki page creation' do
+ counter = Gitlab::UsageDataCounters::WikiPageCounter
+
+ expect { service.execute }.to change { counter.read(:create) }.by 1
+ end
+
+ context 'when the options are bad' do
+ subject(:service) { described_class.new(project, user, bad_opts) }
+
+ it 'does not count a creation event' do
+ counter = Gitlab::UsageDataCounters::WikiPageCounter
+
+ expect { service.execute }.not_to change { counter.read(:create) }
+ end
+
+ it 'reports the error' do
+ expect(service.execute).to be_invalid
+ .and have_attributes(errors: be_present)
+ end
+ end
end
end
diff --git a/spec/services/wiki_pages/destroy_service_spec.rb b/spec/services/wiki_pages/destroy_service_spec.rb
index c74eac4dad6..350a7eb123b 100644
--- a/spec/services/wiki_pages/destroy_service_spec.rb
+++ b/spec/services/wiki_pages/destroy_service_spec.rb
@@ -20,5 +20,17 @@ describe WikiPages::DestroyService do
service.execute(page)
end
+
+ it 'increments the delete count' do
+ counter = Gitlab::UsageDataCounters::WikiPageCounter
+
+ expect { service.execute(page) }.to change { counter.read(:delete) }.by 1
+ end
+
+ it 'does not increment the delete count if the deletion failed' do
+ counter = Gitlab::UsageDataCounters::WikiPageCounter
+
+ expect { service.execute(nil) }.not_to change { counter.read(:delete) }
+ end
end
end
diff --git a/spec/services/wiki_pages/update_service_spec.rb b/spec/services/wiki_pages/update_service_spec.rb
index 19866bd3bfc..d5f46e7b2db 100644
--- a/spec/services/wiki_pages/update_service_spec.rb
+++ b/spec/services/wiki_pages/update_service_spec.rb
@@ -16,6 +16,10 @@ describe WikiPages::UpdateService do
}
end
+ let(:bad_opts) do
+ { title: '' }
+ end
+
subject(:service) { described_class.new(project, user, opts) }
before do
@@ -39,5 +43,26 @@ describe WikiPages::UpdateService do
service.execute(page)
end
+
+ it 'counts edit events' do
+ counter = Gitlab::UsageDataCounters::WikiPageCounter
+
+ expect { service.execute page }.to change { counter.read(:update) }.by 1
+ end
+
+ context 'when the options are bad' do
+ subject(:service) { described_class.new(project, user, bad_opts) }
+
+ it 'does not count an edit event' do
+ counter = Gitlab::UsageDataCounters::WikiPageCounter
+
+ expect { service.execute page }.not_to change { counter.read(:update) }
+ end
+
+ it 'reports the error' do
+ expect(service.execute page).to be_invalid
+ .and have_attributes(errors: be_present)
+ end
+ end
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 95e0d8858b9..a6fb172e79b 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -104,6 +104,7 @@ RSpec.configure do |config|
config.include Rails.application.routes.url_helpers, type: :routing
config.include PolicyHelpers, type: :policy
config.include MemoryUsageHelper
+ config.include ExpectRequestWithStatus, type: :request
if ENV['CI']
# This includes the first try, i.e. tests will be run 4 times before failing.
@@ -134,6 +135,8 @@ RSpec.configure do |config|
allow(Feature).to receive(:enabled?).with(flag).and_return(enabled)
end
+ allow(Gitlab::GitalyClient).to receive(:can_use_disk?).and_return(enabled)
+
# The following can be removed when we remove the staged rollout strategy
# and we can just enable it using instance wide settings
# (ie. ApplicationSetting#auto_devops_enabled)
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
index 56ac208a025..60990879fe2 100644
--- a/spec/support/capybara.rb
+++ b/spec/support/capybara.rb
@@ -58,6 +58,7 @@ Capybara.javascript_driver = :chrome
Capybara.default_max_wait_time = timeout
Capybara.ignore_hidden_elements = true
Capybara.default_normalize_ws = true
+Capybara.enable_aria_label = true
# Keep only the screenshots generated from the last failing test suite
Capybara::Screenshot.prune_strategy = :keep_last_run
diff --git a/spec/support/cycle_analytics_helpers/test_generation.rb b/spec/support/cycle_analytics_helpers/test_generation.rb
index 19b32c84d81..be1c2bc3046 100644
--- a/spec/support/cycle_analytics_helpers/test_generation.rb
+++ b/spec/support/cycle_analytics_helpers/test_generation.rb
@@ -50,7 +50,7 @@ module CycleAnalyticsHelpers
end
median_time_difference = time_differences.sort[2]
- expect(subject[phase].median).to be_within(5).of(median_time_difference)
+ expect(subject[phase].project_median).to be_within(5).of(median_time_difference)
end
context "when the data belongs to another project" do
@@ -80,7 +80,7 @@ module CycleAnalyticsHelpers
# Turn off the stub before checking assertions
allow(self).to receive(:project).and_call_original
- expect(subject[phase].median).to be_nil
+ expect(subject[phase].project_median).to be_nil
end
end
@@ -103,7 +103,7 @@ module CycleAnalyticsHelpers
Timecop.freeze(end_time + 1.day) { post_fn[self, data] } if post_fn
- expect(subject[phase].median).to be_nil
+ expect(subject[phase].project_median).to be_nil
end
end
end
@@ -121,7 +121,7 @@ module CycleAnalyticsHelpers
Timecop.freeze(end_time + 1.day) { post_fn[self, data] } if post_fn
- expect(subject[phase].median).to be_nil
+ expect(subject[phase].project_median).to be_nil
end
end
end
@@ -138,7 +138,7 @@ module CycleAnalyticsHelpers
post_fn[self, data] if post_fn
- expect(subject[phase].median).to be_nil
+ expect(subject[phase].project_median).to be_nil
end
end
end
@@ -146,7 +146,7 @@ module CycleAnalyticsHelpers
context "when none of the start / end conditions are matched" do
it "returns nil" do
- expect(subject[phase].median).to be_nil
+ expect(subject[phase].project_median).to be_nil
end
end
end
diff --git a/spec/support/helpers/expect_request_with_status.rb b/spec/support/helpers/expect_request_with_status.rb
new file mode 100644
index 00000000000..0469a94e336
--- /dev/null
+++ b/spec/support/helpers/expect_request_with_status.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module ExpectRequestWithStatus
+ def expect_request_with_status(status)
+ expect do
+ yield
+
+ expect(response).to have_gitlab_http_status(status)
+ end
+ end
+end
diff --git a/spec/support/helpers/javascript_fixtures_helpers.rb b/spec/support/helpers/javascript_fixtures_helpers.rb
index cdd7724cc13..e9129bd263e 100644
--- a/spec/support/helpers/javascript_fixtures_helpers.rb
+++ b/spec/support/helpers/javascript_fixtures_helpers.rb
@@ -19,7 +19,7 @@ module JavaScriptFixturesHelpers
end
def fixture_root_path
- (Gitlab.ee? ? 'ee/' : '') + 'spec/javascripts/fixtures'
+ 'tmp/tests/frontend/fixtures' + (Gitlab.ee? ? '-ee' : '')
end
# Public: Removes all fixture files from given directory
diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb
index c372a3f0e49..049702be1f6 100644
--- a/spec/support/helpers/stub_configuration.rb
+++ b/spec/support/helpers/stub_configuration.rb
@@ -65,6 +65,10 @@ module StubConfiguration
allow(Gitlab.config.artifacts).to receive_messages(to_settings(messages))
end
+ def stub_pages_setting(messages)
+ allow(Gitlab.config.pages).to receive_messages(to_settings(messages))
+ end
+
def stub_storage_settings(messages)
messages.deep_stringify_keys!
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index 893b10ea752..b062631b995 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -130,7 +130,7 @@ module TestEnv
# Keeps gitlab-shell and gitlab-test
def clean_test_path
Dir[TMP_TEST_PATH].each do |entry|
- unless File.basename(entry) =~ /\A(gitaly|gitlab-(shell|test|test_bare|test-fork|test-fork_bare))\z/
+ unless test_dirs.include?(File.basename(entry))
FileUtils.rm_rf(entry)
end
end
@@ -141,14 +141,6 @@ module TestEnv
FileUtils.mkdir_p(artifacts_path)
end
- def clean_gitlab_test_path
- Dir[TMP_TEST_PATH].each do |entry|
- unless test_dirs.include?(File.basename(entry))
- FileUtils.rm_rf(entry)
- end
- end
- end
-
def setup_gitlab_shell
component_timed_setup('GitLab Shell',
install_dir: Gitlab.config.gitlab_shell.path,
@@ -324,6 +316,7 @@ module TestEnv
# These are directories that should be preserved at cleanup time
def test_dirs
@test_dirs ||= %w[
+ frontend
gitaly
gitlab-shell
gitlab-test
@@ -368,10 +361,7 @@ module TestEnv
# Try to reset without fetching to avoid using the network.
unless reset.call
raise 'Could not fetch test seed repository.' unless system(*%W(#{Gitlab.config.git.bin_path} -C #{repo_path} fetch origin))
-
- # Before we used Git clone's --mirror option, bare repos could end up
- # with missing refs, clearing them and retrying should fix the issue.
- clean_gitlab_test_path && init unless reset.call
+ raise "Could not update test seed repository, please delete #{repo_path} and try again" unless reset.call
end
end
diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
index b4808ac0068..74389c4d82b 100644
--- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
@@ -7,7 +7,7 @@ RSpec.shared_context 'GroupPolicy context' do
let(:maintainer) { create(:user) }
let(:owner) { create(:user) }
let(:admin) { create(:admin) }
- let(:group) { create(:group, :private) }
+ let(:group) { create(:group, :private, :owner_subgroup_creation_only) }
let(:guest_permissions) do
%i[
diff --git a/spec/support/shared_examples/taskable_shared_examples.rb b/spec/support/shared_examples/taskable_shared_examples.rb
index 4056ff06b84..4a1df1ce380 100644
--- a/spec/support/shared_examples/taskable_shared_examples.rb
+++ b/spec/support/shared_examples/taskable_shared_examples.rb
@@ -105,4 +105,25 @@ shared_examples 'a Taskable' do
expect(subject.task_status_short).to match('1 task')
end
end
+
+ describe 'with tasks in blockquotes' do
+ before do
+ subject.description = <<-EOT.strip_heredoc
+ > - [ ] Task a
+ > > - [x] Task a.1
+
+ >>>
+ 1. [ ] Task 1
+ 1. [x] Task 2
+ >>>
+ EOT
+ end
+
+ it 'returns the correct task status' do
+ expect(subject.task_status).to match('2 of')
+ expect(subject.task_status).to match('4 tasks completed')
+ expect(subject.task_status_short).to match('2/')
+ expect(subject.task_status_short).to match('4 tasks')
+ end
+ end
end
diff --git a/yarn.lock b/yarn.lock
index eaa029281d6..46d5b1c01da 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,41 +2,41 @@
# yarn lockfile v1
-"@babel/code-frame@^7.0.0":
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8"
- integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"
+ integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==
dependencies:
"@babel/highlight" "^7.0.0"
-"@babel/core@>=7.1.0", "@babel/core@^7.1.0", "@babel/core@^7.4.4":
- version "7.4.5"
- resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.4.5.tgz#081f97e8ffca65a9b4b0fdc7e274e703f000c06a"
- integrity sha512-OvjIh6aqXtlsA8ujtGKfC7LYWksYSX8yQcM8Ay3LuvVeQ63lcOKgoZWVqcpFwkd29aYU9rVx7jxhfhiEDV9MZA==
+"@babel/core@>=7.2.2", "@babel/core@^7.1.0", "@babel/core@^7.4.4":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.5.5.tgz#17b2686ef0d6bc58f963dddd68ab669755582c30"
+ integrity sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg==
dependencies:
- "@babel/code-frame" "^7.0.0"
- "@babel/generator" "^7.4.4"
- "@babel/helpers" "^7.4.4"
- "@babel/parser" "^7.4.5"
+ "@babel/code-frame" "^7.5.5"
+ "@babel/generator" "^7.5.5"
+ "@babel/helpers" "^7.5.5"
+ "@babel/parser" "^7.5.5"
"@babel/template" "^7.4.4"
- "@babel/traverse" "^7.4.5"
- "@babel/types" "^7.4.4"
+ "@babel/traverse" "^7.5.5"
+ "@babel/types" "^7.5.5"
convert-source-map "^1.1.0"
debug "^4.1.0"
json5 "^2.1.0"
- lodash "^4.17.11"
+ lodash "^4.17.13"
resolve "^1.3.2"
semver "^5.4.1"
source-map "^0.5.0"
-"@babel/generator@^7.4.0", "@babel/generator@^7.4.4":
- version "7.4.4"
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.4.4.tgz#174a215eb843fc392c7edcaabeaa873de6e8f041"
- integrity sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==
+"@babel/generator@^7.4.0", "@babel/generator@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.5.5.tgz#873a7f936a3c89491b43536d12245b626664e3cf"
+ integrity sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==
dependencies:
- "@babel/types" "^7.4.4"
+ "@babel/types" "^7.5.5"
jsesc "^2.5.1"
- lodash "^4.17.11"
+ lodash "^4.17.13"
source-map "^0.5.0"
trim-right "^1.0.1"
@@ -207,14 +207,14 @@
"@babel/traverse" "^7.1.0"
"@babel/types" "^7.0.0"
-"@babel/helpers@^7.4.4":
- version "7.4.4"
- resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.4.4.tgz#868b0ef59c1dd4e78744562d5ce1b59c89f2f2a5"
- integrity sha512-igczbR/0SeuPR8RFfC7tGrbdTbFL3QTvH6D+Z6zNxnTe//GyqmtHmDkzrqDmyZ3eSwPqB/LhyKoU5DXsp+Vp2A==
+"@babel/helpers@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.5.5.tgz#63908d2a73942229d1e6685bc2a0e730dde3b75e"
+ integrity sha512-nRq2BUhxZFnfEn/ciJuhklHvFOqjJUD5wpx+1bxUF2axL9C+v4DE/dmp5sT2dKnpOs4orZWzpAZqlCy8QqE/7g==
dependencies:
"@babel/template" "^7.4.4"
- "@babel/traverse" "^7.4.4"
- "@babel/types" "^7.4.4"
+ "@babel/traverse" "^7.5.5"
+ "@babel/types" "^7.5.5"
"@babel/highlight@^7.0.0":
version "7.0.0"
@@ -225,10 +225,10 @@
esutils "^2.0.2"
js-tokens "^4.0.0"
-"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.4.4", "@babel/parser@^7.4.5":
- version "7.4.5"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.4.5.tgz#04af8d5d5a2b044a2a1bffacc1e5e6673544e872"
- integrity sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==
+"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.4.4", "@babel/parser@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.5.tgz#02f077ac8817d3df4a832ef59de67565e71cca4b"
+ integrity sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==
"@babel/plugin-proposal-async-generator-functions@^7.2.0":
version "7.2.0"
@@ -641,28 +641,28 @@
"@babel/parser" "^7.4.4"
"@babel/types" "^7.4.4"
-"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.4", "@babel/traverse@^7.4.5":
- version "7.4.5"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.4.5.tgz#4e92d1728fd2f1897dafdd321efbff92156c3216"
- integrity sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==
+"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.4", "@babel/traverse@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.5.5.tgz#f664f8f368ed32988cd648da9f72d5ca70f165bb"
+ integrity sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==
dependencies:
- "@babel/code-frame" "^7.0.0"
- "@babel/generator" "^7.4.4"
+ "@babel/code-frame" "^7.5.5"
+ "@babel/generator" "^7.5.5"
"@babel/helper-function-name" "^7.1.0"
"@babel/helper-split-export-declaration" "^7.4.4"
- "@babel/parser" "^7.4.5"
- "@babel/types" "^7.4.4"
+ "@babel/parser" "^7.5.5"
+ "@babel/types" "^7.5.5"
debug "^4.1.0"
globals "^11.1.0"
- lodash "^4.17.11"
+ lodash "^4.17.13"
-"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4":
- version "7.4.4"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.4.4.tgz#8db9e9a629bb7c29370009b4b779ed93fe57d5f0"
- integrity sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==
+"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.5.5.tgz#97b9f728e182785909aa4ab56264f090a028d18a"
+ integrity sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==
dependencies:
esutils "^2.0.2"
- lodash "^4.17.11"
+ lodash "^4.17.13"
to-fast-properties "^2.0.0"
"@cnakazawa/watch@^1.0.3":
@@ -705,21 +705,21 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.67.0.tgz#c7b94eca13b99fd3aaa737fb6dcc0abc41d3c579"
integrity sha512-hJOmWEs6RkjzyKkb1vc9wwKGZIBIP0coHkxu/KgOoxhBVudpGk4CH7xJ6UuB2TKpb0SEh5CC1CzRZfBYaFhsaA==
-"@gitlab/ui@^5.6.0":
- version "5.6.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.6.0.tgz#6b5408050229e2135359f3fce5d0de7718326a0d"
- integrity sha512-EohpACc5OCK8pOWgwB7/QZUcg3tA9k863ku6Ik9NxaRSKt/JIpQ8RI4wCr4UmqhejZLQMD9VZHLUmc9Sf3Mk9w==
+"@gitlab/ui@^5.7.0":
+ version "5.7.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.7.0.tgz#2ce6b431de262f09b2e1dbe77b047d6e8e1ca41d"
+ integrity sha512-zOPFNrCGyZrgqa8OXNhtfRg4aQ6pRCpIV2+alq3/4jllYb3HHHH+jIk/ejLrvNe8+fk7LRNSvJEvuInRLqBbEg==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.2.1"
bootstrap "4.3.1"
- bootstrap-vue "^2.0.0-rc.24"
+ bootstrap-vue "^2.0.0-rc.26"
copy-to-clipboard "^3.0.8"
core-js "^2.6.9"
echarts "^4.2.0-rc.2"
highlight.js "^9.13.1"
js-beautify "^1.8.8"
- lodash "^4.17.11"
+ lodash "^4.17.14"
url-search-params-polyfill "^5.0.0"
vue "^2.6.10"
vue-loader "^15.4.2"
@@ -945,7 +945,7 @@
resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86"
integrity sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==
-"@types/glob@5 - 7":
+"@types/glob@5 - 7", "@types/glob@^7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==
@@ -1275,20 +1275,20 @@ acorn-jsx@^5.0.0:
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e"
integrity sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==
-acorn-walk@^6.0.1:
- version "6.1.1"
- resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.1.1.tgz#d363b66f5fac5f018ff9c3a1e7b6f8e310cc3913"
- integrity sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==
+acorn-walk@^6.0.1, acorn-walk@^6.1.1:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c"
+ integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==
-acorn@^5.5.3, acorn@^5.7.3:
+acorn@^5.5.3:
version "5.7.3"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279"
integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==
-acorn@^6.0.1, acorn@^6.0.2, acorn@^6.0.5:
- version "6.0.5"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.5.tgz#81730c0815f3f3b34d8efa95cb7430965f4d887a"
- integrity sha512-i33Zgp3XWtmZBMNvCr4azvOFeWVw1Rk6p3hfi3LUDvIFraOMywb1kAtrbi+med14m4Xfpqm3zRZMT+c0FNE7kg==
+acorn@^6.0.1, acorn@^6.0.2, acorn@^6.0.5, acorn@^6.0.7:
+ version "6.2.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.2.1.tgz#3ed8422d6dec09e6121cc7a843ca86a330a86b51"
+ integrity sha512-JD0xT5FCRDNyjDda3Lrg/IxFscp9q4tiYtxE1/nOzlKCk7hIRuYjhq1kCNkbPjMRMZuFq20HNQn1I9k8Oj0E+Q==
after@0.8.2:
version "0.8.2"
@@ -1305,10 +1305,10 @@ ajv-keywords@^3.1.0:
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a"
integrity sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=
-ajv@^6.1.0, ajv@^6.5.3, ajv@^6.5.5, ajv@^6.9.1:
- version "6.9.1"
- resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.9.1.tgz#a4d3683d74abc5670e75f0b16520f70a20ea8dc1"
- integrity sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==
+ajv@^6.1.0, ajv@^6.10.2, ajv@^6.5.3, ajv@^6.5.5:
+ version "6.10.2"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
+ integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==
dependencies:
fast-deep-equal "^2.0.1"
fast-json-stable-stringify "^2.0.0"
@@ -1657,17 +1657,18 @@ atob@^2.1.1:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
-autoprefixer@^9.0.0:
- version "9.4.7"
- resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.4.7.tgz#f997994f9a810eae47b38fa6d8a119772051c4ff"
- integrity sha512-qS5wW6aXHkm53Y4z73tFGsUhmZu4aMPV9iHXYlF0c/wxjknXNHuj/1cIQb+6YH692DbJGGWcckAXX+VxKvahMA==
+autoprefixer@^9.5.1:
+ version "9.6.1"
+ resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.6.1.tgz#51967a02d2d2300bb01866c1611ec8348d355a47"
+ integrity sha512-aVo5WxR3VyvyJxcJC3h4FKfwCQvQWb1tSI5VHNibddCVWrcD1NvlxEweg3TSgiPztMnWfjpy2FURKA2kvDE+Tw==
dependencies:
- browserslist "^4.4.1"
- caniuse-lite "^1.0.30000932"
+ browserslist "^4.6.3"
+ caniuse-lite "^1.0.30000980"
+ chalk "^2.4.2"
normalize-range "^0.1.2"
num2fraction "^1.2.2"
- postcss "^7.0.14"
- postcss-value-parser "^3.3.1"
+ postcss "^7.0.17"
+ postcss-value-parser "^4.0.0"
autosize@^4.0.0:
version "4.0.0"
@@ -1921,14 +1922,13 @@ bonjour@^3.5.0:
multicast-dns "^6.0.1"
multicast-dns-service-types "^1.1.0"
-bootstrap-vue@^2.0.0-rc.24:
- version "2.0.0-rc.24"
- resolved "https://registry.yarnpkg.com/bootstrap-vue/-/bootstrap-vue-2.0.0-rc.24.tgz#8ea5bbcd19e0f9b4f87ed4d9ba72abaa35231f32"
- integrity sha512-8rA/I9tOvpNVIuMKD3rdlrUqgVdPEw4vPI0X8OeFJcG2hHvCHeZDF7FmWqxSeehIrUHGDV17HlTGSuP/v1Sp5g==
+bootstrap-vue@^2.0.0-rc.26:
+ version "2.0.0-rc.26"
+ resolved "https://registry.yarnpkg.com/bootstrap-vue/-/bootstrap-vue-2.0.0-rc.26.tgz#2b8b9116a452584ae20ba8310d143635bc9e1375"
+ integrity sha512-AzN+IRTmfR9rLFWNGt+v2XPmQjZiAlH4x5z2kStA3UoJ5LR91K34iZ8apaa6isDo+DfWDcwH4q7OwHM+VNxWwg==
dependencies:
"@nuxt/opencollective" "^0.2.2"
bootstrap "^4.3.1"
- core-js ">=2.6.5 <3.0.0"
popper.js "^1.15.0"
portal-vue "^2.1.5"
vue-functional-data-merge "^3.1.0"
@@ -1982,6 +1982,13 @@ braces@^2.3.0, braces@^2.3.1:
split-string "^3.0.2"
to-regex "^3.0.1"
+braces@^3.0.1:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
+ integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
+ dependencies:
+ fill-range "^7.0.1"
+
brorand@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
@@ -2057,14 +2064,14 @@ browserify-zlib@^0.2.0:
dependencies:
pako "~1.0.5"
-browserslist@^4.4.1, browserslist@^4.5.2, browserslist@^4.5.4:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.6.0.tgz#5274028c26f4d933d5b1323307c1d1da5084c9ff"
- integrity sha512-Jk0YFwXBuMOOol8n6FhgkDzn3mY9PYLYGk29zybF05SbRTsMgPqmTNeQQhOghCxq5oFqAXE3u4sYddr4C0uRhg==
+browserslist@^4.5.2, browserslist@^4.5.4, browserslist@^4.6.3:
+ version "4.6.6"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.6.6.tgz#6e4bf467cde520bc9dbdf3747dafa03531cec453"
+ integrity sha512-D2Nk3W9JL9Fp/gIcWei8LrERCS+eXu9AM5cfXA8WEZ84lFks+ARnZ0q/R69m2SV3Wjma83QDDPxsNKXUwdIsyA==
dependencies:
- caniuse-lite "^1.0.30000967"
- electron-to-chromium "^1.3.133"
- node-releases "^1.1.19"
+ caniuse-lite "^1.0.30000984"
+ electron-to-chromium "^1.3.191"
+ node-releases "^1.1.25"
bs-logger@0.x:
version "0.2.6"
@@ -2264,10 +2271,10 @@ camelcase@^5.0.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
-caniuse-lite@^1.0.30000932, caniuse-lite@^1.0.30000967:
- version "1.0.30000969"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000969.tgz#7664f571f2072657bde70b00a1fc1ba41f1942a9"
- integrity sha512-Kus0yxkoAJgVc0bax7S4gLSlFifCa7MnSZL9p9VuS/HIKEL4seaqh28KIQAAO50cD/rJ5CiJkJFapkdDAlhFxQ==
+caniuse-lite@^1.0.30000980, caniuse-lite@^1.0.30000984:
+ version "1.0.30000985"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000985.tgz#0eb40f6c8a8c219155cbe43c4975c0efb4a0f77f"
+ integrity sha512-1ngiwkgqAYPG0JSSUp3PUDGPKKY59EK7NrGGX+VOxaKCNzRbNc7uXMny+c3VJfZxtoK3wSImTvG9T9sXiTw2+w==
capture-exit@^2.0.0:
version "2.0.0"
@@ -2512,13 +2519,12 @@ cliui@^4.0.0:
strip-ansi "^4.0.0"
wrap-ansi "^2.0.0"
-clone-regexp@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/clone-regexp/-/clone-regexp-1.0.1.tgz#051805cd33173375d82118fc0918606da39fd60f"
- integrity sha512-Fcij9IwRW27XedRIJnSOEupS7RVcXtObJXbcUOX93UCLqqOdRpkvzKywOOSizmEK/Is3S/RHX9dLdfo6R1Q1mw==
+clone-regexp@^2.1.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/clone-regexp/-/clone-regexp-2.2.0.tgz#7d65e00885cd8796405c35a737e7a86b7429e36f"
+ integrity sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==
dependencies:
- is-regexp "^1.0.0"
- is-supported-regexp-flag "^1.0.0"
+ is-regexp "^2.0.0"
clone-response@1.0.2:
version "1.0.2"
@@ -2827,7 +2833,7 @@ core-js@3.0.1:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.0.1.tgz#1343182634298f7f38622f95e73f54e48ddf4738"
integrity sha512-sco40rF+2KlE0ROMvydjkrVMMG1vYilP2ALoRXcYR4obqbYIuV3Bg+51GEDW+HF8n7NRA+iaA4qD0nD9lo9mew==
-"core-js@>=2.6.5 <3.0.0", core-js@^2.2.0, core-js@^2.6.9:
+core-js@^2.2.0, core-js@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2"
integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==
@@ -2847,14 +2853,14 @@ core-util-is@1.0.2, core-util-is@~1.0.0:
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
-cosmiconfig@^5.0.0:
- version "5.0.7"
- resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.7.tgz#39826b292ee0d78eda137dfa3173bd1c21a43b04"
- integrity sha512-PcLqxTKiDmNT6pSpy4N6KtuPwb53W+2tzNvwOZw0WH9N6O0vLIBq0x8aj8Oj75ere4YcGi48bDFCL+3fRJdlNA==
+cosmiconfig@^5.2.0:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a"
+ integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==
dependencies:
import-fresh "^2.0.0"
is-directory "^0.3.1"
- js-yaml "^3.9.0"
+ js-yaml "^3.13.1"
parse-json "^4.0.0"
create-ecdh@^4.0.0:
@@ -3013,6 +3019,11 @@ cssesc@^2.0.0:
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703"
integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==
+cssesc@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
+ integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
+
cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
version "0.3.4"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.4.tgz#8cd52e8a3acfd68d3aed38ee0a640177d2f9d797"
@@ -3432,7 +3443,7 @@ debug@^3.1.0, debug@^3.2.5, debug@^3.2.6:
dependencies:
ms "^2.1.1"
-debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
+debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
@@ -3642,7 +3653,7 @@ diffie-hellman@^5.0.0:
miller-rabin "^4.0.0"
randombytes "^2.0.0"
-dir-glob@^2.2.1:
+dir-glob@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4"
integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==
@@ -3825,10 +3836,10 @@ ejs@^2.6.1:
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0"
integrity sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==
-electron-to-chromium@^1.3.133:
- version "1.3.135"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.135.tgz#f5799b95f2bcd8de17cde47d63392d83a4477041"
- integrity sha512-xXLNstRdVsisPF3pL3H9TVZo2XkMILfqtD6RiWIUmDK2sFX1Bjwqmd8LBp0Kuo2FgKO63JXPoEVGm8WyYdwP0Q==
+electron-to-chromium@^1.3.191:
+ version "1.3.199"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.199.tgz#f9a62a74cda77854310a2abffde8b75591ea09a1"
+ integrity sha512-gachlDdHSK47s0N2e58GH9HMC6Z4ip0SfmYUa5iEbE50AKaOUXysaJnXMfKj0xB245jWbYcyFSH+th3rqsF8hA==
elliptic@^6.0.0:
version "6.4.0"
@@ -3848,6 +3859,11 @@ emoji-regex@^7.0.1, emoji-regex@^7.0.3:
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
+emoji-regex@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+ integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
emoji-unicode-version@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/emoji-unicode-version/-/emoji-unicode-version-0.2.1.tgz#0ebf3666b5414097971d34994e299fce75cdbafc"
@@ -4314,12 +4330,12 @@ execa@^1.0.0:
signal-exit "^3.0.0"
strip-eof "^1.0.0"
-execall@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/execall/-/execall-1.0.0.tgz#73d0904e395b3cab0658b08d09ec25307f29bb73"
- integrity sha1-c9CQTjlbPKsGWLCNCewlMH8pu3M=
+execall@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/execall/-/execall-2.0.0.tgz#16a06b5fe5099df7d00be5d9c06eecded1663b45"
+ integrity sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==
dependencies:
- clone-regexp "^1.0.0"
+ clone-regexp "^2.1.0"
exit@^0.1.2:
version "0.1.2"
@@ -4559,10 +4575,10 @@ file-entry-cache@^2.0.0:
flat-cache "^1.2.1"
object-assign "^4.0.1"
-file-entry-cache@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-4.0.0.tgz#633567d15364aefe0b299e1e217735e8f3a9f6e8"
- integrity sha512-AVSwsnbV8vH/UVbvgEhf3saVQXORNv0ZzSkvkhQIaia5Tia+JhGTaa/ePUSVoPHQyGayQNmYfkzFi3WZV5zcpA==
+file-entry-cache@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c"
+ integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==
dependencies:
flat-cache "^2.0.1"
@@ -4597,6 +4613,13 @@ fill-range@^4.0.0:
repeat-string "^1.6.1"
to-regex-range "^2.1.0"
+fill-range@^7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
+ integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+ dependencies:
+ to-regex-range "^5.0.1"
+
finalhandler@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5"
@@ -4860,6 +4883,11 @@ get-stdin@^6.0.0:
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==
+get-stdin@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-7.0.0.tgz#8d5de98f15171a125c5e516643c7a6d0ea8a96f6"
+ integrity sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==
+
get-stream@3.0.0, get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
@@ -4919,6 +4947,11 @@ glob-to-regexp@^0.3.0:
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
+glob-to-regexp@^0.4.0:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
+ integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
+
"glob@5 - 7", glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1:
version "7.1.4"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
@@ -5007,13 +5040,14 @@ globby@^6.1.0:
pify "^2.0.0"
pinkie-promise "^2.0.0"
-globby@^9.0.0:
- version "9.0.0"
- resolved "https://registry.yarnpkg.com/globby/-/globby-9.0.0.tgz#3800df736dc711266df39b4ce33fe0d481f94c23"
- integrity sha512-q0qiO/p1w/yJ0hk8V9x1UXlgsXUxlGd0AHUOXZVXBO6aznDtpx7M8D1kBrCAItoPm+4l8r6ATXV1JpjY2SBQOw==
+globby@^9.2.0:
+ version "9.2.0"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d"
+ integrity sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==
dependencies:
+ "@types/glob" "^7.1.1"
array-union "^1.0.2"
- dir-glob "^2.2.1"
+ dir-glob "^2.2.2"
fast-glob "^2.2.6"
glob "^7.1.3"
ignore "^4.0.3"
@@ -5337,10 +5371,10 @@ html-minifier@^4.0.0:
relateurl "^0.2.7"
uglify-js "^3.5.1"
-html-tags@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b"
- integrity sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=
+html-tags@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.0.0.tgz#41f57708c9e6b7b46a00a22317d614c4a2bab166"
+ integrity sha512-xiXEBjihaNI+VZ2mKEoI5ZPxqUsevTKM+aeeJ/W4KAg2deGE35minmCJMn51BvwJZmiHaeAxrb2LAS0yZJxuuA==
htmlparser2@^3.10.0, htmlparser2@^3.9.0:
version "3.10.0"
@@ -5457,10 +5491,10 @@ ignore@^4.0.3, ignore@^4.0.6:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
-ignore@^5.0.4:
- version "5.0.5"
- resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.0.5.tgz#c663c548d6ce186fb33616a8ccb5d46e56bdbbf9"
- integrity sha512-kOC8IUb8HSDMVcYrDVezCxpJkzSQWTAzf3olpKM6o9rM5zpojx23O0Fl8Wr4+qJ6ZbPEHqf1fdwev/DS7v7pmA==
+ignore@^5.0.6:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.2.tgz#e28e584d43ad7e92f96995019cc43b9e1ac49558"
+ integrity sha512-vdqWBp7MyzdmHkkRWV5nY+PfGRbYbahfuvsBCh277tq+w9zyNi7h5CYJCK0kmzti9kU+O/cB7sE8HvKv6aXAKQ==
immediate@~3.0.5:
version "3.0.6"
@@ -5485,10 +5519,10 @@ import-lazy@^2.1.0:
resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=
-import-lazy@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-3.1.0.tgz#891279202c8a2280fdbd6674dbd8da1a1dfc67cc"
- integrity sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ==
+import-lazy@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153"
+ integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==
import-local@^2.0.0:
version "2.0.0"
@@ -5789,6 +5823,11 @@ is-fullwidth-code-point@^2.0.0:
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
+is-fullwidth-code-point@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+ integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
is-generator-fn@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
@@ -5838,6 +5877,11 @@ is-number@^3.0.0:
dependencies:
kind-of "^3.0.2"
+is-number@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+ integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
is-obj@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
@@ -5901,6 +5945,11 @@ is-regexp@^1.0.0:
resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069"
integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk=
+is-regexp@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-2.1.0.tgz#cd734a56864e23b956bf4e7c66c396a4c0b22c2d"
+ integrity sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==
+
is-resolvable@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
@@ -5916,11 +5965,6 @@ is-stream@^1.0.0, is-stream@^1.1.0:
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
-is-supported-regexp-flag@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.1.tgz#21ee16518d2c1dd3edd3e9a0d57e50207ac364ca"
- integrity sha512-3vcJecUUrpgCqc/ca0aWeNu64UGgxcvO60K/Fkr1N6RSvfGCTU60UKN68JDmKokgba0rFFJs12EnzOQa14ubKQ==
-
is-symbol@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38"
@@ -6535,7 +6579,7 @@ js-tokens@^3.0.2:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
-js-yaml@^3.12.0, js-yaml@^3.9.0:
+js-yaml@^3.12.0, js-yaml@^3.13.1:
version "3.13.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
@@ -6837,10 +6881,10 @@ kleur@^3.0.2:
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
-known-css-properties@^0.11.0:
- version "0.11.0"
- resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.11.0.tgz#0da784f115ea77c76b81536d7052e90ee6c86a8a"
- integrity sha512-bEZlJzXo5V/ApNNa5z375mJC6Nrz4vG43UgcSCrg2OHC+yuB6j0iDSrY7RQ/+PRofFB03wNIIt9iXIVLr4wc7w==
+known-css-properties@^0.14.0:
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.14.0.tgz#d7032b4334a32dc22e6e46b081ec789daf18756c"
+ integrity sha512-P+0a/gBzLgVlCnK8I7VcD0yuYJscmWn66wH9tlKsQnmVdg689tLEmziwB9PuazZYLkcm07fvWOKCJJqI55sD5Q==
latest-version@^3.0.0:
version "3.1.0"
@@ -6873,6 +6917,11 @@ leven@^2.1.0:
resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580"
integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA=
+leven@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
+ integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==
+
levn@^0.3.0, levn@~0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
@@ -7011,18 +7060,25 @@ lodash.upperfirst@4.3.1:
resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce"
integrity sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984=
-lodash@^4.0.0, lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.5.0, lodash@~4.17.10:
- version "4.17.14"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba"
- integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==
+lodash@^4.0.0, lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.5.0, lodash@~4.17.10:
+ version "4.17.15"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
+ integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
-log-symbols@^2.0.0, log-symbols@^2.1.0, log-symbols@^2.2.0:
+log-symbols@^2.1.0, log-symbols@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==
dependencies:
chalk "^2.0.1"
+log-symbols@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4"
+ integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==
+ dependencies:
+ chalk "^2.4.2"
+
log4js@^3.0.0:
version "3.0.5"
resolved "https://registry.yarnpkg.com/log4js/-/log4js-3.0.5.tgz#b80146bfebad68b430d4f3569556d8a6edfef303"
@@ -7184,10 +7240,10 @@ marked@^0.3.12, marked@~0.3.6:
resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.19.tgz#5d47f709c4c9fc3c216b6d46127280f40b39d790"
integrity sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==
-mathml-tag-names@^2.0.1:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.0.tgz#490b70e062ee24636536e3d9481e333733d00f2c"
- integrity sha512-3Zs9P/0zzwTob2pdgT0CHZuMbnSUSp8MB1bddfm+HDmnFWHGT4jvEZRf+2RuPoa+cjdn/z25SEt5gFTqdhvJAg==
+mathml-tag-names@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.1.tgz#6dff66c99d55ecf739ca53c492e626f1d12a33cc"
+ integrity sha512-pWB896KPGSGkp1XtyzRBftpTzwSOL0Gfk0wLvxt4f2mgzjY19o0LxJ3U25vNWTzsh7da+KTbuXQoQ3lOJZ8WHw==
md5.js@^1.3.4:
version "1.3.4"
@@ -7346,6 +7402,14 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.6, mic
snapdragon "^0.8.1"
to-regex "^3.0.2"
+micromatch@^4.0.0:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259"
+ integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==
+ dependencies:
+ braces "^3.0.1"
+ picomatch "^2.0.5"
+
miller-rabin@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
@@ -7714,10 +7778,10 @@ node-pre-gyp@^0.12.0:
semver "^5.3.0"
tar "^4"
-node-releases@^1.1.19:
- version "1.1.19"
- resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.19.tgz#c492d1e381fea0350b338b646c27867e88e91b3d"
- integrity sha512-SH/B4WwovHbulIALsQllAVwqZZD1kPmKCqrhGfR29dXjLAVZMHvBjD3S6nL9D/J9QkmZ1R92/0wCMDKXUUvyyA==
+node-releases@^1.1.25:
+ version "1.1.25"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.25.tgz#0c2d7dbc7fed30fbe02a9ee3007b8c90bf0133d3"
+ integrity sha512-fI5BXuk83lKEoZDdH3gRhtsNgh05/wZacuXkgbiYkceE7+QIMXOg98n9ZV7mz27B+kFHnqHcUpscZZlGRSmTpQ==
dependencies:
semver "^5.3.0"
@@ -8363,6 +8427,11 @@ performance-now@^2.1.0:
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
+picomatch@^2.0.5:
+ version "2.0.7"
+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6"
+ integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==
+
pify@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@@ -8373,7 +8442,7 @@ pify@^3.0.0:
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
-pify@^4.0.0, pify@^4.0.1:
+pify@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
@@ -8476,17 +8545,17 @@ postcss-html@^0.36.0:
dependencies:
htmlparser2 "^3.10.0"
-postcss-jsx@^0.36.0:
- version "0.36.0"
- resolved "https://registry.yarnpkg.com/postcss-jsx/-/postcss-jsx-0.36.0.tgz#b7685ed3d070a175ef0aa48f83d9015bd772c82d"
- integrity sha512-/lWOSXSX5jlITCKFkuYU2WLFdrncZmjSVyNpHAunEgirZXLwI8RjU556e3Uz4mv0WVHnJA9d3JWb36lK9Yx99g==
+postcss-jsx@^0.36.1:
+ version "0.36.2"
+ resolved "https://registry.yarnpkg.com/postcss-jsx/-/postcss-jsx-0.36.2.tgz#34bcd6752426a60b8df73f069e7595383060a794"
+ integrity sha512-7B8a8a6YDOQFqMQ9UmOo+IPPb2i14Z57Fvc9cOI11B3bWMSY2O6r2XJ3tlcQhUhv93TeN0ntxehTaSWJDQavlg==
dependencies:
- "@babel/core" ">=7.1.0"
+ "@babel/core" ">=7.2.2"
-postcss-less@^3.1.0:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/postcss-less/-/postcss-less-3.1.2.tgz#fb67e7ba351dbdf69de3c52eebd1184c52bfaea6"
- integrity sha512-66ZBVo1JGkQ7r13M97xcHcyarWpgg21RaqIZWZXHE3XOtb5+ywK1uZWeY1DYkYRkIX/l8Hvxnx9iSKB68nFr+w==
+postcss-less@^3.1.4:
+ version "3.1.4"
+ resolved "https://registry.yarnpkg.com/postcss-less/-/postcss-less-3.1.4.tgz#369f58642b5928ef898ffbc1a6e93c958304c5ad"
+ integrity sha512-7TvleQWNM2QLcHqvudt3VYjULVB49uiW6XzEUFmvwHzvsOEF5MwBrIXZDJQvJNFGjJQTzSzZnDoCJ8h/ljyGXA==
dependencies:
postcss "^7.0.14"
@@ -8534,7 +8603,7 @@ postcss-modules-values@^1.3.0:
icss-replace-symbols "^1.1.0"
postcss "^6.0.1"
-postcss-reporter@^6.0.0:
+postcss-reporter@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/postcss-reporter/-/postcss-reporter-6.0.1.tgz#7c055120060a97c8837b4e48215661aafb74245f"
integrity sha512-LpmQjfRWyabc+fRygxZjpRxfhRf9u/fdlKf4VHG4TSPbV2XNsuISzYW1KL+1aQzx53CAppa1bKG4APIB/DOXXw==
@@ -8549,7 +8618,7 @@ postcss-resolve-nested-selector@^0.1.1:
resolved "https://registry.yarnpkg.com/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz#29ccbc7c37dedfac304e9fff0bf1596b3f6a0e4e"
integrity sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=
-postcss-safe-parser@^4.0.0:
+postcss-safe-parser@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-4.0.1.tgz#8756d9e4c36fdce2c72b091bbc8ca176ab1fcdea"
integrity sha512-xZsFA3uX8MO3yAda03QrG3/Eg1LN3EPfjjf07vke/46HERLZyHrTsQ9E1r1w1W//fWEhtYNndo2hQplN2cVpCQ==
@@ -8589,6 +8658,15 @@ postcss-selector-parser@^5.0.0:
indexes-of "^1.0.1"
uniq "^1.0.1"
+postcss-selector-parser@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c"
+ integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==
+ dependencies:
+ cssesc "^3.0.0"
+ indexes-of "^1.0.1"
+ uniq "^1.0.1"
+
postcss-syntax@^0.36.2:
version "0.36.2"
resolved "https://registry.yarnpkg.com/postcss-syntax/-/postcss-syntax-0.36.2.tgz#f08578c7d95834574e5593a82dfbfa8afae3b51c"
@@ -8599,6 +8677,11 @@ postcss-value-parser@^3.3.0, postcss-value-parser@^3.3.1:
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
+postcss-value-parser@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.0.tgz#99a983d365f7b2ad8d0f9b8c3094926eab4b936d"
+ integrity sha512-ESPktioptiSUchCKgggAkzdmkgzKfmp0EU8jXH+5kbIUB+unr0Y4CY9SRMvibuvYUBjNh1ACLbxqYNpdTQOteQ==
+
postcss@^6.0.1, postcss@^6.0.14, postcss@^6.0.23:
version "6.0.23"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
@@ -8608,10 +8691,10 @@ postcss@^6.0.1, postcss@^6.0.14, postcss@^6.0.23:
source-map "^0.6.1"
supports-color "^5.4.0"
-postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.13, postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.7:
- version "7.0.14"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.14.tgz#4527ed6b1ca0d82c53ce5ec1a2041c2346bbd6e5"
- integrity sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==
+postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.7:
+ version "7.0.17"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.17.tgz#4da1bdff5322d4a0acaab4d87f3e782436bad31f"
+ integrity sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ==
dependencies:
chalk "^2.4.2"
source-map "^0.6.1"
@@ -9099,6 +9182,14 @@ readable-stream@~2.0.6:
string_decoder "~0.10.x"
util-deprecate "~1.0.1"
+readdir-enhanced@^2.2.4:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/readdir-enhanced/-/readdir-enhanced-2.2.4.tgz#773fb8a8de5f645fb13d9403746d490d4facb3e6"
+ integrity sha512-JQD83C9gAs5B5j2j40qLn/K83HhR8po3bUonebNeuJQUZbbn7q1HxL9kQuPBtxoXkaUpbtEmpFBw5kzyYnnJDA==
+ dependencies:
+ call-me-maybe "^1.0.1"
+ glob-to-regexp "^0.4.0"
+
readdirp@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78"
@@ -9422,10 +9513,10 @@ resolve-from@^3.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
integrity sha1-six699nWiBvItuZTM17rywoYh0g=
-resolve-from@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
- integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+resolve-from@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
+ integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
resolve-url@^0.2.1:
version "0.2.1"
@@ -9808,6 +9899,11 @@ slash@^2.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
+slash@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
+ integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
+
slice-ansi@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636"
@@ -10208,6 +10304,15 @@ string-width@^3.0.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^5.0.0"
+string-width@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.1.0.tgz#ba846d1daa97c3c596155308063e075ed1c99aff"
+ integrity sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^5.2.0"
+
string_decoder@^1.0.0, string_decoder@^1.1.1, string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
@@ -10244,7 +10349,7 @@ strip-ansi@^4.0.0:
dependencies:
ansi-regex "^3.0.0"
-strip-ansi@^5.0.0:
+strip-ansi@^5.0.0, strip-ansi@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
@@ -10305,79 +10410,75 @@ style-search@^0.1.0:
resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902"
integrity sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=
-stylelint-config-recommended@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-2.1.0.tgz#f526d5c771c6811186d9eaedbed02195fee30858"
- integrity sha512-ajMbivOD7JxdsnlS5945KYhvt7L/HwN6YeYF2BH6kE4UCLJR0YvXMf+2j7nQpJyYLZx9uZzU5G1ZOSBiWAc6yA==
-
-stylelint-error-string-formatter@1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/stylelint-error-string-formatter/-/stylelint-error-string-formatter-1.0.2.tgz#3076c6703d3e0170daeb55fe85030d63c834745e"
- integrity sha512-xN69xRB0eTgYcGKVHWIiH2L+Xx8fRPliTiLjaS4YlbfBqkdTuZh2wjtfvNCkCzBTNeINWa5GpSa9RFYXdtwV6w==
+stylelint-config-recommended@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-2.2.0.tgz#46ab139db4a0e7151fd5f94af155512886c96d3f"
+ integrity sha512-bZ+d4RiNEfmoR74KZtCKmsABdBJr4iXRiCso+6LtMJPw5rd/KnxUWTxht7TbafrTJK1YRjNgnN0iVZaJfc3xJA==
-stylelint-scss@^3.5.4:
- version "3.5.4"
- resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-3.5.4.tgz#ff3ee989ac48f5c4f57313523b5ace059ffd6cc2"
- integrity sha512-hEdEOfFXVqxWcUbenBONW/cAw5cJcEDasY8tGwKNAAn1GDHoZO1ATdWpr+iIk325mPGIQqVb1sUxsRxuL70trw==
+stylelint-scss@^3.9.2:
+ version "3.9.2"
+ resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-3.9.2.tgz#5435174a57696ee52eae40146778a4e62f7ed3a3"
+ integrity sha512-VUh173p3T1qJf016P7yeJ6nxkUpqF5qQ+VSDw3J8P6wEJbA1loaNgBHR3k3skHvUkF+9brLO1ibCHA00pjW3cw==
dependencies:
lodash "^4.17.11"
postcss-media-query-parser "^0.2.3"
postcss-resolve-nested-selector "^0.1.1"
- postcss-selector-parser "^5.0.0"
- postcss-value-parser "^3.3.1"
+ postcss-selector-parser "^6.0.2"
+ postcss-value-parser "^4.0.0"
-stylelint@^9.10.1:
- version "9.10.1"
- resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-9.10.1.tgz#5f0ee3701461dff1d68284e1386efe8f0677a75d"
- integrity sha512-9UiHxZhOAHEgeQ7oLGwrwoDR8vclBKlSX7r4fH0iuu0SfPwFaLkb1c7Q2j1cqg9P7IDXeAV2TvQML/fRQzGBBQ==
+stylelint@^10.1.0:
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-10.1.0.tgz#1bc4c4ce878107e7c396b19226d91ba28268911a"
+ integrity sha512-OmlUXrgzEMLQYj1JPTpyZPR9G4bl0StidfHnGJEMpdiQ0JyTq0MPg1xkHk1/xVJ2rTPESyJCDWjG8Kbpoo7Kuw==
dependencies:
- autoprefixer "^9.0.0"
+ autoprefixer "^9.5.1"
balanced-match "^1.0.0"
- chalk "^2.4.1"
- cosmiconfig "^5.0.0"
- debug "^4.0.0"
- execall "^1.0.0"
- file-entry-cache "^4.0.0"
- get-stdin "^6.0.0"
+ chalk "^2.4.2"
+ cosmiconfig "^5.2.0"
+ debug "^4.1.1"
+ execall "^2.0.0"
+ file-entry-cache "^5.0.1"
+ get-stdin "^7.0.0"
global-modules "^2.0.0"
- globby "^9.0.0"
+ globby "^9.2.0"
globjoin "^0.1.4"
- html-tags "^2.0.0"
- ignore "^5.0.4"
- import-lazy "^3.1.0"
+ html-tags "^3.0.0"
+ ignore "^5.0.6"
+ import-lazy "^4.0.0"
imurmurhash "^0.1.4"
- known-css-properties "^0.11.0"
- leven "^2.1.0"
- lodash "^4.17.4"
- log-symbols "^2.0.0"
- mathml-tag-names "^2.0.1"
+ known-css-properties "^0.14.0"
+ leven "^3.1.0"
+ lodash "^4.17.11"
+ log-symbols "^3.0.0"
+ mathml-tag-names "^2.1.0"
meow "^5.0.0"
- micromatch "^3.1.10"
+ micromatch "^4.0.0"
normalize-selector "^0.2.0"
- pify "^4.0.0"
- postcss "^7.0.13"
+ pify "^4.0.1"
+ postcss "^7.0.14"
postcss-html "^0.36.0"
- postcss-jsx "^0.36.0"
- postcss-less "^3.1.0"
+ postcss-jsx "^0.36.1"
+ postcss-less "^3.1.4"
postcss-markdown "^0.36.0"
postcss-media-query-parser "^0.2.3"
- postcss-reporter "^6.0.0"
+ postcss-reporter "^6.0.1"
postcss-resolve-nested-selector "^0.1.1"
- postcss-safe-parser "^4.0.0"
+ postcss-safe-parser "^4.0.1"
postcss-sass "^0.3.5"
postcss-scss "^2.0.0"
postcss-selector-parser "^3.1.0"
postcss-syntax "^0.36.2"
- postcss-value-parser "^3.3.0"
- resolve-from "^4.0.0"
+ postcss-value-parser "^3.3.1"
+ resolve-from "^5.0.0"
signal-exit "^3.0.2"
- slash "^2.0.0"
+ slash "^3.0.0"
specificity "^0.4.1"
- string-width "^3.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^5.2.0"
style-search "^0.1.0"
sugarss "^2.0.0"
svg-tags "^1.0.0"
- table "^5.0.0"
+ table "^5.2.3"
sugarss@^2.0.0:
version "2.0.0"
@@ -10425,13 +10526,13 @@ symbol-tree@^3.2.2:
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
integrity sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=
-table@^5.0.0, table@^5.0.2:
- version "5.2.3"
- resolved "https://registry.yarnpkg.com/table/-/table-5.2.3.tgz#cde0cc6eb06751c009efab27e8c820ca5b67b7f2"
- integrity sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ==
+table@^5.0.2, table@^5.2.3:
+ version "5.4.4"
+ resolved "https://registry.yarnpkg.com/table/-/table-5.4.4.tgz#6e0f88fdae3692793d1077fd172a4667afe986a6"
+ integrity sha512-IIfEAUx5QlODLblLrGTTLJA7Tk0iLSGBvgY8essPRVNGHAzThujww1YqHLs6h3HfTg55h++RzLHH5Xw/rfv+mg==
dependencies:
- ajv "^6.9.1"
- lodash "^4.17.11"
+ ajv "^6.10.2"
+ lodash "^4.17.14"
slice-ansi "^2.1.0"
string-width "^3.0.0"
@@ -10691,6 +10792,13 @@ to-regex-range@^2.1.0:
is-number "^3.0.0"
repeat-string "^1.6.1"
+to-regex-range@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+ integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+ dependencies:
+ is-number "^7.0.0"
+
to-regex@^3.0.1, to-regex@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
@@ -11377,12 +11485,13 @@ webidl-conversions@^4.0.2:
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
-webpack-bundle-analyzer@^3.0.3:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.0.3.tgz#dbc7fff8f52058b6714a20fddf309d0790e3e0a0"
- integrity sha512-naLWiRfmtH4UJgtUktRTLw6FdoZJ2RvCR9ePbwM9aRMsS/KjFerkPZG9epEvXRAw5d5oPdrs9+3p+afNjxW8Xw==
+webpack-bundle-analyzer@^3.3.2:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.3.2.tgz#3da733a900f515914e729fcebcd4c40dde71fc6f"
+ integrity sha512-7qvJLPKB4rRWZGjVp5U1KEjwutbDHSKboAl0IfafnrdXMrgC0tOtZbQD6Rw0u4cmpgRN4O02Fc0t8eAT+FgGzA==
dependencies:
- acorn "^5.7.3"
+ acorn "^6.0.7"
+ acorn-walk "^6.1.1"
bfj "^6.1.1"
chalk "^2.4.1"
commander "^2.18.0"