summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml4
-rw-r--r--.gitlab/issue_templates/Doc Review.md20
-rw-r--r--.gitlab/issue_templates/Documentation.md75
-rw-r--r--.gitlab/issue_templates/Feature proposal.md13
-rw-r--r--.gitlab/merge_request_templates/Change documentation location.md2
-rw-r--r--.gitlab/merge_request_templates/Database changes.md6
-rw-r--r--.gitlab/merge_request_templates/Documentation.md40
-rw-r--r--CHANGELOG.md247
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--Gemfile6
-rw-r--r--Gemfile.lock18
-rw-r--r--PROCESS.md51
-rw-r--r--VERSION2
-rw-r--r--app/assets/javascripts/badges/components/badge_form.vue2
-rw-r--r--app/assets/javascripts/diffs/components/app.vue35
-rw-r--r--app/assets/javascripts/diffs/components/diff_content.vue30
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue66
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue11
-rw-r--r--app/assets/javascripts/diffs/components/tree_list.vue12
-rw-r--r--app/assets/javascripts/diffs/constants.js6
-rw-r--r--app/assets/javascripts/diffs/store/actions.js13
-rw-r--r--app/assets/javascripts/diffs/store/getters.js3
-rw-r--r--app/assets/javascripts/diffs/store/utils.js9
-rw-r--r--app/assets/javascripts/emoji/no_emoji_validator.js63
-rw-r--r--app/assets/javascripts/error_tracking/store/actions.js14
-rw-r--r--app/assets/javascripts/ide/constants.js16
-rw-r--r--app/assets/javascripts/import_projects/components/import_projects_table.vue101
-rw-r--r--app/assets/javascripts/import_projects/components/import_status.vue47
-rw-r--r--app/assets/javascripts/import_projects/components/imported_project_table_row.vue55
-rw-r--r--app/assets/javascripts/import_projects/components/provider_repo_table_row.vue110
-rw-r--r--app/assets/javascripts/import_projects/constants.js48
-rw-r--r--app/assets/javascripts/import_projects/event_hub.js3
-rw-r--r--app/assets/javascripts/import_projects/index.js47
-rw-r--r--app/assets/javascripts/import_projects/store/actions.js106
-rw-r--r--app/assets/javascripts/import_projects/store/getters.js20
-rw-r--r--app/assets/javascripts/import_projects/store/index.js15
-rw-r--r--app/assets/javascripts/import_projects/store/mutation_types.js11
-rw-r--r--app/assets/javascripts/import_projects/store/mutations.js55
-rw-r--r--app/assets/javascripts/import_projects/store/state.js15
-rw-r--r--app/assets/javascripts/jobs/components/sidebar.vue2
-rw-r--r--app/assets/javascripts/main.js3
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue18
-rw-r--r--app/assets/javascripts/notes/components/note_actions.vue7
-rw-r--r--app/assets/javascripts/notes/components/note_actions/reply_button.vue12
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue68
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue19
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue20
-rw-r--r--app/assets/javascripts/notes/stores/actions.js3
-rw-r--r--app/assets/javascripts/notes/stores/mutation_types.js1
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js7
-rw-r--r--app/assets/javascripts/pages/import/gitea/status/index.js7
-rw-r--r--app/assets/javascripts/pages/import/github/status/index.js7
-rw-r--r--app/assets/javascripts/pages/sessions/new/index.js2
-rw-r--r--app/assets/javascripts/pages/users/user_tabs.js2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_actions.vue16
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_artifacts.vue23
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/ci_icon.vue7
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue16
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/viewers/no_preview.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/viewers/not_diffable.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/panel_resizer.vue7
-rw-r--r--app/assets/javascripts/vue_shared/components/select2_select.vue35
-rw-r--r--app/assets/stylesheets/framework/awards.scss16
-rw-r--r--app/assets/stylesheets/framework/common.scss21
-rw-r--r--app/assets/stylesheets/framework/modal.scss7
-rw-r--r--app/assets/stylesheets/framework/variables.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/ide.scss19
-rw-r--r--app/assets/stylesheets/pages/builds.scss6
-rw-r--r--app/assets/stylesheets/pages/diff.scss33
-rw-r--r--app/assets/stylesheets/pages/import.scss51
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss5
-rw-r--r--app/assets/stylesheets/pages/notes.scss15
-rw-r--r--app/controllers/admin/users_controller.rb10
-rw-r--r--app/controllers/dashboard/milestones_controller.rb4
-rw-r--r--app/controllers/import/gitea_controller.rb20
-rw-r--r--app/controllers/import/github_controller.rb83
-rw-r--r--app/controllers/profiles/preferences_controller.rb10
-rw-r--r--app/controllers/profiles_controller.rb1
-rw-r--r--app/controllers/projects/merge_requests/diffs_controller.rb1
-rw-r--r--app/controllers/projects/tree_controller.rb14
-rw-r--r--app/finders/concerns/finder_methods.rb4
-rw-r--r--app/finders/issuable_finder.rb8
-rw-r--r--app/graphql/resolvers/issues_resolver.rb25
-rw-r--r--app/graphql/types/issuable_state_enum.rb12
-rw-r--r--app/graphql/types/issue_state_enum.rb8
-rw-r--r--app/graphql/types/issue_type.rb2
-rw-r--r--app/graphql/types/merge_request_state_enum.rb10
-rw-r--r--app/graphql/types/merge_request_type.rb3
-rw-r--r--app/helpers/import_helper.rb36
-rw-r--r--app/helpers/issuables_helper.rb2
-rw-r--r--app/helpers/labels_helper.rb4
-rw-r--r--app/helpers/namespaces_helper.rb6
-rw-r--r--app/helpers/preferences_helper.rb4
-rw-r--r--app/models/ci/pipeline.rb4
-rw-r--r--app/models/ci/pipeline_chat_data.rb13
-rw-r--r--app/models/ci/pipeline_enums.rb1
-rw-r--r--app/models/clusters/applications/prometheus.rb5
-rw-r--r--app/models/clusters/cluster.rb4
-rw-r--r--app/models/clusters/concerns/application_status.rb9
-rw-r--r--app/models/commit_collection.rb4
-rw-r--r--app/models/concerns/closed_at_filterable.rb14
-rw-r--r--app/models/concerns/issuable.rb1
-rw-r--r--app/models/concerns/reactive_caching.rb2
-rw-r--r--app/models/error_tracking/project_error_tracking_setting.rb6
-rw-r--r--app/models/merge_request.rb8
-rw-r--r--app/models/merge_request_diff.rb3
-rw-r--r--app/models/personal_access_token.rb5
-rw-r--r--app/models/project_services/slack_slash_commands_service.rb4
-rw-r--r--app/models/user.rb4
-rw-r--r--app/serializers/diff_file_base_entity.rb11
-rw-r--r--app/serializers/diff_file_entity.rb8
-rw-r--r--app/serializers/diff_viewer_entity.rb8
-rw-r--r--app/serializers/namespace_basic_entity.rb6
-rw-r--r--app/serializers/namespace_serializer.rb5
-rw-r--r--app/serializers/project_import_entity.rb13
-rw-r--r--app/serializers/project_serializer.rb12
-rw-r--r--app/serializers/provider_repo_entity.rb25
-rw-r--r--app/serializers/provider_repo_serializer.rb5
-rw-r--r--app/serializers/tree_entity.rb15
-rw-r--r--app/serializers/tree_root_entity.rb27
-rw-r--r--app/serializers/tree_serializer.rb5
-rw-r--r--app/services/ci/create_pipeline_service.rb1
-rw-r--r--app/services/concerns/users/participable_service.rb15
-rw-r--r--app/services/error_tracking/list_issues_service.rb10
-rw-r--r--app/services/prometheus/adapter_service.rb2
-rw-r--r--app/validators/url_validator.rb6
-rw-r--r--app/views/clusters/clusters/_form.html.haml4
-rw-r--r--app/views/dashboard/projects/_starred_empty_state.html.haml9
-rw-r--r--app/views/dashboard/projects/starred.html.haml9
-rw-r--r--app/views/devise/shared/_signup_box.html.haml6
-rw-r--r--app/views/import/_githubish_status.html.haml59
-rw-r--r--app/views/import/github/new.html.haml2
-rw-r--r--app/views/import/github/status.html.haml4
-rw-r--r--app/views/import/manifest/status.html.haml2
-rw-r--r--app/views/profiles/passwords/new.html.haml13
-rw-r--r--app/views/profiles/preferences/show.html.haml6
-rw-r--r--app/views/profiles/show.html.haml3
-rw-r--r--app/views/projects/branches/_branch.html.haml3
-rw-r--r--app/views/projects/commit/_ajax_signature.html.haml2
-rw-r--r--app/views/projects/commit/_ci_menu.html.haml4
-rw-r--r--app/views/projects/commit/_commit_box.html.haml17
-rw-r--r--app/views/projects/commit/_limit_exceeded_message.html.haml4
-rw-r--r--app/views/projects/commit/_other_user_signature_badge.html.haml4
-rw-r--r--app/views/projects/commit/_same_user_different_email_signature_badge.html.haml5
-rw-r--r--app/views/projects/commit/_signature_badge.html.haml4
-rw-r--r--app/views/projects/commit/_unverified_signature_badge.html.haml4
-rw-r--r--app/views/projects/commit/_verified_signature_badge.html.haml5
-rw-r--r--app/views/projects/commit/pipelines.html.haml2
-rw-r--r--app/views/projects/commit/show.html.haml4
-rw-r--r--app/views/projects/issues/_discussion.html.haml2
-rw-r--r--app/views/projects/issues/_import_export.svg1
-rw-r--r--app/views/projects/issues/_issue.html.haml2
-rw-r--r--app/views/projects/issues/import_csv/_modal.html.haml4
-rw-r--r--app/views/projects/issues/show.html.haml10
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml2
-rw-r--r--app/views/projects/pipelines/_info.html.haml2
-rw-r--r--app/views/projects/wikis/edit.html.haml3
-rw-r--r--app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml2
-rw-r--r--app/views/shared/empty_states/_priority_labels.html.haml2
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml2
-rw-r--r--app/workers/all_queues.yml1
-rw-r--r--app/workers/build_finished_worker.rb1
-rw-r--r--app/workers/chat_notification_worker.rb33
-rw-r--r--babel.config.js (renamed from .babelrc.js)2
-rw-r--r--changelogs/README.md10
-rw-r--r--changelogs/unreleased/19745-forms-with-task-lists-can-be-overwritten-when-editing-simultaneously.yml5
-rw-r--r--changelogs/unreleased/2105-add-setting-for-first-day-of-the-week.yml5
-rw-r--r--changelogs/unreleased/24680-support-bamboo-api-polymorphism.yml5
-rw-r--r--changelogs/unreleased/24875-label.yml5
-rw-r--r--changelogs/unreleased/25043-empty-states.yml5
-rw-r--r--changelogs/unreleased/25569-changing-wording-to-delete-when-referring-to-removing-a-branch.yml5
-rw-r--r--changelogs/unreleased/26375-markdown-footnotes-not-working.yml5
-rw-r--r--changelogs/unreleased/28500-empty-states-for-profile-page.yml5
-rw-r--r--changelogs/unreleased/30120-add-flat-square-badge-style.yml5
-rw-r--r--changelogs/unreleased/34555-empty-state-for-starred-projects.yml5
-rw-r--r--changelogs/unreleased/35638-move-language-setting-to-preferences.yml5
-rw-r--r--changelogs/unreleased/36445-better-indication-that-an-issue-has-been-moved-or-marked-as-duplicated.yml5
-rw-r--r--changelogs/unreleased/37990-task-list-bracket.yml5
-rw-r--r--changelogs/unreleased/39010-add-left-margin-to-1st-time-contributor-badge.yml5
-rw-r--r--changelogs/unreleased/40997-gitlab-pages-deploy-jobs-have-a-null-status.yml5
-rw-r--r--changelogs/unreleased/42769-remove-expansion-hover-animation-from-status-icon-buttons.yml5
-rw-r--r--changelogs/unreleased/43681-display-last-activity-and-created-at-datetimes-for-users-in-admin-users.yml5
-rw-r--r--changelogs/unreleased/44332-add-openid-profile-scopes.yml5
-rw-r--r--changelogs/unreleased/44698-recaptcha.yml5
-rw-r--r--changelogs/unreleased/45779-fix-default-visibility-level-for-projects.yml5
-rw-r--r--changelogs/unreleased/45791-number-of-repositories-usage-ping.yml5
-rw-r--r--changelogs/unreleased/46448-add-timestamps-for-each-stage-of-gitlab-rake-gitlab-backup-restore.yml5
-rw-r--r--changelogs/unreleased/47007-related-merge-requests-in-issue-design-restyle.yml5
-rw-r--r--changelogs/unreleased/47988-improve-milestone-queries-with-subqueries.yml5
-rw-r--r--changelogs/unreleased/50013-add-browser-platform-flags.yml5
-rw-r--r--changelogs/unreleased/50352-sort-save.yml5
-rw-r--r--changelogs/unreleased/50433-make-emoji-picker-bigger.yml5
-rw-r--r--changelogs/unreleased/50521-block-emojis-and-symbol-characters-from-user-s-full-names-2.yml5
-rw-r--r--changelogs/unreleased/51754-admin-view-private-personal-snippets.yml5
-rw-r--r--changelogs/unreleased/51759-filter-by-language.yml5
-rw-r--r--changelogs/unreleased/51913-api-getting-projects-for-users-with-dot-gets-404.yml5
-rw-r--r--changelogs/unreleased/52198-timer-is-vertically-misaligned-for-delayed-jobs-in-pipeline-actions.yml5
-rw-r--r--changelogs/unreleased/52275-fix-master-to-be-hyperlink.yml5
-rw-r--r--changelogs/unreleased/52278-squash-checkbox-fix.yml5
-rw-r--r--changelogs/unreleased/52347-lines-changed-statistics-is-not-easily-visible-in-mr-changes-view.yml5
-rw-r--r--changelogs/unreleased/52363-modifies-environment-scope-field-on-cluster-page.yml5
-rw-r--r--changelogs/unreleased/52363-ui-changes-to-cluster-and-ado-pages.yml5
-rw-r--r--changelogs/unreleased/52568-external-mr-diffs.yml5
-rw-r--r--changelogs/unreleased/52674-api-v4-projects-project_id-jobs-endpoint-hits-statement-timeout.yml5
-rw-r--r--changelogs/unreleased/52971-merge-request-file-browser-should-always-be-possible-show-hide.yml5
-rw-r--r--changelogs/unreleased/53104-redesign-group-overview-ui-mvc.yml5
-rw-r--r--changelogs/unreleased/53411-remove_personal_access_tokens_token.yml5
-rw-r--r--changelogs/unreleased/53431-fix-upcoming-milestone-filter-for-groups.yml5
-rw-r--r--changelogs/unreleased/53671-redirect-projects-id-to-project-page.yml5
-rw-r--r--changelogs/unreleased/53676-ip-address-of-gitlab-runner-is-wrong-in-the-runners-description.yml5
-rw-r--r--changelogs/unreleased/53714-inconsistent-text-color-for-labels.yml5
-rw-r--r--changelogs/unreleased/53856-changing-group-visibility-does-not-re-enable-save-button.yml6
-rw-r--r--changelogs/unreleased/53950-commit-comments-displayed-on-a-merge-request.yml5
-rw-r--r--changelogs/unreleased/54167-rename-project-tags-to-project-topics.yml5
-rw-r--r--changelogs/unreleased/54213-standardize-token-value-capitalization-in-filter-bar.yml5
-rw-r--r--changelogs/unreleased/54250-upstream-kubeclient-redirect-patch.yml5
-rw-r--r--changelogs/unreleased/54484-anchor-links-to-comments-or-system-notes-can-break-with-discussion-filters.yml5
-rw-r--r--changelogs/unreleased/54544-update-project-topics-styling-to-use-badges-design.yml5
-rw-r--r--changelogs/unreleased/54725-fix-emoji-button-active-state.yml5
-rw-r--r--changelogs/unreleased/54796-api-sort-tie-breaker-for-pagination.yml5
-rw-r--r--changelogs/unreleased/54905-milestone-search.yml5
-rw-r--r--changelogs/unreleased/55098-ui-bug-adding-group-members-with-lower-permissions.yml5
-rw-r--r--changelogs/unreleased/55109-jira-integration-api-doesn-t-respect-available-format.yml5
-rw-r--r--changelogs/unreleased/55111-gitlab-api-does-not-manage-default_branch_protection-3-value.yml5
-rw-r--r--changelogs/unreleased/55242-skeleton-loading-releases.yml5
-rw-r--r--changelogs/unreleased/55312-svg.yml5
-rw-r--r--changelogs/unreleased/55376-related_merge_requests-api-call-returns-merge-requests-that-are-not-related-to-the-issue.yml5
-rw-r--r--changelogs/unreleased/55495-teamcity-use-revision-in-query.yml5
-rw-r--r--changelogs/unreleased/55628-artifacts-from-a-job-defined-after-a-parallel-job-are-not-downloaded.yml5
-rw-r--r--changelogs/unreleased/55820-adds-common-name-chart-value.yml5
-rw-r--r--changelogs/unreleased/55884-adjust-emoji-and-cancel-buttons-height-in-user-status-modal-when-emoji-is-changed.yml5
-rw-r--r--changelogs/unreleased/55893-artifacts-download.yml5
-rw-r--r--changelogs/unreleased/55945-suggested-change-preview-highlight.yml5
-rw-r--r--changelogs/unreleased/55966-when-ref-is-ambiguous-createpipelineservice-raises-an-error.yml5
-rw-r--r--changelogs/unreleased/56010-user-profile-page-horizonal-whitespace-between-overview-columns-breaks-two-column-layout.yml5
-rw-r--r--changelogs/unreleased/56014-api-merge-request-squash-commit-messages.yml5
-rw-r--r--changelogs/unreleased/56019-archived-stuck.yml5
-rw-r--r--changelogs/unreleased/56036-fix-translation-of-in-in-job-details-sidebar.yml4
-rw-r--r--changelogs/unreleased/56110-cluster-kubernetes-api-500-error-on-post-request.yml5
-rw-r--r--changelogs/unreleased/56172-docs-fix-add-include-to-ci-param-list.yml5
-rw-r--r--changelogs/unreleased/56334-runners-ipv6-address-overlaps-other-values.yml5
-rw-r--r--changelogs/unreleased/56363-inconsitent-file-size-indication-across-different-ci-pages.yml6
-rw-r--r--changelogs/unreleased/56371-don-t-check-confidential-issues-for-spam.yml5
-rw-r--r--changelogs/unreleased/56379-pipeline-stages-job-action-button-icon-is-not-aligned.yml5
-rw-r--r--changelogs/unreleased/56389-remove-unwanted-suggestion-flash-margin.yml5
-rw-r--r--changelogs/unreleased/56398-fix-cluster-installation-loading-state.yml5
-rw-r--r--changelogs/unreleased/56417-update-helm-to-2-12-2.yml5
-rw-r--r--changelogs/unreleased/56492-implement-new-arguments-state-closed_before-and-closed_after-for-issuesresolver-in-graphql.yml5
-rw-r--r--changelogs/unreleased/56507-sh-bump-katex-0.10.0.yml5
-rw-r--r--changelogs/unreleased/56543-project-lists-further-iteration-improvements.yml5
-rw-r--r--changelogs/unreleased/56547-limit-sidekiq-logging-based-on-argument-size.yml5
-rw-r--r--changelogs/unreleased/56556-fix-markdown-table-border.yml5
-rw-r--r--changelogs/unreleased/56622-admin-settings-cannot-read-property-addeventlistener-of-null.yml5
-rw-r--r--changelogs/unreleased/56636-hashed-storage-afterrenameservice.yml5
-rw-r--r--changelogs/unreleased/56764-poor-ui-on-milestone-validation-error-page.yml5
-rw-r--r--changelogs/unreleased/56787-realtime-validation-for-user-fullname-and-username.yml5
-rw-r--r--changelogs/unreleased/56788-unicorn-metric-labels.yml5
-rw-r--r--changelogs/unreleased/56851-blank-values-in-reactive-cache.yml5
-rw-r--r--changelogs/unreleased/56871-list-issues-error.yml5
-rw-r--r--changelogs/unreleased/56937-edit-knative-domain-after-it-has-been-deployed.yml5
-rw-r--r--changelogs/unreleased/56938-diff-file-headers-on-compare-not-quite-right.yml5
-rw-r--r--changelogs/unreleased/57063-implement-new-arguments-iid-for-issuesresolver-in-graphql.yml5
-rw-r--r--changelogs/unreleased/57160-merge-request-tabs-header-is-missing-bottom-border.yml5
-rw-r--r--changelogs/unreleased/57227-absolute-uri-missing-hierarchical-segment.yml5
-rw-r--r--changelogs/unreleased/57353-git-push-fails-on-large-lfs-files-where-the-push-take-a-long-time.yml5
-rw-r--r--changelogs/unreleased/57589-update-workhorse.yml5
-rw-r--r--changelogs/unreleased/57671-fix_merge_request_base_pipeline.yml5
-rw-r--r--changelogs/unreleased/57768-remove-vertical-line.yml5
-rw-r--r--changelogs/unreleased/57784-make-closed-duplicate-and-closed-moved-button-a-link-to-target.yml5
-rw-r--r--changelogs/unreleased/8688-recursive-pipelines-ce-backport.yml5
-rw-r--r--changelogs/unreleased/9841-geo-unable-to-compare-branches-on-secondary.yml5
-rw-r--r--changelogs/unreleased/Projects--dropdown-is-misaligned-on-issue-boards-page.yml5
-rw-r--r--changelogs/unreleased/ab-54270-github-iid.yml5
-rw-r--r--changelogs/unreleased/ac-pages-subgroups.yml5
-rw-r--r--changelogs/unreleased/actioncontroller-parameters-deprecations.yml5
-rw-r--r--changelogs/unreleased/add-badge-count-to-projects-and-groups.yml5
-rw-r--r--changelogs/unreleased/add-related-merge-request-count-to-api-response.yml5
-rw-r--r--changelogs/unreleased/add-uniqueness-validation-to-url-column-in-releases-link-model.yml5
-rw-r--r--changelogs/unreleased/adrianmoisey-GITLAB_PAGES_PREDEFINED_VARIABLES.yml5
-rw-r--r--changelogs/unreleased/adriel-remove-feature-flag.yml5
-rw-r--r--changelogs/unreleased/an-dtracing-test-for-invalid-tracers.yml5
-rw-r--r--changelogs/unreleased/an-gilab-process-name.yml5
-rw-r--r--changelogs/unreleased/an-opentracing-active-record-tracing.yml5
-rw-r--r--changelogs/unreleased/an-opentracing-factory.yml5
-rw-r--r--changelogs/unreleased/an-opentracing-propagation.yml5
-rw-r--r--changelogs/unreleased/an-opentracing-render-tracing.yml5
-rw-r--r--changelogs/unreleased/api-group-labels.yml5
-rw-r--r--changelogs/unreleased/api-nested-group-permission.yml5
-rw-r--r--changelogs/unreleased/api-tags-search.yml5
-rw-r--r--changelogs/unreleased/api-wiki-dot-slug.yml5
-rw-r--r--changelogs/unreleased/auto-devops-custom-domains.yml5
-rw-r--r--changelogs/unreleased/auto-devops-kubectl-1-11-6.yml5
-rw-r--r--changelogs/unreleased/backup_aws_sse-c.yml5
-rw-r--r--changelogs/unreleased/backup_restore_fix_issue_46891.yml5
-rw-r--r--changelogs/unreleased/bump-ingress-chart-112.yml5
-rw-r--r--changelogs/unreleased/bvl-fix-race-condition-creating-signature.yml5
-rw-r--r--changelogs/unreleased/change-badges-example-to-pipeline.yml5
-rw-r--r--changelogs/unreleased/changelogs-readme.yml5
-rw-r--r--changelogs/unreleased/chore-update-js-regex.yml5
-rw-r--r--changelogs/unreleased/cleanup-leagcy-artifact-migration.yml5
-rw-r--r--changelogs/unreleased/cluster_application_version_updated.yml5
-rw-r--r--changelogs/unreleased/cluster_status_for_ugprading.yml5
-rw-r--r--changelogs/unreleased/container-repository-cleanup-api.yml5
-rw-r--r--changelogs/unreleased/custom-helm-chart-repo.yml5
-rw-r--r--changelogs/unreleased/deprecated-force-reload.yml6
-rw-r--r--changelogs/unreleased/diff-file-finder.yml5
-rw-r--r--changelogs/unreleased/diff-tree-collapse-directories.yml5
-rw-r--r--changelogs/unreleased/diff-tree-resizable.yml5
-rw-r--r--changelogs/unreleased/dm-copy-suggestion-as-gfm.yml5
-rw-r--r--changelogs/unreleased/dm-trim-discussion-truncated-line-first-chars.yml5
-rw-r--r--changelogs/unreleased/docs-push-mirror-GitLab-GitHub.yml5
-rw-r--r--changelogs/unreleased/dz-sort-labels-alphabetically.yml5
-rw-r--r--changelogs/unreleased/expire-job-artifacts-worker.yml5
-rw-r--r--changelogs/unreleased/feature-gb-enable-ci-persisted-stages-by-default.yml5
-rw-r--r--changelogs/unreleased/features-document-graphicsmagick-source-installation.yml5
-rw-r--r--changelogs/unreleased/fix-39759-new-project-icon-vertical-align.yml5
-rw-r--r--changelogs/unreleased/fix-403-page-is-rendered-but-404-is-the-response.yml5
-rw-r--r--changelogs/unreleased/fix-49388.yml5
-rw-r--r--changelogs/unreleased/fix-55956-oversized-dropdown-button-custom-notifications.yml5
-rw-r--r--changelogs/unreleased/fix-56558-move-primary-button.yml5
-rw-r--r--changelogs/unreleased/fix-auto-devops-domain-title-on-admin-settings.yml5
-rw-r--r--changelogs/unreleased/fix-repo-settings-file-upload-error.yml5
-rw-r--r--changelogs/unreleased/fix_jira_integration_VCS1019.yml5
-rw-r--r--changelogs/unreleased/fj-55882-fix-files-api-content-disposition.yml5
-rw-r--r--changelogs/unreleased/force-redeploy-on-updated-secrets.yml5
-rw-r--r--changelogs/unreleased/gitaly-update-1-13-0.yml5
-rw-r--r--changelogs/unreleased/gitaly-update-1.18.0.yml5
-rw-r--r--changelogs/unreleased/gitlab-workhorse-update-8.1.0.yml5
-rw-r--r--changelogs/unreleased/gitlab_kubernetes_helm_bump.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-clusters.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-email_rejection_mailer.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-instance_statistics.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-projects-ci.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-projects-commit.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-projects-milestones.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-projects-pages_domains.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-projects-project_members.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-sent_notifications.yml5
-rw-r--r--changelogs/unreleased/gt-remove-unused-button-class.yml5
-rw-r--r--changelogs/unreleased/gt-rename-gray-theme-color-variables.yml5
-rw-r--r--changelogs/unreleased/gt-update-new-password-breadcrumb.yml5
-rw-r--r--changelogs/unreleased/gt-update-string-struture-for-group-runners.yml5
-rw-r--r--changelogs/unreleased/helm-2-12-3.yml5
-rw-r--r--changelogs/unreleased/hnk-master-patch-61932.yml5
-rw-r--r--changelogs/unreleased/homepage-proj-descr-cutoff.yml5
-rw-r--r--changelogs/unreleased/import-go-to-project-cta.yml5
-rw-r--r--changelogs/unreleased/introduce-environment-search-endpoint.yml5
-rw-r--r--changelogs/unreleased/iss-32584-preserve-line-number-fragment-after-redirect.yml6
-rw-r--r--changelogs/unreleased/issue_55744.yml5
-rw-r--r--changelogs/unreleased/jej-avoid-csrf-check-on-saml-failure.yml5
-rw-r--r--changelogs/unreleased/jej-feature-gates-can-be-set-by-group-path.yml5
-rw-r--r--changelogs/unreleased/jlenny-AddPagesTemplates.yml5
-rw-r--r--changelogs/unreleased/jlenny-NewAndroidTemplate.yml5
-rw-r--r--changelogs/unreleased/jprovazn-remove-redcarpet.yml5
-rw-r--r--changelogs/unreleased/kinolaev-master-patch-87865.yml5
-rw-r--r--changelogs/unreleased/knative-list.yml5
-rw-r--r--changelogs/unreleased/knative-show-page.yml5
-rw-r--r--changelogs/unreleased/local-markdown-version-bkp3.yml5
-rw-r--r--changelogs/unreleased/mg-fix-bad-cluster-update-entrypoint.yml5
-rw-r--r--changelogs/unreleased/monospace-registry-tags.yml5
-rw-r--r--changelogs/unreleased/move-job-cancel-btn.yml5
-rw-r--r--changelogs/unreleased/move-permission-check-manual-actions-on-deployments.yml5
-rw-r--r--changelogs/unreleased/move_chatops_to_core.yml5
-rw-r--r--changelogs/unreleased/mr-file-tree-blob-truncate-improvements.yml5
-rw-r--r--changelogs/unreleased/mr-rebase-failing-tests.yml5
-rw-r--r--changelogs/unreleased/not-run-pipeline-on-empty-merge-request.yml5
-rw-r--r--changelogs/unreleased/notebook-multiple-outputs.yml5
-rw-r--r--changelogs/unreleased/notes-awards-double-tooltip-fix.yml5
-rw-r--r--changelogs/unreleased/osw-enforces-project-removal-with-past-failed-attempts.yml5
-rw-r--r--changelogs/unreleased/osw-fix-bottom-expansion-diff-comment.yml5
-rw-r--r--changelogs/unreleased/patch-38.yml5
-rw-r--r--changelogs/unreleased/pl-serialize-ac-parameters.yml5
-rw-r--r--changelogs/unreleased/profile-project-empty-state.yml5
-rw-r--r--changelogs/unreleased/raise-on-unfiltered-params.yml5
-rw-r--r--changelogs/unreleased/rd-update-last_activity_on-on-logins-and-browsing-activity-54947.yml5
-rw-r--r--changelogs/unreleased/refactor-56366-extract-resolve-discussion-button.yml5
-rw-r--r--changelogs/unreleased/refactor-56369-extract-jump-to-next-discussion-button.yml5
-rw-r--r--changelogs/unreleased/refactor-56370-extract-reply-placeholder-component.yml5
-rw-r--r--changelogs/unreleased/remove-cancel-all-button-in-job-list-view.yml5
-rw-r--r--changelogs/unreleased/remove-diff-coloring.yml5
-rw-r--r--changelogs/unreleased/remove-gap-between-mr-tabs-and-file-header.yml5
-rw-r--r--changelogs/unreleased/remove-second-primary-button-on-wiki-edit.yml5
-rw-r--r--changelogs/unreleased/rs-admin-user-case-insensitive.yml5
-rw-r--r--changelogs/unreleased/search-title.yml5
-rw-r--r--changelogs/unreleased/security-22076-sanitize-url-in-names.yml6
-rw-r--r--changelogs/unreleased/security-2770-verify-bundle-import-files.yml5
-rw-r--r--changelogs/unreleased/security-55320-stored-xss-in-user-status.yml5
-rw-r--r--changelogs/unreleased/security-stored-xss-via-katex.yml5
-rw-r--r--changelogs/unreleased/sh-disable-nil-user-id-identity-validation.yml5
-rw-r--r--changelogs/unreleased/sh-encode-content-disposition.yml5
-rw-r--r--changelogs/unreleased/sh-fix-backfill-project-repo-migration.yml5
-rw-r--r--changelogs/unreleased/sh-fix-bitbucket-server-error-handling.yml5
-rw-r--r--changelogs/unreleased/sh-fix-board-user-assigns.yml5
-rw-r--r--changelogs/unreleased/sh-fix-import-redirect-vulnerability.yml5
-rw-r--r--changelogs/unreleased/sh-fix-pages-zip-constant.yml5
-rw-r--r--changelogs/unreleased/sh-fix-snippet-uploads-path-lookup.yml5
-rw-r--r--changelogs/unreleased/sh-fix-upload-snippets-with-relative-url-root.yml5
-rw-r--r--changelogs/unreleased/sh-import-source-branch-github-forks.yml5
-rw-r--r--changelogs/unreleased/sh-include-project-path-for-internal-api.yml5
-rw-r--r--changelogs/unreleased/sh-issue-53419-fix.yml5
-rw-r--r--changelogs/unreleased/sh-preload-associations-for-group-api.yml5
-rw-r--r--changelogs/unreleased/shared_with_group_path.yml5
-rw-r--r--changelogs/unreleased/support-chunking-in-client.yml5
-rw-r--r--changelogs/unreleased/syntax-highlighting-again.yml5
-rw-r--r--changelogs/unreleased/test-permissions.yml5
-rw-r--r--changelogs/unreleased/tooltips-to-top.yml5
-rw-r--r--changelogs/unreleased/twang2218-gitlab-ce-i18n-extract-app-views-search.yml5
-rw-r--r--changelogs/unreleased/update-gitaly.yml5
-rw-r--r--changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-1-45.yml5
-rw-r--r--changelogs/unreleased/update-gitlab-styles.yml5
-rw-r--r--changelogs/unreleased/update-pages-config-only-when-changed.yml5
-rw-r--r--changelogs/unreleased/update-pages-extensionless-urls.yml5
-rw-r--r--changelogs/unreleased/update-sidekiq-cron.yml6
-rw-r--r--changelogs/unreleased/update-smooshpack.yml5
-rw-r--r--changelogs/unreleased/update-spriteicon-from-icon-on-profile.yml5
-rw-r--r--changelogs/unreleased/update-ui-admin-appearance.yml5
-rw-r--r--changelogs/unreleased/update-workhorse-8-2-0.yml5
-rw-r--r--changelogs/unreleased/use-deployment-relation-to-fetch-environment-ce.yml5
-rw-r--r--changelogs/unreleased/use_upgrade_install_for_helm_apps.yml5
-rw-r--r--changelogs/unreleased/winh-add-list-dropdown-height.yml5
-rw-r--r--changelogs/unreleased/workhorse-8-3-0.yml5
-rw-r--r--changelogs/unreleased/yoginth-avatar-on-settings-sidebar.yml5
-rw-r--r--changelogs/unreleased/zj-feature-gate-set-project-path.yml5
-rw-r--r--config/routes/import.rb4
-rw-r--r--config/sidekiq_queues.yml1
-rw-r--r--danger/commit_messages/Dangerfile243
-rw-r--r--danger/documentation/Dangerfile30
-rw-r--r--danger/roulette/Dangerfile3
-rw-r--r--db/migrate/20180209115333_create_chatops_tables.rb26
-rw-r--r--db/migrate/limits_to_mysql.rb13
-rw-r--r--db/post_migrate/20181101091005_steal_digest_column.rb17
-rw-r--r--db/post_migrate/20181101091124_remove_token_from_personal_access_tokens.rb11
-rw-r--r--db/schema.rb12
-rw-r--r--doc/README.md1
-rw-r--r--doc/administration/auth/authentiq.md1
-rw-r--r--doc/administration/auth/ldap.md1
-rw-r--r--doc/administration/auth/okta.md1
-rw-r--r--doc/administration/container_registry.md1
-rw-r--r--doc/administration/high_availability/redis.md7
-rw-r--r--doc/administration/incoming_email.md385
-rw-r--r--doc/administration/index.md29
-rw-r--r--doc/administration/integration/plantuml.md1
-rw-r--r--doc/administration/merge_request_diffs.md33
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md5
-rw-r--r--doc/administration/operations/cleaning_up_redis_sessions.md1
-rw-r--r--doc/administration/repository_storage_types.md1
-rw-r--r--doc/administration/restart_gitlab.md1
-rw-r--r--doc/api/README.md207
-rw-r--r--doc/api/award_emoji.md11
-rw-r--r--doc/api/boards.md6
-rw-r--r--doc/api/commits.md3
-rw-r--r--doc/api/container_registry.md1
-rw-r--r--doc/api/discussions.md1
-rw-r--r--doc/api/features.md3
-rw-r--r--doc/api/group_milestones.md1
-rw-r--r--doc/api/import.md2
-rw-r--r--doc/api/issues.md16
-rw-r--r--doc/api/labels.md1
-rw-r--r--doc/api/lint.md2
-rw-r--r--doc/api/merge_requests.md3
-rw-r--r--doc/api/oauth2.md1
-rw-r--r--doc/api/pages_domains.md12
-rw-r--r--doc/api/project_import_export.md27
-rw-r--r--doc/api/project_snippets.md1
-rw-r--r--doc/api/project_templates.md1
-rw-r--r--doc/api/protected_branches.md1
-rw-r--r--doc/api/releases/index.md2
-rw-r--r--doc/api/search.md2
-rw-r--r--doc/api/services.md1
-rw-r--r--doc/api/sidekiq_metrics.md1
-rw-r--r--doc/api/tags.md1
-rw-r--r--doc/api/templates/gitignores.md12
-rw-r--r--doc/api/users.md1
-rw-r--r--doc/api/wikis.md1
-rw-r--r--doc/articles/openshift_and_gitlab/index.md1
-rw-r--r--doc/ci/README.md247
-rw-r--r--doc/ci/caching/index.md7
-rw-r--r--doc/ci/chatops/README.md61
-rw-r--r--doc/ci/chatops/img/gitlab-chatops-icon-small.pngbin0 -> 2922 bytes
-rw-r--r--doc/ci/chatops/img/gitlab-chatops-icon.pngbin0 -> 12308 bytes
-rw-r--r--doc/ci/docker/README.md8
-rw-r--r--doc/ci/docker/using_docker_images.md4
-rw-r--r--doc/ci/environments.md10
-rw-r--r--doc/ci/examples/README.md1
-rw-r--r--doc/ci/examples/deployment/composer-npm-deploy.md1
-rw-r--r--doc/ci/examples/end_to_end_testing_webdriverio/img/deployed_dependency_update.pngbin0 -> 67788 bytes
-rw-r--r--doc/ci/examples/end_to_end_testing_webdriverio/index.md251
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/index.md7
-rw-r--r--doc/ci/pipelines.md2
-rw-r--r--doc/ci/runners/README.md67
-rw-r--r--doc/ci/ssh_keys/README.md2
-rw-r--r--doc/ci/variables/README.md2
-rw-r--r--doc/ci/yaml/README.md16
-rw-r--r--doc/customization/help_message.md13
-rw-r--r--doc/customization/help_message/help_text.pngbin0 -> 86118 bytes
-rw-r--r--doc/customization/help_message/help_text_on_help_page.pngbin0 -> 24355 bytes
-rw-r--r--doc/customization/index.md18
-rw-r--r--doc/customization/libravatar.md4
-rw-r--r--doc/customization/welcome_message.md13
-rw-r--r--doc/development/architecture.md19
-rw-r--r--doc/development/code_review.md8
-rw-r--r--doc/development/database_debugging.md3
-rw-r--r--doc/development/diffs.md5
-rw-r--r--doc/development/documentation/feature-change-workflow.md167
-rw-r--r--doc/development/documentation/improvement-workflow.md38
-rw-r--r--doc/development/documentation/index.md4
-rw-r--r--doc/development/documentation/site_architecture/global_nav.md3
-rw-r--r--doc/development/documentation/workflow.md6
-rw-r--r--doc/development/fe_guide/accessibility.md1
-rw-r--r--doc/development/fe_guide/design_patterns.md1
-rw-r--r--doc/development/fe_guide/droplab/droplab.md1
-rw-r--r--doc/development/fe_guide/droplab/plugins/input_setter.md1
-rw-r--r--doc/development/fe_guide/performance.md1
-rw-r--r--doc/development/fe_guide/security.md1
-rw-r--r--doc/development/fe_guide/vue.md1
-rw-r--r--doc/development/feature_flags.md1
-rw-r--r--doc/development/file_storage.md1
-rw-r--r--doc/development/i18n/proofreader.md2
-rw-r--r--doc/development/import_export.md23
-rw-r--r--doc/development/migration_style_guide.md1
-rw-r--r--doc/development/new_fe_guide/development/testing.md1
-rw-r--r--doc/development/performance.md16
-rw-r--r--doc/development/policies.md47
-rw-r--r--doc/development/profiling.md2
-rw-r--r--doc/development/rake_tasks.md1
-rw-r--r--doc/development/shell_commands.md5
-rw-r--r--doc/development/testing_guide/best_practices.md10
-rw-r--r--doc/development/testing_guide/frontend_testing.md5
-rw-r--r--doc/development/testing_guide/review_apps.md16
-rw-r--r--doc/development/testing_guide/testing_levels.md4
-rw-r--r--doc/gitlab-basics/create-your-ssh-keys.md1
-rw-r--r--doc/install/aws/index.md1
-rw-r--r--doc/install/azure/index.md217
-rw-r--r--doc/install/kubernetes/gitlab_chart.md2
-rw-r--r--doc/install/kubernetes/gitlab_omnibus.md12
-rw-r--r--doc/install/kubernetes/gitlab_runner_chart.md17
-rw-r--r--doc/install/kubernetes/index.md2
-rw-r--r--doc/install/kubernetes/preparation/eks.md2
-rw-r--r--doc/install/kubernetes/preparation/networking.md4
-rw-r--r--doc/install/kubernetes/preparation/tools_installation.md2
-rw-r--r--doc/integration/akismet.md1
-rw-r--r--doc/integration/cas.md2
-rw-r--r--doc/integration/github.md1
-rw-r--r--doc/integration/omniauth.md1
-rw-r--r--doc/integration/saml.md1
-rw-r--r--doc/integration/slash_commands.md4
-rw-r--r--doc/public_access/public_access.md2
-rw-r--r--doc/raketasks/backup_restore.md29
-rw-r--r--doc/raketasks/user_management.md2
-rw-r--r--doc/system_hooks/system_hooks.md6
-rw-r--r--doc/topics/autodevops/index.md28
-rw-r--r--doc/topics/autodevops/quick_start_guide.md14
-rw-r--r--doc/topics/git/numerous_undo_possibilities_in_git/index.md8
-rw-r--r--doc/university/glossary/README.md1
-rw-r--r--doc/university/high-availability/aws/README.md5
-rw-r--r--doc/university/support/README.md1
-rw-r--r--doc/university/training/end-user/README.md3
-rw-r--r--doc/university/training/topics/git_log.md1
-rw-r--r--doc/university/training/topics/rollback_commits.md1
-rw-r--r--doc/update/mysql_to_postgresql.md3
-rw-r--r--doc/update/restore_after_failure.md1
-rw-r--r--doc/update/upgrading_postgresql_using_slony.md1
-rw-r--r--doc/user/abuse_reports.md1
-rw-r--r--doc/user/admin_area/monitoring/health_check.md1
-rw-r--r--doc/user/admin_area/settings/continuous_integration.md6
-rw-r--r--doc/user/admin_area/settings/index.md4
-rw-r--r--doc/user/discussions/index.md5
-rw-r--r--doc/user/gitlab_com/index.md2
-rw-r--r--doc/user/group/clusters/index.md1
-rw-r--r--doc/user/markdown.md18
-rw-r--r--doc/user/profile/index.md1
-rw-r--r--doc/user/profile/preferences.md6
-rw-r--r--doc/user/project/clusters/eks_and_gitlab/index.md2
-rw-r--r--doc/user/project/clusters/index.md22
-rw-r--r--doc/user/project/clusters/serverless/index.md2
-rw-r--r--doc/user/project/container_registry.md1
-rw-r--r--doc/user/project/cycle_analytics.md4
-rw-r--r--doc/user/project/description_templates.md1
-rw-r--r--doc/user/project/integrations/bamboo.md1
-rw-r--r--doc/user/project/integrations/webhooks.md9
-rw-r--r--doc/user/project/pages/introduction.md4
-rw-r--r--doc/user/project/pipelines/job_artifacts.md2
-rw-r--r--doc/user/project/pipelines/schedules.md2
-rw-r--r--doc/user/project/protected_tags.md3
-rw-r--r--doc/user/project/quick_actions.md1
-rw-r--r--doc/user/project/repository/gpg_signed_commits/index.md1
-rw-r--r--doc/user/project/repository/web_editor.md1
-rw-r--r--doc/user/project/settings/index.md2
-rw-r--r--doc/workflow/forking_workflow.md1
-rw-r--r--jest.config.js4
-rw-r--r--lib/api/entities.rb9
-rw-r--r--lib/api/features.rb1
-rw-r--r--lib/api/helpers.rb8
-rw-r--r--lib/api/helpers/internal_helpers.rb8
-rw-r--r--lib/api/internal.rb9
-rw-r--r--lib/api/issues.rb18
-rw-r--r--lib/api/merge_requests.rb2
-rw-r--r--lib/api/notes.rb2
-rw-r--r--lib/api/services.rb2
-rw-r--r--lib/api/users.rb2
-rw-r--r--lib/bitbucket_server/connection.rb1
-rw-r--r--lib/feature.rb10
-rw-r--r--lib/gitlab/chat.rb10
-rw-r--r--lib/gitlab/chat/command.rb94
-rw-r--r--lib/gitlab/chat/output.rb93
-rw-r--r--lib/gitlab/chat/responder.rb22
-rw-r--r--lib/gitlab/chat/responder/base.rb40
-rw-r--r--lib/gitlab/chat/responder/slack.rb80
-rw-r--r--lib/gitlab/ci/pipeline/chain/command.rb3
-rw-r--r--lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs.rb8
-rw-r--r--lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml10
-rw-r--r--lib/gitlab/danger/helper.rb8
-rw-r--r--lib/gitlab/diff/file.rb4
-rw-r--r--lib/gitlab/etag_caching/router.rb8
-rw-r--r--lib/gitlab/import_export/import_export.yml1
-rw-r--r--lib/gitlab/kubernetes/helm.rb4
-rw-r--r--lib/gitlab/lfs_token.rb13
-rw-r--r--lib/gitlab/slash_commands/application_help.rb25
-rw-r--r--lib/gitlab/slash_commands/command.rb3
-rw-r--r--lib/gitlab/slash_commands/presenters/error.rb17
-rw-r--r--lib/gitlab/slash_commands/presenters/run.rb33
-rw-r--r--lib/gitlab/slash_commands/run.rb44
-rw-r--r--lib/gitlab/usage_data.rb12
-rw-r--r--lib/sentry/client.rb2
-rw-r--r--locale/gitlab.pot123
-rw-r--r--package.json4
-rw-r--r--qa/Gemfile1
-rw-r--r--qa/Gemfile.lock3
-rw-r--r--qa/README.md14
-rw-r--r--qa/qa.rb5
-rw-r--r--qa/qa/page/alert/auto_devops_alert.rb13
-rw-r--r--qa/qa/page/base.rb17
-rw-r--r--qa/qa/page/label/index.rb4
-rw-r--r--qa/qa/page/project/import/github.rb9
-rw-r--r--qa/qa/page/project/issue/show.rb19
-rw-r--r--qa/qa/page/project/job/show.rb27
-rw-r--r--qa/qa/page/project/operations/kubernetes/show.rb13
-rw-r--r--qa/qa/page/project/pipeline/show.rb10
-rw-r--r--qa/qa/resource/kubernetes_cluster.rb10
-rw-r--r--qa/qa/runtime/namespace.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb62
-rw-r--r--qa/qa/support/retrier.rb28
-rw-r--r--qa/spec/spec_helper.rb12
-rw-r--r--qa/spec/spec_helper_spec.rb51
-rwxr-xr-xscripts/build_assets_image4
-rwxr-xr-xscripts/lint-changelog-yaml2
-rwxr-xr-xscripts/lint-doc.sh2
-rw-r--r--spec/controllers/admin/users_controller_spec.rb11
-rw-r--r--spec/controllers/dashboard/milestones_controller_spec.rb9
-rw-r--r--spec/controllers/groups/clusters_controller_spec.rb2
-rw-r--r--spec/controllers/import/gitea_controller_spec.rb8
-rw-r--r--spec/controllers/import/github_controller_spec.rb4
-rw-r--r--spec/controllers/profiles/preferences_controller_spec.rb3
-rw-r--r--spec/controllers/projects/autocomplete_sources_controller_spec.rb38
-rw-r--r--spec/controllers/projects/merge_requests/diffs_controller_spec.rb13
-rw-r--r--spec/factories/import_states.rb (renamed from spec/factories/import_state.rb)0
-rw-r--r--spec/factories/personal_access_tokens.rb5
-rw-r--r--spec/factories/users.rb11
-rw-r--r--spec/features/dashboard/projects_spec.rb11
-rw-r--r--spec/features/issuables/issuable_list_spec.rb24
-rw-r--r--spec/features/merge_request/user_creates_image_diff_notes_spec.rb5
-rw-r--r--spec/features/profiles/user_visits_profile_preferences_page_spec.rb24
-rw-r--r--spec/features/projects/branches_spec.rb32
-rw-r--r--spec/features/projects/wiki/markdown_preview_spec.rb5
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb8
-rw-r--r--spec/features/projects/wiki/user_updates_wiki_page_spec.rb7
-rw-r--r--spec/features/projects/wiki/user_views_wiki_page_spec.rb7
-rw-r--r--spec/features/users/signup_spec.rb28
-rw-r--r--spec/finders/concerns/finder_methods_spec.rb22
-rw-r--r--spec/finders/issues_finder_spec.rb30
-rw-r--r--spec/fixtures/api/schemas/entities/diff_viewer.json11
-rw-r--r--spec/fixtures/security-reports/master/gl-container-scanning-report.json23
-rw-r--r--spec/frontend/lib/utils/ajax_cache_spec.js (renamed from spec/javascripts/lib/utils/ajax_cache_spec.js)85
-rw-r--r--spec/frontend/notes/components/__snapshots__/discussion_jump_to_next_button_spec.js.snap20
-rw-r--r--spec/frontend/notes/components/discussion_jump_to_next_button_spec.js30
-rw-r--r--spec/frontend/test_setup.js18
-rw-r--r--spec/graphql/resolvers/issues_resolver_spec.rb69
-rw-r--r--spec/graphql/types/issuable_state_enum_spec.rb9
-rw-r--r--spec/graphql/types/issue_state_enum_spec.rb9
-rw-r--r--spec/graphql/types/merge_request_state_enum_spec.rb13
-rw-r--r--spec/helpers/import_helper_spec.rb57
-rw-r--r--spec/helpers/labels_helper_spec.rb13
-rw-r--r--spec/helpers/preferences_helper_spec.rb7
-rw-r--r--spec/javascripts/diffs/components/app_spec.js26
-rw-r--r--spec/javascripts/diffs/components/diff_content_spec.js26
-rw-r--r--spec/javascripts/diffs/components/diff_file_header_spec.js20
-rw-r--r--spec/javascripts/diffs/components/diff_file_spec.js35
-rw-r--r--spec/javascripts/diffs/components/tree_list_spec.js12
-rw-r--r--spec/javascripts/diffs/mock_data/diff_discussions.js8
-rw-r--r--spec/javascripts/diffs/mock_data/diff_file.js2
-rw-r--r--spec/javascripts/diffs/store/actions_spec.js19
-rw-r--r--spec/javascripts/diffs/store/getters_spec.js4
-rw-r--r--spec/javascripts/diffs/store/mutations_spec.js10
-rw-r--r--spec/javascripts/diffs/store/utils_spec.js2
-rw-r--r--spec/javascripts/fixtures/blob.rb1
-rw-r--r--spec/javascripts/fixtures/commit.rb1
-rw-r--r--spec/javascripts/fixtures/deploy_keys.rb2
-rw-r--r--spec/javascripts/fixtures/groups.rb2
-rw-r--r--spec/javascripts/fixtures/issues.rb2
-rw-r--r--spec/javascripts/fixtures/merge_requests.rb7
-rw-r--r--spec/javascripts/fixtures/projects.rb12
-rw-r--r--spec/javascripts/fixtures/snippet.rb4
-rw-r--r--spec/javascripts/fixtures/u2f.rb3
-rw-r--r--spec/javascripts/import_projects/components/import_projects_table_spec.js186
-rw-r--r--spec/javascripts/import_projects/components/imported_project_table_row_spec.js50
-rw-r--r--spec/javascripts/import_projects/components/provider_repo_table_row_spec.js91
-rw-r--r--spec/javascripts/import_projects/store/actions_spec.js284
-rw-r--r--spec/javascripts/import_projects/store/getters_spec.js83
-rw-r--r--spec/javascripts/import_projects/store/mutations_spec.js34
-rw-r--r--spec/javascripts/notes/components/discussion_jump_to_next_button_spec.js33
-rw-r--r--spec/javascripts/notes/components/note_actions/reply_button_spec.js22
-rw-r--r--spec/javascripts/notes/components/note_app_spec.js10
-rw-r--r--spec/javascripts/notes/stores/actions_spec.js14
-rw-r--r--spec/javascripts/notes/stores/mutation_spec.js19
-rw-r--r--spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js4
-rw-r--r--spec/javascripts/vue_shared/components/panel_resizer_spec.js9
-rw-r--r--spec/lib/bitbucket_server/client_spec.rb4
-rw-r--r--spec/lib/feature_spec.rb5
-rw-r--r--spec/lib/gitlab/background_migration/digest_column_spec.rb4
-rw-r--r--spec/lib/gitlab/chat/command_spec.rb77
-rw-r--r--spec/lib/gitlab/chat/output_spec.rb101
-rw-r--r--spec/lib/gitlab/chat/responder/base_spec.rb48
-rw-r--r--spec/lib/gitlab/chat/responder/slack_spec.rb77
-rw-r--r--spec/lib/gitlab/chat/responder_spec.rb32
-rw-r--r--spec/lib/gitlab/chat_spec.rb23
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb33
-rw-r--r--spec/lib/gitlab/danger/helper_spec.rb9
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/kubernetes/helm/pod_spec.rb2
-rw-r--r--spec/lib/gitlab/lfs_token_spec.rb36
-rw-r--r--spec/lib/gitlab/slash_commands/application_help_spec.rb19
-rw-r--r--spec/lib/gitlab/slash_commands/presenters/error_spec.rb15
-rw-r--r--spec/lib/gitlab/slash_commands/presenters/run_spec.rb79
-rw-r--r--spec/lib/gitlab/slash_commands/run_spec.rb123
-rw-r--r--spec/lib/sentry/client_spec.rb2
-rw-r--r--spec/models/ci/pipeline_spec.rb1
-rw-r--r--spec/models/clusters/applications/helm_spec.rb7
-rw-r--r--spec/models/clusters/applications/ingress_spec.rb12
-rw-r--r--spec/models/clusters/applications/knative_spec.rb24
-rw-r--r--spec/models/clusters/applications/prometheus_spec.rb73
-rw-r--r--spec/models/clusters/applications/runner_spec.rb12
-rw-r--r--spec/models/clusters/cluster_spec.rb6
-rw-r--r--spec/models/commit_collection_spec.rb14
-rw-r--r--spec/models/concerns/reactive_caching_spec.rb12
-rw-r--r--spec/models/concerns/token_authenticatable_spec.rb235
-rw-r--r--spec/models/error_tracking/project_error_tracking_setting_spec.rb18
-rw-r--r--spec/models/merge_request_spec.rb20
-rw-r--r--spec/models/project_services/slack_slash_commands_service_spec.rb7
-rw-r--r--spec/models/prometheus_metric_spec.rb13
-rw-r--r--spec/models/user_spec.rb15
-rw-r--r--spec/requests/api/features_spec.rb34
-rw-r--r--spec/requests/api/internal_spec.rb5
-rw-r--r--spec/requests/api/issues_spec.rb140
-rw-r--r--spec/routing/import_routing_spec.rb15
-rw-r--r--spec/serializers/diff_file_entity_spec.rb8
-rw-r--r--spec/serializers/namespace_basic_entity_spec.rb18
-rw-r--r--spec/serializers/namespace_serializer_spec.rb9
-rw-r--r--spec/serializers/project_import_entity_spec.rb22
-rw-r--r--spec/serializers/project_serializer_spec.rb44
-rw-r--r--spec/serializers/provider_repo_entity_spec.rb24
-rw-r--r--spec/serializers/provider_repo_serializer_spec.rb9
-rw-r--r--spec/services/error_tracking/list_issues_service_spec.rb26
-rw-r--r--spec/services/error_tracking/list_projects_service_spec.rb4
-rw-r--r--spec/services/prometheus/adapter_service_spec.rb10
-rw-r--r--spec/services/web_hook_service_spec.rb2
-rw-r--r--spec/support/controllers/githubish_import_controller_shared_examples.rb103
-rw-r--r--spec/support/helpers/reactive_caching_helpers.rb2
-rw-r--r--spec/support/shared_contexts/services_shared_context.rb2
-rw-r--r--spec/support/shared_examples/graphql/issuable_state_shared_examples.rb5
-rw-r--r--spec/support/shared_examples/models/cluster_application_status_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/requests/api/merge_requests_list.rb32
-rw-r--r--spec/support/shared_examples/requests/api/notes.rb42
-rw-r--r--spec/support/shared_examples/serializers/diff_file_entity_examples.rb8
-rw-r--r--spec/views/projects/issues/show.html.haml_spec.rb20
-rw-r--r--spec/workers/build_finished_worker_spec.rb19
-rw-r--r--spec/workers/chat_notification_worker_spec.rb91
-rw-r--r--yarn.lock16
783 files changed, 7733 insertions, 3620 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c5cd006a289..0afd59ab007 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -669,8 +669,8 @@ gitlab:assets:compile:
- //@gitlab/gitlabhq
- //@gitlab/gitlab-ee
tags:
- - gitlab-org-delivery
- - high-cpu
+ - docker
+ - gitlab-org
karma:
<<: *dedicated-no-docs-pull-cache-job
diff --git a/.gitlab/issue_templates/Doc Review.md b/.gitlab/issue_templates/Doc Review.md
new file mode 100644
index 00000000000..14ab0c82d59
--- /dev/null
+++ b/.gitlab/issue_templates/Doc Review.md
@@ -0,0 +1,20 @@
+<!-- This issue requests a technical writer review as required for documentation
+ content that was merged without one. -->
+
+<!-- NOTE: Please add a DevOps stage label (format `devops:<stage_name>`)
+ and assign the technical writer who is
+ [listed for that stage](https://about.gitlab.com/handbook/product/categories/#devops-stages). -->
+
+
+## References
+
+Merged MR that introduced documentation requiring review:
+
+Related issue(s):
+
+## Further Details
+
+<!-- Any additional context, questions, or notes for the technical writer. -->
+
+
+/label ~Documentation ~docs-review
diff --git a/.gitlab/issue_templates/Documentation.md b/.gitlab/issue_templates/Documentation.md
index b33ed5bcaa8..c0919aeeda4 100644
--- a/.gitlab/issue_templates/Documentation.md
+++ b/.gitlab/issue_templates/Documentation.md
@@ -1,54 +1,53 @@
-<!--See the general documentation guidelines https://docs.gitlab.com/ee/development/documentation -->
+<!--
-<!-- Mention "documentation" or "docs" in the issue title -->
+* Use this issue template for suggesting new docs or updates to existing docs.
+ Note: Doc work as part of feature development is covered in the Feature Request template.
+
+* For issues related to features of the docs.gitlab.com site, see
+ https://gitlab.com/gitlab-com/gitlab-docs/issues/
-<!-- Use this description template for new docs or updates to existing docs. -->
+* For information about documentation content and process, see
+ https://docs.gitlab.com/ee/development/documentation/ -->
-<!-- Check the documentation structure guidelines for guidance: https://docs.gitlab.com/ee/development/documentation/structure.html-->
+### Type of issue
-- [ ] Documents Feature A <!-- feature name -->
-- [ ] Follow-up from: #XXX, !YYY <!-- Mention related issues, MRs, and epics when available -->
+<!-- Un-comment the line for the applicable doc issue type to add its label.
+ Note that all text on that line is deleted upon issue creation. -->
+<!-- /label ~"docs:fix" - Correction or clarification needed. -->
+<!-- /label ~"docs:new" - New doc needed to cover a new topic or use case. -->
+<!-- /label ~"docs:improvement" - Improving an existing doc; e.g. adding a diagram, adding or rewording text, resolving redundancies, cross-linking, etc. -->
+<!-- /label ~"docs:revamp" - Review a page or group of pages in order to plan and implement major improvements/rewrites. -->
+<!-- /label ~"docs:other" - Anything else. -->
-## New doc or update?
+### Problem to solve
-<!-- Mark either of these boxes: -->
+<!-- Include the following detail as necessary:
+* What product or feature(s) affected?
+* What docs or doc section affected? Include links or paths.
+* Is there a problem with a specific document, or a feature/process that's not addressed sufficiently in docs?
+* Any other ideas or requests?
+-->
-- [ ] New documentation
-- [ ] Update existing documentation
+### Further details
-## Checklists
+<!--
+* Any concepts, procedures, reference info we could add to make it easier to successfully use GitLab?
+* Include use cases, benefits, and/or goals for this work.
+* If adding content: What audience is it intended for? (What roles and scenarios?)
+ For ideas, see personas at https://design.gitlab.com/research/personas or the persona labels at
+ https://gitlab.com/groups/gitlab-org/-/labels?utf8=%E2%9C%93&subscribed=&search=persona%3A
+-->
-### Product Manager
+### Proposal
-<!-- Reference: https://docs.gitlab.com/ee/development/documentation/workflow.html#1-product-manager-s-role-in-the-documentation-process -->
+<!-- Further specifics for how can we solve the problem. -->
-- [ ] Add the correct labels
-- [ ] Add the correct milestone
-- [ ] Indicate the correct document/directory for this feature <!-- (ping the tech writers for help if you're not sure) -->
-- [ ] Fill the doc blurb below
+### Who can address the issue
-#### Documentation blurb
+<!-- What if any special expertise is required to resolve this issue? -->
-<!-- Documentation template: https://docs.gitlab.com/ee/development/documentation/structure.html#documentation-template-for-new-docs -->
+### Other links/references
-- Doc **title**
-
- <!-- write the doc title here -->
-
-- Feature **overview/description**
-
- <!-- Write the feature overview here -->
-
-- Feature **use cases**
-
- <!-- Write the use cases here -->
-
-### Developer
-
-<!-- Reference: https://docs.gitlab.com/ee/development/documentation/workflow.html#2-developer-s-role-in-the-documentation-process -->
-
-- [ ] Copy the doc blurb above and paste it into the doc
-- [ ] Write the tutorial - explain how to use the feature
-- [ ] Submit the MR using the appropriate MR description template
+<!-- E.g. related GitLab issues/MRs -->
/label ~Documentation
diff --git a/.gitlab/issue_templates/Feature proposal.md b/.gitlab/issue_templates/Feature proposal.md
index 1bb8d33ff63..abfbef4b2b0 100644
--- a/.gitlab/issue_templates/Feature proposal.md
+++ b/.gitlab/issue_templates/Feature proposal.md
@@ -1,6 +1,6 @@
### Problem to solve
-<!--- What problem do we solve? -->
+<!-- What problem do we solve? -->
### Target audience
@@ -31,15 +31,20 @@ Existing personas are: (copy relevant personas out of this comment, and delete a
### Further details
-<!--- Include use cases, benefits, and/or goals (contributes to our vision?) -->
+<!-- Include use cases, benefits, and/or goals (contributes to our vision?) -->
### Proposal
-<!--- How are we going to solve the problem? Try to include the user journey! -->
+<!-- How are we going to solve the problem? Try to include the user journey! https://about.gitlab.com/handbook/journeys/#user-journey -->
+
+### Documentation
+
+<!-- See the Feature Change Documentation Workflow https://docs.gitlab.com/ee/development/documentation/feature-change-workflow.html
+Add all known Documentation Requirements here, per https://docs.gitlab.com/ee/development/documentation/feature-change-workflow.html#documentation-requirements -->
### What does success look like, and how can we measure that?
-<!--- Define both the success metrics and acceptance criteria. Note that success metrics indicate the desired business outcomes, while acceptance criteria indicate when the solution is working correctly. If there is no way to measure success, link to an issue that will implement a way to measure this -->
+<!-- Define both the success metrics and acceptance criteria. Note that success metrics indicate the desired business outcomes, while acceptance criteria indicate when the solution is working correctly. If there is no way to measure success, link to an issue that will implement a way to measure this. -->
### Links / references
diff --git a/.gitlab/merge_request_templates/Change documentation location.md b/.gitlab/merge_request_templates/Change documentation location.md
index b4a6d2bd3b4..c80af95d5e5 100644
--- a/.gitlab/merge_request_templates/Change documentation location.md
+++ b/.gitlab/merge_request_templates/Change documentation location.md
@@ -26,7 +26,7 @@ https://docs.gitlab.com/ce/development/documentation/index.html#changing-documen
to the new document if there are any Disqus comments on the old document thread.
- [ ] Update the link in `features.yml` (if applicable)
- [ ] If working on CE and the `ee-compat-check` jobs fails, submit an MR to EE
- with the changes as well (https://docs.gitlab.com/ce/development/writing_documentation.html#cherry-picking-from-ce-to-ee).
+ with the changes as well (https://docs.gitlab.com/ce/development/documentation/index.html#cherry-picking-from-ce-to-ee).
- [ ] Ping one of the technical writers for review.
/label ~Documentation
diff --git a/.gitlab/merge_request_templates/Database changes.md b/.gitlab/merge_request_templates/Database changes.md
index 354393b60e0..3f457174492 100644
--- a/.gitlab/merge_request_templates/Database changes.md
+++ b/.gitlab/merge_request_templates/Database changes.md
@@ -16,7 +16,7 @@ Add a description of your merge request here.
## Database checklist
-- [ ] Conforms to the [database guides](https://docs.gitlab.com/ee/development/README.html#databases-guides)
+- [ ] Conforms to the [database guides](https://docs.gitlab.com/ee/development/README.html#database-guides)
When adding migrations:
@@ -49,10 +49,10 @@ When removing columns, tables, indexes or other 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/index.html#contributing-to-docs)
+- [ ] [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://gitlab.com/gitlab-org/gitlab-ee/blob/master/CONTRIBUTING.md#style-guides)
+- [ ] Conforms to the [style guides](https://docs.gitlab.com/ee/development/contributing/style_guides.html)
/label ~database
diff --git a/.gitlab/merge_request_templates/Documentation.md b/.gitlab/merge_request_templates/Documentation.md
index 8b7e7119790..ba9624aeeab 100644
--- a/.gitlab/merge_request_templates/Documentation.md
+++ b/.gitlab/merge_request_templates/Documentation.md
@@ -1,33 +1,39 @@
-<!--See the general documentation guidelines https://docs.gitlab.com/ee/development/documentation -->
+<!-- Follow the documentation workflow https://docs.gitlab.com/ee/development/documentation/workflow.html -->
+<!-- Additional information is located at https://docs.gitlab.com/ee/development/documentation/ -->
<!-- Mention "documentation" or "docs" in the MR title -->
-
-<!-- Use this description template for new docs or updates to existing docs. For changing documentation location use the "Change documentation location" template -->
+<!-- For changing documentation location use the "Change documentation location" template -->
## What does this MR do?
-<!-- Briefly describe what this MR is about -->
+<!-- Briefly describe what this MR is about. -->
## Related issues
-<!-- Mention the issue(s) this MR closes or is related to -->
-
-Closes
+<!-- Link related issues below. Insert the issue link or reference after the word "Closes" if merging this should automatically close it. -->
## Author's checklist
-- [ ] [Apply the correct labels and milestone](https://docs.gitlab.com/ee/development/documentation/workflow.html#2-developer-s-role-in-the-documentation-process)
-- [ ] Crosslink the document from the higher-level index
-- [ ] Crosslink the document from other subject-related docs
-- [ ] Feature moving tiers? Make sure the change is also reflected in [`features.yml`](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/features.yml)
-- [ ] Correctly apply the product [badges](https://docs.gitlab.com/ee/development/documentation/styleguide.html#product-badges) and [tiers](https://docs.gitlab.com/ee/development/documentation/styleguide.html#gitlab-versions-and-tiers)
-- [ ] [Port the MR to EE (or backport from CE)](https://docs.gitlab.com/ee/development/documentation/index.html#cherry-picking-from-ce-to-ee): _always recommended, required when the `ee-compat-check` job fails_
+- [ ] Follow the [Documentation Guidelines](https://docs.gitlab.com/ee/development/documentation/) and [Style Guide](https://docs.gitlab.com/ee/development/documentation/styleguide.html).
+- [ ] Link docs to and from the higher-level index page, plus other related docs where helpful.
+- [ ] Apply the ~Documentation label.
## Review checklist
-- [ ] Your team's review (required)
-- [ ] PM's review (recommended, but not a blocker)
-- [ ] Technical writer's review (required)
-- [ ] Merge the EE-MR first, CE-MR afterwards
+All reviewers can help ensure accuracy, clarity, completeness, and adherence to the [Documentation Guidelines](https://docs.gitlab.com/ee/development/documentation/) and [Style Guide](https://docs.gitlab.com/ee/development/documentation/styleguide.html).
+
+**1. Primary Reviewer**
+
+* [ ] Review by a code reviewer or other selected colleague to confirm accuracy, clarity, and completeness. This can be skipped for minor fixes without substantive content changes.
+
+**2. Technical Writer**
+
+* [ ] Optional: Technical writer review. If not requested for this MR, must be scheduled post-merge. To request for this MR, assign the writer listed for the applicable [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages).
+
+**3. Maintainer**
+
+1. [ ] Review by assigned maintainer, who can always request/require the above reviews. Maintainer's review can occur before or after a technical writer review.
+1. [ ] Ensure a release milestone is set and that you merge the equivalent EE MR before the CE MR if both exist.
+1. [ ] If there has not been a 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).
/label ~Documentation
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e220d61b316..feda5e0835b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,253 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 11.8.0 (2019-02-22)
+
+### Security (7 changes, 1 of them is from the community)
+
+- Sanitize user full name to clean up any URL to prevent mail clients from auto-linking URLs. !2793
+- Update Helm to 2.12.2 to address Helm client vulnerability. !24418 (Takuya Noguchi)
+- Use sanitized user status message for user popover.
+- Validate bundle files before unpacking them.
+- Alias GitHub and BitBucket OAuth2 callback URLs.
+- Fixed XSS content in KaTex links.
+- Disallows unauthorized users from accessing the pipelines section.
+
+### Removed (2 changes, 1 of them is from the community)
+
+- Removed deprecated Redcarpet markdown engine.
+- Remove Cancel all jobs button in general jobs list view. (Jordi Llull)
+
+### Fixed (84 changes, 20 of them are from the community)
+
+- Fix ambiguous brackets in task lists. !18514 (Jared Deckard <jared.deckard@gmail.com>)
+- Fix lost line number when navigating to a specific line in a protected file before authenticating. !19165 (Scott Escue)
+- Fix suboptimal handling of checkbox and radio input events causing group general settings submit button to stay disabled after changing its visibility. !23022
+- Fix upcoming milestones filter not including group milestones. !23098 (Heinrich Lee Yu)
+- Update runner admin page to make description field larger. !23593 (Sascha Reynolds)
+- Fix Bitbucket Server import not allowing personal projects. !23601
+- Fix bug causing repository mirror settings UI to break. !23712
+- Fix foreground color for labels to ensure consistency of label appearance. !23873 (Nathan Friend)
+- Resolve In Merge Request diff screen, master is not a hyperlink. !23874
+- Show the correct error page when access is denied. !23932
+- Increase reliability and performance of toggling task items. !23938
+- Modify file restore to rectify tar issue. !24000
+- Fix default visibility_level for new projects. !24120 (Fabian Schneider @fabsrc)
+- Footnotes now render properly in markdown. !24168
+- Emoji and cancel button are taller than input in set user status modal. !24173 (Dhiraj Bodicherla)
+- Adjusts duplicated line when commenting on unfolded diff lines (in the bottom). !24201
+- Adjust height of "Add list" dropdown in issue boards. !24227
+- Improves restriction of multiple Kubernetes clusters through API. !24251
+- Fix files/blob api endpoints content disposition. !24267
+- Cleanup stale +deleted repo paths on project removal (adjusts project removal bug). !24269
+- Handle regular job dependencies next to parallelized job dependencies. !24273
+- Proper align Projects dropdown on issue boards page. !24277 (Johann Hubert Sonntagbauer)
+- Resolve When merging an MR, the squash checkbox isnt always supported. !24296
+- Fix Bitbucket Server importer error handling. !24343
+- Fix syntax highlighting for suggested changes preview. !24358
+- API: Support dots in wiki slugs. !24383 (Robert Schilling)
+- Show CI artifact file size with 3 significant digits on 'browse job artifacts' page. !24387
+- API: Support username with dots. !24395 (Robert Schilling)
+- API: Fix default_branch_protection admin setting. !24398 (Robert Schilling)
+- Remove unwanted margin above suggested changes. !24419
+- Prevent checking protected_ref? for ambiguous refs. !24437
+- Update metrics environment dropdown to show complete option set. !24441
+- Fix empty labels of CI builds for gitlab-pages on pipeline page. !24451
+- Do not run spam checks on confidential issues. !24453
+- Upgrade KaTeX to version 0.10.0. !24478 (Andrew Harmon)
+- Avoid overwriting default jaeger values with nil. !24482
+- Display SAML failure messages instead of expecting CSRF token. !24509
+- Adjust vertical alignment for project visibility icons. !24511 (Martin Hobert)
+- Load initUserInternalRegexPlaceholder only when required. !24522
+- Hashed Storage: `AfterRenameService` was receiving the wrong `old_path` under some circunstances. !24526
+- Resolve Runners IPv6 address overlaps other values. !24531
+- Fix 404s with snippet uploads in object storage. !24550
+- Fixed oversized custom project notification selector dropdown. !24557
+- Allow users with full private access to read private personal snippets. !24560
+- Resolve Pipeline stages job action button icon is not aligned. !24577
+- Fix cluster page non-interactive on form validation error. !24583
+- Fix 404s for snippet uploads when relative URL root used. !24588
+- Fix markdown table border. !24601
+- Fix CSS grid on a new Project/Group Milestone. !24614 (Takuya Noguchi)
+- Prevent unload when Recaptcha is open. !24625
+- Clean up unicorn sampler metric labels. !24626 (bjk-gitlab)
+- Support bamboo api polymorphism. !24680 (Alex Lossent)
+- Ensure Cert Manager works with Auto DevOps URLs greater than 64 bytes. !24683
+- Fix failed LDAP logins when nil user_id present. !24749
+- fix display comment avatars issue in IE 11. !24777 (Gokhan Apaydin)
+- Fix template labels not being created on new projects. !24803
+- Fix cluster installation processing spinner. !24814
+- Append prioritized label before pagination. !24815
+- Resolve UI bug adding group members with lower permissions. !24820
+- Make `ActionController::Parameters` serializable for sidekiq jobs. !24864
+- Fix Jira Service password validation on project integration services. !24896 (Daniel Juarez)
+- Fix potential Addressable::URI::InvalidURIError. !24908
+- Update Workhorse to v8.2.0. !24909
+- Encode Content-Disposition filenames. !24919
+- Avoid race conditions when creating GpgSignature. !24939
+- Create the source branch for a GitHub import. !25064
+- Fix suggested changes syntax highlighting. !25116
+- Fix counts in milestones dashboard. !25230
+- Fixes incorrect TLD validation errors for Kubernetes cluster domain. !25262
+- Fix 403 errors when adding an assignee list in project boards. !25263
+- Prevent Auto DevOps from trying to deploy without a domain name. !25308
+- Fix uninitialized constant with GitLab Pages.
+- Increase line height of project summaries. (gfyoung)
+- Remove extra space between MR tab bar and sticky file headers.
+- Correct spacing for comparison page.
+- Update CI YAML param table with include.
+- Return bottom border on MR Tabs.
+- Fixes z-index and margins of archived alert in job page.
+- Fixes archived sticky top bar without perfomance bar.
+- Fixed rebase button not showing in merge request widget.
+- Fixed double tooltips on note awards buttons.
+- Allow suggestions to be copied and pasted as GFM.
+- Fix bug that caused Suggestion Markdown toolbar button to insert snippet with leading +/-/<space>.
+- Moved primary button for labels to follow the design patterns used on rest of the site. (Martin Hobert)
+
+### Changed (37 changes, 11 of them are from the community)
+
+- Change spawning of tooltips to be top by default. !21223
+- Standardize filter value capitlization in filter bar in both issues and boards pages. !23846 (obahareth)
+- Refresh group overview to match project overview. !23866
+- Build number does not need to be tweaked anymore for the TeamCity integration to work properly. !23898
+- Added empty project illustration and updated text to user profile overview. !23973 (Fernando Arias)
+- Modified Knative list view to provide more details. !24072 (Chris Baumbauer)
+- Move cancel & new issue button on job page. !24074
+- Make issuable empty states actionable. !24077
+- Fix code search when text is larger than max gRPC message size. !24111
+- Update string structure for available group runners. !24187 (George Tsiolis)
+- Remove multilingual translation from the word "in" in the job details sidebar. !24192 (Nathan Friend)
+- Fix duplicate project disk path in BackfillLegacyProjectRepositories. !24213
+- Ensured links to a comment or system note anchor resolves to the right note if a user has a discussion filter. !24228
+- Remove expansion hover animation from pipeline status icon buttons. !24268 (Nathan Friend)
+- Redesigned related merge requests in issue page. !24270
+- Return the maximum group access level in the projects API. !24403
+- Update project topics styling to use badges design. !24415
+- Display "commented" only for commit discussions on merge requests. !24427
+- Upgrade js-regex gem to version 3.1. !24433 (rroger)
+- Prevent Sidekiq arguments over 10 KB in size from being logged to JSON. !24493
+- Added Avatar in the settings sidebar. !24515 (Yoginth)
+- Refresh empty states for profile page tabs. !24549
+- remove red/green colors from diff view of no-color syntax theme. !24582 (khm)
+- Get remote IP address of runner. !24624
+- Update last_activity_on for Users on some main GET endpoints. !24642
+- Update metrics dashboard graph design. !24653
+- Update to GitLab SVG icon from Font Awesome in profile for location and work. !24671 (Yoginth)
+- Add template for Android with Fastlane. !24722
+- Display timestamps to messages printed by gitlab:backup:restore rake tasks. (Will Chandler)
+- Show MR statistics in diff comparisons.
+- Make possible to toggle file tree while scrolling through diffs.
+- Use delete instead of remove when referring to `git branch -D`.
+- Add folder header to files in merge request tree list.
+- Added fuzzy file finder to merge requests.
+- Collapse directory structure in merge request file tree.
+- Adds skeleton loading to releases page.
+- Support multiple outputs in jupyter notebooks.
+
+### Performance (8 changes, 1 of them is from the community)
+
+- Remove unused button classes `btn-create` and `comment-btn`. !23232 (George Tsiolis)
+- [API] Omit `X-Total` and `X-Total-Pages` headers when items count is more than 10,000. !23931
+- Improve efficiency of GitHub importer by reducing amount of locks needed. !24102
+- Improve milestone queries using subqueries instead of separate queries for ids. !24325
+- Efficiently remove expired artifacts in `ExpireBuildArtifactsWorker`. !24450
+- Eliminate N+1 queries in /api/groups/:id. !24513
+- Use deployment relation to get an environment name. !24890
+- Do not reload daemon if configuration file of pages does not change.
+
+### Added (35 changes, 18 of them are from the community)
+
+- Add badge count to projects. !18425 (George Tsiolis)
+- API: Add support for group labels. !21368 (Robert Schilling)
+- Add setting for first day of the week. !22755 (Fabian Schneider @fabsrc)
+- Pages for subgroups. !23505
+- Add support for customer provided encryption keys for Amazon S3 remote backups. !23797 (Pepijn Van Eeckhoudt)
+- Add Knative detailed view. !23863 (Chris Baumbauer)
+- Add group full path to project's shared_with_groups. !24052 (Mathieu Parent)
+- Added feature to specify a custom Auto DevOps chart repository. !24162 (walkafwalka)
+- Add flat-square badge style. !24172 (Fabian Schneider @fabsrc)
+- Display last activity and created at datetimes for users. !24181
+- Allow setting of feature gates per project. !24184
+- Save issues/merge request sorting options to backend. !24198
+- Added support for custom hosts/domains to Auto DevOps. !24248 (walkafwalka)
+- Adds milestone search. !24265 (Jacopo Beschi @jacopo-beschi)
+- Allow merge request diffs to be placed into an object store. !24276
+- Add Container Registry API with cleanup function. !24303
+- GitLab now supports the profile and email scopes from OpenID Connect. !24335 (Goten Xiao)
+- Add 'in' filter that modifies scope of 'search' filter to issues and merge requests API. !24350 (Hiroyuki Sato)
+- Add `with_programming_language` filter for projects to API. !24377 (Dylan MacKenzie)
+- API: Support searching for tags. !24385 (Robert Schilling)
+- Document graphicsmagick installation for source installation. !24404 (Alexis Reigel)
+- Redirect GET projects/:id to project page. !24467
+- Indicate on Issue Status if an Issue was Moved. !24470
+- Redeploy Auto DevOps deployment on variable updates. !24498 (walkafwalka)
+- Don't create new merge request pipeline without commits. !24503 (Hiroyuki Sato)
+- Add GitLab Pages predefined CI variables 'CI_PAGES_DOMAIN' and 'CI_PAGES_URL'. !24504 (Adrian Moisey)
+- Moves domain setting from Auto DevOps to Cluster's page. !24580
+- API allows setting the squash commit message when squashing a merge request. !24784
+- Added ability to upgrade cluster applications. !24789
+- Add argument iids for issues in GraphQL. !24802
+- Add repositories count to usage ping data. !24823
+- Add support for extensionless pages URLs. !24876
+- Add templates for most popular Pages templates. !24906
+- Introduce Internal API for searching environment names. !24923
+- Allow admins to invalidate markdown texts by setting local markdown version.
+
+### Other (50 changes, 18 of them are from the community)
+
+- Externalize strings from `/app/views/projects/project_members`. !23227 (Tao Wang)
+- Add CSS & JS global flags to represent browser and platform. !24017
+- Fix deprecation: Passing an argument to force an association to reload is now deprecated. !24136 (Jasper Maes)
+- Cleanup legacy artifact background migration. !24144
+- Bump kubectl in Auto DevOps to 1.11.6. !24176
+- Conditionally initialize the global opentracing tracer. !24186
+- Remove horizontal whitespace on user profile overview on small breakpoints. !24189
+- Bump nginx-ingress chart to 1.1.2. !24203
+- Use monospace font for registry table tag id and tag name. !24205
+- Rename project tags to project topics. !24219
+- Add uniqueness validation to url column in Releases::Link model. !24223
+- Update sidekiq-cron to 1.0.4 and use fugit to replace rufus-scheduler to parse cron syntax. !24235
+- Adds inter-service OpenTracing propagation. !24239
+- Fixes Auto DevOps title on CI/CD admin settings. !24249
+- Upgrade kubeclient to 4.2.2 and swap out monkey-patch to disallow redirects. !24284
+- i18n: externalize strings from 'app/views/search'. !24297 (Tao Wang)
+- Fix several ActionController::Parameters deprecations. !24332 (Jasper Maes)
+- Remove all `$theme-gray-{weight}` variables in favor of `$gray-{weight}`. !24333 (George Tsiolis)
+- Update gitlab-styles to 2.5.1. !24336 (Jasper Maes)
+- Modifies environment scope UI on cluster page. !24376
+- Extract process_name from GitLab::Sentry. !24422
+- Upgrade Gitaly to 1.13.0. !24429
+- Actually set raise_on_unfiltered_parameters to true. !24443 (Jasper Maes)
+- Refactored NoteableDiscussion by extracting ResolveDiscussionButton. !24505 (Martin Hobert)
+- Extracted JumpToNextDiscussionButton to its own component. !24506 (Martin Hobert)
+- Extracted ReplyPlaceholder to its own component. !24507 (Martin Hobert)
+- Block emojis and symbol characters from users full names. !24523
+- Update GitLab Runner Helm Chart to 0.1.45. !24564
+- Updated docs for fields in pushing mirror from GitLab to GitHub. !24566 (Joseph Yu)
+- Upgrade gitlab-workhorse to 8.1.0. !24571
+- Externalize strings from `/app/views/sent_notifications`. !24576 (George Tsiolis)
+- Adds tracing support for ActiveRecord notifications. !24604
+- Externalize strings from `/app/views/projects/ci`. !24617 (George Tsiolis)
+- Move permission check of manual actions of deployments. !24660
+- Externalize strings from `/app/views/clusters`. !24666 (George Tsiolis)
+- Update UI for admin appearance settings. !24685
+- Externalize strings from `/app/views/projects/pages_domains`. !24723 (George Tsiolis)
+- Externalize strings from `/app/views/projects/milestones`. !24726 (George Tsiolis)
+- Add OpenTracing instrumentation for Action View Render events. !24728
+- Expose version for each application in cluster_status JSON endpoint. !24791
+- Externalize strings from `/app/views/instance_statistics`. !24809 (George Tsiolis)
+- Update cluster application version on updated and installed status. !24810
+- Project list UI improvements. !24855
+- Externalize strings from `/app/views/email_rejection_mailer`. !24869 (George Tsiolis)
+- Update Gitaly to v1.17.0. !24873
+- Update Workhorse to v8.3.0. !24959
+- Upgrade gitaly to 1.18.0. !24981
+- Update Workhorse to v8.3.1.
+- Upgraded Codesandbox smooshpack package.
+- Creates mixin to reduce code duplication between CE and EE in graph component.
+
+
## 11.7.5 (2019-02-06)
### Fixed (8 changes)
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 66e2ae6c25c..39893559155 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-1.19.1
+1.20.0
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 917d38ec9f9..acd405b1d62 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-8.4.4
+8.6.0
diff --git a/Gemfile b/Gemfile
index 452860d60b4..67ce615c01e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -143,7 +143,7 @@ gem 'diffy', '~> 3.1.0'
gem 'rack', '2.0.6'
group :unicorn do
- gem 'unicorn', '~> 5.1.0'
+ gem 'unicorn', '~> 5.4.1'
gem 'unicorn-worker-killer', '~> 0.4.4'
end
@@ -184,7 +184,7 @@ gem 're2', '~> 1.1.1'
# Misc
-gem 'version_sorter', '~> 2.1.0'
+gem 'version_sorter', '~> 2.2.4'
# Export Ruby Regex to Javascript
gem 'js_regex', '~> 3.1'
@@ -380,7 +380,7 @@ group :test do
gem 'shoulda-matchers', '~> 3.1.2', require: false
gem 'email_spec', '~> 2.2.0'
gem 'json-schema', '~> 2.8.0'
- gem 'webmock', '~> 2.3.2'
+ gem 'webmock', '~> 3.5.1'
gem 'rails-controller-testing'
gem 'sham_rack', '~> 1.3.6'
gem 'concurrent-ruby', '~> 1.1'
diff --git a/Gemfile.lock b/Gemfile.lock
index 92dd5dac0ca..841f85dc7e5 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -357,7 +357,7 @@ GEM
thor
tilt
hangouts-chat (0.0.5)
- hashdiff (0.3.4)
+ hashdiff (0.3.8)
hashie (3.5.7)
hashie-forbidden_attributes (0.1.1)
hashie (>= 3.0)
@@ -422,7 +422,7 @@ GEM
activerecord
kaminari-core (= 1.0.1)
kaminari-core (1.0.1)
- kgio (2.10.0)
+ kgio (2.11.2)
knapsack (1.17.0)
rake
kubeclient (4.2.2)
@@ -666,7 +666,7 @@ GEM
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (3.0.0)
- raindrops (0.18.0)
+ raindrops (0.19.0)
rake (12.3.2)
rb-fsevent (0.10.2)
rb-inotify (0.9.10)
@@ -898,7 +898,7 @@ GEM
unf_ext
unf_ext (0.0.7.5)
unicode-display_width (1.3.2)
- unicorn (5.1.0)
+ unicorn (5.4.1)
kgio (~> 2.6)
raindrops (~> 0.7)
unicorn-worker-killer (0.4.4)
@@ -916,7 +916,7 @@ GEM
validates_hostname (1.0.6)
activerecord (>= 3.0)
activesupport (>= 3.0)
- version_sorter (2.1.0)
+ version_sorter (2.2.4)
virtus (1.0.5)
axiom-types (~> 0.1)
coercible (~> 1.0)
@@ -925,7 +925,7 @@ GEM
vmstat (2.3.0)
warden (1.2.7)
rack (>= 1.0)
- webmock (2.3.2)
+ webmock (3.5.1)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff
@@ -1169,13 +1169,13 @@ DEPENDENCIES
u2f (~> 0.2.1)
uglifier (~> 2.7.2)
unf (~> 0.1.4)
- unicorn (~> 5.1.0)
+ unicorn (~> 5.4.1)
unicorn-worker-killer (~> 0.4.4)
validates_hostname (~> 1.0.6)
- version_sorter (~> 2.1.0)
+ version_sorter (~> 2.2.4)
virtus (~> 1.0.1)
vmstat (~> 2.3.0)
- webmock (~> 2.3.2)
+ webmock (~> 3.5.1)
webpack-rails (~> 0.9.10)
wikicloth (= 0.8.1)
diff --git a/PROCESS.md b/PROCESS.md
index 7fdac098807..1f99cebe081 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -108,7 +108,19 @@ Merge requests that make changes hidden behind a feature flag, or remove an
existing feature flag because a feature is deemed stable, may be merged (and
picked into the stable branches) up to the 19th of the month. Such merge
requests should have the ~"feature flag" label assigned, and don't require a
-corresponding exception request to be created.
+corresponding exception request to be created.
+
+A level of common sense should be applied when deciding whether to have a feature
+behind a feature flag off or on by default.
+
+The following guideliness can be applied to help make this decision:
+
+* If the feature is not fully ready or functioning, the feature flag should be disabled by default.
+* If the feature is ready but there are concerns about performance or impact, the feature flag should be enabled by default, but
+disabled via chatops before deployment on GitLab.com environments. If the performance concern is confirmed, the final release should have the feature flag disabled by default.
+* In most other cases, the feature flag can be enabled by default.
+
+For more information on rolling out changes using feature flags, read [through the documentation](https://docs.gitlab.com/ee/development/rolling_out_changes_using_feature_flags.html).
In order to build the final package and present the feature for self-hosted
customers, the feature flag should be removed. This should happen before the
@@ -156,8 +168,12 @@ on behalf of the community member.
Every new feature or change should be shipped with its corresponding documentation
in accordance with the
-[documentation process](https://docs.gitlab.com/ee/development/documentation/workflow.html)
-and [structure](https://docs.gitlab.com/ee/development/documentation/structure.html).
+[documentation process](https://docs.gitlab.com/ee/development/documentation/feature-change-workflow.html)
+and [structure](https://docs.gitlab.com/ee/development/documentation/structure.html) guides.
+Note that a technical writer will review all changes to documentation. This can occur
+in the same MR as the feature code, but [if there is not sufficient time or need,
+it can be planned via a follow-up issue for doc review](https://docs.gitlab.com/ee/development/documentation/feature-change-workflow.html#1-product-managers-role),
+and another MR, if needed. Regardless, complete docs must be merged with code by the freeze.
#### What happens if these deadlines are missed?
@@ -186,8 +202,6 @@ and to prevent any last minute surprises.
Merge requests should still be complete, following the [definition of done][done].
-#### Feature merge requests
-
If a merge request is not ready, but the developers and Product Manager
responsible for the feature think it is essential that it is in the release,
they can [ask for an exception](#asking-for-an-exception) in advance. This is
@@ -202,34 +216,17 @@ information, see
[Automatic CE->EE merge][automatic_ce_ee_merge] and
[Guidelines for implementing Enterprise Edition features][ee_features].
-#### Documentation merge requests
-
-Documentation is part of the product and must be shipped with the feature.
-
-The single exception for the feature freeze is documentation, and it can
-be left to be **merged up to the 14th** if:
-
-* There is a follow-up issue to add documentation.
-* It is assigned to the developer writing documentation for this feature, and they
- are aware of it.
-* It is in the correct milestone, with the labels ~Documentation, ~Deliverable,
-~missed-deliverable, and "pick into X.Y" applied.
-* It must be reviewed and approved by a technical writer.
-
-For more information read the process for
-[documentation shipped late](https://docs.gitlab.com/ee/development/documentation/workflow.html#documentation-shipped-late).
-
### After the 7th
Once the stable branch is frozen, the only MRs that can be cherry-picked into
the stable branch are:
* Fixes for [regressions](#regressions) where the affected version `xx.x` in `regression:xx.x` is the current release. See [Managing bugs](#managing-bugs) section.
-* Fixes for security issues
-* Fixes or improvements to automated QA scenarios
-* [Documentation updates](https://docs.gitlab.com/ee/development/documentation/workflow.html#documentation-shipped-late) for changes in the same release
-* New or updated translations (as long as they do not touch application code)
-* Changes that are behind a feature flag and have the ~"feature flag" label
+* Fixes for security issues.
+* Fixes or improvements to automated QA scenarios.
+* [Documentation improvements](https://docs.gitlab.com/ee/development/documentation/workflow.html) for feature changes made in the same release, though initial docs for these features should have already been merged by the freeze, as required.
+* New or updated translations (as long as they do not touch application code).
+* Changes that are behind a feature flag and have the ~"feature flag" label.
During the feature freeze all merge requests that are meant to go into the
upcoming release should have the correct milestone assigned _and_ the
diff --git a/VERSION b/VERSION
index 106400c03a2..00a946e3be3 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-11.8.0-pre
+11.9.0-pre
diff --git a/app/assets/javascripts/badges/components/badge_form.vue b/app/assets/javascripts/badges/components/badge_form.vue
index 85a15b38de1..df74eb2c2f7 100644
--- a/app/assets/javascripts/badges/components/badge_form.vue
+++ b/app/assets/javascripts/badges/components/badge_form.vue
@@ -90,7 +90,7 @@ export default {
},
badgeImageUrlExample() {
const exampleUrl =
- 'https://example.gitlab.com/%{project_path}/badges/%{default_branch}/badge.svg';
+ 'https://example.gitlab.com/%{project_path}/badges/%{default_branch}/pipeline.svg';
return sprintf(s__('Badges|e.g. %{exampleUrl}'), {
exampleUrl,
});
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index f0ce2579ee7..8f47931d14a 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -4,6 +4,7 @@ import Icon from '~/vue_shared/components/icon.vue';
import { __ } from '~/locale';
import createFlash from '~/flash';
import { GlLoadingIcon } from '@gitlab/ui';
+import PanelResizer from '~/vue_shared/components/panel_resizer.vue';
import eventHub from '../../notes/event_hub';
import CompareVersions from './compare_versions.vue';
import DiffFile from './diff_file.vue';
@@ -11,6 +12,13 @@ import NoChanges from './no_changes.vue';
import HiddenFilesWarning from './hidden_files_warning.vue';
import CommitWidget from './commit_widget.vue';
import TreeList from './tree_list.vue';
+import {
+ TREE_LIST_WIDTH_STORAGE_KEY,
+ INITIAL_TREE_WIDTH,
+ MIN_TREE_WIDTH,
+ MAX_TREE_WIDTH,
+ TREE_HIDE_STATS_WIDTH,
+} from '../constants';
export default {
name: 'DiffsApp',
@@ -23,6 +31,7 @@ export default {
CommitWidget,
TreeList,
GlLoadingIcon,
+ PanelResizer,
},
props: {
endpoint: {
@@ -54,8 +63,12 @@ export default {
},
},
data() {
+ const treeWidth =
+ parseInt(localStorage.getItem(TREE_LIST_WIDTH_STORAGE_KEY), 10) || INITIAL_TREE_WIDTH;
+
return {
assignedDiscussions: false,
+ treeWidth,
};
},
computed: {
@@ -96,6 +109,9 @@ export default {
this.startVersion.version_index === this.mergeRequestDiff.version_index)
);
},
+ hideFileStats() {
+ return this.treeWidth <= TREE_HIDE_STATS_WIDTH;
+ },
},
watch: {
diffViewType() {
@@ -142,6 +158,7 @@ export default {
'startRenderDiffsQueue',
'assignDiscussionsToDiff',
'setHighlightedRow',
+ 'cacheTreeListWidth',
]),
fetchData() {
this.fetchDiffFiles()
@@ -184,6 +201,8 @@ export default {
}
},
},
+ minTreeWidth: MIN_TREE_WIDTH,
+ maxTreeWidth: MAX_TREE_WIDTH,
};
</script>
@@ -209,7 +228,21 @@ export default {
:data-can-create-note="getNoteableData.current_user.can_create_note"
class="files d-flex prepend-top-default"
>
- <div v-show="showTreeList" class="diff-tree-list"><tree-list /></div>
+ <div
+ v-show="showTreeList"
+ :style="{ width: `${treeWidth}px` }"
+ class="diff-tree-list js-diff-tree-list"
+ >
+ <panel-resizer
+ :size.sync="treeWidth"
+ :start-size="treeWidth"
+ :min-size="$options.minTreeWidth"
+ :max-size="$options.maxTreeWidth"
+ side="right"
+ @resize-end="cacheTreeListWidth"
+ />
+ <tree-list :hide-file-stats="hideFileStats" />
+ </div>
<div class="diff-files-holder">
<commit-widget v-if="commit" :commit="commit" />
<template v-if="renderDiffFiles">
diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue
index 6dc2f5d3f68..cb92093db32 100644
--- a/app/assets/javascripts/diffs/components/diff_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_content.vue
@@ -1,7 +1,8 @@
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
-import EmptyFileViewer from '~/vue_shared/components/diff_viewer/viewers/empty_file.vue';
+import NotDiffableViewer from '~/vue_shared/components/diff_viewer/viewers/not_diffable.vue';
+import NoPreviewViewer from '~/vue_shared/components/diff_viewer/viewers/no_preview.vue';
import InlineDiffView from './inline_diff_view.vue';
import ParallelDiffView from './parallel_diff_view.vue';
import NoteForm from '../../notes/components/note_form.vue';
@@ -9,6 +10,7 @@ import ImageDiffOverlay from './image_diff_overlay.vue';
import DiffDiscussions from './diff_discussions.vue';
import { IMAGE_DIFF_POSITION_TYPE } from '../constants';
import { getDiffMode } from '../store/utils';
+import { diffViewerModes } from '~/ide/constants';
export default {
components: {
@@ -18,7 +20,8 @@ export default {
NoteForm,
DiffDiscussions,
ImageDiffOverlay,
- EmptyFileViewer,
+ NotDiffableViewer,
+ NoPreviewViewer,
},
props: {
diffFile: {
@@ -42,11 +45,17 @@ export default {
diffMode() {
return getDiffMode(this.diffFile);
},
+ diffViewerMode() {
+ return this.diffFile.viewer.name;
+ },
isTextFile() {
- return this.diffFile.viewer.name === 'text';
+ return this.diffViewerMode === diffViewerModes.text;
+ },
+ noPreview() {
+ return this.diffViewerMode === diffViewerModes.no_preview;
},
- errorMessage() {
- return this.diffFile.viewer.error;
+ notDiffable() {
+ return this.diffViewerMode === diffViewerModes.not_diffable;
},
diffFileCommentForm() {
return this.getCommentFormForDiffFile(this.diffFile.file_hash);
@@ -78,11 +87,10 @@ export default {
<template>
<div class="diff-content">
- <div v-if="!errorMessage" class="diff-viewer">
+ <div class="diff-viewer">
<template v-if="isTextFile">
- <empty-file-viewer v-if="diffFile.empty" />
<inline-diff-view
- v-else-if="isInlineView"
+ v-if="isInlineView"
:diff-file="diffFile"
:diff-lines="diffFile.highlighted_diff_lines || []"
:help-page-path="helpPagePath"
@@ -94,9 +102,12 @@ export default {
:help-page-path="helpPagePath"
/>
</template>
+ <not-diffable-viewer v-else-if="notDiffable" />
+ <no-preview-viewer v-else-if="noPreview" />
<diff-viewer
v-else
:diff-mode="diffMode"
+ :diff-viewer-mode="diffViewerMode"
:new-path="diffFile.new_path"
:new-sha="diffFile.diff_refs.head_sha"
:old-path="diffFile.old_path"
@@ -132,8 +143,5 @@ export default {
</div>
</diff-viewer>
</div>
- <div v-else class="diff-viewer">
- <div class="nothing-here-block" v-html="errorMessage"></div>
- </div>
</div>
</template>
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 449f7007077..1141a197c6a 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -7,6 +7,7 @@ import { GlLoadingIcon } from '@gitlab/ui';
import eventHub from '../../notes/event_hub';
import DiffFileHeader from './diff_file_header.vue';
import DiffContent from './diff_content.vue';
+import { diffViewerErrors } from '~/ide/constants';
export default {
components: {
@@ -33,15 +34,13 @@ export default {
return {
isLoadingCollapsedDiff: false,
forkMessageVisible: false,
+ isCollapsed: this.file.viewer.collapsed || false,
};
},
computed: {
...mapState('diffs', ['currentDiffFileId']),
...mapGetters(['isNotesFetched']),
...mapGetters('diffs', ['getDiffFileDiscussions']),
- isCollapsed() {
- return this.file.collapsed || false;
- },
viewBlobLink() {
return sprintf(
__('You can %{linkStart}view the blob%{linkEnd} instead.'),
@@ -52,17 +51,6 @@ export default {
false,
);
},
- showExpandMessage() {
- return (
- this.isCollapsed ||
- (!this.file.highlighted_diff_lines &&
- !this.isLoadingCollapsedDiff &&
- !this.file.too_large &&
- this.file.text &&
- !this.file.renamed_file &&
- !this.file.mode_changed)
- );
- },
showLoadingIcon() {
return this.isLoadingCollapsedDiff || (!this.file.renderIt && !this.isCollapsed);
},
@@ -73,9 +61,15 @@ export default {
this.file.parallel_diff_lines.length > 0
);
},
+ isFileTooLarge() {
+ return this.file.viewer.error === diffViewerErrors.too_large;
+ },
+ errorMessage() {
+ return this.file.viewer.error_message;
+ },
},
watch: {
- 'file.collapsed': function fileCollapsedWatch(newVal, oldVal) {
+ isCollapsed: function fileCollapsedWatch(newVal, oldVal) {
if (!newVal && oldVal && !this.hasDiffLines) {
this.handleLoadCollapsedDiff();
}
@@ -85,13 +79,13 @@ export default {
eventHub.$on(`loadCollapsedDiff/${this.file.file_hash}`, this.handleLoadCollapsedDiff);
},
methods: {
- ...mapActions('diffs', ['loadCollapsedDiff', 'assignDiscussionsToDiff']),
+ ...mapActions('diffs', ['loadCollapsedDiff', 'assignDiscussionsToDiff', 'setRenderIt']),
handleToggle() {
if (!this.hasDiffLines) {
this.handleLoadCollapsedDiff();
} else {
- this.file.collapsed = !this.file.collapsed;
- this.file.renderIt = true;
+ this.isCollapsed = !this.isCollapsed;
+ this.setRenderIt(this.file);
}
},
handleLoadCollapsedDiff() {
@@ -100,8 +94,8 @@ export default {
this.loadCollapsedDiff(this.file)
.then(() => {
this.isLoadingCollapsedDiff = false;
- this.file.collapsed = false;
- this.file.renderIt = true;
+ this.isCollapsed = false;
+ this.setRenderIt(this.file);
})
.then(() => {
requestIdleCallback(
@@ -164,21 +158,25 @@ export default {
Cancel
</button>
</div>
-
- <diff-content
- v-if="!isCollapsed && file.renderIt"
- :class="{ hidden: isCollapsed || file.too_large }"
- :diff-file="file"
- :help-page-path="helpPagePath"
- />
<gl-loading-icon v-if="showLoadingIcon" class="diff-content loading" />
- <div v-else-if="showExpandMessage" class="nothing-here-block diff-collapsed">
- {{ __('This diff is collapsed.') }}
- <a class="click-to-expand js-click-to-expand" href="#" @click.prevent="handleToggle">{{
- __('Click to expand it.')
- }}</a>
- </div>
- <div v-if="file.too_large" class="nothing-here-block diff-collapsed js-too-large-diff">
+ <template v-else>
+ <div v-if="errorMessage" class="diff-viewer">
+ <div class="nothing-here-block" v-html="errorMessage"></div>
+ </div>
+ <div v-else-if="isCollapsed" class="nothing-here-block diff-collapsed">
+ {{ __('This diff is collapsed.') }}
+ <a class="click-to-expand js-click-to-expand" href="#" @click.prevent="handleToggle">{{
+ __('Click to expand it.')
+ }}</a>
+ </div>
+ <diff-content
+ v-else
+ :class="{ hidden: isCollapsed || isFileTooLarge }"
+ :diff-file="file"
+ :help-page-path="helpPagePath"
+ />
+ </template>
+ <div v-if="isFileTooLarge" class="nothing-here-block diff-collapsed js-too-large-diff">
{{ __('This source diff could not be displayed because it is too large.') }}
<span v-html="viewBlobLink"></span>
</div>
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index 60586d4a607..2b801898345 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -8,6 +8,7 @@ import FileIcon from '~/vue_shared/components/file_icon.vue';
import { GlTooltipDirective } from '@gitlab/ui';
import { truncateSha } from '~/lib/utils/text_utility';
import { __, s__, sprintf } from '~/locale';
+import { diffViewerModes } from '~/ide/constants';
import EditButton from './edit_button.vue';
import DiffStats from './diff_stats.vue';
@@ -118,6 +119,12 @@ export default {
gfmCopyText() {
return `\`${this.diffFile.file_path}\``;
},
+ isFileRenamed() {
+ return this.diffFile.viewer.name === diffViewerModes.renamed;
+ },
+ isModeChanged() {
+ return this.diffFile.viewer.name === diffViewerModes.mode_changed;
+ },
},
mounted() {
polyfillSticky(this.$refs.header);
@@ -165,7 +172,7 @@ export default {
aria-hidden="true"
css-classes="js-file-icon append-right-5"
/>
- <span v-if="diffFile.renamed_file">
+ <span v-if="isFileRenamed">
<strong
v-gl-tooltip
:title="diffFile.old_path"
@@ -193,7 +200,7 @@ export default {
css-class="btn-default btn-transparent btn-clipboard"
/>
- <small v-if="diffFile.mode_changed" ref="fileMode">
+ <small v-if="isModeChanged" ref="fileMode">
{{ diffFile.a_mode }} → {{ diffFile.b_mode }}
</small>
diff --git a/app/assets/javascripts/diffs/components/tree_list.vue b/app/assets/javascripts/diffs/components/tree_list.vue
index 7e00b994541..8fc3af15bea 100644
--- a/app/assets/javascripts/diffs/components/tree_list.vue
+++ b/app/assets/javascripts/diffs/components/tree_list.vue
@@ -13,6 +13,12 @@ export default {
Icon,
FileRow,
},
+ props: {
+ hideFileStats: {
+ type: Boolean,
+ required: true,
+ },
+ },
data() {
return {
search: '',
@@ -40,6 +46,9 @@ export default {
return acc;
}, []);
},
+ fileRowExtraComponent() {
+ return this.hideFileStats ? null : FileRowStats;
+ },
},
methods: {
...mapActions('diffs', ['toggleTreeOpen', 'scrollToFile', 'toggleFileFinder']),
@@ -48,7 +57,6 @@ export default {
},
},
shortcutKeyCharacter: `${/Mac/i.test(navigator.userAgent) ? '&#8984;' : 'Ctrl'}+P`,
- FileRowStats,
diffTreeFiltering: gon.features && gon.features.diffTreeFiltering,
};
</script>
@@ -98,7 +106,7 @@ export default {
:file="file"
:level="0"
:hide-extra-on-tree="true"
- :extra-component="$options.FileRowStats"
+ :extra-component="fileRowExtraComponent"
:show-changed-icon="true"
@toggleTreeOpen="toggleTreeOpen"
@clickFile="scrollToFile"
diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js
index bd188d9de9e..7002655ea49 100644
--- a/app/assets/javascripts/diffs/constants.js
+++ b/app/assets/javascripts/diffs/constants.js
@@ -36,3 +36,9 @@ export const MR_TREE_SHOW_KEY = 'mr_tree_show';
export const TREE_TYPE = 'tree';
export const TREE_LIST_STORAGE_KEY = 'mr_diff_tree_list';
export const WHITESPACE_STORAGE_KEY = 'mr_show_whitespace';
+export const TREE_LIST_WIDTH_STORAGE_KEY = 'mr_tree_list_width';
+
+export const INITIAL_TREE_WIDTH = 320;
+export const MIN_TREE_WIDTH = 240;
+export const MAX_TREE_WIDTH = 400;
+export const TREE_HIDE_STATS_WIDTH = 260;
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 7fb66ce433b..82ff2e3be76 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -16,7 +16,9 @@ import {
MR_TREE_SHOW_KEY,
TREE_LIST_STORAGE_KEY,
WHITESPACE_STORAGE_KEY,
+ TREE_LIST_WIDTH_STORAGE_KEY,
} from '../constants';
+import { diffViewerModes } from '~/ide/constants';
export const setBaseConfig = ({ commit }, options) => {
const { endpoint, projectPath } = options;
@@ -91,7 +93,7 @@ export const renderFileForDiscussionId = ({ commit, rootState, state }, discussi
commit(types.RENDER_FILE, file);
}
- if (file.collapsed) {
+ if (file.viewer.collapsed) {
eventHub.$emit(`loadCollapsedDiff/${file.file_hash}`);
scrollToElement(document.getElementById(file.file_hash));
} else {
@@ -105,7 +107,8 @@ export const startRenderDiffsQueue = ({ state, commit }) => {
const checkItem = () =>
new Promise(resolve => {
const nextFile = state.diffFiles.find(
- file => !file.renderIt && (!file.collapsed || !file.text),
+ file =>
+ !file.renderIt && (!file.viewer.collapsed || !file.viewer.name === diffViewerModes.text),
);
if (nextFile) {
@@ -128,6 +131,8 @@ export const startRenderDiffsQueue = ({ state, commit }) => {
return checkItem();
};
+export const setRenderIt = ({ commit }, file) => commit(types.RENDER_FILE, file);
+
export const setInlineDiffViewType = ({ commit }) => {
commit(types.SET_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE);
@@ -300,5 +305,9 @@ export const toggleFileFinder = ({ commit }, visible) => {
commit(types.TOGGLE_FILE_FINDER_VISIBLE, visible);
};
+export const cacheTreeListWidth = (_, size) => {
+ localStorage.setItem(TREE_LIST_WIDTH_STORAGE_KEY, size);
+};
+
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js
index 0e1ad654a2b..4e7e5306995 100644
--- a/app/assets/javascripts/diffs/store/getters.js
+++ b/app/assets/javascripts/diffs/store/getters.js
@@ -4,7 +4,8 @@ export const isParallelView = state => state.diffViewType === PARALLEL_DIFF_VIEW
export const isInlineView = state => state.diffViewType === INLINE_DIFF_VIEW_TYPE;
-export const hasCollapsedFile = state => state.diffFiles.some(file => file.collapsed);
+export const hasCollapsedFile = state =>
+ state.diffFiles.some(file => file.viewer && file.viewer.collapsed);
export const commitId = state => (state.commit && state.commit.id ? state.commit.id : null);
diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js
index 062024b8cdd..247d1e65fea 100644
--- a/app/assets/javascripts/diffs/store/utils.js
+++ b/app/assets/javascripts/diffs/store/utils.js
@@ -1,6 +1,6 @@
import _ from 'underscore';
-import { diffModes } from '~/ide/constants';
import { truncatePathMiddleToLength } from '~/lib/utils/text_utility';
+import { diffModes, diffViewerModes } from '~/ide/constants';
import {
LINE_POSITION_LEFT,
LINE_POSITION_RIGHT,
@@ -248,7 +248,8 @@ export function prepareDiffData(diffData) {
Object.assign(file, {
renderIt: showingLines < LINES_TO_BE_RENDERED_DIRECTLY,
- collapsed: file.text && showingLines > MAX_LINES_TO_BE_RENDERED,
+ collapsed:
+ file.viewer.name === diffViewerModes.text && showingLines > MAX_LINES_TO_BE_RENDERED,
discussions: [],
});
}
@@ -404,7 +405,9 @@ export const getDiffMode = diffFile => {
const diffModeKey = Object.keys(diffModes).find(key => diffFile[`${key}_file`]);
return (
diffModes[diffModeKey] ||
- (diffFile.mode_changed && diffModes.mode_changed) ||
+ (diffFile.viewer &&
+ diffFile.viewer.name === diffViewerModes.mode_changed &&
+ diffViewerModes.mode_changed) ||
diffModes.replaced
);
};
diff --git a/app/assets/javascripts/emoji/no_emoji_validator.js b/app/assets/javascripts/emoji/no_emoji_validator.js
new file mode 100644
index 00000000000..0fd4dd74953
--- /dev/null
+++ b/app/assets/javascripts/emoji/no_emoji_validator.js
@@ -0,0 +1,63 @@
+import { __ } from '~/locale';
+import emojiRegex from 'emoji-regex';
+
+const invalidInputClass = 'gl-field-error-outline';
+
+export default class NoEmojiValidator {
+ constructor(opts = {}) {
+ const container = opts.container || '';
+ this.noEmojiEmelents = document.querySelectorAll(`${container} .js-block-emoji`);
+
+ this.noEmojiEmelents.forEach(element =>
+ element.addEventListener('input', this.eventHandler.bind(this)),
+ );
+ }
+
+ eventHandler(event) {
+ this.inputDomElement = event.target;
+ this.inputErrorMessage = this.inputDomElement.nextSibling;
+
+ const { value } = this.inputDomElement;
+
+ this.validatePattern(value);
+ this.setValidationStateAndMessage();
+ }
+
+ validatePattern(value) {
+ const pattern = emojiRegex();
+ this.hasEmojis = new RegExp(pattern).test(value);
+
+ if (this.hasEmojis) {
+ this.inputDomElement.setCustomValidity(__('Invalid input, please avoid emojis'));
+ } else {
+ this.inputDomElement.setCustomValidity('');
+ }
+ }
+
+ setValidationStateAndMessage() {
+ if (!this.inputDomElement.checkValidity()) {
+ this.setInvalidState();
+ } else {
+ this.clearFieldValidationState();
+ }
+ }
+
+ clearFieldValidationState() {
+ this.inputDomElement.classList.remove(invalidInputClass);
+ this.inputErrorMessage.classList.add('hide');
+ }
+
+ setInvalidState() {
+ this.inputDomElement.classList.add(invalidInputClass);
+ this.setErrorMessage();
+ }
+
+ setErrorMessage() {
+ if (this.hasEmojis) {
+ this.inputErrorMessage.innerHTML = this.inputDomElement.validationMessage;
+ } else {
+ this.inputErrorMessage.innerHTML = this.inputDomElement.title;
+ }
+ this.inputErrorMessage.classList.remove('hide');
+ }
+}
diff --git a/app/assets/javascripts/error_tracking/store/actions.js b/app/assets/javascripts/error_tracking/store/actions.js
index 2e192c958ba..11aec312368 100644
--- a/app/assets/javascripts/error_tracking/store/actions.js
+++ b/app/assets/javascripts/error_tracking/store/actions.js
@@ -2,7 +2,7 @@ import Service from '../services';
import * as types from './mutation_types';
import createFlash from '~/flash';
import Poll from '~/lib/utils/poll';
-import { __ } from '~/locale';
+import { __, sprintf } from '~/locale';
let eTagPoll;
@@ -19,9 +19,17 @@ export function startPolling({ commit }, endpoint) {
commit(types.SET_EXTERNAL_URL, data.external_url);
commit(types.SET_LOADING, false);
},
- errorCallback: () => {
+ errorCallback: response => {
+ let errorMessage = '';
+ if (response && response.data && response.data.message) {
+ errorMessage = response.data.message;
+ }
commit(types.SET_LOADING, false);
- createFlash(__('Failed to load errors from Sentry'));
+ createFlash(
+ sprintf(__(`Failed to load errors from Sentry. Error message: %{errorMessage}`), {
+ errorMessage,
+ }),
+ );
},
});
diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js
index 804ebae4555..7c560c89695 100644
--- a/app/assets/javascripts/ide/constants.js
+++ b/app/assets/javascripts/ide/constants.js
@@ -24,6 +24,22 @@ export const diffModes = {
mode_changed: 'mode_changed',
};
+export const diffViewerModes = Object.freeze({
+ not_diffable: 'not_diffable',
+ no_preview: 'no_preview',
+ added: 'added',
+ deleted: 'deleted',
+ renamed: 'renamed',
+ mode_changed: 'mode_changed',
+ text: 'text',
+ image: 'image',
+});
+
+export const diffViewerErrors = Object.freeze({
+ too_large: 'too_large',
+ stored_externally: 'server_side_but_stored_externally',
+});
+
export const rightSidebarViews = {
pipelines: { name: 'pipelines-list', keepAlive: true },
jobsDetail: { name: 'jobs-detail', keepAlive: false },
diff --git a/app/assets/javascripts/import_projects/components/import_projects_table.vue b/app/assets/javascripts/import_projects/components/import_projects_table.vue
new file mode 100644
index 00000000000..777f8fa6691
--- /dev/null
+++ b/app/assets/javascripts/import_projects/components/import_projects_table.vue
@@ -0,0 +1,101 @@
+<script>
+import { mapActions, mapState, mapGetters } from 'vuex';
+import { GlLoadingIcon } from '@gitlab/ui';
+import LoadingButton from '~/vue_shared/components/loading_button.vue';
+import { __, sprintf } from '~/locale';
+import ImportedProjectTableRow from './imported_project_table_row.vue';
+import ProviderRepoTableRow from './provider_repo_table_row.vue';
+import eventHub from '../event_hub';
+
+export default {
+ name: 'ImportProjectsTable',
+ components: {
+ ImportedProjectTableRow,
+ ProviderRepoTableRow,
+ LoadingButton,
+ GlLoadingIcon,
+ },
+ props: {
+ providerTitle: {
+ type: String,
+ required: true,
+ },
+ },
+
+ computed: {
+ ...mapState(['importedProjects', 'providerRepos', 'isLoadingRepos']),
+ ...mapGetters(['isImportingAnyRepo', 'hasProviderRepos', 'hasImportedProjects']),
+
+ emptyStateText() {
+ return sprintf(__('No %{providerTitle} repositories available to import'), {
+ providerTitle: this.providerTitle,
+ });
+ },
+
+ fromHeaderText() {
+ return sprintf(__('From %{providerTitle}'), { providerTitle: this.providerTitle });
+ },
+ },
+
+ mounted() {
+ return this.fetchRepos();
+ },
+
+ beforeDestroy() {
+ this.stopJobsPolling();
+ this.clearJobsEtagPoll();
+ },
+
+ methods: {
+ ...mapActions(['fetchRepos', 'fetchJobs', 'stopJobsPolling', 'clearJobsEtagPoll']),
+
+ importAll() {
+ eventHub.$emit('importAll');
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <div class="d-flex justify-content-between align-items-end flex-wrap mb-3">
+ <p class="light text-nowrap mt-2 my-sm-0">
+ {{ s__('ImportProjects|Select the projects you want to import') }}
+ </p>
+ <loading-button
+ container-class="btn btn-success js-import-all"
+ :loading="isImportingAnyRepo"
+ :label="__('Import all repositories')"
+ :disabled="!hasProviderRepos"
+ type="button"
+ @click="importAll"
+ />
+ </div>
+ <gl-loading-icon
+ v-if="isLoadingRepos"
+ class="js-loading-button-icon import-projects-loading-icon"
+ :size="4"
+ />
+ <div v-else-if="hasProviderRepos || hasImportedProjects" class="table-responsive">
+ <table class="table import-table">
+ <thead>
+ <th class="import-jobs-from-col">{{ fromHeaderText }}</th>
+ <th class="import-jobs-to-col">{{ __('To GitLab') }}</th>
+ <th class="import-jobs-status-col">{{ __('Status') }}</th>
+ <th class="import-jobs-cta-col"></th>
+ </thead>
+ <tbody>
+ <imported-project-table-row
+ v-for="project in importedProjects"
+ :key="project.id"
+ :project="project"
+ />
+ <provider-repo-table-row v-for="repo in providerRepos" :key="repo.id" :repo="repo" />
+ </tbody>
+ </table>
+ </div>
+ <div v-else class="text-center">
+ <strong>{{ emptyStateText }}</strong>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/import_projects/components/import_status.vue b/app/assets/javascripts/import_projects/components/import_status.vue
new file mode 100644
index 00000000000..9e3347a657f
--- /dev/null
+++ b/app/assets/javascripts/import_projects/components/import_status.vue
@@ -0,0 +1,47 @@
+<script>
+import { GlLoadingIcon } from '@gitlab/ui';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import STATUS_MAP from '../constants';
+
+export default {
+ name: 'ImportStatus',
+ components: {
+ CiIcon,
+ GlLoadingIcon,
+ },
+ props: {
+ status: {
+ type: String,
+ required: true,
+ },
+ },
+
+ computed: {
+ mappedStatus() {
+ return STATUS_MAP[this.status];
+ },
+
+ ciIconStatus() {
+ const { icon } = this.mappedStatus;
+
+ return {
+ icon: `status_${icon}`,
+ group: icon,
+ };
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-loading-icon
+ v-if="mappedStatus.loadingIcon"
+ :inline="true"
+ :class="mappedStatus.textClass"
+ class="align-middle mr-2"
+ />
+ <ci-icon v-else css-classes="align-middle mr-2" :status="ciIconStatus" />
+ <span :class="mappedStatus.textClass">{{ mappedStatus.text }}</span>
+ </div>
+</template>
diff --git a/app/assets/javascripts/import_projects/components/imported_project_table_row.vue b/app/assets/javascripts/import_projects/components/imported_project_table_row.vue
new file mode 100644
index 00000000000..ab2bd87ee9f
--- /dev/null
+++ b/app/assets/javascripts/import_projects/components/imported_project_table_row.vue
@@ -0,0 +1,55 @@
+<script>
+import ImportStatus from './import_status.vue';
+import { STATUSES } from '../constants';
+
+export default {
+ name: 'ImportedProjectTableRow',
+ components: {
+ ImportStatus,
+ },
+ props: {
+ project: {
+ type: Object,
+ required: true,
+ },
+ },
+
+ computed: {
+ displayFullPath() {
+ return this.project.fullPath.replace(/^\//, '');
+ },
+
+ isFinished() {
+ return this.project.importStatus === STATUSES.FINISHED;
+ },
+ },
+};
+</script>
+
+<template>
+ <tr class="js-imported-project import-row">
+ <td>
+ <a
+ :href="project.providerLink"
+ rel="noreferrer noopener"
+ target="_blank"
+ class="js-provider-link"
+ >
+ {{ project.importSource }}
+ </a>
+ </td>
+ <td class="js-full-path">{{ displayFullPath }}</td>
+ <td><import-status :status="project.importStatus" /></td>
+ <td>
+ <a
+ v-if="isFinished"
+ class="btn btn-default js-go-to-project"
+ :href="project.fullPath"
+ rel="noreferrer noopener"
+ target="_blank"
+ >
+ {{ __('Go to project') }}
+ </a>
+ </td>
+ </tr>
+</template>
diff --git a/app/assets/javascripts/import_projects/components/provider_repo_table_row.vue b/app/assets/javascripts/import_projects/components/provider_repo_table_row.vue
new file mode 100644
index 00000000000..7cc29fa1b91
--- /dev/null
+++ b/app/assets/javascripts/import_projects/components/provider_repo_table_row.vue
@@ -0,0 +1,110 @@
+<script>
+import { mapState, mapGetters, mapActions } from 'vuex';
+import Select2Select from '~/vue_shared/components/select2_select.vue';
+import { __ } from '~/locale';
+import LoadingButton from '~/vue_shared/components/loading_button.vue';
+import eventHub from '../event_hub';
+import { STATUSES } from '../constants';
+import ImportStatus from './import_status.vue';
+
+export default {
+ name: 'ProviderRepoTableRow',
+ components: {
+ Select2Select,
+ LoadingButton,
+ ImportStatus,
+ },
+ props: {
+ repo: {
+ type: Object,
+ required: true,
+ },
+ },
+
+ data() {
+ return {
+ targetNamespace: this.$store.state.defaultTargetNamespace,
+ newName: this.repo.sanitizedName,
+ };
+ },
+
+ computed: {
+ ...mapState(['namespaces', 'reposBeingImported', 'ciCdOnly']),
+
+ ...mapGetters(['namespaceSelectOptions']),
+
+ importButtonText() {
+ return this.ciCdOnly ? __('Connect') : __('Import');
+ },
+
+ select2Options() {
+ return {
+ data: this.namespaceSelectOptions,
+ containerCssClass:
+ 'import-namespace-select js-namespace-select qa-project-namespace-select',
+ };
+ },
+
+ isLoadingImport() {
+ return this.reposBeingImported.includes(this.repo.id);
+ },
+
+ status() {
+ return this.isLoadingImport ? STATUSES.SCHEDULING : STATUSES.NONE;
+ },
+ },
+
+ created() {
+ eventHub.$on('importAll', () => this.importRepo());
+ },
+
+ methods: {
+ ...mapActions(['fetchImport']),
+
+ importRepo() {
+ return this.fetchImport({
+ newName: this.newName,
+ targetNamespace: this.targetNamespace,
+ repo: this.repo,
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <tr class="qa-project-import-row js-provider-repo import-row">
+ <td>
+ <a
+ :href="repo.providerLink"
+ rel="noreferrer noopener"
+ target="_blank"
+ class="js-provider-link"
+ >
+ {{ repo.fullName }}
+ </a>
+ </td>
+ <td class="d-flex flex-wrap flex-lg-nowrap">
+ <select2-select v-model="targetNamespace" :options="select2Options" />
+ <span class="px-2 import-slash-divider d-flex justify-content-center align-items-center"
+ >/</span
+ >
+ <input
+ v-model="newName"
+ type="text"
+ class="form-control import-project-name-input js-new-name qa-project-path-field"
+ />
+ </td>
+ <td><import-status :status="status" /></td>
+ <td>
+ <button
+ v-if="!isLoadingImport"
+ type="button"
+ class="qa-import-button js-import-button btn btn-default"
+ @click="importRepo"
+ >
+ {{ importButtonText }}
+ </button>
+ </td>
+ </tr>
+</template>
diff --git a/app/assets/javascripts/import_projects/constants.js b/app/assets/javascripts/import_projects/constants.js
new file mode 100644
index 00000000000..ad33ca158d2
--- /dev/null
+++ b/app/assets/javascripts/import_projects/constants.js
@@ -0,0 +1,48 @@
+import { __ } from '../locale';
+
+// The `scheduling` status is only present on the client-side,
+// it is used as the status when we are requesting to start an import.
+
+export const STATUSES = {
+ FINISHED: 'finished',
+ FAILED: 'failed',
+ SCHEDULED: 'scheduled',
+ STARTED: 'started',
+ NONE: 'none',
+ SCHEDULING: 'scheduling',
+};
+
+const STATUS_MAP = {
+ [STATUSES.FINISHED]: {
+ icon: 'success',
+ text: __('Done'),
+ textClass: 'text-success',
+ },
+ [STATUSES.FAILED]: {
+ icon: 'failed',
+ text: __('Failed'),
+ textClass: 'text-danger',
+ },
+ [STATUSES.SCHEDULED]: {
+ icon: 'pending',
+ text: __('Scheduled'),
+ textClass: 'text-warning',
+ },
+ [STATUSES.STARTED]: {
+ icon: 'running',
+ text: __('Running…'),
+ textClass: 'text-info',
+ },
+ [STATUSES.NONE]: {
+ icon: 'created',
+ text: __('Not started'),
+ textClass: 'text-muted',
+ },
+ [STATUSES.SCHEDULING]: {
+ loadingIcon: true,
+ text: __('Scheduling'),
+ textClass: 'text-warning',
+ },
+};
+
+export default STATUS_MAP;
diff --git a/app/assets/javascripts/import_projects/event_hub.js b/app/assets/javascripts/import_projects/event_hub.js
new file mode 100644
index 00000000000..0948c2e5352
--- /dev/null
+++ b/app/assets/javascripts/import_projects/event_hub.js
@@ -0,0 +1,3 @@
+import Vue from 'vue';
+
+export default new Vue();
diff --git a/app/assets/javascripts/import_projects/index.js b/app/assets/javascripts/import_projects/index.js
new file mode 100644
index 00000000000..5c77484aee1
--- /dev/null
+++ b/app/assets/javascripts/import_projects/index.js
@@ -0,0 +1,47 @@
+import Vue from 'vue';
+import { mapActions } from 'vuex';
+import Translate from '../vue_shared/translate';
+import ImportProjectsTable from './components/import_projects_table.vue';
+import { parseBoolean } from '../lib/utils/common_utils';
+import store from './store';
+
+Vue.use(Translate);
+
+export default function mountImportProjectsTable(mountElement) {
+ if (!mountElement) return undefined;
+
+ const {
+ reposPath,
+ provider,
+ providerTitle,
+ canSelectNamespace,
+ jobsPath,
+ importPath,
+ ciCdOnly,
+ } = mountElement.dataset;
+
+ return new Vue({
+ el: mountElement,
+ store,
+
+ created() {
+ this.setInitialData({
+ reposPath,
+ provider,
+ jobsPath,
+ importPath,
+ defaultTargetNamespace: gon.current_username,
+ ciCdOnly: parseBoolean(ciCdOnly),
+ canSelectNamespace: parseBoolean(canSelectNamespace),
+ });
+ },
+
+ methods: {
+ ...mapActions(['setInitialData']),
+ },
+
+ render(createElement) {
+ return createElement(ImportProjectsTable, { props: { providerTitle } });
+ },
+ });
+}
diff --git a/app/assets/javascripts/import_projects/store/actions.js b/app/assets/javascripts/import_projects/store/actions.js
new file mode 100644
index 00000000000..c44500937cc
--- /dev/null
+++ b/app/assets/javascripts/import_projects/store/actions.js
@@ -0,0 +1,106 @@
+import Visibility from 'visibilityjs';
+import * as types from './mutation_types';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import Poll from '~/lib/utils/poll';
+import createFlash from '~/flash';
+import { s__, sprintf } from '~/locale';
+import axios from '~/lib/utils/axios_utils';
+
+let eTagPoll;
+
+export const clearJobsEtagPoll = () => {
+ eTagPoll = null;
+};
+export const stopJobsPolling = () => {
+ if (eTagPoll) eTagPoll.stop();
+};
+export const restartJobsPolling = () => {
+ if (eTagPoll) eTagPoll.restart();
+};
+
+export const setInitialData = ({ commit }, data) => commit(types.SET_INITIAL_DATA, data);
+
+export const requestRepos = ({ commit }, repos) => commit(types.REQUEST_REPOS, repos);
+export const receiveReposSuccess = ({ commit }, repos) =>
+ commit(types.RECEIVE_REPOS_SUCCESS, repos);
+export const receiveReposError = ({ commit }) => commit(types.RECEIVE_REPOS_ERROR);
+export const fetchRepos = ({ state, dispatch }) => {
+ dispatch('requestRepos');
+
+ return axios
+ .get(state.reposPath)
+ .then(({ data }) =>
+ dispatch('receiveReposSuccess', convertObjectPropsToCamelCase(data, { deep: true })),
+ )
+ .then(() => dispatch('fetchJobs'))
+ .catch(() => {
+ createFlash(
+ sprintf(s__('ImportProjects|Requesting your %{provider} repositories failed'), {
+ provider: state.provider,
+ }),
+ );
+
+ dispatch('receiveReposError');
+ });
+};
+
+export const requestImport = ({ commit, state }, repoId) => {
+ if (!state.reposBeingImported.includes(repoId)) commit(types.REQUEST_IMPORT, repoId);
+};
+export const receiveImportSuccess = ({ commit }, { importedProject, repoId }) =>
+ commit(types.RECEIVE_IMPORT_SUCCESS, { importedProject, repoId });
+export const receiveImportError = ({ commit }, repoId) =>
+ commit(types.RECEIVE_IMPORT_ERROR, repoId);
+export const fetchImport = ({ state, dispatch }, { newName, targetNamespace, repo }) => {
+ dispatch('requestImport', repo.id);
+
+ return axios
+ .post(state.importPath, {
+ ci_cd_only: state.ciCdOnly,
+ new_name: newName,
+ repo_id: repo.id,
+ target_namespace: targetNamespace,
+ })
+ .then(({ data }) =>
+ dispatch('receiveImportSuccess', {
+ importedProject: convertObjectPropsToCamelCase(data, { deep: true }),
+ repoId: repo.id,
+ }),
+ )
+ .catch(() => {
+ createFlash(s__('ImportProjects|Importing the project failed'));
+
+ dispatch('receiveImportError', { repoId: repo.id });
+ });
+};
+
+export const receiveJobsSuccess = ({ commit }, updatedProjects) =>
+ commit(types.RECEIVE_JOBS_SUCCESS, updatedProjects);
+export const fetchJobs = ({ state, dispatch }) => {
+ if (eTagPoll) return;
+
+ eTagPoll = new Poll({
+ resource: {
+ fetchJobs: () => axios.get(state.jobsPath),
+ },
+ method: 'fetchJobs',
+ successCallback: ({ data }) =>
+ dispatch('receiveJobsSuccess', convertObjectPropsToCamelCase(data, { deep: true })),
+ errorCallback: () => createFlash(s__('ImportProjects|Updating the imported projects failed')),
+ });
+
+ if (!Visibility.hidden()) {
+ eTagPoll.makeRequest();
+ }
+
+ Visibility.change(() => {
+ if (!Visibility.hidden()) {
+ dispatch('restartJobsPolling');
+ } else {
+ dispatch('stopJobsPolling');
+ }
+ });
+};
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/import_projects/store/getters.js b/app/assets/javascripts/import_projects/store/getters.js
new file mode 100644
index 00000000000..f03474a8404
--- /dev/null
+++ b/app/assets/javascripts/import_projects/store/getters.js
@@ -0,0 +1,20 @@
+export const namespaceSelectOptions = state => {
+ const serializedNamespaces = state.namespaces.map(({ fullPath }) => ({
+ id: fullPath,
+ text: fullPath,
+ }));
+
+ return [
+ { text: 'Groups', children: serializedNamespaces },
+ {
+ text: 'Users',
+ children: [{ id: state.defaultTargetNamespace, text: state.defaultTargetNamespace }],
+ },
+ ];
+};
+
+export const isImportingAnyRepo = state => state.reposBeingImported.length > 0;
+
+export const hasProviderRepos = state => state.providerRepos.length > 0;
+
+export const hasImportedProjects = state => state.importedProjects.length > 0;
diff --git a/app/assets/javascripts/import_projects/store/index.js b/app/assets/javascripts/import_projects/store/index.js
new file mode 100644
index 00000000000..6ac9bfd8189
--- /dev/null
+++ b/app/assets/javascripts/import_projects/store/index.js
@@ -0,0 +1,15 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import state from './state';
+import * as actions from './actions';
+import * as getters from './getters';
+import mutations from './mutations';
+
+Vue.use(Vuex);
+
+export default new Vuex.Store({
+ state: state(),
+ actions,
+ mutations,
+ getters,
+});
diff --git a/app/assets/javascripts/import_projects/store/mutation_types.js b/app/assets/javascripts/import_projects/store/mutation_types.js
new file mode 100644
index 00000000000..6ba3fd6f29e
--- /dev/null
+++ b/app/assets/javascripts/import_projects/store/mutation_types.js
@@ -0,0 +1,11 @@
+export const SET_INITIAL_DATA = 'SET_INITIAL_DATA';
+
+export const REQUEST_REPOS = 'REQUEST_REPOS';
+export const RECEIVE_REPOS_SUCCESS = 'RECEIVE_REPOS_SUCCESS';
+export const RECEIVE_REPOS_ERROR = 'RECEIVE_REPOS_ERROR';
+
+export const REQUEST_IMPORT = 'REQUEST_IMPORT';
+export const RECEIVE_IMPORT_SUCCESS = 'RECEIVE_IMPORT_SUCCESS';
+export const RECEIVE_IMPORT_ERROR = 'RECEIVE_IMPORT_ERROR';
+
+export const RECEIVE_JOBS_SUCCESS = 'RECEIVE_JOBS_SUCCESS';
diff --git a/app/assets/javascripts/import_projects/store/mutations.js b/app/assets/javascripts/import_projects/store/mutations.js
new file mode 100644
index 00000000000..b88de0268e7
--- /dev/null
+++ b/app/assets/javascripts/import_projects/store/mutations.js
@@ -0,0 +1,55 @@
+import Vue from 'vue';
+import * as types from './mutation_types';
+
+export default {
+ [types.SET_INITIAL_DATA](state, data) {
+ Object.assign(state, data);
+ },
+
+ [types.REQUEST_REPOS](state) {
+ state.isLoadingRepos = true;
+ },
+
+ [types.RECEIVE_REPOS_SUCCESS](state, { importedProjects, providerRepos, namespaces }) {
+ state.isLoadingRepos = false;
+
+ state.importedProjects = importedProjects;
+ state.providerRepos = providerRepos;
+ state.namespaces = namespaces;
+ },
+
+ [types.RECEIVE_REPOS_ERROR](state) {
+ state.isLoadingRepos = false;
+ },
+
+ [types.REQUEST_IMPORT](state, repoId) {
+ state.reposBeingImported.push(repoId);
+ },
+
+ [types.RECEIVE_IMPORT_SUCCESS](state, { importedProject, repoId }) {
+ const existingRepoIndex = state.reposBeingImported.indexOf(repoId);
+ if (state.reposBeingImported.includes(repoId))
+ state.reposBeingImported.splice(existingRepoIndex, 1);
+
+ const providerRepoIndex = state.providerRepos.findIndex(
+ providerRepo => providerRepo.id === repoId,
+ );
+ state.providerRepos.splice(providerRepoIndex, 1);
+ state.importedProjects.unshift(importedProject);
+ },
+
+ [types.RECEIVE_IMPORT_ERROR](state, repoId) {
+ const repoIndex = state.reposBeingImported.indexOf(repoId);
+ if (state.reposBeingImported.includes(repoId)) state.reposBeingImported.splice(repoIndex, 1);
+ },
+
+ [types.RECEIVE_JOBS_SUCCESS](state, updatedProjects) {
+ updatedProjects.forEach(updatedProject => {
+ const existingProject = state.importedProjects.find(
+ importedProject => importedProject.id === updatedProject.id,
+ );
+
+ Vue.set(existingProject, 'importStatus', updatedProject.importStatus);
+ });
+ },
+};
diff --git a/app/assets/javascripts/import_projects/store/state.js b/app/assets/javascripts/import_projects/store/state.js
new file mode 100644
index 00000000000..637fef6e53c
--- /dev/null
+++ b/app/assets/javascripts/import_projects/store/state.js
@@ -0,0 +1,15 @@
+export default () => ({
+ reposPath: '',
+ importPath: '',
+ jobsPath: '',
+ currentProjectId: '',
+ provider: '',
+ currentUsername: '',
+ importedProjects: [],
+ providerRepos: [],
+ namespaces: [],
+ reposBeingImported: [],
+ isLoadingRepos: false,
+ canSelectNamespace: false,
+ ciCdOnly: false,
+});
diff --git a/app/assets/javascripts/jobs/components/sidebar.vue b/app/assets/javascripts/jobs/components/sidebar.vue
index a2141dc3760..1691ac62100 100644
--- a/app/assets/javascripts/jobs/components/sidebar.vue
+++ b/app/assets/javascripts/jobs/components/sidebar.vue
@@ -110,7 +110,7 @@ export default {
<div class="sidebar-container">
<div class="blocks-container">
<div class="block d-flex flex-nowrap align-items-center">
- <h4 class="my-0 mr-2">{{ job.name }}</h4>
+ <h4 class="my-0 mr-2 text-break-word">{{ job.name }}</h4>
<div class="flex-grow-1 flex-shrink-0 text-right">
<gl-link
v-if="job.retry_path"
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 63db4938cd7..1b722c0505a 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -78,7 +78,6 @@ function deferredInitialisation() {
initUserPopovers();
if (document.querySelector('.search')) initSearchAutocomplete();
- if (document.querySelector('#js-peek')) initPerformanceBar({ container: '#js-peek' });
addSelectOnFocusBehaviour('.js-select-on-focus');
@@ -145,6 +144,8 @@ document.addEventListener('DOMContentLoaded', () => {
const $sidebarGutterToggle = $('.js-sidebar-toggle');
let bootstrapBreakpoint = bp.getBreakpointSize();
+ if (document.querySelector('#js-peek')) initPerformanceBar({ container: '#js-peek' });
+
initLayoutNav();
// Set the default path for all cookies to GitLab's root directory
diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue
index 376d4114efd..d8947e8ca50 100644
--- a/app/assets/javascripts/notes/components/diff_with_note.vue
+++ b/app/assets/javascripts/notes/components/diff_with_note.vue
@@ -5,6 +5,7 @@ import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue';
import { GlSkeletonLoading } from '@gitlab/ui';
import { getDiffMode } from '~/diffs/store/utils';
+import { diffViewerModes } from '~/ide/constants';
export default {
components: {
@@ -31,6 +32,12 @@ export default {
diffMode() {
return getDiffMode(this.discussion.diff_file);
},
+ diffViewerMode() {
+ return this.discussion.diff_file.viewer.name;
+ },
+ isTextFile() {
+ return this.diffViewerMode === diffViewerModes.text;
+ },
hasTruncatedDiffLines() {
return (
this.discussion.truncated_diff_lines && this.discussion.truncated_diff_lines.length !== 0
@@ -58,18 +65,14 @@ export default {
</script>
<template>
- <div :class="{ 'text-file': discussion.diff_file.text }" class="diff-file file-holder">
+ <div :class="{ 'text-file': isTextFile }" class="diff-file file-holder">
<diff-file-header
:discussion-path="discussion.discussion_path"
:diff-file="discussion.diff_file"
:can-current-user-fork="false"
- :expanded="!discussion.diff_file.collapsed"
+ :expanded="!discussion.diff_file.viewer.collapsed"
/>
- <div
- v-if="discussion.diff_file.text"
- :class="$options.userColorSchemeClass"
- class="diff-content code"
- >
+ <div v-if="isTextFile" :class="$options.userColorSchemeClass" class="diff-content code">
<table>
<template v-if="hasTruncatedDiffLines">
<tr
@@ -109,6 +112,7 @@ export default {
<div v-else>
<diff-viewer
:diff-mode="diffMode"
+ :diff-viewer-mode="diffViewerMode"
:new-path="discussion.diff_file.new_path"
:new-sha="discussion.diff_file.diff_refs.head_sha"
:old-path="discussion.diff_file.old_path"
diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue
index 969d5b69c25..de1ea0f58d6 100644
--- a/app/assets/javascripts/notes/components/note_actions.vue
+++ b/app/assets/javascripts/notes/components/note_actions.vue
@@ -23,11 +23,6 @@ export default {
type: [String, Number],
required: true,
},
- discussionId: {
- type: String,
- required: false,
- default: '',
- },
noteUrl: {
type: String,
required: false,
@@ -176,7 +171,7 @@ export default {
v-if="showReplyButton"
ref="replyButton"
class="js-reply-button"
- :note-id="discussionId"
+ @startReplying="$emit('startReplying')"
/>
<div v-if="canEdit" class="note-actions-item">
<button
diff --git a/app/assets/javascripts/notes/components/note_actions/reply_button.vue b/app/assets/javascripts/notes/components/note_actions/reply_button.vue
index b2f9d7f128a..f50cab81efe 100644
--- a/app/assets/javascripts/notes/components/note_actions/reply_button.vue
+++ b/app/assets/javascripts/notes/components/note_actions/reply_button.vue
@@ -1,5 +1,4 @@
<script>
-import { mapActions } from 'vuex';
import { GlTooltipDirective, GlButton } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
@@ -12,15 +11,6 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
- props: {
- noteId: {
- type: String,
- required: true,
- },
- },
- methods: {
- ...mapActions(['convertToDiscussion']),
- },
};
</script>
@@ -32,7 +22,7 @@ export default {
class="note-action-button"
variant="transparent"
:title="__('Reply to comment')"
- @click="convertToDiscussion(noteId)"
+ @click="$emit('startReplying')"
>
<icon name="comment" css-classes="link-highlight" />
</gl-button>
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index ded084e9b10..2d6fd8b116f 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -26,6 +26,7 @@ import resolvable from '../mixins/resolvable';
import discussionNavigation from '../mixins/discussion_navigation';
import ReplyPlaceholder from './discussion_reply_placeholder.vue';
import jumpToNextDiscussionButton from './discussion_jump_to_next_button.vue';
+import eventHub from '../event_hub';
export default {
name: 'NoteableDiscussion',
@@ -246,6 +247,12 @@ export default {
}
},
},
+ created() {
+ eventHub.$on('startReplying', this.onStartReplying);
+ },
+ beforeDestroy() {
+ eventHub.$off('startReplying', this.onStartReplying);
+ },
methods: {
...mapActions([
'saveNote',
@@ -253,6 +260,7 @@ export default {
'removePlaceholderNotes',
'toggleResolveNote',
'expandDiscussion',
+ 'removeConvertedDiscussion',
]),
truncateSha,
componentName(note) {
@@ -292,6 +300,10 @@ export default {
}
}
+ if (this.convertedDisscussionIds.includes(this.discussion.id)) {
+ this.removeConvertedDiscussion(this.discussion.id);
+ }
+
this.isReplying = false;
this.resetAutoSave();
},
@@ -345,6 +357,11 @@ Please check your network connection and try again.`;
deleteNoteHandler(note) {
this.$emit('noteDeleted', this.discussion, note);
},
+ onStartReplying(discussionId) {
+ if (this.discussion.id === discussionId) {
+ this.showReplyForm();
+ }
+ },
},
};
</script>
@@ -363,30 +380,32 @@ Please check your network connection and try again.`;
:img-size="40"
/>
</div>
- <note-header
- :author="author"
- :created-at="initialDiscussion.created_at"
- :note-id="initialDiscussion.id"
- :include-toggle="true"
- :expanded="discussion.expanded"
- @toggleHandler="toggleDiscussionHandler"
- >
- <span v-html="actionText"></span>
- </note-header>
- <note-edited-text
- v-if="discussion.resolved"
- :edited-at="discussion.resolved_at"
- :edited-by="discussion.resolved_by"
- :action-text="resolvedText"
- class-name="discussion-headline-light js-discussion-headline"
- />
- <note-edited-text
- v-else-if="lastUpdatedAt"
- :edited-at="lastUpdatedAt"
- :edited-by="lastUpdatedBy"
- action-text="Last updated"
- class-name="discussion-headline-light js-discussion-headline"
- />
+ <div class="timeline-content">
+ <note-header
+ :author="author"
+ :created-at="initialDiscussion.created_at"
+ :note-id="initialDiscussion.id"
+ :include-toggle="true"
+ :expanded="discussion.expanded"
+ @toggleHandler="toggleDiscussionHandler"
+ >
+ <span v-html="actionText"></span>
+ </note-header>
+ <note-edited-text
+ v-if="discussion.resolved"
+ :edited-at="discussion.resolved_at"
+ :edited-by="discussion.resolved_by"
+ :action-text="resolvedText"
+ class-name="discussion-headline-light js-discussion-headline"
+ />
+ <note-edited-text
+ v-else-if="lastUpdatedAt"
+ :edited-at="lastUpdatedAt"
+ :edited-by="lastUpdatedBy"
+ action-text="Last updated"
+ class-name="discussion-headline-light js-discussion-headline"
+ />
+ </div>
</div>
<div v-if="shouldShowDiscussions" class="discussion-body">
<component
@@ -405,6 +424,7 @@ Please check your network connection and try again.`;
:help-page-path="helpPagePath"
:show-reply-button="canReply"
@handleDeleteNote="deleteNoteHandler"
+ @startReplying="showReplyForm"
>
<note-edited-text
v-if="discussion.resolved"
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index 56108a58010..04e74a43acc 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -29,11 +29,6 @@ export default {
type: Object,
required: true,
},
- discussion: {
- type: Object,
- required: false,
- default: null,
- },
line: {
type: Object,
required: false,
@@ -49,6 +44,11 @@ export default {
required: false,
default: () => null,
},
+ showReplyButton: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -91,13 +91,6 @@ export default {
}
return '';
},
- showReplyButton() {
- if (!this.discussion || !this.getNoteableData.current_user.can_create_note) {
- return false;
- }
-
- return this.discussion.individual_note && !this.commentsDisabled;
- },
actionText() {
if (!this.commit) {
return '';
@@ -260,10 +253,10 @@ export default {
:is-resolved="note.resolved"
:is-resolving="isResolving"
:resolved-by="note.resolved_by"
- :discussion-id="discussionId"
@handleEdit="editHandler"
@handleDelete="deleteHandler"
@handleResolve="resolveHandler"
+ @startReplying="$emit('startReplying')"
/>
</div>
<div class="timeline-discussion-body">
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index 9eb69dd91ae..8d3f6d902f8 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -64,6 +64,7 @@ export default {
'getNotesDataByProp',
'isLoading',
'commentsDisabled',
+ 'getNoteableData',
]),
noteableType() {
return this.noteableData.noteableType;
@@ -79,6 +80,9 @@ export default {
return this.discussions;
},
+ canReply() {
+ return this.getNoteableData.current_user.can_create_note && !this.commentsDisabled;
+ },
},
watch: {
shouldShow() {
@@ -86,8 +90,15 @@ export default {
this.fetchNotes();
}
},
+ allDiscussions() {
+ if (this.discussonsCount) {
+ this.discussonsCount.textContent = this.allDiscussions.length;
+ }
+ },
},
created() {
+ this.discussonsCount = document.querySelector('.js-discussions-count');
+
this.setNotesData(this.notesData);
this.setNoteableData(this.noteableData);
this.setUserData(this.userData);
@@ -129,6 +140,7 @@ export default {
'setNotesFetchedState',
'expandDiscussion',
'startTaskList',
+ 'convertToDiscussion',
]),
fetchNotes() {
if (this.isFetching) return null;
@@ -176,6 +188,11 @@ export default {
}
}
},
+ startReplying(discussionId) {
+ return this.convertToDiscussion(discussionId)
+ .then(() => this.$nextTick())
+ .then(() => eventHub.$emit('startReplying', discussionId));
+ },
},
systemNote: constants.SYSTEM_NOTE,
};
@@ -206,7 +223,8 @@ export default {
v-else
:key="discussion.id"
:note="discussion.notes[0]"
- :discussion="discussion"
+ :show-reply-button="canReply"
+ @startReplying="startReplying(discussion.id)"
/>
</template>
<noteable-discussion
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index 1ae8b9a0686..1a0dba69a7c 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -443,5 +443,8 @@ export const submitSuggestion = (
export const convertToDiscussion = ({ commit }, noteId) =>
commit(types.CONVERT_TO_DISCUSSION, noteId);
+export const removeConvertedDiscussion = ({ commit }, noteId) =>
+ commit(types.REMOVE_CONVERTED_DISCUSSION, noteId);
+
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/notes/stores/mutation_types.js b/app/assets/javascripts/notes/stores/mutation_types.js
index 2bffedad336..796370920bb 100644
--- a/app/assets/javascripts/notes/stores/mutation_types.js
+++ b/app/assets/javascripts/notes/stores/mutation_types.js
@@ -18,6 +18,7 @@ export const SET_NOTES_LOADING_STATE = 'SET_NOTES_LOADING_STATE';
export const DISABLE_COMMENTS = 'DISABLE_COMMENTS';
export const APPLY_SUGGESTION = 'APPLY_SUGGESTION';
export const CONVERT_TO_DISCUSSION = 'CONVERT_TO_DISCUSSION';
+export const REMOVE_CONVERTED_DISCUSSION = 'REMOVE_CONVERTED_DISCUSSION';
// DISCUSSION
export const COLLAPSE_DISCUSSION = 'COLLAPSE_DISCUSSION';
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index 23ef843bb75..ae6f8b7790a 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -269,4 +269,11 @@ export default {
const convertedDisscussionIds = [...state.convertedDisscussionIds, discussionId];
Object.assign(state, { convertedDisscussionIds });
},
+
+ [types.REMOVE_CONVERTED_DISCUSSION](state, discussionId) {
+ const convertedDisscussionIds = [...state.convertedDisscussionIds];
+
+ convertedDisscussionIds.splice(convertedDisscussionIds.indexOf(discussionId), 1);
+ Object.assign(state, { convertedDisscussionIds });
+ },
};
diff --git a/app/assets/javascripts/pages/import/gitea/status/index.js b/app/assets/javascripts/pages/import/gitea/status/index.js
new file mode 100644
index 00000000000..dcd84f0faf9
--- /dev/null
+++ b/app/assets/javascripts/pages/import/gitea/status/index.js
@@ -0,0 +1,7 @@
+import mountImportProjectsTable from '~/import_projects';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const mountElement = document.getElementById('import-projects-mount-element');
+
+ mountImportProjectsTable(mountElement);
+});
diff --git a/app/assets/javascripts/pages/import/github/status/index.js b/app/assets/javascripts/pages/import/github/status/index.js
new file mode 100644
index 00000000000..dcd84f0faf9
--- /dev/null
+++ b/app/assets/javascripts/pages/import/github/status/index.js
@@ -0,0 +1,7 @@
+import mountImportProjectsTable from '~/import_projects';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const mountElement = document.getElementById('import-projects-mount-element');
+
+ mountImportProjectsTable(mountElement);
+});
diff --git a/app/assets/javascripts/pages/sessions/new/index.js b/app/assets/javascripts/pages/sessions/new/index.js
index d54bff88f70..e1a3f42a71f 100644
--- a/app/assets/javascripts/pages/sessions/new/index.js
+++ b/app/assets/javascripts/pages/sessions/new/index.js
@@ -1,5 +1,6 @@
import $ from 'jquery';
import UsernameValidator from './username_validator';
+import NoEmojiValidator from '../../../emoji/no_emoji_validator';
import SigninTabsMemoizer from './signin_tabs_memoizer';
import OAuthRememberMe from './oauth_remember_me';
import preserveUrlFragment from './preserve_url_fragment';
@@ -7,6 +8,7 @@ import preserveUrlFragment from './preserve_url_fragment';
document.addEventListener('DOMContentLoaded', () => {
new UsernameValidator(); // eslint-disable-line no-new
new SigninTabsMemoizer(); // eslint-disable-line no-new
+ new NoEmojiValidator(); // eslint-disable-line no-new
new OAuthRememberMe({
container: $('.omniauth-container'),
diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js
index 39cd891c111..636308c5401 100644
--- a/app/assets/javascripts/pages/users/user_tabs.js
+++ b/app/assets/javascripts/pages/users/user_tabs.js
@@ -221,7 +221,7 @@ export default class UserTabs {
const monthsAgo = UserTabs.getVisibleCalendarPeriod($calendarWrap);
const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath');
const utcOffset = $calendarWrap.data('utcOffset');
- const calendarHint = __('Issues, merge requests, pushes and comments.');
+ const calendarHint = __('Issues, merge requests, pushes, and comments.');
$calendarWrap.html(CALENDAR_TEMPLATE);
diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
index 0152e2fbe04..244d332f38f 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_actions.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
@@ -59,28 +59,30 @@ export default {
</script>
<template>
<div class="btn-group">
- <gl-button
+ <button
v-gl-tooltip
+ type="button"
:disabled="isLoading"
class="dropdown-new btn btn-default js-pipeline-dropdown-manual-actions"
- title="Manual job"
+ :title="__('Manual job')"
data-toggle="dropdown"
- aria-label="Manual job"
+ :aria-label="__('Manual job')"
>
- <icon name="play" class="icon-play" /> <i class="fa fa-caret-down" aria-hidden="true"> </i>
+ <icon name="play" class="icon-play" />
+ <i class="fa fa-caret-down" aria-hidden="true"></i>
<gl-loading-icon v-if="isLoading" />
- </gl-button>
+ </button>
<ul class="dropdown-menu dropdown-menu-right">
<li v-for="action in actions" :key="action.path">
<gl-button
:class="{ disabled: isActionDisabled(action) }"
:disabled="isActionDisabled(action)"
- class="js-pipeline-action-link no-btn btn"
+ class="js-pipeline-action-link no-btn btn d-flex align-items-center justify-content-between flex-wrap"
@click="onClickAction(action)"
>
{{ action.name }}
- <span v-if="action.scheduled_at" class="pull-right">
+ <span v-if="action.scheduled_at">
<icon name="clock" />
<gl-countdown :end-date-string="action.scheduled_at" />
</span>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue
index 908b10afee6..2ab0ad4d013 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue
@@ -1,5 +1,5 @@
<script>
-import { GlLink, GlButton, GlTooltipDirective } from '@gitlab/ui';
+import { GlLink, GlTooltipDirective } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
export default {
@@ -9,7 +9,6 @@ export default {
components: {
Icon,
GlLink,
- GlButton,
},
props: {
artifacts: {
@@ -21,20 +20,22 @@ export default {
</script>
<template>
<div class="btn-group" role="group">
- <gl-button
+ <button
v-gl-tooltip
- class="dropdown-toggle build-artifacts js-pipeline-dropdown-download"
- title="Artifacts"
+ type="button"
+ class="dropdown-toggle build-artifacts btn btn-default js-pipeline-dropdown-download"
+ :title="__('Artifacts')"
data-toggle="dropdown"
- aria-label="Artifacts"
+ :aria-label="__('Artifacts')"
>
- <icon name="download" /> <i class="fa fa-caret-down" aria-hidden="true"> </i>
- </gl-button>
+ <icon name="download" />
+ <i class="fa fa-caret-down" aria-hidden="true"></i>
+ </button>
<ul class="dropdown-menu dropdown-menu-right">
<li v-for="(artifact, i) in artifacts" :key="i">
- <gl-link :href="artifact.path" rel="nofollow" download>
- Download {{ artifact.name }} artifacts
- </gl-link>
+ <gl-link :href="artifact.path" rel="nofollow" download
+ >Download {{ artifact.name }} artifacts</gl-link
+ >
</li>
</ul>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
index abbbe19c5ef..57c4dfbe3b7 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
@@ -315,7 +315,7 @@ export default {
:endpoint="mr.testResultsPath"
/>
- <div class="mr-widget-section p-0">
+ <div class="mr-widget-section">
<component :is="componentName" :mr="mr" :service="service" />
<section v-if="shouldRenderCollaborationStatus" class="mr-info-list mr-links">
diff --git a/app/assets/javascripts/vue_shared/components/ci_icon.vue b/app/assets/javascripts/vue_shared/components/ci_icon.vue
index b8eb555106f..2f498c4fa2a 100644
--- a/app/assets/javascripts/vue_shared/components/ci_icon.vue
+++ b/app/assets/javascripts/vue_shared/components/ci_icon.vue
@@ -46,6 +46,11 @@ export default {
required: false,
default: false,
},
+ cssClasses: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
computed: {
cssClass() {
@@ -59,5 +64,5 @@ export default {
};
</script>
<template>
- <span :class="cssClass"> <icon :name="icon" :size="size" /> </span>
+ <span :class="cssClass"> <icon :name="icon" :size="size" :css-classes="cssClasses" /> </span>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
index 75c66ed850b..ebb253ff422 100644
--- a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
@@ -1,6 +1,5 @@
<script>
-import { diffModes } from '~/ide/constants';
-import { viewerInformationForPath } from '../content_viewer/lib/viewer_utils';
+import { diffViewerModes, diffModes } from '~/ide/constants';
import ImageDiffViewer from './viewers/image_diff_viewer.vue';
import DownloadDiffViewer from './viewers/download_diff_viewer.vue';
import RenamedFile from './viewers/renamed.vue';
@@ -12,6 +11,10 @@ export default {
type: String,
required: true,
},
+ diffViewerMode: {
+ type: String,
+ required: true,
+ },
newPath: {
type: String,
required: true,
@@ -46,7 +49,7 @@ export default {
},
computed: {
viewer() {
- if (this.diffMode === diffModes.renamed) {
+ if (this.diffViewerMode === diffViewerModes.renamed) {
return RenamedFile;
} else if (this.diffMode === diffModes.mode_changed) {
return ModeChanged;
@@ -54,11 +57,8 @@ export default {
if (!this.newPath) return null;
- const previewInfo = viewerInformationForPath(this.newPath);
- if (!previewInfo) return DownloadDiffViewer;
-
- switch (previewInfo.id) {
- case 'image':
+ switch (this.diffViewerMode) {
+ case diffViewerModes.image:
return ImageDiffViewer;
default:
return DownloadDiffViewer;
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/no_preview.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/no_preview.vue
new file mode 100644
index 00000000000..c5cdddf2f64
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/no_preview.vue
@@ -0,0 +1,5 @@
+<template>
+ <div class="nothing-here-block">
+ {{ __('No preview for this file type') }}
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/not_diffable.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/not_diffable.vue
new file mode 100644
index 00000000000..d4d3038f066
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/not_diffable.vue
@@ -0,0 +1,5 @@
+<template>
+ <div class="nothing-here-block">
+ {{ __('This diff was suppressed by a .gitattributes entry.') }}
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/panel_resizer.vue b/app/assets/javascripts/vue_shared/components/panel_resizer.vue
index bf736a378dd..8d81940eb91 100644
--- a/app/assets/javascripts/vue_shared/components/panel_resizer.vue
+++ b/app/assets/javascripts/vue_shared/components/panel_resizer.vue
@@ -28,11 +28,12 @@ export default {
data() {
return {
size: this.startSize,
+ isDragging: false,
};
},
computed: {
className() {
- return `drag-${this.side}`;
+ return [`position-${this.side}-0`, { 'is-dragging': this.isDragging }];
},
cursorStyle() {
if (this.enabled) {
@@ -57,6 +58,7 @@ export default {
startDrag(e) {
if (this.enabled) {
e.preventDefault();
+ this.isDragging = true;
this.startPos = e.clientX;
this.currentStartSize = this.size;
document.addEventListener('mousemove', this.drag);
@@ -80,6 +82,7 @@ export default {
},
endDrag(e) {
e.preventDefault();
+ this.isDragging = false;
document.removeEventListener('mousemove', this.drag);
this.$emit('resize-end', this.size);
},
@@ -91,7 +94,7 @@ export default {
<div
:class="className"
:style="cursorStyle"
- class="drag-handle"
+ class="position-absolute position-top-0 position-bottom-0 drag-handle"
@mousedown="startDrag"
@dblclick="resetSize"
></div>
diff --git a/app/assets/javascripts/vue_shared/components/select2_select.vue b/app/assets/javascripts/vue_shared/components/select2_select.vue
new file mode 100644
index 00000000000..3074ea859cc
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/select2_select.vue
@@ -0,0 +1,35 @@
+<script>
+import $ from 'jquery';
+import 'select2/select2';
+
+export default {
+ name: 'Select2Select',
+ props: {
+ options: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ value: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+
+ mounted() {
+ $(this.$refs.dropdownInput)
+ .val(this.value)
+ .select2(this.options)
+ .on('change', event => this.$emit('input', event.target.value));
+ },
+
+ beforeDestroy() {
+ $(this.$refs.dropdownInput).select2('destroy');
+ },
+};
+</script>
+
+<template>
+ <input ref="dropdownInput" type="hidden" />
+</template>
diff --git a/app/assets/stylesheets/framework/awards.scss b/app/assets/stylesheets/framework/awards.scss
index ad650d45314..5cfd5bbd4f5 100644
--- a/app/assets/stylesheets/framework/awards.scss
+++ b/app/assets/stylesheets/framework/awards.scss
@@ -15,7 +15,7 @@
margin-top: 3px;
padding: $gl-padding;
z-index: 300;
- width: 300px;
+ width: $award-emoji-width;
font-size: 14px;
background-color: $white-light;
border: 1px solid $border-white-light;
@@ -55,6 +55,10 @@
transform: none;
}
}
+
+ @include media-breakpoint-down(xs) {
+ width: $award-emoji-width-xs;
+ }
}
.emoji-search {
@@ -229,10 +233,10 @@
height: $default-icon-size;
width: $default-icon-size;
border-radius: 50%;
+ }
- path {
- fill: $border-gray-normal;
- }
+ path {
+ fill: $border-gray-normal;
}
}
@@ -243,6 +247,10 @@
left: 10px;
bottom: 6px;
opacity: 0;
+
+ path {
+ fill: $award-emoji-positive-add-lines;
+ }
}
.award-control-text {
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index c5c3b66438c..fa424532879 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -48,6 +48,15 @@
color: $brand-info;
}
+.text-break-word {
+ word-break: break-all;
+}
+
+.text-underline,
+.text-underline:hover {
+ text-decoration: underline;
+}
+
.hint { font-style: italic; color: $gl-gray-400; }
.light { color: $gl-text-color; }
@@ -442,3 +451,15 @@ img.emoji {
.position-left-0 { left: 0; }
.position-right-0 { right: 0; }
.position-top-0 { top: 0; }
+
+.drag-handle {
+ width: 4px;
+
+ &:hover {
+ background-color: $white-normal;
+ }
+
+ &.is-dragging {
+ background-color: $gray-600;
+ }
+}
diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss
index ace46e32b18..3703b7568c8 100644
--- a/app/assets/stylesheets/framework/modal.scss
+++ b/app/assets/stylesheets/framework/modal.scss
@@ -111,10 +111,11 @@ body.modal-open {
flex-grow: 1;
height: 56px;
padding: $gl-btn-padding $gl-btn-padding 0;
+ text-align: right;
- > svg {
- float: right;
- height: 100%;
+ .illustration {
+ height: inherit;
+ width: initial;
}
}
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 530b1154550..dc1a73ed923 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -405,6 +405,8 @@ $status-icon-size: 22px;
$award-emoji-menu-shadow: rgba(0, 0, 0, 0.175);
$award-emoji-positive-add-bg: #fed159;
$award-emoji-positive-add-lines: #bb9c13;
+$award-emoji-width: 376px;
+$award-emoji-width-xs: 300px;
/*
* Search Box
diff --git a/app/assets/stylesheets/page_bundles/ide.scss b/app/assets/stylesheets/page_bundles/ide.scss
index 2ac98b5d18f..a80158943c6 100644
--- a/app/assets/stylesheets/page_bundles/ide.scss
+++ b/app/assets/stylesheets/page_bundles/ide.scss
@@ -682,25 +682,6 @@ $ide-commit-header-height: 48px;
flex: 1;
}
-.drag-handle {
- position: absolute;
- top: 0;
- bottom: 0;
- width: 4px;
-
- &:hover {
- background-color: $white-normal;
- }
-
- &.drag-right {
- right: 0;
- }
-
- &.drag-left {
- left: 0;
- }
-}
-
.ide-commit-list-container {
display: flex;
flex: 1;
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index 65f46e3852a..fa5a182243c 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -75,7 +75,11 @@
@include build-trace-top-bar(35px);
&.has-archived-block {
- top: $header-height + $performance-bar-height + 28px;
+ top: $header-height + 28px;
+
+ .with-performance-bar & {
+ top: $header-height + $performance-bar-height + 28px;
+ }
}
&.affix {
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 528496944e9..d001dff7986 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -1026,12 +1026,30 @@
}
.diff-tree-list {
- width: 320px;
+ position: -webkit-sticky;
+ position: sticky;
+ $top-pos: $header-height + $mr-tabs-height + $mr-version-controls-height + 10px;
+ top: $header-height + $mr-tabs-height + $mr-version-controls-height + 10px;
+ max-height: calc(100vh - #{$top-pos});
+ padding-right: $gl-padding;
+ z-index: 202;
+
+ .with-performance-bar & {
+ $performance-bar-top-pos: $performance-bar-height + $top-pos;
+ top: $performance-bar-top-pos;
+ max-height: calc(100vh - #{$performance-bar-top-pos});
+ }
+
+ .drag-handle {
+ bottom: 16px;
+ transform: translateX(-6px);
+ }
}
.diff-files-holder {
flex: 1;
min-width: 0;
+ z-index: 201;
}
.compare-versions-container {
@@ -1039,23 +1057,12 @@
}
.tree-list-holder {
- position: -webkit-sticky;
- position: sticky;
- $top-pos: $header-height + $mr-tabs-height + $mr-version-controls-height + 10px;
- top: $header-height + $mr-tabs-height + $mr-version-controls-height + 10px;
- max-height: calc(100vh - #{$top-pos});
- padding-right: $gl-padding;
+ height: 100%;
.file-row {
margin-left: 0;
margin-right: 0;
}
-
- .with-performance-bar & {
- $performance-bar-top-pos: $performance-bar-height + $top-pos;
- top: $performance-bar-top-pos;
- max-height: calc(100vh - #{$performance-bar-top-pos});
- }
}
.tree-list-scroll {
diff --git a/app/assets/stylesheets/pages/import.scss b/app/assets/stylesheets/pages/import.scss
index a4f76a9495a..7f800367cad 100644
--- a/app/assets/stylesheets/pages/import.scss
+++ b/app/assets/stylesheets/pages/import.scss
@@ -1,20 +1,51 @@
-.import-jobs-from-col,
.import-jobs-to-col {
- width: 40%;
+ width: 39%;
}
.import-jobs-status-col {
- width: 20%;
+ width: 15%;
}
-.btn-import {
- .loading-icon {
- display: none;
+.import-jobs-cta-col {
+ width: 1%;
+}
+
+.import-project-name-input {
+ border-radius: 0 $border-radius-default $border-radius-default 0;
+ position: relative;
+ left: -1px;
+ max-width: 300px;
+}
+
+.import-namespace-select {
+ width: auto !important;
+
+ > .select2-choice {
+ border-radius: $border-radius-default 0 0 $border-radius-default;
+ position: relative;
+ left: 1px;
}
+}
- &.is-loading {
- .loading-icon {
- display: inline-block;
- }
+.import-slash-divider {
+ background-color: $gray-lightest;
+ border: 1px solid $border-color;
+}
+
+.import-row {
+ height: 55px;
+}
+
+.import-table {
+ .import-jobs-from-col,
+ .import-jobs-to-col,
+ .import-jobs-status-col,
+ .import-jobs-cta-col {
+ border-bottom-width: 1px;
+ padding-left: $gl-padding;
}
}
+
+.import-projects-loading-icon {
+ margin-top: $gl-padding-32;
+}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 790d438c7e2..cfd3faab122 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -82,7 +82,6 @@
}
.mr-widget-body,
-.mr-widget-section,
.mr-widget-content,
.mr-widget-footer {
padding: $gl-padding;
@@ -735,7 +734,7 @@
.mr-version-controls {
position: relative;
- z-index: 103;
+ z-index: 203;
background: $gray-light;
color: $gl-text-color;
margin-top: -1px;
@@ -809,7 +808,7 @@
.merge-request-tabs-holder {
top: $header-height;
- z-index: 200;
+ z-index: 300;
background-color: $white-light;
border-bottom: 1px solid $border-color;
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 23b9e4f9416..7e7eff1346a 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -494,11 +494,6 @@ $note-form-margin-left: 72px;
.discussion-notes {
margin-left: 0;
border-left: 0;
-
- .notes {
- position: relative;
- @include vertical-line(52px);
- }
}
.note-wrapper {
@@ -550,6 +545,11 @@ $note-form-margin-left: 72px;
.note-header-info {
padding-bottom: 0;
}
+
+ .timeline-content {
+ overflow-x: auto;
+ overflow-y: hidden;
+ }
}
.unresolved {
@@ -597,7 +597,6 @@ $note-form-margin-left: 72px;
.note-headline-meta {
display: inline-block;
- white-space: nowrap;
.system-note-message {
white-space: normal;
@@ -607,6 +606,10 @@ $note-form-margin-left: 72px;
color: $gl-text-color-disabled;
}
+ .note-timestamp {
+ white-space: nowrap;
+ }
+
a:hover {
text-decoration: underline;
}
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index e93be1c1ba2..0eae007715a 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Admin::UsersController < Admin::ApplicationController
+ include RoutableActions
+
before_action :user, except: [:index, :new, :create]
before_action :check_impersonation_availability, only: :impersonate
@@ -177,11 +179,13 @@ class Admin::UsersController < Admin::ApplicationController
user == current_user
end
- # rubocop: disable CodeReuse/ActiveRecord
def user
- @user ||= User.find_by!(username: params[:id])
+ @user ||= find_routable!(User, params[:id])
+ end
+
+ def build_canonical_path(user)
+ url_for(safe_params.merge(id: user.to_param))
end
- # rubocop: enable CodeReuse/ActiveRecord
def redirect_back_or_admin_user(options = {})
redirect_back_or_default(default: default_route, options: options)
diff --git a/app/controllers/dashboard/milestones_controller.rb b/app/controllers/dashboard/milestones_controller.rb
index 9484e4d30cd..912036da0ea 100644
--- a/app/controllers/dashboard/milestones_controller.rb
+++ b/app/controllers/dashboard/milestones_controller.rb
@@ -25,8 +25,6 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController
private
def group_milestones
- groups = GroupsFinder.new(current_user, all_available: false).execute
-
DashboardGroupMilestone.build_collection(groups, params)
end
@@ -45,6 +43,6 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController
end
def groups
- @groups ||= GroupsFinder.new(current_user, state_all: true).execute
+ @groups ||= GroupsFinder.new(current_user, all_available: false).execute
end
end
diff --git a/app/controllers/import/gitea_controller.rb b/app/controllers/import/gitea_controller.rb
index f067ef625aa..68ad8650dba 100644
--- a/app/controllers/import/gitea_controller.rb
+++ b/app/controllers/import/gitea_controller.rb
@@ -1,8 +1,10 @@
# frozen_string_literal: true
class Import::GiteaController < Import::GithubController
+ extend ::Gitlab::Utils::Override
+
def new
- if session[access_token_key].present? && session[host_key].present?
+ if session[access_token_key].present? && provider_url.present?
redirect_to status_import_url
end
end
@@ -12,8 +14,8 @@ class Import::GiteaController < Import::GithubController
super
end
+ # Must be defined or it will 404
def status
- @gitea_host_url = session[host_key]
super
end
@@ -23,25 +25,33 @@ class Import::GiteaController < Import::GithubController
:"#{provider}_host_url"
end
- # Overridden methods
+ override :provider
def provider
:gitea
end
+ override :provider_url
+ def provider_url
+ session[host_key]
+ end
+
# Gitea is not yet an OAuth provider
# See https://github.com/go-gitea/gitea/issues/27
+ override :logged_in_with_provider?
def logged_in_with_provider?
false
end
+ override :provider_auth
def provider_auth
- if session[access_token_key].blank? || session[host_key].blank?
+ if session[access_token_key].blank? || provider_url.blank?
redirect_to new_import_gitea_url,
alert: 'You need to specify both an Access Token and a Host URL.'
end
end
+ override :client_options
def client_options
- { host: session[host_key], api_version: 'v1' }
+ { host: provider_url, api_version: 'v1' }
end
end
diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb
index 3fbc0817e95..aa4aa0fbdac 100644
--- a/app/controllers/import/github_controller.rb
+++ b/app/controllers/import/github_controller.rb
@@ -1,8 +1,11 @@
# frozen_string_literal: true
class Import::GithubController < Import::BaseController
+ include ImportHelper
+
before_action :verify_import_enabled
- before_action :provider_auth, only: [:status, :jobs, :create]
+ before_action :provider_auth, only: [:status, :realtime_changes, :create]
+ before_action :expire_etag_cache, only: [:status, :create]
rescue_from Octokit::Unauthorized, with: :provider_unauthorized
@@ -24,30 +27,37 @@ class Import::GithubController < Import::BaseController
redirect_to status_import_url
end
- # rubocop: disable CodeReuse/ActiveRecord
def status
- @repos = client.repos
- @already_added_projects = find_already_added_projects(provider)
- already_added_projects_names = @already_added_projects.pluck(:import_source)
-
- @repos.reject! { |repo| already_added_projects_names.include? repo.full_name }
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- def jobs
- render json: find_jobs(provider)
+ # Request repos to display error page if provider token is invalid
+ # Improving in https://gitlab.com/gitlab-org/gitlab-ce/issues/55585
+ client_repos
+
+ respond_to do |format|
+ format.json do
+ render json: { imported_projects: serialized_imported_projects,
+ provider_repos: serialized_provider_repos,
+ namespaces: serialized_namespaces }
+ end
+ format.html
+ end
end
def create
result = Import::GithubService.new(client, current_user, import_params).execute(access_params, provider)
if result[:status] == :success
- render json: ProjectSerializer.new.represent(result[:project])
+ render json: serialized_imported_projects(result[:project])
else
render json: { errors: result[:message] }, status: result[:http_status]
end
end
+ def realtime_changes
+ Gitlab::PollingInterval.set_header(response, interval: 3_000)
+
+ render json: find_jobs(provider)
+ end
+
private
def import_params
@@ -58,10 +68,45 @@ class Import::GithubController < Import::BaseController
[:repo_id, :new_name, :target_namespace]
end
+ def serialized_imported_projects(projects = already_added_projects)
+ ProjectSerializer.new.represent(projects, serializer: :import, provider_url: provider_url)
+ end
+
+ def serialized_provider_repos
+ repos = client_repos.reject { |repo| already_added_project_names.include? repo.full_name }
+ ProviderRepoSerializer.new(current_user: current_user).represent(repos, provider: provider, provider_url: provider_url)
+ end
+
+ def serialized_namespaces
+ NamespaceSerializer.new.represent(namespaces)
+ end
+
+ def already_added_projects
+ @already_added_projects ||= find_already_added_projects(provider)
+ end
+
+ def already_added_project_names
+ @already_added_projects_names ||= already_added_projects.pluck(:import_source) # rubocop:disable CodeReuse/ActiveRecord
+ end
+
+ def namespaces
+ current_user.manageable_groups_with_routes
+ end
+
+ def expire_etag_cache
+ Gitlab::EtagCaching::Store.new.tap do |store|
+ store.touch(realtime_changes_path)
+ end
+ end
+
def client
@client ||= Gitlab::LegacyGithubImport::Client.new(session[access_token_key], client_options)
end
+ def client_repos
+ @client_repos ||= client.repos
+ end
+
def verify_import_enabled
render_404 unless import_enabled?
end
@@ -74,6 +119,10 @@ class Import::GithubController < Import::BaseController
__send__("#{provider}_import_enabled?") # rubocop:disable GitlabSecurity/PublicSend
end
+ def realtime_changes_path
+ public_send("realtime_changes_import_#{provider}_path", format: :json) # rubocop:disable GitlabSecurity/PublicSend
+ end
+
def new_import_url
public_send("new_import_#{provider}_url", extra_import_params) # rubocop:disable GitlabSecurity/PublicSend
end
@@ -105,6 +154,14 @@ class Import::GithubController < Import::BaseController
:github
end
+ def provider_url
+ strong_memoize(:provider_url) do
+ provider = Gitlab::Auth::OAuth::Provider.config_for('github')
+
+ provider&.dig('url').presence || 'https://github.com'
+ end
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def logged_in_with_provider?
current_user.identities.exists?(provider: provider)
diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb
index 94002095739..0227af2c266 100644
--- a/app/controllers/profiles/preferences_controller.rb
+++ b/app/controllers/profiles/preferences_controller.rb
@@ -37,6 +37,14 @@ class Profiles::PreferencesController < Profiles::ApplicationController
end
def preferences_param_names
- [:color_scheme_id, :layout, :dashboard, :project_view, :theme_id, :first_day_of_week]
+ [
+ :color_scheme_id,
+ :layout,
+ :dashboard,
+ :project_view,
+ :theme_id,
+ :first_day_of_week,
+ :preferred_language
+ ]
end
end
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 15248d2d08f..b9c52618d4b 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -104,7 +104,6 @@ class ProfilesController < Profiles::ApplicationController
:username,
:website_url,
:organization,
- :preferred_language,
:private_profile,
:include_private_contributions,
status: [:emoji, :message]
diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb
index ddffbb17ace..518d41bd3fb 100644
--- a/app/controllers/projects/merge_requests/diffs_controller.rb
+++ b/app/controllers/projects/merge_requests/diffs_controller.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
class Projects::MergeRequests::DiffsController < Projects::MergeRequests::ApplicationController
- include DiffForPath
include DiffHelper
include RendersNotes
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index 3fe300dcfc0..edebfc55c17 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -31,20 +31,6 @@ class Projects::TreeController < Projects::ApplicationController
lfs_blob_ids
@last_commit = @repository.last_commit_for_path(@commit.id, @tree.path) || @commit
end
-
- format.js do
- # Disable cache so browser history works
- no_cache_headers
- end
-
- format.json do
- page_title @path.presence || _("Files"), @ref, @project.full_name
-
- # n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/38261
- Gitlab::GitalyClient.allow_n_plus_1_calls do
- render json: TreeSerializer.new(project: @project, repository: @repository, ref: @ref).represent(@tree)
- end
- end
end
end
diff --git a/app/finders/concerns/finder_methods.rb b/app/finders/concerns/finder_methods.rb
index 5290313585f..8de3276184d 100644
--- a/app/finders/concerns/finder_methods.rb
+++ b/app/finders/concerns/finder_methods.rb
@@ -3,13 +3,13 @@
module FinderMethods
# rubocop: disable CodeReuse/ActiveRecord
def find_by!(*args)
- raise_not_found_unless_authorized execute.find_by!(*args)
+ raise_not_found_unless_authorized execute.reorder(nil).find_by!(*args)
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def find_by(*args)
- if_authorized execute.find_by(*args)
+ if_authorized execute.reorder(nil).find_by(*args)
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 23af2e0521c..5870f158690 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -94,6 +94,7 @@ class IssuableFinder
items = by_scope(items)
items = by_created_at(items)
items = by_updated_at(items)
+ items = by_closed_at(items)
items = by_state(items)
items = by_group(items)
items = by_assignee(items)
@@ -353,6 +354,13 @@ class IssuableFinder
items
end
+ def by_closed_at(items)
+ items = items.closed_after(params[:closed_after]) if params[:closed_after].present?
+ items = items.closed_before(params[:closed_before]) if params[:closed_before].present?
+
+ items
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def by_state(items)
case params[:state].to_s
diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb
index fd1b46ba860..b98d8bd1fff 100644
--- a/app/graphql/resolvers/issues_resolver.rb
+++ b/app/graphql/resolvers/issues_resolver.rb
@@ -9,7 +9,30 @@ module Resolvers
argument :iids, [GraphQL::ID_TYPE],
required: false,
description: 'The list of IIDs of issues, e.g., [1, 2]'
-
+ argument :state, Types::IssuableStateEnum,
+ required: false,
+ description: "Current state of Issue"
+ argument :label_name, GraphQL::STRING_TYPE.to_list_type,
+ required: false,
+ description: "Labels applied to the Issue"
+ argument :created_before, Types::TimeType,
+ required: false,
+ description: "Issues created before this date"
+ argument :created_after, Types::TimeType,
+ required: false,
+ description: "Issues created after this date"
+ argument :updated_before, Types::TimeType,
+ required: false,
+ description: "Issues updated before this date"
+ argument :updated_after, Types::TimeType,
+ required: false,
+ description: "Issues updated after this date"
+ argument :closed_before, Types::TimeType,
+ required: false,
+ description: "Issues closed before this date"
+ argument :closed_after, Types::TimeType,
+ required: false,
+ description: "Issues closed after this date"
argument :search, GraphQL::STRING_TYPE,
required: false
argument :sort, Types::Sort,
diff --git a/app/graphql/types/issuable_state_enum.rb b/app/graphql/types/issuable_state_enum.rb
new file mode 100644
index 00000000000..f2f6d6c6cab
--- /dev/null
+++ b/app/graphql/types/issuable_state_enum.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Types
+ class IssuableStateEnum < BaseEnum
+ graphql_name 'IssuableState'
+ description 'State of a GitLab issue or merge request'
+
+ value 'opened'
+ value 'closed'
+ value 'locked'
+ end
+end
diff --git a/app/graphql/types/issue_state_enum.rb b/app/graphql/types/issue_state_enum.rb
new file mode 100644
index 00000000000..6521407fc9d
--- /dev/null
+++ b/app/graphql/types/issue_state_enum.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+module Types
+ class IssueStateEnum < IssuableStateEnum
+ graphql_name 'IssueState'
+ description 'State of a GitLab issue'
+ end
+end
diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb
index a8f2f7914a8..87f6b1f8278 100644
--- a/app/graphql/types/issue_type.rb
+++ b/app/graphql/types/issue_type.rb
@@ -11,7 +11,7 @@ module Types
field :iid, GraphQL::ID_TYPE, null: false
field :title, GraphQL::STRING_TYPE, null: false
field :description, GraphQL::STRING_TYPE, null: true
- field :state, GraphQL::STRING_TYPE, null: false
+ field :state, IssueStateEnum, null: false
field :author, Types::UserType,
null: false,
diff --git a/app/graphql/types/merge_request_state_enum.rb b/app/graphql/types/merge_request_state_enum.rb
new file mode 100644
index 00000000000..92f52726ab3
--- /dev/null
+++ b/app/graphql/types/merge_request_state_enum.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module Types
+ class MergeRequestStateEnum < IssuableStateEnum
+ graphql_name 'MergeRequestState'
+ description 'State of a GitLab merge request'
+
+ value 'merged'
+ end
+end
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index 47b915b451e..7827b6e3717 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -12,7 +12,7 @@ module Types
field :iid, GraphQL::ID_TYPE, null: false
field :title, GraphQL::STRING_TYPE, null: false
field :description, GraphQL::STRING_TYPE, null: true
- field :state, GraphQL::STRING_TYPE, null: true
+ field :state, MergeRequestStateEnum, null: false
field :created_at, Types::TimeType, null: false
field :updated_at, Types::TimeType, null: false
field :source_project, Types::ProjectType, null: true
@@ -38,7 +38,6 @@ module Types
field :should_be_rebased, GraphQL::BOOLEAN_TYPE, method: :should_be_rebased?, null: false
field :rebase_commit_sha, GraphQL::STRING_TYPE, null: true
field :rebase_in_progress, GraphQL::BOOLEAN_TYPE, method: :rebase_in_progress?, null: false
- field :diff_head_sha, GraphQL::STRING_TYPE, null: true
field :merge_commit_message, GraphQL::STRING_TYPE, method: :default_merge_commit_message, null: true, deprecation_reason: "Renamed to defaultMergeCommitMessage"
field :default_merge_commit_message, GraphQL::STRING_TYPE, null: true
field :merge_ongoing, GraphQL::BOOLEAN_TYPE, method: :merge_ongoing?, null: false
diff --git a/app/helpers/import_helper.rb b/app/helpers/import_helper.rb
index d3befd87ccc..3d494c3de6a 100644
--- a/app/helpers/import_helper.rb
+++ b/app/helpers/import_helper.rb
@@ -18,10 +18,8 @@ module ImportHelper
"#{namespace}/#{name}"
end
- def provider_project_link(provider, full_path)
- url = __send__("#{provider}_project_url", full_path) # rubocop:disable GitlabSecurity/PublicSend
-
- link_to full_path, url, target: '_blank', rel: 'noopener noreferrer'
+ def provider_project_link_url(provider_url, full_path)
+ Gitlab::Utils.append_path(provider_url, full_path)
end
def import_will_timeout_message(_ci_cd_only)
@@ -46,10 +44,6 @@ module ImportHelper
_('Please wait while we import the repository for you. Refresh at will.')
end
- def import_github_title
- _('Import repositories from GitHub')
- end
-
def import_github_authorize_message
_('To import GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:')
end
@@ -73,30 +67,4 @@ module ImportHelper
_('Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token.').html_safe % { github_integration_link: github_integration_link }
end
end
-
- def import_githubish_choose_repository_message
- _('Choose which repositories you want to import.')
- end
-
- def import_all_githubish_repositories_button_label
- _('Import all repositories')
- end
-
- private
-
- def github_project_url(full_path)
- Gitlab::Utils.append_path(github_root_url, full_path)
- end
-
- def github_root_url
- strong_memoize(:github_url) do
- provider = Gitlab::Auth::OAuth::Provider.config_for('github')
-
- provider&.dig('url').presence || 'https://github.com'
- end
- end
-
- def gitea_project_url(full_path)
- Gitlab::Utils.append_path(@gitea_host_url, full_path)
- end
end
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 1a471034972..af28e6fcb93 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -200,7 +200,7 @@ module IssuablesHelper
author_output
end
- output << content_tag(:span, (issuable_first_contribution_icon if issuable.first_contribution?), class: 'has-tooltip', title: _('1st contribution!'))
+ output << content_tag(:span, (issuable_first_contribution_icon if issuable.first_contribution?), class: 'has-tooltip prepend-left-4', title: _('1st contribution!'))
output << content_tag(:span, (issuable.task_status if issuable.tasks?), id: "task_status", class: "d-none d-sm-none d-md-inline-block prepend-left-8")
output << content_tag(:span, (issuable.task_status_short if issuable.tasks?), id: "task_status_short", class: "d-md-none")
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index 39f661b5f0c..bd53add80ca 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -227,6 +227,10 @@ module LabelsHelper
"#{action} at #{level} level"
end
+ def labels_sorted_by_title(labels)
+ labels.sort_by(&:title)
+ end
+
# Required for Banzai::Filter::LabelReferenceFilter
module_function :render_colored_label, :text_color_for_bg, :escape_once
end
diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb
index 6c65e573307..ea3bcfc791a 100644
--- a/app/helpers/namespaces_helper.rb
+++ b/app/helpers/namespaces_helper.rb
@@ -5,11 +5,8 @@ module NamespacesHelper
params.dig(:project, :namespace_id) || params[:namespace_id]
end
- # rubocop: disable CodeReuse/ActiveRecord
def namespaces_options(selected = :current_user, display_path: false, groups: nil, extra_group: nil, groups_only: false)
- groups ||= current_user.manageable_groups
- .eager_load(:route)
- .order('routes.path')
+ groups ||= current_user.manageable_groups_with_routes
users = [current_user.namespace]
selected_id = selected
@@ -43,7 +40,6 @@ module NamespacesHelper
grouped_options_for_select(options, selected_id)
end
- # rubocop: enable CodeReuse/ActiveRecord
def namespace_icon(namespace, size = 40)
if namespace.is_a?(Group)
diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
index bc1742e8167..eed529f93db 100644
--- a/app/helpers/preferences_helper.rb
+++ b/app/helpers/preferences_helper.rb
@@ -62,6 +62,10 @@ module PreferencesHelper
Gitlab::ColorSchemes.for_user(current_user).css_class
end
+ def language_choices
+ Gitlab::I18n::AVAILABLE_LANGUAGES.map { |value, label| [label, value] }
+ end
+
private
# Ensure that anyone adding new options updates `DASHBOARD_CHOICES` too
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 372f6d678f6..eb15347b4e1 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -47,6 +47,8 @@ module Ci
has_many :auto_canceled_pipelines, class_name: 'Ci::Pipeline', foreign_key: 'auto_canceled_by_id'
has_many :auto_canceled_jobs, class_name: 'CommitStatus', foreign_key: 'auto_canceled_by_id'
+ has_one :chat_data, class_name: 'Ci::PipelineChatData'
+
accepts_nested_attributes_for :variables, reject_if: :persisted?
delegate :id, to: :project, prefix: true
@@ -315,7 +317,7 @@ module Ci
def ordered_stages
return legacy_stages unless complete?
- if Feature.enabled?('ci_pipeline_persisted_stages')
+ if Feature.enabled?('ci_pipeline_persisted_stages', default_enabled: true)
stages
else
legacy_stages
diff --git a/app/models/ci/pipeline_chat_data.rb b/app/models/ci/pipeline_chat_data.rb
new file mode 100644
index 00000000000..8d37500fec5
--- /dev/null
+++ b/app/models/ci/pipeline_chat_data.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Ci
+ class PipelineChatData < ActiveRecord::Base
+ self.table_name = 'ci_pipeline_chat_data'
+
+ belongs_to :chat_name
+
+ validates :pipeline_id, presence: true
+ validates :chat_name_id, presence: true
+ validates :response_url, presence: true
+ end
+end
diff --git a/app/models/ci/pipeline_enums.rb b/app/models/ci/pipeline_enums.rb
index 2994aaae4aa..4be4fdb1ff2 100644
--- a/app/models/ci/pipeline_enums.rb
+++ b/app/models/ci/pipeline_enums.rb
@@ -22,6 +22,7 @@ module Ci
schedule: 4,
api: 5,
external: 6,
+ chat: 8,
merge_request: 10
}
end
diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb
index 52c440ffb2f..fa7ce363531 100644
--- a/app/models/clusters/applications/prometheus.rb
+++ b/app/models/clusters/applications/prometheus.rb
@@ -6,7 +6,6 @@ module Clusters
include PrometheusAdapter
VERSION = '6.7.3'
- READY_STATUS = [:installed, :updating, :updated, :update_errored].freeze
self.table_name = 'clusters_applications_prometheus'
@@ -25,10 +24,6 @@ module Clusters
end
end
- def ready?
- READY_STATUS.include?(status_name)
- end
-
def chart
'stable/prometheus'
end
diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb
index 7025fc2cc02..be3e6a05e1e 100644
--- a/app/models/clusters/cluster.rb
+++ b/app/models/clusters/cluster.rb
@@ -50,7 +50,7 @@ module Clusters
validates :name, cluster_name: true
validates :cluster_type, presence: true
- validates :domain, allow_blank: true, hostname: { allow_numeric_hostname: true, require_valid_tld: true }
+ validates :domain, allow_blank: true, hostname: { allow_numeric_hostname: true }
validate :restrict_modification, on: :update
validate :no_groups, unless: :group_type?
@@ -99,7 +99,7 @@ module Clusters
where('NOT EXISTS (?)', subquery)
end
- scope :with_knative_installed, -> { joins(:application_knative).merge(Clusters::Applications::Knative.installed) }
+ scope :with_knative_installed, -> { joins(:application_knative).merge(Clusters::Applications::Knative.available) }
scope :preload_knative, -> {
preload(
diff --git a/app/models/clusters/concerns/application_status.rb b/app/models/clusters/concerns/application_status.rb
index 5c0164831bc..1273ed83abe 100644
--- a/app/models/clusters/concerns/application_status.rb
+++ b/app/models/clusters/concerns/application_status.rb
@@ -6,7 +6,14 @@ module Clusters
extend ActiveSupport::Concern
included do
- scope :installed, -> { where(status: self.state_machines[:status].states[:installed].value) }
+ scope :available, -> do
+ where(
+ status: [
+ self.state_machines[:status].states[:installed].value,
+ self.state_machines[:status].states[:updated].value
+ ]
+ )
+ end
state_machine :status, initial: :not_installable do
state :not_installable, value: -2
diff --git a/app/models/commit_collection.rb b/app/models/commit_collection.rb
index 42ec5b5e664..a9a2e9c81eb 100644
--- a/app/models/commit_collection.rb
+++ b/app/models/commit_collection.rb
@@ -20,8 +20,8 @@ class CommitCollection
commits.each(&block)
end
- def committers
- emails = without_merge_commits.map(&:committer_email).uniq
+ def authors
+ emails = without_merge_commits.map(&:author_email).uniq
User.by_any_email(emails)
end
diff --git a/app/models/concerns/closed_at_filterable.rb b/app/models/concerns/closed_at_filterable.rb
new file mode 100644
index 00000000000..239c2e47611
--- /dev/null
+++ b/app/models/concerns/closed_at_filterable.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module ClosedAtFilterable
+ extend ActiveSupport::Concern
+
+ included do
+ scope :closed_before, ->(date) { where(scoped_table[:closed_at].lteq(date)) }
+ scope :closed_after, ->(date) { where(scoped_table[:closed_at].gteq(date)) }
+
+ def self.scoped_table
+ arel_table.alias(table_name)
+ end
+ end
+end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 0a77fbeba08..429a63f83cc 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -23,6 +23,7 @@ module Issuable
include Sortable
include CreatedAtFilterable
include UpdatedAtFilterable
+ include ClosedAtFilterable
# This object is used to gather issuable meta data for displaying
# upvotes, downvotes, notes and closing merge requests count for issues and merge requests
diff --git a/app/models/concerns/reactive_caching.rb b/app/models/concerns/reactive_caching.rb
index d3572875fb3..de77ca3e963 100644
--- a/app/models/concerns/reactive_caching.rb
+++ b/app/models/concerns/reactive_caching.rb
@@ -76,7 +76,7 @@ module ReactiveCaching
begin
data = Rails.cache.read(full_reactive_cache_key(*args))
- yield data if data.present?
+ yield data unless data.nil?
rescue InvalidateReactiveCache
refresh_reactive_cache!(*args)
nil
diff --git a/app/models/error_tracking/project_error_tracking_setting.rb b/app/models/error_tracking/project_error_tracking_setting.rb
index 31084c54bdc..57283a78ea9 100644
--- a/app/models/error_tracking/project_error_tracking_setting.rb
+++ b/app/models/error_tracking/project_error_tracking_setting.rb
@@ -58,7 +58,7 @@ module ErrorTracking
def list_sentry_issues(opts = {})
with_reactive_cache('list_issues', opts.stringify_keys) do |result|
- { issues: result }
+ result
end
end
@@ -69,8 +69,10 @@ module ErrorTracking
def calculate_reactive_cache(request, opts)
case request
when 'list_issues'
- sentry_client.list_issues(**opts.symbolize_keys)
+ { issues: sentry_client.list_issues(**opts.symbolize_keys) }
end
+ rescue Sentry::Client::Error => e
+ { error: e.message }
end
# http://HOST/api/0/projects/ORG/PROJECT
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 2035bffd829..75fca96ce0a 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -286,12 +286,12 @@ class MergeRequest < ActiveRecord::Base
work_in_progress?(title) ? title : "WIP: #{title}"
end
- def committers
- @committers ||= commits.committers
+ def commit_authors
+ @commit_authors ||= commits.authors
end
def authors
- User.from_union([committers, User.where(id: self.author_id)])
+ User.from_union([commit_authors, User.where(id: self.author_id)])
end
# Verifies if title has changed not taking into account WIP prefix
@@ -1322,7 +1322,7 @@ class MergeRequest < ActiveRecord::Base
def base_pipeline
@base_pipeline ||= project.ci_pipelines
.order(id: :desc)
- .find_by(sha: diff_base_sha)
+ .find_by(sha: diff_base_sha, ref: target_branch)
end
def discussions_rendered_on_frontend?
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 712347e76ed..e286a4e57f2 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -12,9 +12,6 @@ class MergeRequestDiff < ActiveRecord::Base
# Don't display more than 100 commits at once
COMMITS_SAFE_SIZE = 100
- ignore_column :st_commits,
- :st_diffs
-
belongs_to :merge_request
manual_inverse_association :merge_request, :merge_request_diff
diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb
index 73a58f2420e..ed78a46eaf3 100644
--- a/app/models/personal_access_token.rb
+++ b/app/models/personal_access_token.rb
@@ -2,8 +2,11 @@
class PersonalAccessToken < ActiveRecord::Base
include Expirable
+ include IgnorableColumn
include TokenAuthenticatable
- add_authentication_token_field :token, digest: true, fallback: true
+
+ add_authentication_token_field :token, digest: true
+ ignore_column :token
REDIS_EXPIRY_TIME = 3.minutes
diff --git a/app/models/project_services/slack_slash_commands_service.rb b/app/models/project_services/slack_slash_commands_service.rb
index 6c82e088231..6a454070fe2 100644
--- a/app/models/project_services/slack_slash_commands_service.rb
+++ b/app/models/project_services/slack_slash_commands_service.rb
@@ -22,6 +22,10 @@ class SlackSlashCommandsService < SlashCommandsService
end
end
+ def chat_responder
+ ::Gitlab::Chat::Responder::Slack
+ end
+
private
def format(text)
diff --git a/app/models/user.rb b/app/models/user.rb
index 0d52006db28..fd32d838e53 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1168,6 +1168,10 @@ class User < ApplicationRecord
Gitlab::ObjectHierarchy.new(owned_or_maintainers_groups).base_and_descendants
end
+ def manageable_groups_with_routes
+ manageable_groups.eager_load(:route).order('routes.path')
+ end
+
def namespaces
namespace_ids = groups.pluck(:id)
namespace_ids.push(namespace.id)
diff --git a/app/serializers/diff_file_base_entity.rb b/app/serializers/diff_file_base_entity.rb
index 06a8db78476..ede9e04b722 100644
--- a/app/serializers/diff_file_base_entity.rb
+++ b/app/serializers/diff_file_base_entity.rb
@@ -72,17 +72,20 @@ class DiffFileBaseEntity < Grape::Entity
expose :old_path
expose :new_path
expose :new_file?, as: :new_file
- expose :collapsed?, as: :collapsed
- expose :text?, as: :text
+ expose :renamed_file?, as: :renamed_file
+ expose :deleted_file?, as: :deleted_file
+
expose :diff_refs
+
expose :stored_externally?, as: :stored_externally
expose :external_storage
- expose :renamed_file?, as: :renamed_file
- expose :deleted_file?, as: :deleted_file
+
expose :mode_changed?, as: :mode_changed
expose :a_mode
expose :b_mode
+ expose :viewer, using: DiffViewerEntity
+
private
def memoized_submodule_links(diff_file)
diff --git a/app/serializers/diff_file_entity.rb b/app/serializers/diff_file_entity.rb
index b0aaec3326d..01ee7af37ed 100644
--- a/app/serializers/diff_file_entity.rb
+++ b/app/serializers/diff_file_entity.rb
@@ -4,12 +4,10 @@ class DiffFileEntity < DiffFileBaseEntity
include CommitsHelper
include IconsHelper
- expose :too_large?, as: :too_large
- expose :empty?, as: :empty
expose :added_lines
expose :removed_lines
- expose :load_collapsed_diff_url, if: -> (diff_file, options) { diff_file.text? && options[:merge_request] } do |diff_file|
+ expose :load_collapsed_diff_url, if: -> (diff_file, options) { diff_file.viewer.collapsed? && options[:merge_request] } do |diff_file|
merge_request = options[:merge_request]
project = merge_request.target_project
@@ -36,10 +34,6 @@ class DiffFileEntity < DiffFileBaseEntity
project_blob_path(project, tree_join(diff_file.content_sha, diff_file.new_path))
end
- expose :viewer, using: DiffViewerEntity do |diff_file|
- diff_file.rich_viewer || diff_file.simple_viewer
- end
-
expose :replaced_view_path, if: -> (_, options) { options[:merge_request] } do |diff_file|
image_diff = diff_file.rich_viewer && diff_file.rich_viewer.partial_name == 'image'
image_replaced = diff_file.old_content_sha && diff_file.old_content_sha != diff_file.content_sha
diff --git a/app/serializers/diff_viewer_entity.rb b/app/serializers/diff_viewer_entity.rb
index 587fa2347fd..45faca6cb2f 100644
--- a/app/serializers/diff_viewer_entity.rb
+++ b/app/serializers/diff_viewer_entity.rb
@@ -1,10 +1,8 @@
# frozen_string_literal: true
class DiffViewerEntity < Grape::Entity
- # Partial name refers directly to a Rails feature, let's avoid
- # using this on the frontend.
expose :partial_name, as: :name
- expose :error do |diff_viewer|
- diff_viewer.render_error_message
- end
+ expose :render_error, as: :error
+ expose :render_error_message, as: :error_message
+ expose :collapsed?, as: :collapsed
end
diff --git a/app/serializers/namespace_basic_entity.rb b/app/serializers/namespace_basic_entity.rb
new file mode 100644
index 00000000000..8bcbb2bca60
--- /dev/null
+++ b/app/serializers/namespace_basic_entity.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class NamespaceBasicEntity < Grape::Entity
+ expose :id
+ expose :full_path
+end
diff --git a/app/serializers/namespace_serializer.rb b/app/serializers/namespace_serializer.rb
new file mode 100644
index 00000000000..bf3f154b558
--- /dev/null
+++ b/app/serializers/namespace_serializer.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class NamespaceSerializer < BaseSerializer
+ entity NamespaceBasicEntity
+end
diff --git a/app/serializers/project_import_entity.rb b/app/serializers/project_import_entity.rb
new file mode 100644
index 00000000000..9b51af685e7
--- /dev/null
+++ b/app/serializers/project_import_entity.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class ProjectImportEntity < ProjectEntity
+ include ImportHelper
+
+ expose :import_source
+ expose :import_status
+ expose :human_import_status_name
+
+ expose :provider_link do |project, options|
+ provider_project_link_url(options[:provider_url], project[:import_source])
+ end
+end
diff --git a/app/serializers/project_serializer.rb b/app/serializers/project_serializer.rb
index 23b96c2fc9e..52ac2fa0e09 100644
--- a/app/serializers/project_serializer.rb
+++ b/app/serializers/project_serializer.rb
@@ -1,5 +1,15 @@
# frozen_string_literal: true
class ProjectSerializer < BaseSerializer
- entity ProjectEntity
+ def represent(project, opts = {})
+ entity =
+ case opts[:serializer]
+ when :import
+ ProjectImportEntity
+ else
+ ProjectEntity
+ end
+
+ super(project, opts, entity)
+ end
end
diff --git a/app/serializers/provider_repo_entity.rb b/app/serializers/provider_repo_entity.rb
new file mode 100644
index 00000000000..d70aaa91324
--- /dev/null
+++ b/app/serializers/provider_repo_entity.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class ProviderRepoEntity < Grape::Entity
+ include ImportHelper
+
+ expose :id
+ expose :full_name
+ expose :owner_name do |provider_repo, options|
+ owner_name(provider_repo, options[:provider])
+ end
+
+ expose :sanitized_name do |provider_repo|
+ sanitize_project_name(provider_repo[:name])
+ end
+
+ expose :provider_link do |provider_repo, options|
+ provider_project_link_url(options[:provider_url], provider_repo[:full_name])
+ end
+
+ private
+
+ def owner_name(provider_repo, provider)
+ provider_repo.dig(:owner, :login) if provider == :github
+ end
+end
diff --git a/app/serializers/provider_repo_serializer.rb b/app/serializers/provider_repo_serializer.rb
new file mode 100644
index 00000000000..8a73f6fe6df
--- /dev/null
+++ b/app/serializers/provider_repo_serializer.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class ProviderRepoSerializer < BaseSerializer
+ entity ProviderRepoEntity
+end
diff --git a/app/serializers/tree_entity.rb b/app/serializers/tree_entity.rb
deleted file mode 100644
index 9b7dc80e1d9..00000000000
--- a/app/serializers/tree_entity.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-class TreeEntity < Grape::Entity
- include RequestAwareEntity
-
- expose :id, :path, :name, :mode
-
- expose :icon do |tree|
- IconsHelper.file_type_icon_class('folder', tree.mode, tree.name)
- end
-
- expose :url do |tree|
- project_tree_path(request.project, File.join(request.ref, tree.path))
- end
-end
diff --git a/app/serializers/tree_root_entity.rb b/app/serializers/tree_root_entity.rb
deleted file mode 100644
index f1cfcd943d8..00000000000
--- a/app/serializers/tree_root_entity.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# frozen_string_literal: true
-
-# TODO: Inherit from TreeEntity, when `Tree` implements `id` and `name` like `Gitlab::Git::Tree`.
-class TreeRootEntity < Grape::Entity
- include RequestAwareEntity
-
- expose :path
-
- expose :trees, using: TreeEntity
- expose :blobs, using: BlobEntity
- expose :submodules, using: SubmoduleEntity
-
- expose :parent_tree_url do |tree|
- path = tree.path.sub(%r{\A/}, '')
- next unless path.present?
-
- path_segments = path.split('/')
- path_segments.pop
- parent_tree_path = path_segments.join('/')
-
- project_tree_path(request.project, File.join(request.ref, parent_tree_path))
- end
-
- expose :last_commit_path do |tree|
- logs_file_project_ref_path(request.project, request.ref, tree.path)
- end
-end
diff --git a/app/serializers/tree_serializer.rb b/app/serializers/tree_serializer.rb
deleted file mode 100644
index 536b8ab1ae2..00000000000
--- a/app/serializers/tree_serializer.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-class TreeSerializer < BaseSerializer
- entity TreeRootEntity
-end
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index 354e53a367c..c4f69175de3 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -36,6 +36,7 @@ module Ci
project: project,
current_user: current_user,
push_options: params[:push_options],
+ chat_data: params[:chat_data],
**extra_options(options))
sequence = Gitlab::Ci::Pipeline::Chain::Sequence
diff --git a/app/services/concerns/users/participable_service.rb b/app/services/concerns/users/participable_service.rb
index 5b408bd96c7..6713b6617ae 100644
--- a/app/services/concerns/users/participable_service.rb
+++ b/app/services/concerns/users/participable_service.rb
@@ -11,7 +11,7 @@ module Users
def noteable_owner
return [] unless noteable && noteable.author.present?
- [as_hash(noteable.author)]
+ [user_as_hash(noteable.author)]
end
def participants_in_noteable
@@ -23,21 +23,24 @@ module Users
def sorted(users)
users.uniq.to_a.compact.sort_by(&:username).map do |user|
- as_hash(user)
+ user_as_hash(user)
end
end
def groups
current_user.authorized_groups.sort_by(&:path).map do |group|
- count = group.users.count
- { username: group.full_path, name: group.full_name, count: count, avatar_url: group.avatar_url }
+ group_as_hash(group)
end
end
private
- def as_hash(user)
- { username: user.username, name: user.name, avatar_url: user.avatar_url }
+ def user_as_hash(user)
+ { type: user.class.name, username: user.username, name: user.name, avatar_url: user.avatar_url }
+ end
+
+ def group_as_hash(group)
+ { type: group.class.name, username: group.full_path, name: group.full_name, avatar_url: group.avatar_url, count: group.users.count }
end
end
end
diff --git a/app/services/error_tracking/list_issues_service.rb b/app/services/error_tracking/list_issues_service.rb
index 4cc35cfa4a8..a6c6bec9598 100644
--- a/app/services/error_tracking/list_issues_service.rb
+++ b/app/services/error_tracking/list_issues_service.rb
@@ -6,15 +6,19 @@ module ErrorTracking
DEFAULT_LIMIT = 20
def execute
- return error('not enabled') unless enabled?
- return error('access denied') unless can_read?
+ return error('Error Tracking is not enabled') unless enabled?
+ return error('Access denied', :unauthorized) unless can_read?
result = project_error_tracking_setting
.list_sentry_issues(issue_status: issue_status, limit: limit)
# our results are not yet ready
unless result
- return error('not ready', :no_content)
+ return error('Not ready. Try again later', :no_content)
+ end
+
+ if result[:error].present?
+ return error(result[:error], :bad_request)
end
success(issues: result[:issues])
diff --git a/app/services/prometheus/adapter_service.rb b/app/services/prometheus/adapter_service.rb
index a791845ba20..3be958e1613 100644
--- a/app/services/prometheus/adapter_service.rb
+++ b/app/services/prometheus/adapter_service.rb
@@ -30,7 +30,7 @@ module Prometheus
return unless deployment_platform.respond_to?(:cluster)
cluster = deployment_platform.cluster
- return unless cluster.application_prometheus&.ready?
+ return unless cluster.application_prometheus&.available?
cluster.application_prometheus
end
diff --git a/app/validators/url_validator.rb b/app/validators/url_validator.rb
index d3e32818dc7..3fd015c3cf5 100644
--- a/app/validators/url_validator.rb
+++ b/app/validators/url_validator.rb
@@ -93,6 +93,12 @@ class UrlValidator < ActiveModel::EachValidator
end
def allow_setting_local_requests?
+ # We cannot use Gitlab::CurrentSettings as ApplicationSetting itself
+ # uses UrlValidator to validate urls. This ends up in a cycle
+ # when Gitlab::CurrentSettings creates an ApplicationSetting which then
+ # calls this validator.
+ #
+ # See https://gitlab.com/gitlab-org/gitlab-ee/issues/9833
ApplicationSetting.current&.allow_local_requests_from_hooks_and_services?
end
end
diff --git a/app/views/clusters/clusters/_form.html.haml b/app/views/clusters/clusters/_form.html.haml
index 7acd9ce0562..9fb91a39387 100644
--- a/app/views/clusters/clusters/_form.html.haml
+++ b/app/views/clusters/clusters/_form.html.haml
@@ -28,7 +28,7 @@
.form-group
%h5= s_('ClusterIntegration|Base domain')
- = field.text_field :base_domain, class: 'col-md-6 form-control js-select-on-focus'
+ = field.text_field :base_domain, class: 'col-md-6 form-control js-select-on-focus qa-base-domain'
.form-text.text-muted
- auto_devops_url = help_page_path('topics/autodevops/index')
- auto_devops_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: auto_devops_url }
@@ -43,4 +43,4 @@
- if can?(current_user, :update_cluster, @cluster)
.form-group
- = field.submit _('Save changes'), class: 'btn btn-success'
+ = field.submit _('Save changes'), class: 'btn btn-success qa-save-domain'
diff --git a/app/views/dashboard/projects/_starred_empty_state.html.haml b/app/views/dashboard/projects/_starred_empty_state.html.haml
new file mode 100644
index 00000000000..bea27f1a456
--- /dev/null
+++ b/app/views/dashboard/projects/_starred_empty_state.html.haml
@@ -0,0 +1,9 @@
+.row.empty-state
+ .col-12
+ .svg-content.svg-250
+ = image_tag 'illustrations/starred_empty.svg'
+ .text-content
+ %h4.text-center
+ = s_("StarredProjectsEmptyState|You don't have starred projects yet.")
+ %p.text-secondary
+ = s_("StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page.")
diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml
index ad08409c8fe..3a45f6df017 100644
--- a/app/views/dashboard/projects/starred.html.haml
+++ b/app/views/dashboard/projects/starred.html.haml
@@ -1,8 +1,8 @@
- @hide_top_links = true
- @no_container = true
-- breadcrumb_title "Projects"
-- page_title "Starred Projects"
-- header_title "Projects", dashboard_projects_path
+- breadcrumb_title _("Projects")
+- page_title _("Starred Projects")
+- header_title _("Projects"), dashboard_projects_path
= render_if_exists "shared/gold_trial_callout"
@@ -13,5 +13,4 @@
- if params[:filter_projects] || any_projects?(@projects)
= render 'projects'
- else
- %h3.page-title You don't have starred projects yet
- %p.slead Visit project page and press on star icon and it will appear on this page.
+ = render 'starred_empty_state'
diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml
index 004a3528d4b..9c7ca6ebbd4 100644
--- a/app/views/devise/shared/_signup_box.html.haml
+++ b/app/views/devise/shared/_signup_box.html.haml
@@ -3,12 +3,12 @@
= form_for(resource, as: "new_#{resource_name}", url: registration_path(resource_name), html: { class: "new_new_user gl-show-field-errors", "aria-live" => "assertive" }) do |f|
.devise-errors
= devise_error_messages!
- .form-group
+ .name.form-group
= f.label :name, 'Full name', class: 'label-bold'
- = f.text_field :name, class: "form-control top qa-new-user-name", required: true, title: "This field is required."
+ = f.text_field :name, class: "form-control top qa-new-user-name js-block-emoji", required: true, title: _("This field is required.")
.username.form-group
= f.label :username, class: 'label-bold'
- = f.text_field :username, class: "form-control middle qa-new-user-username", pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: 'Please create a username with only alphanumeric characters.'
+ = f.text_field :username, class: "form-control middle qa-new-user-username js-block-emoji", pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.")
%p.validation-error.hide Username is already taken.
%p.validation-success.hide Username is available.
%p.validation-pending.hide Checking username availability...
diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml
index f4a29ed18dc..b05c039c85c 100644
--- a/app/views/import/_githubish_status.html.haml
+++ b/app/views/import/_githubish_status.html.haml
@@ -1,56 +1,9 @@
- provider = local_assigns.fetch(:provider)
- provider_title = Gitlab::ImportSources.title(provider)
-%p.light
- = import_githubish_choose_repository_message
-%hr
-%p
- = button_tag class: "btn btn-import btn-success js-import-all" do
- = import_all_githubish_repositories_button_label
- = icon("spinner spin", class: "loading-icon")
-
-.table-responsive
- %table.table.import-jobs
- %colgroup.import-jobs-from-col
- %colgroup.import-jobs-to-col
- %colgroup.import-jobs-status-col
- %thead
- %tr
- %th= _('From %{provider_title}') % { provider_title: provider_title }
- %th= _('To GitLab')
- %th= _('Status')
- %tbody
- - @already_added_projects.each do |project|
- %tr{ id: "project_#{project.id}", class: project_status_css_class(project.import_status) }
- %td
- = provider_project_link(provider, project.import_source)
- %td
- = link_to project.full_path, [project.namespace.becomes(Namespace), project]
- %td.job-status
- = render 'import/project_status', project: project
-
- - @repos.each do |repo|
- %tr{ id: "repo_#{repo.id}", data: { qa: { repo_path: repo.full_name } } }
- %td
- = provider_project_link(provider, repo.full_name)
- %td.import-target
- %fieldset.row
- .input-group
- .project-path.input-group-prepend
- - if current_user.can_select_namespace?
- - selected = params[:namespace_id] || :current_user
- - opts = current_user.can_create_group? ? { extra_group: Group.new(name: repo.owner.login, path: repo.owner.login) } : {}
- = select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'input-group-text select2 js-select-namespace qa-project-namespace-select', tabindex: 1 }
- - else
- = text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true
- %span.input-group-prepend
- .input-group-text /
- = text_field_tag :path, sanitize_project_name(repo.name), class: "input-mini form-control", tabindex: 2, autofocus: true, required: true
- %td.import-actions.job-status
- = button_tag class: "btn btn-import js-add-to-import" do
- = has_ci_cd_only_params? ? _('Connect') : _('Import')
- = icon("spinner spin", class: "loading-icon")
-
-.js-importer-status{ data: { jobs_import_path: url_for([:jobs, :import, provider]),
- import_path: url_for([:import, provider]),
- ci_cd_only: has_ci_cd_only_params?.to_s } }
+#import-projects-mount-element{ data: { provider: provider, provider_title: provider_title,
+ can_select_namespace: current_user.can_select_namespace?.to_s,
+ ci_cd_only: has_ci_cd_only_params?.to_s,
+ repos_path: url_for([:status, :import, provider, format: :json]),
+ jobs_path: url_for([:realtime_changes, :import, provider, format: :json]),
+ import_path: url_for([:import, provider, format: :json]) } }
diff --git a/app/views/import/github/new.html.haml b/app/views/import/github/new.html.haml
index 6ff25f2c842..cf32c5c9387 100644
--- a/app/views/import/github/new.html.haml
+++ b/app/views/import/github/new.html.haml
@@ -4,7 +4,7 @@
- header_title _("Projects"), root_path
%h3.page-title
- = icon 'github', text: import_github_title
+ = icon 'github', text: _('Import repositories from GitHub')
- if github_import_configured?
%p
diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml
index be057be6d1a..ee295e70cce 100644
--- a/app/views/import/github/status.html.haml
+++ b/app/views/import/github/status.html.haml
@@ -2,7 +2,7 @@
- page_title title
- breadcrumb_title title
- header_title _("Projects"), root_path
-%h3.page-title
- = icon 'github', text: import_github_title
+%h3.page-title.mb-0
+ = icon 'github', class: 'fa-2x', text: _('Import repositories from GitHub')
= render 'import/githubish_status', provider: 'github'
diff --git a/app/views/import/manifest/status.html.haml b/app/views/import/manifest/status.html.haml
index 5b2e1005398..3d4abc32b88 100644
--- a/app/views/import/manifest/status.html.haml
+++ b/app/views/import/manifest/status.html.haml
@@ -7,7 +7,7 @@
%p
= button_tag class: "btn btn-import btn-success js-import-all" do
- = import_all_githubish_repositories_button_label
+ = _('Import all repositories')
= icon("spinner spin", class: "loading-icon")
.table-responsive
diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml
index d265f3c44ba..4b84835429c 100644
--- a/app/views/profiles/passwords/new.html.haml
+++ b/app/views/profiles/passwords/new.html.haml
@@ -1,12 +1,13 @@
-- page_title "New Password"
-- header_title "New Password"
-%h3.page-title Set up new password
+- page_title _('New Password')
+- breadcrumb_title _('New Password')
+
+%h3.page-title= _('Set up new password')
%hr
= form_for @user, url: profile_password_path, method: :post do |f|
%p.slead
- Please set a new password before proceeding.
+ = _('Please set a new password before proceeding.')
%br
- After a successful password update you will be redirected to login screen.
+ = _('After a successful password update you will be redirected to login screen.')
= form_errors(@user)
@@ -22,4 +23,4 @@
.col-sm-10
= f.password_field :password_confirmation, required: true, class: 'form-control'
.form-actions
- = f.submit 'Set new password', class: "btn btn-success"
+ = f.submit _('Set new password'), class: 'btn btn-success'
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index 1a9aca1f6bf..bfe1c3ddf33 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -73,6 +73,12 @@
= link_to _('Learn more'), help_page_path('user/profile/preferences', anchor: 'localization'), target: '_blank'
.col-lg-8
.form-group
+ = f.label :preferred_language, class: 'label-bold' do
+ = _('Language')
+ = f.select :preferred_language, language_choices, {}, class: 'select2'
+ .form-text.text-muted
+ = s_('Preferences|This feature is experimental and translations are not complete yet')
+ .form-group
= f.label :first_day_of_week, class: 'label-bold' do
= _('First day of the week')
= f.select :first_day_of_week, first_day_of_week_choices_with_default, {}, class: 'form-control'
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 753316b27e2..4d3d92d09c0 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -95,9 +95,6 @@
= f.select :commit_email, options_for_select(commit_email_select_options(@user), selected: selected_commit_email(@user)),
{ help: s_("Profiles|This email will be used for web based operations, such as edits and merges. %{learn_more}").html_safe % { learn_more: commit_email_docs_link } },
control_class: 'select2 input-lg'
- = f.select :preferred_language, Gitlab::I18n::AVAILABLE_LANGUAGES.map { |value, label| [label, value] },
- { help: s_("Profiles|This feature is experimental and translations are not complete yet") },
- control_class: 'select2 input-lg'
= f.text_field :skype, class: 'input-md', placeholder: s_("Profiles|username")
= f.text_field :linkedin, class: 'input-md', help: s_("Profiles|Your LinkedIn profile name from linkedin.com/in/profilename")
= f.text_field :twitter, class: 'input-md', placeholder: s_("Profiles|@username")
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index c64ad1c8147..91c51d5e091 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -53,9 +53,8 @@
= _('Merge request')
- if branch.name != @repository.root_ref
- = link_to project_compare_index_path(@project, from: @repository.root_ref, to: branch.name),
+ = link_to project_compare_path(@project, @repository.root_ref, branch.name),
class: "btn btn-default #{'prepend-left-10' unless merge_project}",
- method: :post,
title: s_('Branches|Compare') do
= s_('Branches|Compare')
diff --git a/app/views/projects/commit/_ajax_signature.html.haml b/app/views/projects/commit/_ajax_signature.html.haml
index eb677cff5f0..ae9aef5a9b0 100644
--- a/app/views/projects/commit/_ajax_signature.html.haml
+++ b/app/views/projects/commit/_ajax_signature.html.haml
@@ -1,2 +1,2 @@
- if commit.has_signature?
- %a{ href: 'javascript:void(0)', tabindex: 0, class: commit_signature_badge_classes('js-loading-gpg-badge'), data: { toggle: 'tooltip', placement: 'top', title: 'GPG signature (loading...)', 'commit-sha' => commit.sha } }
+ %a{ href: 'javascript:void(0)', tabindex: 0, class: commit_signature_badge_classes('js-loading-gpg-badge'), data: { toggle: 'tooltip', placement: 'top', title: _('GPG signature (loading...)'), 'commit-sha' => commit.sha } }
diff --git a/app/views/projects/commit/_ci_menu.html.haml b/app/views/projects/commit/_ci_menu.html.haml
index 8b6e3e42ea1..41f5fb3dcbd 100644
--- a/app/views/projects/commit/_ci_menu.html.haml
+++ b/app/views/projects/commit/_ci_menu.html.haml
@@ -3,10 +3,10 @@
%ul.nav-links.no-top.no-bottom.commit-ci-menu.nav.nav-tabs
= nav_link(path: 'commit#show') do
= link_to project_commit_path(@project, @commit.id) do
- Changes
+ = _('Changes')
%span.badge.badge-pill= @diffs.size
- if any_pipelines
= nav_link(path: 'commit#pipelines') do
= link_to pipelines_project_commit_path(@project, @commit.id) do
- Pipelines
+ = _('Pipelines')
%span.badge.badge-pill.js-pipelines-mr-count= @commit.pipelines.size
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 90fee2d70be..a0db48bf8ff 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -6,8 +6,8 @@
%strong
#{ s_('CommitBoxTitle|Commit') }
%span.commit-sha= @commit.short_id
- = clipboard_button(text: @commit.id, title: _("Copy commit SHA to clipboard"))
- %span.d-none.d-sm-inline authored
+ = clipboard_button(text: @commit.id, title: _('Copy commit SHA to clipboard'))
+ %span.d-none.d-sm-inline= _('authored')
#{time_ago_with_tooltip(@commit.authored_date)}
%span= s_('ByAuthor|by')
= author_avatar(@commit, size: 24, has_tooltip: false)
@@ -43,13 +43,13 @@
= cherry_pick_commit_link(@commit, project_commit_path(@project, @commit.id), has_tooltip: false)
- if can?(current_user, :push_code, @project)
%li.clearfix
- = link_to s_("CreateTag|Tag"), new_project_tag_path(@project, ref: @commit)
+ = link_to s_('CreateTag|Tag'), new_project_tag_path(@project, ref: @commit)
%li.divider
%li.dropdown-header
#{ _('Download') }
- unless @commit.parents.length > 1
- %li= link_to s_("DownloadCommit|Email Patches"), project_commit_path(@project, @commit, format: :patch), class: "qa-email-patches"
- %li= link_to s_("DownloadCommit|Plain Diff"), project_commit_path(@project, @commit, format: :diff), class: "qa-plain-diff"
+ %li= link_to s_('DownloadCommit|Email Patches'), project_commit_path(@project, @commit, format: :patch), class: "qa-email-patches"
+ %li= link_to s_('DownloadCommit|Plain Diff'), project_commit_path(@project, @commit, format: :diff), class: "qa-plain-diff"
.commit-box{ data: { project_path: project_path(@project) } }
%h3.commit-title
@@ -95,8 +95,5 @@
.well-segment
= icon('info-circle fw')
- This commit is part of merge request
- = succeed '.' do
- = link_to @merge_request.to_reference, diffs_project_merge_request_path(@project, @merge_request, commit_id: @commit.id)
-
- Comments created here will be created in the context of that merge request.
+ - link_to_merge_request = link_to(@merge_request.to_reference, diffs_project_merge_request_path(@project, @merge_request, commit_id: @commit.id))
+ = _('This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request.').html_safe % { link_to_merge_request: link_to_merge_request }
diff --git a/app/views/projects/commit/_limit_exceeded_message.html.haml b/app/views/projects/commit/_limit_exceeded_message.html.haml
index a264f3517c4..7d3c0582d0b 100644
--- a/app/views/projects/commit/_limit_exceeded_message.html.haml
+++ b/app/views/projects/commit/_limit_exceeded_message.html.haml
@@ -1,8 +1,8 @@
-.has-tooltip{ class: "limit-box limit-box-#{objects} prepend-left-5", data: { title: "Project has too many #{label_for_message} to search"} }
+.has-tooltip{ class: "limit-box limit-box-#{objects} prepend-left-5", data: { title: _('Project has too many %{label_for_message} to search') % { label_for_message: label_for_message } } }
.limit-icon
- if objects == :branch
= sprite_icon('fork', size: 12)
- else
= icon('tag')
.limit-message
- %span #{label_for_message.capitalize} unavailable
+ %span= _('%{label_for_message} unavailable') % { label_for_message: label_for_message.capitalize }
diff --git a/app/views/projects/commit/_other_user_signature_badge.html.haml b/app/views/projects/commit/_other_user_signature_badge.html.haml
index d7bf2dc0cb6..bb843bee7c9 100644
--- a/app/views/projects/commit/_other_user_signature_badge.html.haml
+++ b/app/views/projects/commit/_other_user_signature_badge.html.haml
@@ -1,6 +1,6 @@
- title = capture do
- This commit was signed with a different user's verified signature.
+ = _("This commit was signed with a different user's verified signature.")
-- locals = { signature: signature, title: title, label: 'Unverified', css_class: 'invalid', icon: 'status_notfound_borderless', show_user: true }
+- locals = { signature: signature, title: title, label: _('Unverified'), css_class: 'invalid', icon: 'status_notfound_borderless', show_user: true }
= render partial: 'projects/commit/signature_badge', locals: locals
diff --git a/app/views/projects/commit/_same_user_different_email_signature_badge.html.haml b/app/views/projects/commit/_same_user_different_email_signature_badge.html.haml
index 22ffd66ff8e..d282ab4f520 100644
--- a/app/views/projects/commit/_same_user_different_email_signature_badge.html.haml
+++ b/app/views/projects/commit/_same_user_different_email_signature_badge.html.haml
@@ -1,7 +1,6 @@
- title = capture do
- This commit was signed with a verified signature, but the committer email
- is <strong>not verified</strong> to belong to the same user.
+ = _('This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user.').html_safe
-- locals = { signature: signature, title: title, label: 'Unverified', css_class: ['invalid'], icon: 'status_notfound_borderless', show_user: true }
+- locals = { signature: signature, title: title, label: _('Unverified'), css_class: ['invalid'], icon: 'status_notfound_borderless', show_user: true }
= render partial: 'projects/commit/signature_badge', locals: locals
diff --git a/app/views/projects/commit/_signature_badge.html.haml b/app/views/projects/commit/_signature_badge.html.haml
index c4d986ef742..1331fa179fc 100644
--- a/app/views/projects/commit/_signature_badge.html.haml
+++ b/app/views/projects/commit/_signature_badge.html.haml
@@ -19,10 +19,10 @@
.clearfix
= render partial: 'projects/commit/signature_badge_user', locals: { signature: signature }
- GPG Key ID:
+ = _('GPG Key ID:')
%span.monospace= signature.gpg_key_primary_keyid
- = link_to('Learn more about signing commits', help_page_path('user/project/repository/gpg_signed_commits/index.md'), class: 'gpg-popover-help-link')
+ = link_to(_('Learn more about signing commits'), help_page_path('user/project/repository/gpg_signed_commits/index.md'), class: 'gpg-popover-help-link')
%a{ href: 'javascript:void(0)', tabindex: 0, class: css_classes, data: { toggle: 'popover', html: 'true', placement: 'top', title: title, content: content } }
= label
diff --git a/app/views/projects/commit/_unverified_signature_badge.html.haml b/app/views/projects/commit/_unverified_signature_badge.html.haml
index 00e1efe0582..294f916d18f 100644
--- a/app/views/projects/commit/_unverified_signature_badge.html.haml
+++ b/app/views/projects/commit/_unverified_signature_badge.html.haml
@@ -1,6 +1,6 @@
- title = capture do
- This commit was signed with an <strong>unverified</strong> signature.
+ = _('This commit was signed with an <strong>unverified</strong> signature.').html_safe
-- locals = { signature: signature, title: title, label: 'Unverified', css_class: 'invalid', icon: 'status_notfound_borderless' }
+- locals = { signature: signature, title: title, label: _('Unverified'), css_class: 'invalid', icon: 'status_notfound_borderless' }
= render partial: 'projects/commit/signature_badge', locals: locals
diff --git a/app/views/projects/commit/_verified_signature_badge.html.haml b/app/views/projects/commit/_verified_signature_badge.html.haml
index 31408806be7..4964b1b8ee7 100644
--- a/app/views/projects/commit/_verified_signature_badge.html.haml
+++ b/app/views/projects/commit/_verified_signature_badge.html.haml
@@ -1,7 +1,6 @@
- title = capture do
- This commit was signed with a <strong>verified</strong> signature and the
- committer email is verified to belong to the same user.
+ = _('This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user.').html_safe
-- locals = { signature: signature, title: title, label: 'Verified', css_class: 'valid', icon: 'status_success_borderless', show_user: true }
+- locals = { signature: signature, title: title, label: _('Verified'), css_class: 'valid', icon: 'status_success_borderless', show_user: true }
= render partial: 'projects/commit/signature_badge', locals: locals
diff --git a/app/views/projects/commit/pipelines.html.haml b/app/views/projects/commit/pipelines.html.haml
index c66ea873dba..f8c27f4c026 100644
--- a/app/views/projects/commit/pipelines.html.haml
+++ b/app/views/projects/commit/pipelines.html.haml
@@ -1,4 +1,4 @@
-- page_title 'Pipelines', "#{@commit.title} (#{@commit.short_id})", 'Commits'
+- page_title _('Pipelines'), "#{@commit.title} (#{@commit.short_id})", _('Commits')
= render 'commit_box'
= render 'ci_menu'
diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml
index fe9a8ac4182..34226167288 100644
--- a/app/views/projects/commit/show.html.haml
+++ b/app/views/projects/commit/show.html.haml
@@ -1,10 +1,10 @@
- @no_container = true
-- add_to_breadcrumbs "Commits", project_commits_path(@project)
+- add_to_breadcrumbs _('Commits'), project_commits_path(@project)
- breadcrumb_title @commit.short_id
- container_class = !fluid_layout && diff_view == :inline ? 'container-limited' : ''
- limited_container_width = fluid_layout ? '' : 'limit-container-width'
- @content_class = limited_container_width
-- page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
+- page_title "#{@commit.title} (#{@commit.short_id})", _('Commits')
- page_description @commit.description
.container-fluid{ class: [limited_container_width, container_class] }
diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml
index 4917f4b8903..42b6aaa2634 100644
--- a/app/views/projects/issues/_discussion.html.haml
+++ b/app/views/projects/issues/_discussion.html.haml
@@ -5,7 +5,7 @@
= link_to 'Reopen issue', issue_path(@issue, issue: {state_event: :reopen}, format: 'json'), data: {original_text: "Reopen issue", alternative_text: "Comment & reopen issue"}, class: "btn btn-nr btn-reopen btn-comment js-note-target-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
= link_to 'Close issue', issue_path(@issue, issue: {state_event: :close}, format: 'json'), data: {original_text: "Close issue", alternative_text: "Comment & close issue"}, class: "btn btn-nr btn-close btn-comment js-note-target-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
-%section.js-vue-notes-event
+%section.issuable-discussion.js-vue-notes-event
#js-vue-notes{ data: { notes_data: notes_data(@issue).to_json,
noteable_data: serialize_issuable(@issue),
noteable_type: 'Issue',
diff --git a/app/views/projects/issues/_import_export.svg b/app/views/projects/issues/_import_export.svg
deleted file mode 100644
index 53c35d12f57..00000000000
--- a/app/views/projects/issues/_import_export.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 238 111" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><rect id="4" width="82" rx="3" height="28" fill="#fff"/><path id="5" d="m68.926 12.09v-2.41c0-.665-.437-.888-.975-.507l-6.552 4.631c-.542.383-.539.998 0 1.379l6.552 4.631c.542.383.975.154.975-.507v-2.41h4.874c.668 0 1.2-.538 1.2-1.201v-2.406c0-.668-.537-1.201-1.2-1.201h-4.874" fill="#fc8a51"/><path id="6" d="m4 24h74v-20h-74v20m-4-21c0-1.655 1.338-2.996 2.991-2.996h76.02c1.652 0 2.991 1.35 2.991 2.996v22.01c0 1.655-1.338 2.996-2.991 2.996h-76.02c-1.652 0-2.991-1.35-2.991-2.996v-22.01"/><circle id="2" cx="16" cy="14" r="7"/><circle id="0" cx="16" cy="14" r="7"/><mask id="3" width="14" height="14" x="0" y="0" fill="#fff"><use xlink:href="#2"/></mask><mask id="1" width="14" height="14" x="0" y="0" fill="#fff"><use xlink:href="#0"/></mask></defs><g fill="none" fill-rule="evenodd"><rect width="98" height="111" fill="#fff" rx="6"/><path fill="#e5e5e5" fill-rule="nonzero" d="m4 6.01v98.99c0 1.11.897 2.01 2 2.01h85.998c1.105 0 2-.897 2-2.01v-98.99c0-1.11-.897-2.01-2-2.01h-85.998c-1.105 0-2 .897-2 2.01m-4 0c0-3.318 2.685-6.01 6-6.01h85.998c3.314 0 6 2.689 6 6.01v98.99c0 3.318-2.685 6.01-6 6.01h-85.998c-3.314 0-6-2.689-6-6.01v-98.99"/><rect width="76" height="85" x="11" y="12" fill="#f9f9f9" rx="3"/><g transform="translate(37 59)"><use xlink:href="#4"/><path fill="#e5e5e5" fill-rule="nonzero" d="m4 24h74v-20h-74v20m-4-21c0-1.655 1.338-2.996 2.991-2.996h76.02c1.652 0 2.991 1.35 2.991 2.996v22.01c0 1.655-1.338 2.996-2.991 2.996h-76.02c-1.652 0-2.991-1.35-2.991-2.996v-22.01"/><use fill="#fff" stroke="#6b4fbb" stroke-width="8" mask="url(#1)" xlink:href="#0"/><use xlink:href="#5"/></g><g transform="translate(140)"><path fill="#fff" d="m0 4h94v103h-94z"/><path fill="#e5e5e5" fill-rule="nonzero" d="m0 74v30.993c0 3.318 2.687 6.01 6 6.01h85.998c3.316 0 6-2.69 6-6.01v-98.99c0-3.318-2.687-6.01-6-6.01h-85.998c-3.316 0-6 2.69-6 6.01v.993h4v-.993c0-1.11.896-2.01 2-2.01h85.998c1.105 0 2 .897 2 2.01v98.99c0 1.11-.896 2.01-2 2.01h-85.998c-1.105 0-2-.897-2-2.01v-30.993h-4"/><g fill="#f9f9f9"><rect width="82" height="28" x="8" y="12" rx="3"/><rect width="82" height="28" x="8" y="43" rx="3"/></g></g><g fill-rule="nonzero" transform="translate(148 73)"><use fill="#e5e5e5" xlink:href="#6"/><path fill="#6b4fbb" d="m17 17c1.657 0 3-1.343 3-3 0-1.657-1.343-3-3-3-1.657 0-3 1.343-3 3 0 1.657 1.343 3 3 3m0 4c-3.866 0-7-3.134-7-7 0-3.866 3.134-7 7-7 3.866 0 7 3.134 7 7 0 3.866-3.134 7-7 7"/></g><g transform="translate(25 24)"><use xlink:href="#4"/><use fill="#e5e5e5" fill-rule="nonzero" xlink:href="#6"/><use fill="#fff" stroke="#6b4fbb" stroke-width="8" mask="url(#3)" xlink:href="#2"/><use xlink:href="#5"/></g><g transform="translate(107 10)"><use xlink:href="#4"/><use fill="#fc8a51" fill-opacity=".3" fill-rule="nonzero" xlink:href="#6"/><path fill="#6b4fbb" fill-rule="nonzero" d="m16 17c1.657 0 3-1.343 3-3 0-1.657-1.343-3-3-3-1.657 0-3 1.343-3 3 0 1.657 1.343 3 3 3m0 4c-3.866 0-7-3.134-7-7 0-3.866 3.134-7 7-7 3.866 0 7 3.134 7 7 0 3.866-3.134 7-7 7" id="7"/><use xlink:href="#5"/></g><g transform="translate(128 41)"><use xlink:href="#4"/><use fill="#fc8a51" fill-opacity=".3" fill-rule="nonzero" xlink:href="#6"/><use xlink:href="#7"/><path fill="#fc8a51" d="m66.926 12.09v-2.41c0-.665-.437-.888-.975-.507l-6.552 4.631c-.542.383-.539.998 0 1.379l6.552 4.631c.542.383.975.154.975-.507v-2.41h4.874c.668 0 1.2-.538 1.2-1.201v-2.406c0-.668-.537-1.201-1.2-1.201h-4.874"/></g></g></svg> \ No newline at end of file
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index 31c72f2f759..ce7c7091c93 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -36,7 +36,7 @@
= issue.due_date.to_s(:medium)
- if issue.labels.any?
&nbsp;
- - issue.labels.each do |label|
+ - labels_sorted_by_title(issue.labels).each do |label|
= link_to_label(label, subject: issue.project, css_class: 'label-link')
.issuable-meta
diff --git a/app/views/projects/issues/import_csv/_modal.html.haml b/app/views/projects/issues/import_csv/_modal.html.haml
index 5339c4325b9..86bc54786ad 100644
--- a/app/views/projects/issues/import_csv/_modal.html.haml
+++ b/app/views/projects/issues/import_csv/_modal.html.haml
@@ -5,8 +5,8 @@
.modal-header
%h3
= _('Import issues')
- .import-export-svg-container
- = render 'projects/issues/import_export.svg'
+ .svg-content.import-export-svg-container
+ = image_tag 'illustrations/export-import.svg', alt: _('Import/Export illustration'), class: 'illustration'
%a.close{ href: '#', 'data-dismiss' => 'modal' } ×
.modal-body
.modal-text
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 653b7d4c6f3..3a674da6e87 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -14,9 +14,12 @@
.detail-page-header-body
.issuable-status-box.status-box.status-box-issue-closed{ class: issue_button_visibility(@issue, false) }
= sprite_icon('mobile-issue-close', size: 16, css_class: 'd-block d-sm-none')
- %span.d-none.d-sm-block
+ .d-none.d-sm-block
- if @issue.moved?
- = _("Closed (moved)")
+ - moved_link_start = "<a href=\"#{issue_path(@issue.moved_to)}\" class=\"text-white text-underline\">".html_safe
+ - moved_link_end = '</a>'.html_safe
+ = s_('IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})').html_safe % {moved_link_start: moved_link_start,
+ moved_link_end: moved_link_end}
- else
= _("Closed")
.issuable-status-box.status-box.status-box-open{ class: issue_button_visibility(@issue, true) }
@@ -88,7 +91,6 @@
#js-vue-discussion-filter{ data: { default_filter: current_user&.notes_filter_for(@issue), notes_filters: UserPreference.notes_filters.to_json } }
= render 'new_branch' unless @issue.confidential?
- %section.issuable-discussion
- = render 'projects/issues/discussion'
+ = render_if_exists 'projects/issues/discussion'
= render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @issue.assignees
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index ac29cd8f679..90916191d97 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -34,7 +34,7 @@
= merge_request.target_branch
- if merge_request.labels.any?
&nbsp;
- - merge_request.labels.each do |label|
+ - labels_sorted_by_title(merge_request.labels).each do |label|
= link_to_label(label, subject: merge_request.project, type: :merge_request, css_class: 'label-link')
.issuable-meta
diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml
index 69a47faabed..9c2efd6aa35 100644
--- a/app/views/projects/pipelines/_info.html.haml
+++ b/app/views/projects/pipelines/_info.html.haml
@@ -23,7 +23,7 @@
- if @pipeline.queued_duration
= "(queued for #{time_interval_in_words(@pipeline.queued_duration)})"
- .well-segment
+ .well-segment.qa-pipeline-badges
.icon-container
= sprite_icon('flag')
- if @pipeline.latest?
diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml
index 26671a7b7d2..1277ea6c743 100644
--- a/app/views/projects/wikis/edit.html.haml
+++ b/app/views/projects/wikis/edit.html.haml
@@ -23,9 +23,6 @@
= s_("Wiki|Create Page")
.nav-controls
- - if can?(current_user, :create_wiki, @project)
- = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-success", "data-toggle" => "modal" do
- = s_("Wiki|New page")
- if @page.persisted?
= link_to project_wiki_history_path(@project, @page), class: "btn" do
= s_("Wiki|Page history")
diff --git a/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml b/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml
index 422be28737c..755fd3a17d3 100644
--- a/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml
+++ b/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml
@@ -1,5 +1,5 @@
- if show_auto_devops_implicitly_enabled_banner?(project, current_user)
- .auto-devops-implicitly-enabled-banner.alert.alert-warning
+ .qa-auto-devops-banner.auto-devops-implicitly-enabled-banner.alert.alert-warning
- more_information_link = link_to _('More information'), help_page_path('topics/autodevops/index.md'), target: '_blank', class: 'alert-link'
- auto_devops_message = s_("AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}") % { more_information_link: more_information_link }
= auto_devops_message.html_safe
diff --git a/app/views/shared/empty_states/_priority_labels.html.haml b/app/views/shared/empty_states/_priority_labels.html.haml
index 555cb4f4af9..bba3475d244 100644
--- a/app/views/shared/empty_states/_priority_labels.html.haml
+++ b/app/views/shared/empty_states/_priority_labels.html.haml
@@ -1,4 +1,4 @@
.text-center
- .svg-content
+ .svg-content.qa-label-svg
= image_tag 'illustrations/priority_labels.svg'
%p Star labels to start sorting by priority
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 0520eda37a4..9596c1df20e 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -133,7 +133,7 @@
#js-confidential-entry-point
-# haml-lint:disable InlineJavaScript
- %script#js-lock-issue-data{ type: "application/json" }= { is_locked: issuable_sidebar[:discussion_locked], is_editable: can_edit_issuable }.to_json.html_safe
+ %script#js-lock-issue-data{ type: "application/json" }= { is_locked: !!issuable_sidebar[:discussion_locked], is_editable: can_edit_issuable }.to_json.html_safe
#js-lock-entry-point
.js-sidebar-participants-entry-point
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 410411b1294..d0fc130b04f 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -101,6 +101,7 @@
- authorized_projects
- background_migration
+- chat_notification
- create_gpg_signature
- delete_merged_branches
- delete_user
diff --git a/app/workers/build_finished_worker.rb b/app/workers/build_finished_worker.rb
index ae853ec9316..adc38226405 100644
--- a/app/workers/build_finished_worker.rb
+++ b/app/workers/build_finished_worker.rb
@@ -30,5 +30,6 @@ class BuildFinishedWorker
# We execute these async as these are independent operations.
BuildHooksWorker.perform_async(build.id)
ArchiveTraceWorker.perform_async(build.id)
+ ChatNotificationWorker.perform_async(build.id) if build.pipeline.chat?
end
end
diff --git a/app/workers/chat_notification_worker.rb b/app/workers/chat_notification_worker.rb
new file mode 100644
index 00000000000..25a306e94d8
--- /dev/null
+++ b/app/workers/chat_notification_worker.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+class ChatNotificationWorker
+ include ApplicationWorker
+
+ RESCHEDULE_INTERVAL = 2.seconds
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def perform(build_id)
+ Ci::Build.find_by(id: build_id).try do |build|
+ send_response(build)
+ end
+ rescue Gitlab::Chat::Output::MissingBuildSectionError
+ # The creation of traces and sections appears to be eventually consistent.
+ # As a result it's possible for us to run the above code before the trace
+ # sections are present. To better handle such cases we'll just reschedule
+ # the job instead of producing an error.
+ self.class.perform_in(RESCHEDULE_INTERVAL, build_id)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def send_response(build)
+ Gitlab::Chat::Responder.responder_for(build).try do |responder|
+ if build.success?
+ output = Gitlab::Chat::Output.new(build)
+
+ responder.success(output.to_s)
+ else
+ responder.failure
+ end
+ end
+ end
+end
diff --git a/.babelrc.js b/babel.config.js
index 1b05a67354e..e3de8ef2d83 100644
--- a/.babelrc.js
+++ b/babel.config.js
@@ -1,3 +1,5 @@
+/* eslint-disable import/no-commonjs, filenames/match-regex */
+
const BABEL_ENV = process.env.BABEL_ENV || process.env.NODE_ENV || null;
const presets = [
diff --git a/changelogs/README.md b/changelogs/README.md
new file mode 100644
index 00000000000..c4113ccb863
--- /dev/null
+++ b/changelogs/README.md
@@ -0,0 +1,10 @@
+# Generating changelog entries
+
+To generate and validate your changelog entries:
+
+1. Run `bin/changelog` to generate.
+1. Run `scripts/lint-changelog-yaml` to validate.
+
+See [development/changelog] documentation for detailed usage.
+
+[development/changelog]: https://docs.gitlab.com/ee/development/changelog.html
diff --git a/changelogs/unreleased/19745-forms-with-task-lists-can-be-overwritten-when-editing-simultaneously.yml b/changelogs/unreleased/19745-forms-with-task-lists-can-be-overwritten-when-editing-simultaneously.yml
deleted file mode 100644
index b1177e1717e..00000000000
--- a/changelogs/unreleased/19745-forms-with-task-lists-can-be-overwritten-when-editing-simultaneously.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Increase reliability and performance of toggling task items
-merge_request: 23938
-author:
-type: fixed
diff --git a/changelogs/unreleased/2105-add-setting-for-first-day-of-the-week.yml b/changelogs/unreleased/2105-add-setting-for-first-day-of-the-week.yml
deleted file mode 100644
index f4a52b1aacd..00000000000
--- a/changelogs/unreleased/2105-add-setting-for-first-day-of-the-week.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add setting for first day of the week
-merge_request: 22755
-author: Fabian Schneider @fabsrc
-type: added
diff --git a/changelogs/unreleased/24680-support-bamboo-api-polymorphism.yml b/changelogs/unreleased/24680-support-bamboo-api-polymorphism.yml
deleted file mode 100644
index 5117195cd0c..00000000000
--- a/changelogs/unreleased/24680-support-bamboo-api-polymorphism.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "Support bamboo api polymorphism"
-merge_request: 24680
-author: Alex Lossent
-type: fixed \ No newline at end of file
diff --git a/changelogs/unreleased/24875-label.yml b/changelogs/unreleased/24875-label.yml
deleted file mode 100644
index 1f9d2222edf..00000000000
--- a/changelogs/unreleased/24875-label.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Append prioritized label before pagination
-merge_request: 24815
-author:
-type: fixed
diff --git a/changelogs/unreleased/25043-empty-states.yml b/changelogs/unreleased/25043-empty-states.yml
deleted file mode 100644
index 529a8b3206f..00000000000
--- a/changelogs/unreleased/25043-empty-states.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make issuable empty states actionable
-merge_request: 24077
-author:
-type: changed
diff --git a/changelogs/unreleased/25569-changing-wording-to-delete-when-referring-to-removing-a-branch.yml b/changelogs/unreleased/25569-changing-wording-to-delete-when-referring-to-removing-a-branch.yml
deleted file mode 100644
index 02a667073ca..00000000000
--- a/changelogs/unreleased/25569-changing-wording-to-delete-when-referring-to-removing-a-branch.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use delete instead of remove when referring to `git branch -D`
-merge_request: !23966
-author:
-type: changed
diff --git a/changelogs/unreleased/26375-markdown-footnotes-not-working.yml b/changelogs/unreleased/26375-markdown-footnotes-not-working.yml
deleted file mode 100644
index 86adef84a2a..00000000000
--- a/changelogs/unreleased/26375-markdown-footnotes-not-working.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Footnotes now render properly in markdown
-merge_request: 24168
-author:
-type: fixed
diff --git a/changelogs/unreleased/28500-empty-states-for-profile-page.yml b/changelogs/unreleased/28500-empty-states-for-profile-page.yml
deleted file mode 100644
index 53f840521ae..00000000000
--- a/changelogs/unreleased/28500-empty-states-for-profile-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Refresh empty states for profile page tabs
-merge_request: 24549
-author:
-type: changed
diff --git a/changelogs/unreleased/30120-add-flat-square-badge-style.yml b/changelogs/unreleased/30120-add-flat-square-badge-style.yml
deleted file mode 100644
index a542a58d3fc..00000000000
--- a/changelogs/unreleased/30120-add-flat-square-badge-style.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add flat-square badge style
-merge_request: 24172
-author: Fabian Schneider @fabsrc
-type: added
diff --git a/changelogs/unreleased/34555-empty-state-for-starred-projects.yml b/changelogs/unreleased/34555-empty-state-for-starred-projects.yml
new file mode 100644
index 00000000000..926d3a2eecf
--- /dev/null
+++ b/changelogs/unreleased/34555-empty-state-for-starred-projects.yml
@@ -0,0 +1,5 @@
+---
+title: Improve empty state for starred projects
+merge_request: 25138
+author:
+type: changed
diff --git a/changelogs/unreleased/35638-move-language-setting-to-preferences.yml b/changelogs/unreleased/35638-move-language-setting-to-preferences.yml
new file mode 100644
index 00000000000..d8658218676
--- /dev/null
+++ b/changelogs/unreleased/35638-move-language-setting-to-preferences.yml
@@ -0,0 +1,5 @@
+---
+title: Move language setting to preferences
+merge_request: 25427
+author: Fabian Schneider @fabsrc
+type: changed
diff --git a/changelogs/unreleased/36445-better-indication-that-an-issue-has-been-moved-or-marked-as-duplicated.yml b/changelogs/unreleased/36445-better-indication-that-an-issue-has-been-moved-or-marked-as-duplicated.yml
deleted file mode 100644
index 70b561ccbf6..00000000000
--- a/changelogs/unreleased/36445-better-indication-that-an-issue-has-been-moved-or-marked-as-duplicated.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Indicate on Issue Status if an Issue was Moved
-merge_request: 24470
-author:
-type: added
diff --git a/changelogs/unreleased/37990-task-list-bracket.yml b/changelogs/unreleased/37990-task-list-bracket.yml
deleted file mode 100644
index ffa77cf0af7..00000000000
--- a/changelogs/unreleased/37990-task-list-bracket.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix ambiguous brackets in task lists
-merge_request: 18514
-author: Jared Deckard <jared.deckard@gmail.com>
-type: fixed
diff --git a/changelogs/unreleased/39010-add-left-margin-to-1st-time-contributor-badge.yml b/changelogs/unreleased/39010-add-left-margin-to-1st-time-contributor-badge.yml
new file mode 100644
index 00000000000..758b97deb3b
--- /dev/null
+++ b/changelogs/unreleased/39010-add-left-margin-to-1st-time-contributor-badge.yml
@@ -0,0 +1,5 @@
+---
+title: Add left margin to 1st time contributor badge
+merge_request: 25216
+author: Gokhan Apaydin
+type: fixed
diff --git a/changelogs/unreleased/40997-gitlab-pages-deploy-jobs-have-a-null-status.yml b/changelogs/unreleased/40997-gitlab-pages-deploy-jobs-have-a-null-status.yml
deleted file mode 100644
index 01036253151..00000000000
--- a/changelogs/unreleased/40997-gitlab-pages-deploy-jobs-have-a-null-status.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix empty labels of CI builds for gitlab-pages on pipeline page
-merge_request: 24451
-author:
-type: fixed
diff --git a/changelogs/unreleased/42769-remove-expansion-hover-animation-from-status-icon-buttons.yml b/changelogs/unreleased/42769-remove-expansion-hover-animation-from-status-icon-buttons.yml
deleted file mode 100644
index 5a4ff8b3358..00000000000
--- a/changelogs/unreleased/42769-remove-expansion-hover-animation-from-status-icon-buttons.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove expansion hover animation from pipeline status icon buttons
-merge_request: 24268
-author: Nathan Friend
-type: changed
diff --git a/changelogs/unreleased/43681-display-last-activity-and-created-at-datetimes-for-users-in-admin-users.yml b/changelogs/unreleased/43681-display-last-activity-and-created-at-datetimes-for-users-in-admin-users.yml
deleted file mode 100644
index 0fbf6314a27..00000000000
--- a/changelogs/unreleased/43681-display-last-activity-and-created-at-datetimes-for-users-in-admin-users.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Display last activity and created at datetimes for users
-merge_request: 24181
-author:
-type: added
diff --git a/changelogs/unreleased/44332-add-openid-profile-scopes.yml b/changelogs/unreleased/44332-add-openid-profile-scopes.yml
deleted file mode 100644
index b554fab5139..00000000000
--- a/changelogs/unreleased/44332-add-openid-profile-scopes.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: GitLab now supports the profile and email scopes from OpenID Connect
-merge_request: 24335
-author: Goten Xiao
-type: added
diff --git a/changelogs/unreleased/44698-recaptcha.yml b/changelogs/unreleased/44698-recaptcha.yml
deleted file mode 100644
index e1760a6c635..00000000000
--- a/changelogs/unreleased/44698-recaptcha.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Prevent unload when Recaptcha is open
-merge_request: 24625
-author:
-type: fixed
diff --git a/changelogs/unreleased/45779-fix-default-visibility-level-for-projects.yml b/changelogs/unreleased/45779-fix-default-visibility-level-for-projects.yml
deleted file mode 100644
index b4cba5041d1..00000000000
--- a/changelogs/unreleased/45779-fix-default-visibility-level-for-projects.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix default visibility_level for new projects
-merge_request: 24120
-author: Fabian Schneider @fabsrc
-type: fixed
diff --git a/changelogs/unreleased/45791-number-of-repositories-usage-ping.yml b/changelogs/unreleased/45791-number-of-repositories-usage-ping.yml
deleted file mode 100644
index 8d1f5df56ea..00000000000
--- a/changelogs/unreleased/45791-number-of-repositories-usage-ping.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add repositories count to usage ping data
-merge_request: 24823
-author:
-type: added
diff --git a/changelogs/unreleased/46448-add-timestamps-for-each-stage-of-gitlab-rake-gitlab-backup-restore.yml b/changelogs/unreleased/46448-add-timestamps-for-each-stage-of-gitlab-rake-gitlab-backup-restore.yml
deleted file mode 100644
index 4ce6787570a..00000000000
--- a/changelogs/unreleased/46448-add-timestamps-for-each-stage-of-gitlab-rake-gitlab-backup-restore.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Display timestamps to messages printed by gitlab:backup:restore rake tasks
-merge_request:
-author: Will Chandler
-type: changed
diff --git a/changelogs/unreleased/47007-related-merge-requests-in-issue-design-restyle.yml b/changelogs/unreleased/47007-related-merge-requests-in-issue-design-restyle.yml
deleted file mode 100644
index 28e2a4cc377..00000000000
--- a/changelogs/unreleased/47007-related-merge-requests-in-issue-design-restyle.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Redesigned related merge requests in issue page.
-merge_request: 24270
-author:
-type: changed
diff --git a/changelogs/unreleased/47988-improve-milestone-queries-with-subqueries.yml b/changelogs/unreleased/47988-improve-milestone-queries-with-subqueries.yml
deleted file mode 100644
index d1a80ab43cf..00000000000
--- a/changelogs/unreleased/47988-improve-milestone-queries-with-subqueries.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve milestone queries using subqueries instead of separate queries for ids
-merge_request: 24325
-author:
-type: performance
diff --git a/changelogs/unreleased/50013-add-browser-platform-flags.yml b/changelogs/unreleased/50013-add-browser-platform-flags.yml
deleted file mode 100644
index 6176b8b64a7..00000000000
--- a/changelogs/unreleased/50013-add-browser-platform-flags.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add CSS & JS global flags to represent browser and platform
-merge_request: 24017
-author:
-type: other
diff --git a/changelogs/unreleased/50352-sort-save.yml b/changelogs/unreleased/50352-sort-save.yml
deleted file mode 100644
index cd046c8b785..00000000000
--- a/changelogs/unreleased/50352-sort-save.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Save issues/merge request sorting options to backend
-merge_request: 24198
-author:
-type: added
diff --git a/changelogs/unreleased/50433-make-emoji-picker-bigger.yml b/changelogs/unreleased/50433-make-emoji-picker-bigger.yml
new file mode 100644
index 00000000000..8fcf41df09d
--- /dev/null
+++ b/changelogs/unreleased/50433-make-emoji-picker-bigger.yml
@@ -0,0 +1,5 @@
+---
+title: Make emoji picker bigger
+merge_request: 25187
+author: Jacopo Beschi @jacopo-beschi
+type: changed
diff --git a/changelogs/unreleased/50521-block-emojis-and-symbol-characters-from-user-s-full-names-2.yml b/changelogs/unreleased/50521-block-emojis-and-symbol-characters-from-user-s-full-names-2.yml
deleted file mode 100644
index 04caf8262c6..00000000000
--- a/changelogs/unreleased/50521-block-emojis-and-symbol-characters-from-user-s-full-names-2.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Block emojis and symbol characters from users full names
-merge_request: 24523
-author:
-type: other
diff --git a/changelogs/unreleased/51754-admin-view-private-personal-snippets.yml b/changelogs/unreleased/51754-admin-view-private-personal-snippets.yml
deleted file mode 100644
index cf3d73fce0c..00000000000
--- a/changelogs/unreleased/51754-admin-view-private-personal-snippets.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow users with full private access to read private personal snippets.
-merge_request: 24560
-author:
-type: fixed
diff --git a/changelogs/unreleased/51759-filter-by-language.yml b/changelogs/unreleased/51759-filter-by-language.yml
deleted file mode 100644
index 6b5bedd6b2d..00000000000
--- a/changelogs/unreleased/51759-filter-by-language.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add `with_programming_language` filter for projects to API
-merge_request: 24377
-author: Dylan MacKenzie
-type: added
diff --git a/changelogs/unreleased/51913-api-getting-projects-for-users-with-dot-gets-404.yml b/changelogs/unreleased/51913-api-getting-projects-for-users-with-dot-gets-404.yml
deleted file mode 100644
index 9d72efdd52a..00000000000
--- a/changelogs/unreleased/51913-api-getting-projects-for-users-with-dot-gets-404.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Support username with dots'
-merge_request: 24395
-author: Robert Schilling
-type: fixed
diff --git a/changelogs/unreleased/52198-timer-is-vertically-misaligned-for-delayed-jobs-in-pipeline-actions.yml b/changelogs/unreleased/52198-timer-is-vertically-misaligned-for-delayed-jobs-in-pipeline-actions.yml
new file mode 100644
index 00000000000..84062c6db91
--- /dev/null
+++ b/changelogs/unreleased/52198-timer-is-vertically-misaligned-for-delayed-jobs-in-pipeline-actions.yml
@@ -0,0 +1,5 @@
+---
+title: 'Timer and action name aligned vertically for delayed jobs in pipeline actions'
+merge_request: 25117
+author: Gokhan Apaydin
+type: fixed
diff --git a/changelogs/unreleased/52275-fix-master-to-be-hyperlink.yml b/changelogs/unreleased/52275-fix-master-to-be-hyperlink.yml
deleted file mode 100644
index c1cde0ceff6..00000000000
--- a/changelogs/unreleased/52275-fix-master-to-be-hyperlink.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Resolve In Merge Request diff screen, master is not a hyperlink
-merge_request: 23874
-author:
-type: fixed
diff --git a/changelogs/unreleased/52278-squash-checkbox-fix.yml b/changelogs/unreleased/52278-squash-checkbox-fix.yml
deleted file mode 100644
index c81748ae419..00000000000
--- a/changelogs/unreleased/52278-squash-checkbox-fix.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Resolve When merging an MR, the squash checkbox isnt always supported
-merge_request: 24296
-author:
-type: fixed
diff --git a/changelogs/unreleased/52347-lines-changed-statistics-is-not-easily-visible-in-mr-changes-view.yml b/changelogs/unreleased/52347-lines-changed-statistics-is-not-easily-visible-in-mr-changes-view.yml
deleted file mode 100644
index cf1c4378f18..00000000000
--- a/changelogs/unreleased/52347-lines-changed-statistics-is-not-easily-visible-in-mr-changes-view.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Show MR statistics in diff comparisons
-merge_request: !24569
-author:
-type: changed
diff --git a/changelogs/unreleased/52363-modifies-environment-scope-field-on-cluster-page.yml b/changelogs/unreleased/52363-modifies-environment-scope-field-on-cluster-page.yml
deleted file mode 100644
index 07cb35e6529..00000000000
--- a/changelogs/unreleased/52363-modifies-environment-scope-field-on-cluster-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Modifies environment scope UI on cluster page
-merge_request: 24376
-author:
-type: other
diff --git a/changelogs/unreleased/52363-ui-changes-to-cluster-and-ado-pages.yml b/changelogs/unreleased/52363-ui-changes-to-cluster-and-ado-pages.yml
deleted file mode 100644
index eb4851971fb..00000000000
--- a/changelogs/unreleased/52363-ui-changes-to-cluster-and-ado-pages.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Moves domain setting from Auto DevOps to Cluster's page
-merge_request: 24580
-author:
-type: added
diff --git a/changelogs/unreleased/52568-external-mr-diffs.yml b/changelogs/unreleased/52568-external-mr-diffs.yml
deleted file mode 100644
index b1c9d5cb809..00000000000
--- a/changelogs/unreleased/52568-external-mr-diffs.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow merge request diffs to be placed into an object store
-merge_request: 24276
-author:
-type: added
diff --git a/changelogs/unreleased/52674-api-v4-projects-project_id-jobs-endpoint-hits-statement-timeout.yml b/changelogs/unreleased/52674-api-v4-projects-project_id-jobs-endpoint-hits-statement-timeout.yml
deleted file mode 100644
index f79078c1fd9..00000000000
--- a/changelogs/unreleased/52674-api-v4-projects-project_id-jobs-endpoint-hits-statement-timeout.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "[API] Omit `X-Total` and `X-Total-Pages` headers when items count is more than 10,000"
-merge_request: 23931
-author:
-type: performance
diff --git a/changelogs/unreleased/52971-merge-request-file-browser-should-always-be-possible-show-hide.yml b/changelogs/unreleased/52971-merge-request-file-browser-should-always-be-possible-show-hide.yml
deleted file mode 100644
index b661c55957d..00000000000
--- a/changelogs/unreleased/52971-merge-request-file-browser-should-always-be-possible-show-hide.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make possible to toggle file tree while scrolling through diffs
-merge_request: !24103
-author:
-type: changed
diff --git a/changelogs/unreleased/53104-redesign-group-overview-ui-mvc.yml b/changelogs/unreleased/53104-redesign-group-overview-ui-mvc.yml
deleted file mode 100644
index cb810b7ac7f..00000000000
--- a/changelogs/unreleased/53104-redesign-group-overview-ui-mvc.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Refresh group overview to match project overview
-merge_request: 23866
-author:
-type: changed
diff --git a/changelogs/unreleased/53411-remove_personal_access_tokens_token.yml b/changelogs/unreleased/53411-remove_personal_access_tokens_token.yml
new file mode 100644
index 00000000000..32cca07f58d
--- /dev/null
+++ b/changelogs/unreleased/53411-remove_personal_access_tokens_token.yml
@@ -0,0 +1,5 @@
+---
+title: Remove undigested token column from personal_access_tokens table from the database
+merge_request: 22743
+author:
+type: changed
diff --git a/changelogs/unreleased/53431-fix-upcoming-milestone-filter-for-groups.yml b/changelogs/unreleased/53431-fix-upcoming-milestone-filter-for-groups.yml
deleted file mode 100644
index 1e9c7f3913c..00000000000
--- a/changelogs/unreleased/53431-fix-upcoming-milestone-filter-for-groups.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix upcoming milestones filter not including group milestones
-merge_request: 23098
-author: Heinrich Lee Yu
-type: fixed
diff --git a/changelogs/unreleased/53671-redirect-projects-id-to-project-page.yml b/changelogs/unreleased/53671-redirect-projects-id-to-project-page.yml
deleted file mode 100644
index 08c5ded05d5..00000000000
--- a/changelogs/unreleased/53671-redirect-projects-id-to-project-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Redirect GET projects/:id to project page
-merge_request: 24467
-author:
-type: added
diff --git a/changelogs/unreleased/53676-ip-address-of-gitlab-runner-is-wrong-in-the-runners-description.yml b/changelogs/unreleased/53676-ip-address-of-gitlab-runner-is-wrong-in-the-runners-description.yml
deleted file mode 100644
index 12a6509e6f7..00000000000
--- a/changelogs/unreleased/53676-ip-address-of-gitlab-runner-is-wrong-in-the-runners-description.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Get remote IP address of runner
-merge_request: 24624
-author:
-type: changed
diff --git a/changelogs/unreleased/53714-inconsistent-text-color-for-labels.yml b/changelogs/unreleased/53714-inconsistent-text-color-for-labels.yml
deleted file mode 100644
index d804e2df2cd..00000000000
--- a/changelogs/unreleased/53714-inconsistent-text-color-for-labels.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix foreground color for labels to ensure consistency of label appearance
-merge_request: 23873
-author: Nathan Friend
-type: fixed
diff --git a/changelogs/unreleased/53856-changing-group-visibility-does-not-re-enable-save-button.yml b/changelogs/unreleased/53856-changing-group-visibility-does-not-re-enable-save-button.yml
deleted file mode 100644
index 1daa72fb9c4..00000000000
--- a/changelogs/unreleased/53856-changing-group-visibility-does-not-re-enable-save-button.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Fix suboptimal handling of checkbox and radio input events causing
- group general settings submit button to stay disabled after changing its visibility
-merge_request: 23022
-author:
-type: fixed
diff --git a/changelogs/unreleased/53950-commit-comments-displayed-on-a-merge-request.yml b/changelogs/unreleased/53950-commit-comments-displayed-on-a-merge-request.yml
deleted file mode 100644
index adaaed7f1aa..00000000000
--- a/changelogs/unreleased/53950-commit-comments-displayed-on-a-merge-request.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Display "commented" only for commit discussions on merge requests
-merge_request: 24427
-author:
-type: changed
diff --git a/changelogs/unreleased/54167-rename-project-tags-to-project-topics.yml b/changelogs/unreleased/54167-rename-project-tags-to-project-topics.yml
deleted file mode 100644
index 6fc8aa1a195..00000000000
--- a/changelogs/unreleased/54167-rename-project-tags-to-project-topics.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rename project tags to project topics
-merge_request: 24219
-author:
-type: other
diff --git a/changelogs/unreleased/54213-standardize-token-value-capitalization-in-filter-bar.yml b/changelogs/unreleased/54213-standardize-token-value-capitalization-in-filter-bar.yml
deleted file mode 100644
index 37dea77b8d2..00000000000
--- a/changelogs/unreleased/54213-standardize-token-value-capitalization-in-filter-bar.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Standardize filter value capitlization in filter bar in both issues and boards pages
-merge_request: 23846
-author: obahareth
-type: changed
diff --git a/changelogs/unreleased/54250-upstream-kubeclient-redirect-patch.yml b/changelogs/unreleased/54250-upstream-kubeclient-redirect-patch.yml
deleted file mode 100644
index d1bdbccb20a..00000000000
--- a/changelogs/unreleased/54250-upstream-kubeclient-redirect-patch.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade kubeclient to 4.2.2 and swap out monkey-patch to disallow redirects
-merge_request: 24284
-author:
-type: other
diff --git a/changelogs/unreleased/54484-anchor-links-to-comments-or-system-notes-can-break-with-discussion-filters.yml b/changelogs/unreleased/54484-anchor-links-to-comments-or-system-notes-can-break-with-discussion-filters.yml
deleted file mode 100644
index 4d543db567d..00000000000
--- a/changelogs/unreleased/54484-anchor-links-to-comments-or-system-notes-can-break-with-discussion-filters.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Ensured links to a comment or system note anchor resolves to the right note if a user has a discussion filter.
-merge_request: 24228
-author:
-type: changed
diff --git a/changelogs/unreleased/54544-update-project-topics-styling-to-use-badges-design.yml b/changelogs/unreleased/54544-update-project-topics-styling-to-use-badges-design.yml
deleted file mode 100644
index de12c66e9ef..00000000000
--- a/changelogs/unreleased/54544-update-project-topics-styling-to-use-badges-design.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update project topics styling to use badges design
-merge_request: 24415
-author:
-type: changed
diff --git a/changelogs/unreleased/54725-fix-emoji-button-active-state.yml b/changelogs/unreleased/54725-fix-emoji-button-active-state.yml
new file mode 100644
index 00000000000..4f0a436cc87
--- /dev/null
+++ b/changelogs/unreleased/54725-fix-emoji-button-active-state.yml
@@ -0,0 +1,5 @@
+---
+title: Fix hover and active state colors of award emoji button
+merge_request: 25295
+author:
+type: fixed
diff --git a/changelogs/unreleased/54796-api-sort-tie-breaker-for-pagination.yml b/changelogs/unreleased/54796-api-sort-tie-breaker-for-pagination.yml
new file mode 100644
index 00000000000..92b27f63f82
--- /dev/null
+++ b/changelogs/unreleased/54796-api-sort-tie-breaker-for-pagination.yml
@@ -0,0 +1,5 @@
+---
+title: 'API: Sort tie breaker with id DESC'
+merge_request: 25311
+author: Nermin Vehabovic
+type: changed
diff --git a/changelogs/unreleased/54905-milestone-search.yml b/changelogs/unreleased/54905-milestone-search.yml
deleted file mode 100644
index 88717242e7c..00000000000
--- a/changelogs/unreleased/54905-milestone-search.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds milestone search
-merge_request: 24265
-author: Jacopo Beschi @jacopo-beschi
-type: added
diff --git a/changelogs/unreleased/55098-ui-bug-adding-group-members-with-lower-permissions.yml b/changelogs/unreleased/55098-ui-bug-adding-group-members-with-lower-permissions.yml
deleted file mode 100644
index f22524ef4b2..00000000000
--- a/changelogs/unreleased/55098-ui-bug-adding-group-members-with-lower-permissions.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Resolve UI bug adding group members with lower permissions
-merge_request: 24820
-author:
-type: fixed
diff --git a/changelogs/unreleased/55109-jira-integration-api-doesn-t-respect-available-format.yml b/changelogs/unreleased/55109-jira-integration-api-doesn-t-respect-available-format.yml
new file mode 100644
index 00000000000..c58cdc19555
--- /dev/null
+++ b/changelogs/unreleased/55109-jira-integration-api-doesn-t-respect-available-format.yml
@@ -0,0 +1,5 @@
+---
+title: 'API: Support Jira transition ID as string'
+merge_request: 24400
+author: Robert Schilling
+type: fixed
diff --git a/changelogs/unreleased/55111-gitlab-api-does-not-manage-default_branch_protection-3-value.yml b/changelogs/unreleased/55111-gitlab-api-does-not-manage-default_branch_protection-3-value.yml
deleted file mode 100644
index b609fc2d60b..00000000000
--- a/changelogs/unreleased/55111-gitlab-api-does-not-manage-default_branch_protection-3-value.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Fix default_branch_protection admin setting'
-merge_request: 24398
-author: Robert Schilling
-type: fixed
diff --git a/changelogs/unreleased/55242-skeleton-loading-releases.yml b/changelogs/unreleased/55242-skeleton-loading-releases.yml
deleted file mode 100644
index 43cda64ce04..00000000000
--- a/changelogs/unreleased/55242-skeleton-loading-releases.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds skeleton loading to releases page
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/55312-svg.yml b/changelogs/unreleased/55312-svg.yml
new file mode 100644
index 00000000000..a6260aeaf2a
--- /dev/null
+++ b/changelogs/unreleased/55312-svg.yml
@@ -0,0 +1,5 @@
+---
+title: Use export-import svgs from gitlab-svgs
+merge_request: 24954
+author:
+type: other
diff --git a/changelogs/unreleased/55376-related_merge_requests-api-call-returns-merge-requests-that-are-not-related-to-the-issue.yml b/changelogs/unreleased/55376-related_merge_requests-api-call-returns-merge-requests-that-are-not-related-to-the-issue.yml
new file mode 100644
index 00000000000..d2f24d6f499
--- /dev/null
+++ b/changelogs/unreleased/55376-related_merge_requests-api-call-returns-merge-requests-that-are-not-related-to-the-issue.yml
@@ -0,0 +1,5 @@
+---
+title: 'API: Ensure that related merge requests are referenced cross-project'
+merge_request: 25222
+author: Robert Schilling
+type: fixed
diff --git a/changelogs/unreleased/55495-teamcity-use-revision-in-query.yml b/changelogs/unreleased/55495-teamcity-use-revision-in-query.yml
deleted file mode 100644
index 724de733b7c..00000000000
--- a/changelogs/unreleased/55495-teamcity-use-revision-in-query.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Build number does not need to be tweaked anymore for the TeamCity integration to work properly.
-merge_request: 23898
-author:
-type: changed
diff --git a/changelogs/unreleased/55628-artifacts-from-a-job-defined-after-a-parallel-job-are-not-downloaded.yml b/changelogs/unreleased/55628-artifacts-from-a-job-defined-after-a-parallel-job-are-not-downloaded.yml
deleted file mode 100644
index 071036cd568..00000000000
--- a/changelogs/unreleased/55628-artifacts-from-a-job-defined-after-a-parallel-job-are-not-downloaded.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Handle regular job dependencies next to parallelized job dependencies.
-merge_request: 24273
-author:
-type: fixed
diff --git a/changelogs/unreleased/55820-adds-common-name-chart-value.yml b/changelogs/unreleased/55820-adds-common-name-chart-value.yml
deleted file mode 100644
index 1871abbfc6b..00000000000
--- a/changelogs/unreleased/55820-adds-common-name-chart-value.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Ensure Cert Manager works with Auto DevOps URLs greater than 64 bytes
-merge_request: 24683
-author:
-type: fixed
diff --git a/changelogs/unreleased/55884-adjust-emoji-and-cancel-buttons-height-in-user-status-modal-when-emoji-is-changed.yml b/changelogs/unreleased/55884-adjust-emoji-and-cancel-buttons-height-in-user-status-modal-when-emoji-is-changed.yml
deleted file mode 100644
index 2fbf334f5e9..00000000000
--- a/changelogs/unreleased/55884-adjust-emoji-and-cancel-buttons-height-in-user-status-modal-when-emoji-is-changed.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Emoji and cancel button are taller than input in set user status modal
-merge_request: 24173
-author: Dhiraj Bodicherla
-type: fixed
diff --git a/changelogs/unreleased/55893-artifacts-download.yml b/changelogs/unreleased/55893-artifacts-download.yml
new file mode 100644
index 00000000000..30c118b7094
--- /dev/null
+++ b/changelogs/unreleased/55893-artifacts-download.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes not working dropdowns in pipelines page
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/55945-suggested-change-preview-highlight.yml b/changelogs/unreleased/55945-suggested-change-preview-highlight.yml
deleted file mode 100644
index 997290a5d50..00000000000
--- a/changelogs/unreleased/55945-suggested-change-preview-highlight.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix syntax highlighting for suggested changes preview
-merge_request: 24358
-author:
-type: fixed
diff --git a/changelogs/unreleased/55966-when-ref-is-ambiguous-createpipelineservice-raises-an-error.yml b/changelogs/unreleased/55966-when-ref-is-ambiguous-createpipelineservice-raises-an-error.yml
deleted file mode 100644
index 01a162944d3..00000000000
--- a/changelogs/unreleased/55966-when-ref-is-ambiguous-createpipelineservice-raises-an-error.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Prevent checking protected_ref? for ambiguous refs.
-merge_request: 24437
-author:
-type: fixed
diff --git a/changelogs/unreleased/56010-user-profile-page-horizonal-whitespace-between-overview-columns-breaks-two-column-layout.yml b/changelogs/unreleased/56010-user-profile-page-horizonal-whitespace-between-overview-columns-breaks-two-column-layout.yml
deleted file mode 100644
index 407346bbf22..00000000000
--- a/changelogs/unreleased/56010-user-profile-page-horizonal-whitespace-between-overview-columns-breaks-two-column-layout.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove horizontal whitespace on user profile overview on small breakpoints
-merge_request: 24189
-author:
-type: other
diff --git a/changelogs/unreleased/56014-api-merge-request-squash-commit-messages.yml b/changelogs/unreleased/56014-api-merge-request-squash-commit-messages.yml
deleted file mode 100644
index e324baa94a3..00000000000
--- a/changelogs/unreleased/56014-api-merge-request-squash-commit-messages.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: API allows setting the squash commit message when squashing a merge request
-merge_request: 24784
-author:
-type: added
diff --git a/changelogs/unreleased/56019-archived-stuck.yml b/changelogs/unreleased/56019-archived-stuck.yml
deleted file mode 100644
index de3698a327b..00000000000
--- a/changelogs/unreleased/56019-archived-stuck.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixes z-index and margins of archived alert in job page
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/56036-fix-translation-of-in-in-job-details-sidebar.yml b/changelogs/unreleased/56036-fix-translation-of-in-in-job-details-sidebar.yml
deleted file mode 100644
index ff9d4f2c175..00000000000
--- a/changelogs/unreleased/56036-fix-translation-of-in-in-job-details-sidebar.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-title: Remove multilingual translation from the word "in" in the job details sidebar.
-merge_request: 24192
-author: Nathan Friend
-type: changed
diff --git a/changelogs/unreleased/56110-cluster-kubernetes-api-500-error-on-post-request.yml b/changelogs/unreleased/56110-cluster-kubernetes-api-500-error-on-post-request.yml
deleted file mode 100644
index 4da14114225..00000000000
--- a/changelogs/unreleased/56110-cluster-kubernetes-api-500-error-on-post-request.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improves restriction of multiple Kubernetes clusters through API
-merge_request: 24251
-author:
-type: fixed
diff --git a/changelogs/unreleased/56172-docs-fix-add-include-to-ci-param-list.yml b/changelogs/unreleased/56172-docs-fix-add-include-to-ci-param-list.yml
deleted file mode 100644
index 92592290ac4..00000000000
--- a/changelogs/unreleased/56172-docs-fix-add-include-to-ci-param-list.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update CI YAML param table with include
-merge_request: !24309
-author:
-type: fixed
diff --git a/changelogs/unreleased/56334-runners-ipv6-address-overlaps-other-values.yml b/changelogs/unreleased/56334-runners-ipv6-address-overlaps-other-values.yml
deleted file mode 100644
index 8a6adef5dae..00000000000
--- a/changelogs/unreleased/56334-runners-ipv6-address-overlaps-other-values.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Resolve Runners IPv6 address overlaps other values
-merge_request: 24531
-author:
-type: fixed
diff --git a/changelogs/unreleased/56363-inconsitent-file-size-indication-across-different-ci-pages.yml b/changelogs/unreleased/56363-inconsitent-file-size-indication-across-different-ci-pages.yml
deleted file mode 100644
index 7c923422534..00000000000
--- a/changelogs/unreleased/56363-inconsitent-file-size-indication-across-different-ci-pages.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Show CI artifact file size with 3 significant digits on 'browse job artifacts'
- page
-merge_request: 24387
-author:
-type: fixed
diff --git a/changelogs/unreleased/56371-don-t-check-confidential-issues-for-spam.yml b/changelogs/unreleased/56371-don-t-check-confidential-issues-for-spam.yml
deleted file mode 100644
index fcfa29977d1..00000000000
--- a/changelogs/unreleased/56371-don-t-check-confidential-issues-for-spam.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Do not run spam checks on confidential issues
-merge_request: 24453
-author:
-type: fixed
diff --git a/changelogs/unreleased/56379-pipeline-stages-job-action-button-icon-is-not-aligned.yml b/changelogs/unreleased/56379-pipeline-stages-job-action-button-icon-is-not-aligned.yml
deleted file mode 100644
index ec8a1d9d6ea..00000000000
--- a/changelogs/unreleased/56379-pipeline-stages-job-action-button-icon-is-not-aligned.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Resolve Pipeline stages job action button icon is not aligned
-merge_request: 24577
-author:
-type: fixed
diff --git a/changelogs/unreleased/56389-remove-unwanted-suggestion-flash-margin.yml b/changelogs/unreleased/56389-remove-unwanted-suggestion-flash-margin.yml
deleted file mode 100644
index 3494feb9be1..00000000000
--- a/changelogs/unreleased/56389-remove-unwanted-suggestion-flash-margin.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove unwanted margin above suggested changes.
-merge_request: 24419
-author:
-type: fixed
diff --git a/changelogs/unreleased/56398-fix-cluster-installation-loading-state.yml b/changelogs/unreleased/56398-fix-cluster-installation-loading-state.yml
deleted file mode 100644
index 19ff408ddf4..00000000000
--- a/changelogs/unreleased/56398-fix-cluster-installation-loading-state.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix cluster installation processing spinner
-merge_request: 24814
-author:
-type: fixed
diff --git a/changelogs/unreleased/56417-update-helm-to-2-12-2.yml b/changelogs/unreleased/56417-update-helm-to-2-12-2.yml
deleted file mode 100644
index f01915c532f..00000000000
--- a/changelogs/unreleased/56417-update-helm-to-2-12-2.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update Helm to 2.12.2 to address Helm client vulnerability
-merge_request: 24418
-author: Takuya Noguchi
-type: security
diff --git a/changelogs/unreleased/56492-implement-new-arguments-state-closed_before-and-closed_after-for-issuesresolver-in-graphql.yml b/changelogs/unreleased/56492-implement-new-arguments-state-closed_before-and-closed_after-for-issuesresolver-in-graphql.yml
new file mode 100644
index 00000000000..9b7aed82d49
--- /dev/null
+++ b/changelogs/unreleased/56492-implement-new-arguments-state-closed_before-and-closed_after-for-issuesresolver-in-graphql.yml
@@ -0,0 +1,5 @@
+---
+title: "Implement new arguments `state`, `closed_before` and `closed_after` for `IssuesResolver` in GraphQL"
+merge_request: 24910
+author:
+type: changed
diff --git a/changelogs/unreleased/56507-sh-bump-katex-0.10.0.yml b/changelogs/unreleased/56507-sh-bump-katex-0.10.0.yml
deleted file mode 100644
index 671e204da21..00000000000
--- a/changelogs/unreleased/56507-sh-bump-katex-0.10.0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade KaTeX to version 0.10.0
-merge_request: 24478
-author: Andrew Harmon
-type: fixed \ No newline at end of file
diff --git a/changelogs/unreleased/56543-project-lists-further-iteration-improvements.yml b/changelogs/unreleased/56543-project-lists-further-iteration-improvements.yml
deleted file mode 100644
index 388ff1d062a..00000000000
--- a/changelogs/unreleased/56543-project-lists-further-iteration-improvements.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Project list UI improvements
-merge_request: 24855
-author:
-type: other
diff --git a/changelogs/unreleased/56547-limit-sidekiq-logging-based-on-argument-size.yml b/changelogs/unreleased/56547-limit-sidekiq-logging-based-on-argument-size.yml
deleted file mode 100644
index 9ef274f3b49..00000000000
--- a/changelogs/unreleased/56547-limit-sidekiq-logging-based-on-argument-size.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Prevent Sidekiq arguments over 10 KB in size from being logged to JSON
-merge_request: 24493
-author:
-type: changed
diff --git a/changelogs/unreleased/56556-fix-markdown-table-border.yml b/changelogs/unreleased/56556-fix-markdown-table-border.yml
deleted file mode 100644
index 7724f49d4e9..00000000000
--- a/changelogs/unreleased/56556-fix-markdown-table-border.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix markdown table border.
-merge_request: 24601
-author:
-type: fixed
diff --git a/changelogs/unreleased/56622-admin-settings-cannot-read-property-addeventlistener-of-null.yml b/changelogs/unreleased/56622-admin-settings-cannot-read-property-addeventlistener-of-null.yml
deleted file mode 100644
index 52b2db0e999..00000000000
--- a/changelogs/unreleased/56622-admin-settings-cannot-read-property-addeventlistener-of-null.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Load initUserInternalRegexPlaceholder only when required
-merge_request: 24522
-author:
-type: fixed
diff --git a/changelogs/unreleased/56636-hashed-storage-afterrenameservice.yml b/changelogs/unreleased/56636-hashed-storage-afterrenameservice.yml
deleted file mode 100644
index 1f808850554..00000000000
--- a/changelogs/unreleased/56636-hashed-storage-afterrenameservice.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Hashed Storage: `AfterRenameService` was receiving the wrong `old_path` under some circunstances'
-merge_request: 24526
-author:
-type: fixed
diff --git a/changelogs/unreleased/56764-poor-ui-on-milestone-validation-error-page.yml b/changelogs/unreleased/56764-poor-ui-on-milestone-validation-error-page.yml
deleted file mode 100644
index 089ffd47321..00000000000
--- a/changelogs/unreleased/56764-poor-ui-on-milestone-validation-error-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix CSS grid on a new Project/Group Milestone
-merge_request: 24614
-author: Takuya Noguchi
-type: fixed
diff --git a/changelogs/unreleased/56787-realtime-validation-for-user-fullname-and-username.yml b/changelogs/unreleased/56787-realtime-validation-for-user-fullname-and-username.yml
new file mode 100644
index 00000000000..cc3a60479d3
--- /dev/null
+++ b/changelogs/unreleased/56787-realtime-validation-for-user-fullname-and-username.yml
@@ -0,0 +1,5 @@
+---
+title: Add realtime validation for user fullname and username on validation
+merge_request: 25017
+author: Ehsan Abdulqader @EhsanZ
+type: added
diff --git a/changelogs/unreleased/56788-unicorn-metric-labels.yml b/changelogs/unreleased/56788-unicorn-metric-labels.yml
deleted file mode 100644
index 824c981780c..00000000000
--- a/changelogs/unreleased/56788-unicorn-metric-labels.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Clean up unicorn sampler metric labels
-merge_request: 24626
-author: bjk-gitlab
-type: fixed
diff --git a/changelogs/unreleased/56851-blank-values-in-reactive-cache.yml b/changelogs/unreleased/56851-blank-values-in-reactive-cache.yml
new file mode 100644
index 00000000000..5b9253793be
--- /dev/null
+++ b/changelogs/unreleased/56851-blank-values-in-reactive-cache.yml
@@ -0,0 +1,5 @@
+---
+title: Allow empty values such as [] to be stored in reactive cache
+merge_request: 25283
+author:
+type: fixed
diff --git a/changelogs/unreleased/56871-list-issues-error.yml b/changelogs/unreleased/56871-list-issues-error.yml
new file mode 100644
index 00000000000..af5585c6b5d
--- /dev/null
+++ b/changelogs/unreleased/56871-list-issues-error.yml
@@ -0,0 +1,5 @@
+---
+title: Display error message when API call to list Sentry issues fails
+merge_request: 24936
+author:
+type: fixed
diff --git a/changelogs/unreleased/56937-edit-knative-domain-after-it-has-been-deployed.yml b/changelogs/unreleased/56937-edit-knative-domain-after-it-has-been-deployed.yml
new file mode 100644
index 00000000000..11d93b34700
--- /dev/null
+++ b/changelogs/unreleased/56937-edit-knative-domain-after-it-has-been-deployed.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes functions finder for upgraded Knative app
+merge_request: 25067
+author:
+type: fixed
diff --git a/changelogs/unreleased/56938-diff-file-headers-on-compare-not-quite-right.yml b/changelogs/unreleased/56938-diff-file-headers-on-compare-not-quite-right.yml
deleted file mode 100644
index f619a009a63..00000000000
--- a/changelogs/unreleased/56938-diff-file-headers-on-compare-not-quite-right.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Correct spacing for comparison page
-merge_request: !24783
-author:
-type: fixed
diff --git a/changelogs/unreleased/57063-implement-new-arguments-iid-for-issuesresolver-in-graphql.yml b/changelogs/unreleased/57063-implement-new-arguments-iid-for-issuesresolver-in-graphql.yml
deleted file mode 100644
index b05ab07e14c..00000000000
--- a/changelogs/unreleased/57063-implement-new-arguments-iid-for-issuesresolver-in-graphql.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add argument iids for issues in GraphQL
-merge_request: 24802
-author:
-type: added
diff --git a/changelogs/unreleased/57160-merge-request-tabs-header-is-missing-bottom-border.yml b/changelogs/unreleased/57160-merge-request-tabs-header-is-missing-bottom-border.yml
deleted file mode 100644
index 3146d07db3d..00000000000
--- a/changelogs/unreleased/57160-merge-request-tabs-header-is-missing-bottom-border.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Return bottom border on MR Tabs
-merge_request: !25198
-author:
-type: fixed
diff --git a/changelogs/unreleased/57227-absolute-uri-missing-hierarchical-segment.yml b/changelogs/unreleased/57227-absolute-uri-missing-hierarchical-segment.yml
deleted file mode 100644
index 3a663ce2132..00000000000
--- a/changelogs/unreleased/57227-absolute-uri-missing-hierarchical-segment.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix potential Addressable::URI::InvalidURIError
-merge_request: 24908
-author:
-type: fixed
diff --git a/changelogs/unreleased/57353-git-push-fails-on-large-lfs-files-where-the-push-take-a-long-time.yml b/changelogs/unreleased/57353-git-push-fails-on-large-lfs-files-where-the-push-take-a-long-time.yml
new file mode 100644
index 00000000000..46f82afda62
--- /dev/null
+++ b/changelogs/unreleased/57353-git-push-fails-on-large-lfs-files-where-the-push-take-a-long-time.yml
@@ -0,0 +1,5 @@
+---
+title: Provide expires_in in LFS authentication payload
+merge_request: 25082
+author:
+type: fixed
diff --git a/changelogs/unreleased/57589-update-workhorse.yml b/changelogs/unreleased/57589-update-workhorse.yml
deleted file mode 100644
index 525913bba4c..00000000000
--- a/changelogs/unreleased/57589-update-workhorse.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update Workhorse to v8.3.1
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/57671-fix_merge_request_base_pipeline.yml b/changelogs/unreleased/57671-fix_merge_request_base_pipeline.yml
new file mode 100644
index 00000000000..d89819eee60
--- /dev/null
+++ b/changelogs/unreleased/57671-fix_merge_request_base_pipeline.yml
@@ -0,0 +1,5 @@
+---
+title: Ensure the base pipeline of a Merge Request belongs to its target branch
+merge_request: 25226
+author:
+type: fixed
diff --git a/changelogs/unreleased/57768-remove-vertical-line.yml b/changelogs/unreleased/57768-remove-vertical-line.yml
new file mode 100644
index 00000000000..b73b0fa229e
--- /dev/null
+++ b/changelogs/unreleased/57768-remove-vertical-line.yml
@@ -0,0 +1,5 @@
+---
+title: Remove vertical connecting line placeholder from diff discussion notes
+merge_request: 25292
+author:
+type: fixed
diff --git a/changelogs/unreleased/57784-make-closed-duplicate-and-closed-moved-button-a-link-to-target.yml b/changelogs/unreleased/57784-make-closed-duplicate-and-closed-moved-button-a-link-to-target.yml
new file mode 100644
index 00000000000..2775d9f4e36
--- /dev/null
+++ b/changelogs/unreleased/57784-make-closed-duplicate-and-closed-moved-button-a-link-to-target.yml
@@ -0,0 +1,5 @@
+---
+title: Add Link from Closed (moved) Issues to Moved Issue
+merge_request: 25300
+author:
+type: added
diff --git a/changelogs/unreleased/8688-recursive-pipelines-ce-backport.yml b/changelogs/unreleased/8688-recursive-pipelines-ce-backport.yml
deleted file mode 100644
index cd7b56a1e05..00000000000
--- a/changelogs/unreleased/8688-recursive-pipelines-ce-backport.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Creates mixin to reduce code duplication between CE and EE in graph component
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/9841-geo-unable-to-compare-branches-on-secondary.yml b/changelogs/unreleased/9841-geo-unable-to-compare-branches-on-secondary.yml
new file mode 100644
index 00000000000..c014edf9c09
--- /dev/null
+++ b/changelogs/unreleased/9841-geo-unable-to-compare-branches-on-secondary.yml
@@ -0,0 +1,5 @@
+---
+title: Allow users to compare branches on a read-only instance
+merge_request: 25414
+author:
+type: fixed
diff --git a/changelogs/unreleased/Projects--dropdown-is-misaligned-on-issue-boards-page.yml b/changelogs/unreleased/Projects--dropdown-is-misaligned-on-issue-boards-page.yml
deleted file mode 100644
index 49511294c48..00000000000
--- a/changelogs/unreleased/Projects--dropdown-is-misaligned-on-issue-boards-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Proper align Projects dropdown on issue boards page
-merge_request: 24277
-author: Johann Hubert Sonntagbauer
-type: fixed
diff --git a/changelogs/unreleased/ab-54270-github-iid.yml b/changelogs/unreleased/ab-54270-github-iid.yml
deleted file mode 100644
index 1776b0aeb86..00000000000
--- a/changelogs/unreleased/ab-54270-github-iid.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve efficiency of GitHub importer by reducing amount of locks needed.
-merge_request: 24102
-author:
-type: performance
diff --git a/changelogs/unreleased/ac-pages-subgroups.yml b/changelogs/unreleased/ac-pages-subgroups.yml
deleted file mode 100644
index ef5a0c1872e..00000000000
--- a/changelogs/unreleased/ac-pages-subgroups.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Pages for subgroups
-merge_request: 23505
-author:
-type: added
diff --git a/changelogs/unreleased/actioncontroller-parameters-deprecations.yml b/changelogs/unreleased/actioncontroller-parameters-deprecations.yml
deleted file mode 100644
index ddd15c37542..00000000000
--- a/changelogs/unreleased/actioncontroller-parameters-deprecations.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix several ActionController::Parameters deprecations
-merge_request: 24332
-author: Jasper Maes
-type: other
diff --git a/changelogs/unreleased/add-badge-count-to-projects-and-groups.yml b/changelogs/unreleased/add-badge-count-to-projects-and-groups.yml
deleted file mode 100644
index e200bbaa806..00000000000
--- a/changelogs/unreleased/add-badge-count-to-projects-and-groups.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add badge count to projects
-merge_request: 18425
-author: George Tsiolis
-type: added
diff --git a/changelogs/unreleased/add-related-merge-request-count-to-api-response.yml b/changelogs/unreleased/add-related-merge-request-count-to-api-response.yml
new file mode 100644
index 00000000000..7438053a84f
--- /dev/null
+++ b/changelogs/unreleased/add-related-merge-request-count-to-api-response.yml
@@ -0,0 +1,5 @@
+---
+title: Add related merge request count to api response
+merge_request: 24974
+author:
+type: added
diff --git a/changelogs/unreleased/add-uniqueness-validation-to-url-column-in-releases-link-model.yml b/changelogs/unreleased/add-uniqueness-validation-to-url-column-in-releases-link-model.yml
deleted file mode 100644
index 7d767e220f7..00000000000
--- a/changelogs/unreleased/add-uniqueness-validation-to-url-column-in-releases-link-model.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add uniqueness validation to url column in Releases::Link model
-merge_request: 24223
-author:
-type: other
diff --git a/changelogs/unreleased/adrianmoisey-GITLAB_PAGES_PREDEFINED_VARIABLES.yml b/changelogs/unreleased/adrianmoisey-GITLAB_PAGES_PREDEFINED_VARIABLES.yml
deleted file mode 100644
index a664c44e1d7..00000000000
--- a/changelogs/unreleased/adrianmoisey-GITLAB_PAGES_PREDEFINED_VARIABLES.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add GitLab Pages predefined CI variables 'CI_PAGES_DOMAIN' and 'CI_PAGES_URL'
-merge_request: 24504
-author: Adrian Moisey
-type: added
diff --git a/changelogs/unreleased/adriel-remove-feature-flag.yml b/changelogs/unreleased/adriel-remove-feature-flag.yml
deleted file mode 100644
index d442e120d60..00000000000
--- a/changelogs/unreleased/adriel-remove-feature-flag.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update metrics dashboard graph design
-merge_request: 24653
-author:
-type: changed
diff --git a/changelogs/unreleased/an-dtracing-test-for-invalid-tracers.yml b/changelogs/unreleased/an-dtracing-test-for-invalid-tracers.yml
deleted file mode 100644
index 5365260cbae..00000000000
--- a/changelogs/unreleased/an-dtracing-test-for-invalid-tracers.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Avoid overwriting default jaeger values with nil
-merge_request: 24482
-author:
-type: fixed
diff --git a/changelogs/unreleased/an-gilab-process-name.yml b/changelogs/unreleased/an-gilab-process-name.yml
deleted file mode 100644
index 72d811ee21f..00000000000
--- a/changelogs/unreleased/an-gilab-process-name.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Extract process_name from GitLab::Sentry
-merge_request: 24422
-author:
-type: other
diff --git a/changelogs/unreleased/an-opentracing-active-record-tracing.yml b/changelogs/unreleased/an-opentracing-active-record-tracing.yml
deleted file mode 100644
index 59b480675ec..00000000000
--- a/changelogs/unreleased/an-opentracing-active-record-tracing.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds tracing support for ActiveRecord notifications
-merge_request: 24604
-author:
-type: other
diff --git a/changelogs/unreleased/an-opentracing-factory.yml b/changelogs/unreleased/an-opentracing-factory.yml
deleted file mode 100644
index c04736f3e63..00000000000
--- a/changelogs/unreleased/an-opentracing-factory.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Conditionally initialize the global opentracing tracer
-merge_request: 24186
-author:
-type: other
diff --git a/changelogs/unreleased/an-opentracing-propagation.yml b/changelogs/unreleased/an-opentracing-propagation.yml
deleted file mode 100644
index d9aa7cd0048..00000000000
--- a/changelogs/unreleased/an-opentracing-propagation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds inter-service OpenTracing propagation
-merge_request: 24239
-author:
-type: other
diff --git a/changelogs/unreleased/an-opentracing-render-tracing.yml b/changelogs/unreleased/an-opentracing-render-tracing.yml
deleted file mode 100644
index 6ff7f1f3cf2..00000000000
--- a/changelogs/unreleased/an-opentracing-render-tracing.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add OpenTracing instrumentation for Action View Render events
-merge_request: 24728
-author:
-type: other
diff --git a/changelogs/unreleased/api-group-labels.yml b/changelogs/unreleased/api-group-labels.yml
deleted file mode 100644
index 0df6f15a9b6..00000000000
--- a/changelogs/unreleased/api-group-labels.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Add support for group labels'
-merge_request: 21368
-author: Robert Schilling
-type: added
diff --git a/changelogs/unreleased/api-nested-group-permission.yml b/changelogs/unreleased/api-nested-group-permission.yml
deleted file mode 100644
index 3ec0df6893f..00000000000
--- a/changelogs/unreleased/api-nested-group-permission.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Return the maximum group access level in the projects API
-merge_request: 24403
-author:
-type: changed
diff --git a/changelogs/unreleased/api-tags-search.yml b/changelogs/unreleased/api-tags-search.yml
deleted file mode 100644
index 1501acd5a9e..00000000000
--- a/changelogs/unreleased/api-tags-search.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Support searching for tags'
-merge_request: 24385
-author: Robert Schilling
-type: added
diff --git a/changelogs/unreleased/api-wiki-dot-slug.yml b/changelogs/unreleased/api-wiki-dot-slug.yml
deleted file mode 100644
index 82c76fa7450..00000000000
--- a/changelogs/unreleased/api-wiki-dot-slug.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Support dots in wiki slugs'
-merge_request: 24383
-author: Robert Schilling
-type: fixed
diff --git a/changelogs/unreleased/auto-devops-custom-domains.yml b/changelogs/unreleased/auto-devops-custom-domains.yml
deleted file mode 100644
index 37e8ee26a4d..00000000000
--- a/changelogs/unreleased/auto-devops-custom-domains.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added support for custom hosts/domains to Auto DevOps
-merge_request: 24248
-author: walkafwalka
-type: added
diff --git a/changelogs/unreleased/auto-devops-kubectl-1-11-6.yml b/changelogs/unreleased/auto-devops-kubectl-1-11-6.yml
deleted file mode 100644
index 1a8cdead4ac..00000000000
--- a/changelogs/unreleased/auto-devops-kubectl-1-11-6.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Bump kubectl in Auto DevOps to 1.11.6
-merge_request: 24176
-author:
-type: other
diff --git a/changelogs/unreleased/backup_aws_sse-c.yml b/changelogs/unreleased/backup_aws_sse-c.yml
deleted file mode 100644
index 78b57d7efc3..00000000000
--- a/changelogs/unreleased/backup_aws_sse-c.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-title: Add support for customer provided encryption keys for Amazon S3 remote backups
-merge_request: 23797
-author: Pepijn Van Eeckhoudt
-type: added
-
diff --git a/changelogs/unreleased/backup_restore_fix_issue_46891.yml b/changelogs/unreleased/backup_restore_fix_issue_46891.yml
deleted file mode 100644
index b8fe3b1b861..00000000000
--- a/changelogs/unreleased/backup_restore_fix_issue_46891.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Modify file restore to rectify tar issue
-merge_request: 24000
-author:
-type: fixed
diff --git a/changelogs/unreleased/bump-ingress-chart-112.yml b/changelogs/unreleased/bump-ingress-chart-112.yml
deleted file mode 100644
index 8a46fedb4b0..00000000000
--- a/changelogs/unreleased/bump-ingress-chart-112.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Bump nginx-ingress chart to 1.1.2
-merge_request: 24203
-author:
-type: other
diff --git a/changelogs/unreleased/bvl-fix-race-condition-creating-signature.yml b/changelogs/unreleased/bvl-fix-race-condition-creating-signature.yml
deleted file mode 100644
index 307b4f526bb..00000000000
--- a/changelogs/unreleased/bvl-fix-race-condition-creating-signature.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Avoid race conditions when creating GpgSignature
-merge_request: 24939
-author:
-type: fixed
diff --git a/changelogs/unreleased/change-badges-example-to-pipeline.yml b/changelogs/unreleased/change-badges-example-to-pipeline.yml
new file mode 100644
index 00000000000..8ed4d77fd6c
--- /dev/null
+++ b/changelogs/unreleased/change-badges-example-to-pipeline.yml
@@ -0,0 +1,5 @@
+---
+title: Change badges.svg example to pipeline.svg
+merge_request: 25157
+author: Aviad Levy
+type: fixed
diff --git a/changelogs/unreleased/changelogs-readme.yml b/changelogs/unreleased/changelogs-readme.yml
new file mode 100644
index 00000000000..9f391699575
--- /dev/null
+++ b/changelogs/unreleased/changelogs-readme.yml
@@ -0,0 +1,5 @@
+---
+title: add readme to changelogs directory
+merge_request: 25209
+author: "@glensc"
+type: added
diff --git a/changelogs/unreleased/chore-update-js-regex.yml b/changelogs/unreleased/chore-update-js-regex.yml
deleted file mode 100644
index d45d0b47457..00000000000
--- a/changelogs/unreleased/chore-update-js-regex.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade js-regex gem to version 3.1
-merge_request: 24433
-author: rroger
-type: changed
diff --git a/changelogs/unreleased/cleanup-leagcy-artifact-migration.yml b/changelogs/unreleased/cleanup-leagcy-artifact-migration.yml
deleted file mode 100644
index 6e8dac97249..00000000000
--- a/changelogs/unreleased/cleanup-leagcy-artifact-migration.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Cleanup legacy artifact background migration
-merge_request: 24144
-author:
-type: other
diff --git a/changelogs/unreleased/cluster_application_version_updated.yml b/changelogs/unreleased/cluster_application_version_updated.yml
deleted file mode 100644
index 34fe55dcc5e..00000000000
--- a/changelogs/unreleased/cluster_application_version_updated.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update cluster application version on updated and installed status
-merge_request: 24810
-author:
-type: other
diff --git a/changelogs/unreleased/cluster_status_for_ugprading.yml b/changelogs/unreleased/cluster_status_for_ugprading.yml
deleted file mode 100644
index ca1f8b3a786..00000000000
--- a/changelogs/unreleased/cluster_status_for_ugprading.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Expose version for each application in cluster_status JSON endpoint
-merge_request: 24791
-author:
-type: other
diff --git a/changelogs/unreleased/container-repository-cleanup-api.yml b/changelogs/unreleased/container-repository-cleanup-api.yml
deleted file mode 100644
index c2b23a9add0..00000000000
--- a/changelogs/unreleased/container-repository-cleanup-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add Container Registry API with cleanup function
-merge_request: 24303
-author:
-type: added
diff --git a/changelogs/unreleased/custom-helm-chart-repo.yml b/changelogs/unreleased/custom-helm-chart-repo.yml
deleted file mode 100644
index 592d2f60ca2..00000000000
--- a/changelogs/unreleased/custom-helm-chart-repo.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added feature to specify a custom Auto DevOps chart repository
-merge_request: 24162
-author: walkafwalka
-type: added
diff --git a/changelogs/unreleased/deprecated-force-reload.yml b/changelogs/unreleased/deprecated-force-reload.yml
deleted file mode 100644
index 2a0e97089e0..00000000000
--- a/changelogs/unreleased/deprecated-force-reload.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: 'Fix deprecation: Passing an argument to force an association to reload is
- now deprecated'
-merge_request: 24136
-author: Jasper Maes
-type: other
diff --git a/changelogs/unreleased/diff-file-finder.yml b/changelogs/unreleased/diff-file-finder.yml
deleted file mode 100644
index 3160e9fc91b..00000000000
--- a/changelogs/unreleased/diff-file-finder.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added fuzzy file finder to merge requests
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/diff-tree-collapse-directories.yml b/changelogs/unreleased/diff-tree-collapse-directories.yml
deleted file mode 100644
index 6eae48f2352..00000000000
--- a/changelogs/unreleased/diff-tree-collapse-directories.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Collapse directory structure in merge request file tree
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/diff-tree-resizable.yml b/changelogs/unreleased/diff-tree-resizable.yml
new file mode 100644
index 00000000000..7411640aea5
--- /dev/null
+++ b/changelogs/unreleased/diff-tree-resizable.yml
@@ -0,0 +1,5 @@
+---
+title: Make file tree in merge requests resizable
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/dm-copy-suggestion-as-gfm.yml b/changelogs/unreleased/dm-copy-suggestion-as-gfm.yml
deleted file mode 100644
index 96115e6ade1..00000000000
--- a/changelogs/unreleased/dm-copy-suggestion-as-gfm.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow suggestions to be copied and pasted as GFM
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/dm-trim-discussion-truncated-line-first-chars.yml b/changelogs/unreleased/dm-trim-discussion-truncated-line-first-chars.yml
deleted file mode 100644
index 1e1fa8295c3..00000000000
--- a/changelogs/unreleased/dm-trim-discussion-truncated-line-first-chars.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix bug that caused Suggestion Markdown toolbar button to insert snippet with leading +/-/<space>
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/docs-push-mirror-GitLab-GitHub.yml b/changelogs/unreleased/docs-push-mirror-GitLab-GitHub.yml
deleted file mode 100644
index 4539a9b7985..00000000000
--- a/changelogs/unreleased/docs-push-mirror-GitLab-GitHub.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Updated docs for fields in pushing mirror from GitLab to GitHub
-merge_request: 24566
-author: Joseph Yu
-type: other
diff --git a/changelogs/unreleased/dz-sort-labels-alphabetically.yml b/changelogs/unreleased/dz-sort-labels-alphabetically.yml
new file mode 100644
index 00000000000..acfde3de999
--- /dev/null
+++ b/changelogs/unreleased/dz-sort-labels-alphabetically.yml
@@ -0,0 +1,5 @@
+---
+title: Sort labels alphabetically on issues and merge requests list
+merge_request: 25470
+author:
+type: changed
diff --git a/changelogs/unreleased/expire-job-artifacts-worker.yml b/changelogs/unreleased/expire-job-artifacts-worker.yml
deleted file mode 100644
index cda6e9ff497..00000000000
--- a/changelogs/unreleased/expire-job-artifacts-worker.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Efficiently remove expired artifacts in `ExpireBuildArtifactsWorker`
-merge_request: 24450
-author:
-type: performance
diff --git a/changelogs/unreleased/feature-gb-enable-ci-persisted-stages-by-default.yml b/changelogs/unreleased/feature-gb-enable-ci-persisted-stages-by-default.yml
new file mode 100644
index 00000000000..ad92135d401
--- /dev/null
+++ b/changelogs/unreleased/feature-gb-enable-ci-persisted-stages-by-default.yml
@@ -0,0 +1,5 @@
+---
+title: Enable persisted pipeline stages by default
+merge_request: 25347
+author:
+type: performance
diff --git a/changelogs/unreleased/features-document-graphicsmagick-source-installation.yml b/changelogs/unreleased/features-document-graphicsmagick-source-installation.yml
deleted file mode 100644
index b224cace4bf..00000000000
--- a/changelogs/unreleased/features-document-graphicsmagick-source-installation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Document graphicsmagick installation for source installation
-merge_request: 24404
-author: Alexis Reigel
-type: added
diff --git a/changelogs/unreleased/fix-39759-new-project-icon-vertical-align.yml b/changelogs/unreleased/fix-39759-new-project-icon-vertical-align.yml
deleted file mode 100644
index 3d87807dbc1..00000000000
--- a/changelogs/unreleased/fix-39759-new-project-icon-vertical-align.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adjust vertical alignment for project visibility icons
-merge_request: 24511
-author: Martin Hobert
-type: fixed
diff --git a/changelogs/unreleased/fix-403-page-is-rendered-but-404-is-the-response.yml b/changelogs/unreleased/fix-403-page-is-rendered-but-404-is-the-response.yml
deleted file mode 100644
index eda69b32094..00000000000
--- a/changelogs/unreleased/fix-403-page-is-rendered-but-404-is-the-response.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Show the correct error page when access is denied
-merge_request: 23932
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-49388.yml b/changelogs/unreleased/fix-49388.yml
deleted file mode 100644
index f8b5e3e1943..00000000000
--- a/changelogs/unreleased/fix-49388.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update metrics environment dropdown to show complete option set
-merge_request: 24441
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-55956-oversized-dropdown-button-custom-notifications.yml b/changelogs/unreleased/fix-55956-oversized-dropdown-button-custom-notifications.yml
deleted file mode 100644
index e33699a2112..00000000000
--- a/changelogs/unreleased/fix-55956-oversized-dropdown-button-custom-notifications.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed oversized custom project notification selector dropdown
-merge_request: 24557
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-56558-move-primary-button.yml b/changelogs/unreleased/fix-56558-move-primary-button.yml
deleted file mode 100644
index 4dcc896b327..00000000000
--- a/changelogs/unreleased/fix-56558-move-primary-button.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Moved primary button for labels to follow the design patterns used on rest of the site
-merge_request:
-author: Martin Hobert
-type: fixed
diff --git a/changelogs/unreleased/fix-auto-devops-domain-title-on-admin-settings.yml b/changelogs/unreleased/fix-auto-devops-domain-title-on-admin-settings.yml
deleted file mode 100644
index bb0b193a846..00000000000
--- a/changelogs/unreleased/fix-auto-devops-domain-title-on-admin-settings.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixes Auto DevOps title on CI/CD admin settings
-merge_request: 24249
-author:
-type: other
diff --git a/changelogs/unreleased/fix-repo-settings-file-upload-error.yml b/changelogs/unreleased/fix-repo-settings-file-upload-error.yml
deleted file mode 100644
index b219fdfaa1e..00000000000
--- a/changelogs/unreleased/fix-repo-settings-file-upload-error.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix bug causing repository mirror settings UI to break
-merge_request: 23712
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix_jira_integration_VCS1019.yml b/changelogs/unreleased/fix_jira_integration_VCS1019.yml
deleted file mode 100644
index 3582ec1fe0f..00000000000
--- a/changelogs/unreleased/fix_jira_integration_VCS1019.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix Jira Service password validation on project integration services.
-merge_request: 24896
-author: Daniel Juarez
-type: fixed
diff --git a/changelogs/unreleased/fj-55882-fix-files-api-content-disposition.yml b/changelogs/unreleased/fj-55882-fix-files-api-content-disposition.yml
deleted file mode 100644
index f64b29644b0..00000000000
--- a/changelogs/unreleased/fj-55882-fix-files-api-content-disposition.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix files/blob api endpoints content disposition
-merge_request: 24267
-author:
-type: fixed
diff --git a/changelogs/unreleased/force-redeploy-on-updated-secrets.yml b/changelogs/unreleased/force-redeploy-on-updated-secrets.yml
deleted file mode 100644
index 3b727c99dd5..00000000000
--- a/changelogs/unreleased/force-redeploy-on-updated-secrets.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Redeploy Auto DevOps deployment on variable updates
-merge_request: 24498
-author: walkafwalka
-type: added
diff --git a/changelogs/unreleased/gitaly-update-1-13-0.yml b/changelogs/unreleased/gitaly-update-1-13-0.yml
deleted file mode 100644
index 73de25a532d..00000000000
--- a/changelogs/unreleased/gitaly-update-1-13-0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade Gitaly to 1.13.0
-merge_request: 24429
-author:
-type: other
diff --git a/changelogs/unreleased/gitaly-update-1.18.0.yml b/changelogs/unreleased/gitaly-update-1.18.0.yml
deleted file mode 100644
index 392527f5e5d..00000000000
--- a/changelogs/unreleased/gitaly-update-1.18.0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade gitaly to 1.18.0
-merge_request: 24981
-author:
-type: other
diff --git a/changelogs/unreleased/gitlab-workhorse-update-8.1.0.yml b/changelogs/unreleased/gitlab-workhorse-update-8.1.0.yml
deleted file mode 100644
index 1e0160c4d40..00000000000
--- a/changelogs/unreleased/gitlab-workhorse-update-8.1.0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade gitlab-workhorse to 8.1.0
-merge_request: 24571
-author:
-type: other
diff --git a/changelogs/unreleased/gitlab_kubernetes_helm_bump.yml b/changelogs/unreleased/gitlab_kubernetes_helm_bump.yml
new file mode 100644
index 00000000000..b8668d338de
--- /dev/null
+++ b/changelogs/unreleased/gitlab_kubernetes_helm_bump.yml
@@ -0,0 +1,5 @@
+---
+title: Bump Helm and kubectl used in Kubernetes integration to 2.12.3 and 1.11.7 respectively
+merge_request: 25268
+author:
+type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-clusters.yml b/changelogs/unreleased/gt-externalize-app-views-clusters.yml
deleted file mode 100644
index 6d2284ead37..00000000000
--- a/changelogs/unreleased/gt-externalize-app-views-clusters.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Externalize strings from `/app/views/clusters`
-merge_request: 24666
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-email_rejection_mailer.yml b/changelogs/unreleased/gt-externalize-app-views-email_rejection_mailer.yml
deleted file mode 100644
index 8f6fbdceb54..00000000000
--- a/changelogs/unreleased/gt-externalize-app-views-email_rejection_mailer.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Externalize strings from `/app/views/email_rejection_mailer`
-merge_request: 24869
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-instance_statistics.yml b/changelogs/unreleased/gt-externalize-app-views-instance_statistics.yml
deleted file mode 100644
index a3bf54a1339..00000000000
--- a/changelogs/unreleased/gt-externalize-app-views-instance_statistics.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Externalize strings from `/app/views/instance_statistics`
-merge_request: 24809
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-projects-ci.yml b/changelogs/unreleased/gt-externalize-app-views-projects-ci.yml
deleted file mode 100644
index ecc878ab892..00000000000
--- a/changelogs/unreleased/gt-externalize-app-views-projects-ci.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Externalize strings from `/app/views/projects/ci`
-merge_request: 24617
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-projects-commit.yml b/changelogs/unreleased/gt-externalize-app-views-projects-commit.yml
new file mode 100644
index 00000000000..29dbf2367b7
--- /dev/null
+++ b/changelogs/unreleased/gt-externalize-app-views-projects-commit.yml
@@ -0,0 +1,5 @@
+---
+title: Externalize strings from `/app/views/projects/commit`
+merge_request: 24668
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-projects-milestones.yml b/changelogs/unreleased/gt-externalize-app-views-projects-milestones.yml
deleted file mode 100644
index 56aaac812bb..00000000000
--- a/changelogs/unreleased/gt-externalize-app-views-projects-milestones.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Externalize strings from `/app/views/projects/milestones`
-merge_request: 24726
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-projects-pages_domains.yml b/changelogs/unreleased/gt-externalize-app-views-projects-pages_domains.yml
deleted file mode 100644
index f60776a2ed8..00000000000
--- a/changelogs/unreleased/gt-externalize-app-views-projects-pages_domains.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Externalize strings from `/app/views/projects/pages_domains`
-merge_request: 24723
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-projects-project_members.yml b/changelogs/unreleased/gt-externalize-app-views-projects-project_members.yml
deleted file mode 100644
index 1acea10fcaa..00000000000
--- a/changelogs/unreleased/gt-externalize-app-views-projects-project_members.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Externalize strings from `/app/views/projects/project_members`
-merge_request: 23227
-author: Tao Wang
-type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-sent_notifications.yml b/changelogs/unreleased/gt-externalize-app-views-sent_notifications.yml
deleted file mode 100644
index e77b5376fa8..00000000000
--- a/changelogs/unreleased/gt-externalize-app-views-sent_notifications.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Externalize strings from `/app/views/sent_notifications`
-merge_request: 24576
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/gt-remove-unused-button-class.yml b/changelogs/unreleased/gt-remove-unused-button-class.yml
deleted file mode 100644
index f7889e1d6f6..00000000000
--- a/changelogs/unreleased/gt-remove-unused-button-class.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove unused button classes `btn-create` and `comment-btn`
-merge_request: 23232
-author: George Tsiolis
-type: performance
diff --git a/changelogs/unreleased/gt-rename-gray-theme-color-variables.yml b/changelogs/unreleased/gt-rename-gray-theme-color-variables.yml
deleted file mode 100644
index b612bb3ee39..00000000000
--- a/changelogs/unreleased/gt-rename-gray-theme-color-variables.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove all `$theme-gray-{weight}` variables in favor of `$gray-{weight}`
-merge_request: 24333
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/gt-update-new-password-breadcrumb.yml b/changelogs/unreleased/gt-update-new-password-breadcrumb.yml
new file mode 100644
index 00000000000..43ea2f0d44b
--- /dev/null
+++ b/changelogs/unreleased/gt-update-new-password-breadcrumb.yml
@@ -0,0 +1,5 @@
+---
+title: Update new password breadcrumb
+merge_request: 25037
+author: George Tsiolis
+type: fixed
diff --git a/changelogs/unreleased/gt-update-string-struture-for-group-runners.yml b/changelogs/unreleased/gt-update-string-struture-for-group-runners.yml
deleted file mode 100644
index fa06a78adae..00000000000
--- a/changelogs/unreleased/gt-update-string-struture-for-group-runners.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update string structure for available group runners
-merge_request: 24187
-author: George Tsiolis
-type: changed
diff --git a/changelogs/unreleased/helm-2-12-3.yml b/changelogs/unreleased/helm-2-12-3.yml
new file mode 100644
index 00000000000..0d0d904a9cb
--- /dev/null
+++ b/changelogs/unreleased/helm-2-12-3.yml
@@ -0,0 +1,5 @@
+---
+title: Bump Helm and kubectl in Auto DevOps to 2.12.3 and 1.11.7 respectively
+merge_request: 25072
+author:
+type: other
diff --git a/changelogs/unreleased/hnk-master-patch-61932.yml b/changelogs/unreleased/hnk-master-patch-61932.yml
deleted file mode 100644
index 8cc9d0057a9..00000000000
--- a/changelogs/unreleased/hnk-master-patch-61932.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update runner admin page to make description field larger
-merge_request: 23593
-author: Sascha Reynolds
-type: fixed
diff --git a/changelogs/unreleased/homepage-proj-descr-cutoff.yml b/changelogs/unreleased/homepage-proj-descr-cutoff.yml
deleted file mode 100644
index 837c01f6722..00000000000
--- a/changelogs/unreleased/homepage-proj-descr-cutoff.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Increase line height of project summaries
-merge_request:
-author: gfyoung
-type: fixed
diff --git a/changelogs/unreleased/import-go-to-project-cta.yml b/changelogs/unreleased/import-go-to-project-cta.yml
new file mode 100644
index 00000000000..ae719f08790
--- /dev/null
+++ b/changelogs/unreleased/import-go-to-project-cta.yml
@@ -0,0 +1,5 @@
+---
+title: Improve GitHub and Gitea project import table UI
+merge_request: 24606
+author:
+type: other
diff --git a/changelogs/unreleased/introduce-environment-search-endpoint.yml b/changelogs/unreleased/introduce-environment-search-endpoint.yml
deleted file mode 100644
index 01851ba7d27..00000000000
--- a/changelogs/unreleased/introduce-environment-search-endpoint.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Introduce Internal API for searching environment names
-merge_request: 24923
-author:
-type: added
diff --git a/changelogs/unreleased/iss-32584-preserve-line-number-fragment-after-redirect.yml b/changelogs/unreleased/iss-32584-preserve-line-number-fragment-after-redirect.yml
deleted file mode 100644
index 8025cd472bd..00000000000
--- a/changelogs/unreleased/iss-32584-preserve-line-number-fragment-after-redirect.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Fix lost line number when navigating to a specific line in a protected file
- before authenticating.
-merge_request: 19165
-author: Scott Escue
-type: fixed
diff --git a/changelogs/unreleased/issue_55744.yml b/changelogs/unreleased/issue_55744.yml
deleted file mode 100644
index 6a643732b18..00000000000
--- a/changelogs/unreleased/issue_55744.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix template labels not being created on new projects
-merge_request: 24803
-author:
-type: fixed
diff --git a/changelogs/unreleased/jej-avoid-csrf-check-on-saml-failure.yml b/changelogs/unreleased/jej-avoid-csrf-check-on-saml-failure.yml
deleted file mode 100644
index 18cced2906a..00000000000
--- a/changelogs/unreleased/jej-avoid-csrf-check-on-saml-failure.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Display SAML failure messages instead of expecting CSRF token
-merge_request: 24509
-author:
-type: fixed
diff --git a/changelogs/unreleased/jej-feature-gates-can-be-set-by-group-path.yml b/changelogs/unreleased/jej-feature-gates-can-be-set-by-group-path.yml
new file mode 100644
index 00000000000..ba882112f70
--- /dev/null
+++ b/changelogs/unreleased/jej-feature-gates-can-be-set-by-group-path.yml
@@ -0,0 +1,5 @@
+---
+title: Allow setting feature flags per GitLab group through the API
+merge_request: 25022
+author:
+type: added
diff --git a/changelogs/unreleased/jlenny-AddPagesTemplates.yml b/changelogs/unreleased/jlenny-AddPagesTemplates.yml
deleted file mode 100644
index 0985e4e18ed..00000000000
--- a/changelogs/unreleased/jlenny-AddPagesTemplates.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add templates for most popular Pages templates
-merge_request: 24906
-author:
-type: added
diff --git a/changelogs/unreleased/jlenny-NewAndroidTemplate.yml b/changelogs/unreleased/jlenny-NewAndroidTemplate.yml
deleted file mode 100644
index ae8c58da859..00000000000
--- a/changelogs/unreleased/jlenny-NewAndroidTemplate.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add template for Android with Fastlane
-merge_request: 24722
-author:
-type: changed
diff --git a/changelogs/unreleased/jprovazn-remove-redcarpet.yml b/changelogs/unreleased/jprovazn-remove-redcarpet.yml
deleted file mode 100644
index 4e12de2d19b..00000000000
--- a/changelogs/unreleased/jprovazn-remove-redcarpet.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Removed deprecated Redcarpet markdown engine.
-merge_request:
-author:
-type: removed
diff --git a/changelogs/unreleased/kinolaev-master-patch-87865.yml b/changelogs/unreleased/kinolaev-master-patch-87865.yml
new file mode 100644
index 00000000000..b4dbc2c0e1f
--- /dev/null
+++ b/changelogs/unreleased/kinolaev-master-patch-87865.yml
@@ -0,0 +1,5 @@
+---
+title: Fix rollout status for statefulsets and daemonsets
+merge_request: 24972
+author: Sergej Nikolaev <kinolaev@gmail.com>
+type: fixed
diff --git a/changelogs/unreleased/knative-list.yml b/changelogs/unreleased/knative-list.yml
deleted file mode 100644
index 754d8e172cf..00000000000
--- a/changelogs/unreleased/knative-list.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Modified Knative list view to provide more details
-merge_request: 24072
-author: Chris Baumbauer
-type: changed
diff --git a/changelogs/unreleased/knative-show-page.yml b/changelogs/unreleased/knative-show-page.yml
deleted file mode 100644
index a48b754940f..00000000000
--- a/changelogs/unreleased/knative-show-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add Knative detailed view
-merge_request: 23863
-author: Chris Baumbauer
-type: added
diff --git a/changelogs/unreleased/local-markdown-version-bkp3.yml b/changelogs/unreleased/local-markdown-version-bkp3.yml
deleted file mode 100644
index ce5bff6ae6b..00000000000
--- a/changelogs/unreleased/local-markdown-version-bkp3.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow admins to invalidate markdown texts by setting local markdown version.
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/mg-fix-bad-cluster-update-entrypoint.yml b/changelogs/unreleased/mg-fix-bad-cluster-update-entrypoint.yml
deleted file mode 100644
index 932850cc825..00000000000
--- a/changelogs/unreleased/mg-fix-bad-cluster-update-entrypoint.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix cluster page non-interactive on form validation error
-merge_request: 24583
-author:
-type: fixed
diff --git a/changelogs/unreleased/monospace-registry-tags.yml b/changelogs/unreleased/monospace-registry-tags.yml
deleted file mode 100644
index b5992707d8c..00000000000
--- a/changelogs/unreleased/monospace-registry-tags.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use monospace font for registry table tag id and tag name
-merge_request: 24205
-author:
-type: other
diff --git a/changelogs/unreleased/move-job-cancel-btn.yml b/changelogs/unreleased/move-job-cancel-btn.yml
deleted file mode 100644
index 41f8e1be5f8..00000000000
--- a/changelogs/unreleased/move-job-cancel-btn.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Move cancel & new issue button on job page
-merge_request: 24074
-author:
-type: changed
diff --git a/changelogs/unreleased/move-permission-check-manual-actions-on-deployments.yml b/changelogs/unreleased/move-permission-check-manual-actions-on-deployments.yml
deleted file mode 100644
index 9e979b48ad1..00000000000
--- a/changelogs/unreleased/move-permission-check-manual-actions-on-deployments.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Move permission check of manual actions of deployments
-merge_request: 24660
-author:
-type: other
diff --git a/changelogs/unreleased/move_chatops_to_core.yml b/changelogs/unreleased/move_chatops_to_core.yml
new file mode 100644
index 00000000000..7a75efedfa8
--- /dev/null
+++ b/changelogs/unreleased/move_chatops_to_core.yml
@@ -0,0 +1,5 @@
+---
+title: Move ChatOps to Core
+merge_request: 24780
+author:
+type: changed
diff --git a/changelogs/unreleased/mr-file-tree-blob-truncate-improvements.yml b/changelogs/unreleased/mr-file-tree-blob-truncate-improvements.yml
deleted file mode 100644
index b01962591c6..00000000000
--- a/changelogs/unreleased/mr-file-tree-blob-truncate-improvements.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add folder header to files in merge request tree list
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/mr-rebase-failing-tests.yml b/changelogs/unreleased/mr-rebase-failing-tests.yml
deleted file mode 100644
index 07ae05766b1..00000000000
--- a/changelogs/unreleased/mr-rebase-failing-tests.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed rebase button not showing in merge request widget
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/not-run-pipeline-on-empty-merge-request.yml b/changelogs/unreleased/not-run-pipeline-on-empty-merge-request.yml
deleted file mode 100644
index 732e4baf4e9..00000000000
--- a/changelogs/unreleased/not-run-pipeline-on-empty-merge-request.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Don't create new merge request pipeline without commits
-merge_request: 24503
-author: Hiroyuki Sato
-type: added
diff --git a/changelogs/unreleased/notebook-multiple-outputs.yml b/changelogs/unreleased/notebook-multiple-outputs.yml
deleted file mode 100644
index 38cc52c0634..00000000000
--- a/changelogs/unreleased/notebook-multiple-outputs.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Support multiple outputs in jupyter notebooks
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/notes-awards-double-tooltip-fix.yml b/changelogs/unreleased/notes-awards-double-tooltip-fix.yml
deleted file mode 100644
index 23338a60c2a..00000000000
--- a/changelogs/unreleased/notes-awards-double-tooltip-fix.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed double tooltips on note awards buttons
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/osw-enforces-project-removal-with-past-failed-attempts.yml b/changelogs/unreleased/osw-enforces-project-removal-with-past-failed-attempts.yml
deleted file mode 100644
index 6a2a67e7aa8..00000000000
--- a/changelogs/unreleased/osw-enforces-project-removal-with-past-failed-attempts.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Cleanup stale +deleted repo paths on project removal (adjusts project removal bug)
-merge_request: 24269
-author:
-type: fixed
diff --git a/changelogs/unreleased/osw-fix-bottom-expansion-diff-comment.yml b/changelogs/unreleased/osw-fix-bottom-expansion-diff-comment.yml
deleted file mode 100644
index b2ac53312ae..00000000000
--- a/changelogs/unreleased/osw-fix-bottom-expansion-diff-comment.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adjusts duplicated line when commenting on unfolded diff lines (in the bottom)
-merge_request: 24201
-author:
-type: fixed
diff --git a/changelogs/unreleased/patch-38.yml b/changelogs/unreleased/patch-38.yml
deleted file mode 100644
index 9179fc6846e..00000000000
--- a/changelogs/unreleased/patch-38.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: fix display comment avatars issue in IE 11
-merge_request: 24777
-author: Gokhan Apaydin
-type: fixed
diff --git a/changelogs/unreleased/pl-serialize-ac-parameters.yml b/changelogs/unreleased/pl-serialize-ac-parameters.yml
deleted file mode 100644
index aad222b5506..00000000000
--- a/changelogs/unreleased/pl-serialize-ac-parameters.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make `ActionController::Parameters` serializable for sidekiq jobs
-merge_request: 24864
-author:
-type: fixed
diff --git a/changelogs/unreleased/profile-project-empty-state.yml b/changelogs/unreleased/profile-project-empty-state.yml
deleted file mode 100644
index 484306d5b98..00000000000
--- a/changelogs/unreleased/profile-project-empty-state.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added empty project illustration and updated text to user profile overview
-merge_request: 23973
-author: Fernando Arias
-type: changed
diff --git a/changelogs/unreleased/raise-on-unfiltered-params.yml b/changelogs/unreleased/raise-on-unfiltered-params.yml
deleted file mode 100644
index 531e9ba807e..00000000000
--- a/changelogs/unreleased/raise-on-unfiltered-params.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Actually set raise_on_unfiltered_parameters to true
-merge_request: 24443
-author: Jasper Maes
-type: other
diff --git a/changelogs/unreleased/rd-update-last_activity_on-on-logins-and-browsing-activity-54947.yml b/changelogs/unreleased/rd-update-last_activity_on-on-logins-and-browsing-activity-54947.yml
deleted file mode 100644
index abce9dcc0c6..00000000000
--- a/changelogs/unreleased/rd-update-last_activity_on-on-logins-and-browsing-activity-54947.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update last_activity_on for Users on some main GET endpoints
-merge_request: 24642
-author:
-type: changed
diff --git a/changelogs/unreleased/refactor-56366-extract-resolve-discussion-button.yml b/changelogs/unreleased/refactor-56366-extract-resolve-discussion-button.yml
deleted file mode 100644
index 98859e8aa07..00000000000
--- a/changelogs/unreleased/refactor-56366-extract-resolve-discussion-button.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Refactored NoteableDiscussion by extracting ResolveDiscussionButton
-merge_request: 24505
-author: Martin Hobert
-type: other
diff --git a/changelogs/unreleased/refactor-56369-extract-jump-to-next-discussion-button.yml b/changelogs/unreleased/refactor-56369-extract-jump-to-next-discussion-button.yml
deleted file mode 100644
index 9a0d16c2d70..00000000000
--- a/changelogs/unreleased/refactor-56369-extract-jump-to-next-discussion-button.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Extracted JumpToNextDiscussionButton to its own component
-author: Martin Hobert
-merge_request: 24506
-type: other
diff --git a/changelogs/unreleased/refactor-56370-extract-reply-placeholder-component.yml b/changelogs/unreleased/refactor-56370-extract-reply-placeholder-component.yml
deleted file mode 100644
index a216d294b30..00000000000
--- a/changelogs/unreleased/refactor-56370-extract-reply-placeholder-component.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Extracted ReplyPlaceholder to its own component
-merge_request: 24507
-author: Martin Hobert
-type: other
diff --git a/changelogs/unreleased/remove-cancel-all-button-in-job-list-view.yml b/changelogs/unreleased/remove-cancel-all-button-in-job-list-view.yml
deleted file mode 100644
index 06546bc5a8e..00000000000
--- a/changelogs/unreleased/remove-cancel-all-button-in-job-list-view.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove Cancel all jobs button in general jobs list view
-merge_request:
-author: Jordi Llull
-type: removed
diff --git a/changelogs/unreleased/remove-diff-coloring.yml b/changelogs/unreleased/remove-diff-coloring.yml
deleted file mode 100644
index 1ee1b525c35..00000000000
--- a/changelogs/unreleased/remove-diff-coloring.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'remove red/green colors from diff view of no-color syntax theme'
-merge_request: 24582
-author: khm
-type: changed
diff --git a/changelogs/unreleased/remove-gap-between-mr-tabs-and-file-header.yml b/changelogs/unreleased/remove-gap-between-mr-tabs-and-file-header.yml
deleted file mode 100644
index ce8e1829b48..00000000000
--- a/changelogs/unreleased/remove-gap-between-mr-tabs-and-file-header.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove extra space between MR tab bar and sticky file headers
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/remove-second-primary-button-on-wiki-edit.yml b/changelogs/unreleased/remove-second-primary-button-on-wiki-edit.yml
new file mode 100644
index 00000000000..045fbbb48b7
--- /dev/null
+++ b/changelogs/unreleased/remove-second-primary-button-on-wiki-edit.yml
@@ -0,0 +1,5 @@
+---
+title: Remove second primary button on wiki edit
+merge_request: 19959
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/rs-admin-user-case-insensitive.yml b/changelogs/unreleased/rs-admin-user-case-insensitive.yml
new file mode 100644
index 00000000000..40398c46a1e
--- /dev/null
+++ b/changelogs/unreleased/rs-admin-user-case-insensitive.yml
@@ -0,0 +1,5 @@
+---
+title: Admin section finds users case-insensitively
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/search-title.yml b/changelogs/unreleased/search-title.yml
deleted file mode 100644
index ff0933ed0b2..00000000000
--- a/changelogs/unreleased/search-title.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add 'in' filter that modifies scope of 'search' filter to issues and merge requests API
-merge_request: 24350
-author: Hiroyuki Sato
-type: added
diff --git a/changelogs/unreleased/security-22076-sanitize-url-in-names.yml b/changelogs/unreleased/security-22076-sanitize-url-in-names.yml
deleted file mode 100644
index 4e0ad4dd4c4..00000000000
--- a/changelogs/unreleased/security-22076-sanitize-url-in-names.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Sanitize user full name to clean up any URL to prevent mail clients from auto-linking
- URLs
-merge_request: 2793
-author:
-type: security
diff --git a/changelogs/unreleased/security-2770-verify-bundle-import-files.yml b/changelogs/unreleased/security-2770-verify-bundle-import-files.yml
deleted file mode 100644
index dea40dd1ef1..00000000000
--- a/changelogs/unreleased/security-2770-verify-bundle-import-files.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Validate bundle files before unpacking them
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/security-55320-stored-xss-in-user-status.yml b/changelogs/unreleased/security-55320-stored-xss-in-user-status.yml
deleted file mode 100644
index 8ea9ae0ccdf..00000000000
--- a/changelogs/unreleased/security-55320-stored-xss-in-user-status.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use sanitized user status message for user popover
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/security-stored-xss-via-katex.yml b/changelogs/unreleased/security-stored-xss-via-katex.yml
deleted file mode 100644
index a71ae1123f2..00000000000
--- a/changelogs/unreleased/security-stored-xss-via-katex.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed XSS content in KaTex links
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/sh-disable-nil-user-id-identity-validation.yml b/changelogs/unreleased/sh-disable-nil-user-id-identity-validation.yml
deleted file mode 100644
index 5af3bdce51b..00000000000
--- a/changelogs/unreleased/sh-disable-nil-user-id-identity-validation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix failed LDAP logins when nil user_id present
-merge_request: 24749
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-encode-content-disposition.yml b/changelogs/unreleased/sh-encode-content-disposition.yml
deleted file mode 100644
index b40ee6a85a8..00000000000
--- a/changelogs/unreleased/sh-encode-content-disposition.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Encode Content-Disposition filenames
-merge_request: 24919
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-backfill-project-repo-migration.yml b/changelogs/unreleased/sh-fix-backfill-project-repo-migration.yml
deleted file mode 100644
index d1d4412eb50..00000000000
--- a/changelogs/unreleased/sh-fix-backfill-project-repo-migration.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix duplicate project disk path in BackfillLegacyProjectRepositories
-merge_request: 24213
-author:
-type: changed
diff --git a/changelogs/unreleased/sh-fix-bitbucket-server-error-handling.yml b/changelogs/unreleased/sh-fix-bitbucket-server-error-handling.yml
deleted file mode 100644
index 87405fa0a78..00000000000
--- a/changelogs/unreleased/sh-fix-bitbucket-server-error-handling.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix Bitbucket Server importer error handling
-merge_request: 24343
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-board-user-assigns.yml b/changelogs/unreleased/sh-fix-board-user-assigns.yml
deleted file mode 100644
index 89c228107f0..00000000000
--- a/changelogs/unreleased/sh-fix-board-user-assigns.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix 403 errors when adding an assignee list in project boards
-merge_request: 25263
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-import-redirect-vulnerability.yml b/changelogs/unreleased/sh-fix-import-redirect-vulnerability.yml
deleted file mode 100644
index addf327b69d..00000000000
--- a/changelogs/unreleased/sh-fix-import-redirect-vulnerability.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Alias GitHub and BitBucket OAuth2 callback URLs
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/sh-fix-pages-zip-constant.yml b/changelogs/unreleased/sh-fix-pages-zip-constant.yml
deleted file mode 100644
index fcd8aa45825..00000000000
--- a/changelogs/unreleased/sh-fix-pages-zip-constant.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix uninitialized constant with GitLab Pages
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-snippet-uploads-path-lookup.yml b/changelogs/unreleased/sh-fix-snippet-uploads-path-lookup.yml
deleted file mode 100644
index 414c8663049..00000000000
--- a/changelogs/unreleased/sh-fix-snippet-uploads-path-lookup.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix 404s with snippet uploads in object storage
-merge_request: 24550
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-upload-snippets-with-relative-url-root.yml b/changelogs/unreleased/sh-fix-upload-snippets-with-relative-url-root.yml
deleted file mode 100644
index 8bc1e4b4f8a..00000000000
--- a/changelogs/unreleased/sh-fix-upload-snippets-with-relative-url-root.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix 404s for snippet uploads when relative URL root used
-merge_request: 24588
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-import-source-branch-github-forks.yml b/changelogs/unreleased/sh-import-source-branch-github-forks.yml
deleted file mode 100644
index b5ea60202c0..00000000000
--- a/changelogs/unreleased/sh-import-source-branch-github-forks.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Create the source branch for a GitHub import
-merge_request: 25064
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-include-project-path-for-internal-api.yml b/changelogs/unreleased/sh-include-project-path-for-internal-api.yml
new file mode 100644
index 00000000000..1973049e9e3
--- /dev/null
+++ b/changelogs/unreleased/sh-include-project-path-for-internal-api.yml
@@ -0,0 +1,5 @@
+---
+title: Include gl_project_path in API /internal/allowed response
+merge_request: 25314
+author:
+type: other
diff --git a/changelogs/unreleased/sh-issue-53419-fix.yml b/changelogs/unreleased/sh-issue-53419-fix.yml
deleted file mode 100644
index ab8b65857e2..00000000000
--- a/changelogs/unreleased/sh-issue-53419-fix.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix Bitbucket Server import not allowing personal projects
-merge_request: 23601
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-preload-associations-for-group-api.yml b/changelogs/unreleased/sh-preload-associations-for-group-api.yml
deleted file mode 100644
index 24e424b7efb..00000000000
--- a/changelogs/unreleased/sh-preload-associations-for-group-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Eliminate N+1 queries in /api/groups/:id
-merge_request: 24513
-author:
-type: performance
diff --git a/changelogs/unreleased/shared_with_group_path.yml b/changelogs/unreleased/shared_with_group_path.yml
deleted file mode 100644
index 73ba9a9f30a..00000000000
--- a/changelogs/unreleased/shared_with_group_path.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add group full path to project's shared_with_groups
-merge_request: 24052
-author: Mathieu Parent
-type: added
diff --git a/changelogs/unreleased/support-chunking-in-client.yml b/changelogs/unreleased/support-chunking-in-client.yml
deleted file mode 100644
index e50648ea4b2..00000000000
--- a/changelogs/unreleased/support-chunking-in-client.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix code search when text is larger than max gRPC message size
-merge_request: 24111
-author:
-type: changed
diff --git a/changelogs/unreleased/syntax-highlighting-again.yml b/changelogs/unreleased/syntax-highlighting-again.yml
deleted file mode 100644
index 14223fc0927..00000000000
--- a/changelogs/unreleased/syntax-highlighting-again.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix suggested changes syntax highlighting
-merge_request: 25116
-author:
-type: fixed
diff --git a/changelogs/unreleased/test-permissions.yml b/changelogs/unreleased/test-permissions.yml
deleted file mode 100644
index cfb69fdcb1e..00000000000
--- a/changelogs/unreleased/test-permissions.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Disallows unauthorized users from accessing the pipelines section.
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/tooltips-to-top.yml b/changelogs/unreleased/tooltips-to-top.yml
deleted file mode 100644
index 51bf127089e..00000000000
--- a/changelogs/unreleased/tooltips-to-top.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Change spawning of tooltips to be top by default
-merge_request: 21223
-author:
-type: changed
diff --git a/changelogs/unreleased/twang2218-gitlab-ce-i18n-extract-app-views-search.yml b/changelogs/unreleased/twang2218-gitlab-ce-i18n-extract-app-views-search.yml
deleted file mode 100644
index 1af1fe09f33..00000000000
--- a/changelogs/unreleased/twang2218-gitlab-ce-i18n-extract-app-views-search.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'i18n: externalize strings from ''app/views/search'''
-merge_request: 24297
-author: Tao Wang
-type: other
diff --git a/changelogs/unreleased/update-gitaly.yml b/changelogs/unreleased/update-gitaly.yml
deleted file mode 100644
index 4ba42a689a7..00000000000
--- a/changelogs/unreleased/update-gitaly.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update Gitaly to v1.17.0
-merge_request: 24873
-author:
-type: other
diff --git a/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-1-45.yml b/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-1-45.yml
deleted file mode 100644
index 7d92929221f..00000000000
--- a/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-1-45.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update GitLab Runner Helm Chart to 0.1.45
-merge_request: 24564
-author:
-type: other
diff --git a/changelogs/unreleased/update-gitlab-styles.yml b/changelogs/unreleased/update-gitlab-styles.yml
deleted file mode 100644
index 379f0ad4486..00000000000
--- a/changelogs/unreleased/update-gitlab-styles.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update gitlab-styles to 2.5.1
-merge_request: 24336
-author: Jasper Maes
-type: other
diff --git a/changelogs/unreleased/update-pages-config-only-when-changed.yml b/changelogs/unreleased/update-pages-config-only-when-changed.yml
deleted file mode 100644
index 8d9e02df678..00000000000
--- a/changelogs/unreleased/update-pages-config-only-when-changed.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Do not reload daemon if configuration file of pages does not change
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/update-pages-extensionless-urls.yml b/changelogs/unreleased/update-pages-extensionless-urls.yml
deleted file mode 100644
index 13b3e1df500..00000000000
--- a/changelogs/unreleased/update-pages-extensionless-urls.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add support for extensionless pages URLs
-merge_request: 24876
-author:
-type: added
diff --git a/changelogs/unreleased/update-sidekiq-cron.yml b/changelogs/unreleased/update-sidekiq-cron.yml
deleted file mode 100644
index edce32e3753..00000000000
--- a/changelogs/unreleased/update-sidekiq-cron.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Update sidekiq-cron to 1.0.4 and use fugit to replace rufus-scheduler to parse
- cron syntax
-merge_request: 24235
-author:
-type: other
diff --git a/changelogs/unreleased/update-smooshpack.yml b/changelogs/unreleased/update-smooshpack.yml
deleted file mode 100644
index a9222088d53..00000000000
--- a/changelogs/unreleased/update-smooshpack.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgraded Codesandbox smooshpack package
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/update-spriteicon-from-icon-on-profile.yml b/changelogs/unreleased/update-spriteicon-from-icon-on-profile.yml
deleted file mode 100644
index 32259bfacd4..00000000000
--- a/changelogs/unreleased/update-spriteicon-from-icon-on-profile.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update to GitLab SVG icon from Font Awesome in profile for location and work
-merge_request: 24671
-author: Yoginth
-type: changed
diff --git a/changelogs/unreleased/update-ui-admin-appearance.yml b/changelogs/unreleased/update-ui-admin-appearance.yml
deleted file mode 100644
index 7bc35029d77..00000000000
--- a/changelogs/unreleased/update-ui-admin-appearance.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update UI for admin appearance settings
-merge_request: 24685
-author:
-type: other
diff --git a/changelogs/unreleased/update-workhorse-8-2-0.yml b/changelogs/unreleased/update-workhorse-8-2-0.yml
deleted file mode 100644
index 7d593917a25..00000000000
--- a/changelogs/unreleased/update-workhorse-8-2-0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update Workhorse to v8.2.0
-merge_request: 24909
-author:
-type: fixed
diff --git a/changelogs/unreleased/use-deployment-relation-to-fetch-environment-ce.yml b/changelogs/unreleased/use-deployment-relation-to-fetch-environment-ce.yml
deleted file mode 100644
index 1ec276b4abc..00000000000
--- a/changelogs/unreleased/use-deployment-relation-to-fetch-environment-ce.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use deployment relation to get an environment name
-merge_request: 24890
-author:
-type: performance
diff --git a/changelogs/unreleased/use_upgrade_install_for_helm_apps.yml b/changelogs/unreleased/use_upgrade_install_for_helm_apps.yml
deleted file mode 100644
index b41c3cfa1ab..00000000000
--- a/changelogs/unreleased/use_upgrade_install_for_helm_apps.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added ability to upgrade cluster applications
-merge_request: 24789
-author:
-type: added
diff --git a/changelogs/unreleased/winh-add-list-dropdown-height.yml b/changelogs/unreleased/winh-add-list-dropdown-height.yml
deleted file mode 100644
index 6bcedc15cc9..00000000000
--- a/changelogs/unreleased/winh-add-list-dropdown-height.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adjust height of "Add list" dropdown in issue boards
-merge_request: 24227
-author:
-type: fixed
diff --git a/changelogs/unreleased/workhorse-8-3-0.yml b/changelogs/unreleased/workhorse-8-3-0.yml
deleted file mode 100644
index 6ae01d64ae5..00000000000
--- a/changelogs/unreleased/workhorse-8-3-0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update Workhorse to v8.3.0
-merge_request: 24959
-author:
-type: other
diff --git a/changelogs/unreleased/yoginth-avatar-on-settings-sidebar.yml b/changelogs/unreleased/yoginth-avatar-on-settings-sidebar.yml
deleted file mode 100644
index 0ec76f9ce02..00000000000
--- a/changelogs/unreleased/yoginth-avatar-on-settings-sidebar.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added Avatar in the settings sidebar
-merge_request: 24515
-author: Yoginth
-type: changed
diff --git a/changelogs/unreleased/zj-feature-gate-set-project-path.yml b/changelogs/unreleased/zj-feature-gate-set-project-path.yml
deleted file mode 100644
index b426a2f3fe7..00000000000
--- a/changelogs/unreleased/zj-feature-gate-set-project-path.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow setting of feature gates per project
-merge_request: 24184
-author:
-type: added
diff --git a/config/routes/import.rb b/config/routes/import.rb
index da5c31d0062..24013eb2c88 100644
--- a/config/routes/import.rb
+++ b/config/routes/import.rb
@@ -12,13 +12,13 @@ namespace :import do
post :personal_access_token
get :status
get :callback
- get :jobs
+ get :realtime_changes
end
resource :gitea, only: [:create, :new], controller: :gitea do
post :personal_access_token
get :status
- get :jobs
+ get :realtime_changes
end
resource :gitlab, only: [:create], controller: :gitlab do
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index 1e094c03171..90cd787d5ac 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -86,3 +86,4 @@
- [delete_stored_files, 1]
- [remote_mirror_notification, 2]
- [import_issues_csv, 2]
+ - [chat_notification, 2]
diff --git a/danger/commit_messages/Dangerfile b/danger/commit_messages/Dangerfile
index c20c8b77e6a..d9ba71a73cd 100644
--- a/danger/commit_messages/Dangerfile
+++ b/danger/commit_messages/Dangerfile
@@ -64,11 +64,12 @@ def too_many_changed_lines?(commit)
lines_changed_in_commit(commit) >= 30
end
-def lint_commits(commits)
- failures = false
- emoji_checker = EmojiChecker.new
+def emoji_checker
+ @emoji_checker ||= EmojiChecker.new
+end
- unicode_emoji_regex = %r((
+def unicode_emoji_regex
+ @unicode_emoji_regex ||= %r((
[\u{1F300}-\u{1F5FF}] |
[\u{1F1E6}-\u{1F1FF}] |
[\u{2700}-\u{27BF}] |
@@ -77,120 +78,132 @@ def lint_commits(commits)
[\u{1F680}-\u{1F6FF}] |
[\u{2600}-\u{26FF}]
))x
+end
+
+def lint_commit(commit)
+ # For now we'll ignore merge commits, as getting rid of those is a problem
+ # separate from enforcing good commit messages.
+ return false if commit.message.start_with?('Merge branch')
+
+ # We ignore revert commits as they are well structured by Git already
+ return false if commit.message.start_with?('Revert "')
+
+ failures = false
+ subject, separator, details = commit.message.split("\n", 3)
+
+ if subject.split.length < 3
+ fail_commit(
+ commit,
+ 'The commit subject must contain at least three words'
+ )
+
+ failures = true
+ end
+
+ if subject.length > 72
+ fail_commit(
+ commit,
+ 'The commit subject may not be longer than 72 characters'
+ )
+
+ failures = true
+ elsif subject.length > 50
+ warn_commit(
+ commit,
+ "This commit's subject line is acceptable, but please try to [reduce it to 50 characters](#{URL_LIMIT_SUBJECT})."
+ )
+ end
+
+ unless subject_starts_with_capital?(subject)
+ fail_commit(commit, 'The commit subject must start with a capital letter')
+ failures = true
+ end
+
+ if subject.end_with?('.')
+ fail_commit(commit, 'The commit subject must not end with a period')
+ failures = true
+ end
+
+ if separator && !separator.empty?
+ fail_commit(
+ commit,
+ 'The commit subject and body must be separated by a blank line'
+ )
+
+ failures = true
+ end
+
+ details&.each_line do |line|
+ line = line.strip
- commits.each do |commit|
- # For now we'll ignore merge commits, as getting rid of those is a problem
- # separate from enforcing good commit messages.
- next if commit.message.start_with?('Merge branch')
-
- subject, separator, details = commit.message.split("\n", 3)
-
- if subject.split.length < 3
- fail_commit(
- commit,
- 'The commit subject must contain at least three words'
- )
-
- failures = true
- end
-
- if subject.length > 72
- fail_commit(
- commit,
- 'The commit subject may not be longer than 72 characters'
- )
-
- failures = true
- elsif subject.length > 50
- warn_commit(
- commit,
- "This commit's subject line is acceptable, but please try to [reduce it to 50 characters](#{URL_LIMIT_SUBJECT})."
- )
- end
-
- unless subject_starts_with_capital?(subject)
- fail_commit(commit, 'The commit subject must start with a capital letter')
- failures = true
- end
-
- if subject.end_with?('.')
- fail_commit(commit, 'The commit subject must not end with a period')
- failures = true
- end
-
- if separator && !separator.empty?
- fail_commit(
- commit,
- 'The commit subject and body must be separated by a blank line'
- )
-
- failures = true
- end
-
- details&.each_line do |line|
- line = line.strip
-
- next if line.length <= 72
-
- url_size = line.scan(%r((https?://\S+))).sum { |(url)| url.length }
-
- # If the line includes a URL, we'll allow it to exceed 72 characters, but
- # only if the line _without_ the URL does not exceed this limit.
- next if line.length - url_size <= 72
-
- fail_commit(
- commit,
- 'The commit body should not contain more than 72 characters per line'
- )
-
- failures = true
- end
-
- if !details && too_many_changed_lines?(commit)
- fail_commit(
- commit,
- 'Commits that change 30 or more lines across at least three files ' \
- 'must describe these changes in the commit body'
- )
-
- failures = true
- end
-
- if emoji_checker.includes_emoji?(commit.message)
- fail_commit(
- commit,
- 'Avoid the use of Markdown Emoji such as `:+1:`. ' \
- 'These add no value to the commit message, ' \
- 'and are displayed as plain text outside of GitLab'
- )
-
- failures = true
- end
-
- if commit.message.match?(unicode_emoji_regex)
- fail_commit(
- commit,
- 'Avoid the use of Unicode Emoji. ' \
- 'These add no value to the commit message, ' \
- 'and may not be displayed properly everywhere'
- )
-
- failures = true
- end
-
- if commit.message.match?(%r(([\w\-\/]+)?(#|!|&|%)\d+\b))
- fail_commit(
- commit,
- 'Use full URLs instead of short references ' \
- '(`gitlab-org/gitlab-ce#123` or `!123`), as short references are ' \
- 'displayed as plain text outside of GitLab'
- )
-
- failures = true
- end
+ next if line.length <= 72
+
+ url_size = line.scan(%r((https?://\S+))).sum { |(url)| url.length }
+
+ # If the line includes a URL, we'll allow it to exceed 72 characters, but
+ # only if the line _without_ the URL does not exceed this limit.
+ next if line.length - url_size <= 72
+
+ fail_commit(
+ commit,
+ 'The commit body should not contain more than 72 characters per line'
+ )
+
+ failures = true
+ end
+
+ if !details && too_many_changed_lines?(commit)
+ fail_commit(
+ commit,
+ 'Commits that change 30 or more lines across at least three files ' \
+ 'must describe these changes in the commit body'
+ )
+
+ failures = true
+ end
+
+ if emoji_checker.includes_emoji?(commit.message)
+ fail_commit(
+ commit,
+ 'Avoid the use of Markdown Emoji such as `:+1:`. ' \
+ 'These add no value to the commit message, ' \
+ 'and are displayed as plain text outside of GitLab'
+ )
+
+ failures = true
+ end
+
+ if commit.message.match?(unicode_emoji_regex)
+ fail_commit(
+ commit,
+ 'Avoid the use of Unicode Emoji. ' \
+ 'These add no value to the commit message, ' \
+ 'and may not be displayed properly everywhere'
+ )
+
+ failures = true
+ end
+
+ if commit.message.match?(%r(([\w\-\/]+)?(#|!|&|%)\d+\b))
+ fail_commit(
+ commit,
+ 'Use full URLs instead of short references ' \
+ '(`gitlab-org/gitlab-ce#123` or `!123`), as short references are ' \
+ 'displayed as plain text outside of GitLab'
+ )
+
+ failures = true
+ end
+
+ failures
+end
+
+def lint_commits(commits)
+ failed = commits.reject do |commit|
+ lint_commit(commit)
end
- if failures
+ if failed.any?
markdown(<<~MARKDOWN)
## Commit message standards
diff --git a/danger/documentation/Dangerfile b/danger/documentation/Dangerfile
index dd0e3f6deb6..0cf478b4f89 100644
--- a/danger/documentation/Dangerfile
+++ b/danger/documentation/Dangerfile
@@ -1,36 +1,36 @@
# frozen_string_literal: true
-docs_paths_to_review = helper.changes_by_category[:documentation]
+docs_paths_to_review = helper.changes_by_category[:docs]
unless docs_paths_to_review.empty?
- message 'This merge request adds or changes files that require a ' \
- 'review from the Docs team.'
+ message 'This merge request adds or changes files that require a review ' \
+ 'from the Technical Writing team whether before or after merging.'
markdown(<<~MARKDOWN)
-## Docs review
+## Documentation review
-The following files require a review from the Documentation team:
+The following files will require a review from the [Technical Writing](https://about.gitlab.com/handbook/product/technical-writing/) team:
* #{docs_paths_to_review.map { |path| "`#{path}`" }.join("\n* ")}
-When your content is ready for review, assign the MR to a technical writer
-according to the [DevOps stages](https://about.gitlab.com/handbook/product/categories/#devops-stages)
-in the table below. If necessary, mention them in a comment explaining what needs
-to be reviewed.
+Per the [documentation workflows](https://docs.gitlab.com/ee/development/documentation/workflow.html), the review may occur prior to merging this MR **or** after a maintainer has agreed to review and merge this MR without a tech writer review. (Meanwhile, you are welcome to involve a technical writer at any time if you have questions about writing or updating the documentation. GitLabbers are also welcome to use the [#docs](https://gitlab.slack.com/archives/C16HYA2P5) channel on Slack.)
+
+The technical writer who, by default, will review this content or collaborate as needed is dependent on the [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages) to which the content applies:
| Tech writer | Stage(s) |
| ------------ | ------------------------------------------------------------ |
| `@marcia` | ~Create ~Release + ~"development guidelines" |
-| `@axil` | ~Distribution ~Gitaly ~Gitter ~Monitor ~Package ~Secure |
+| `@axil` | ~Distribution ~Gitaly ~Gitter ~Monitor ~Package ~Secure |
| `@eread` | ~Manage ~Configure ~Geo ~Verify |
| `@mikelewis` | ~Plan |
-You are welcome to mention them sooner if you have questions about writing or
-updating the documentation. GitLabbers are also welcome to use the
-[#docs](https://gitlab.slack.com/archives/C16HYA2P5) channel on Slack.
+**To request a review prior to merging**
+
+- Assign the MR to the applicable technical writer, above. If you are not sure which category the change falls within, or the change is not part of one of these categories, mention one of the usernames above.
+
+**To request a review to commence after merging**
-If you are not sure which category the change falls within, or the change is not
-part of one of these categories, mention one of the usernames above.
+- Create an issue for a doc review [using the Doc Review template](https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issuable_template=Doc%20Review) and assign it to the applicable technicial writer from the table.
MARKDOWN
unless gitlab.mr_labels.include?('Documentation')
diff --git a/danger/roulette/Dangerfile b/danger/roulette/Dangerfile
index 5c3d7a4ca49..6cf54d0f854 100644
--- a/danger/roulette/Dangerfile
+++ b/danger/roulette/Dangerfile
@@ -53,6 +53,9 @@ def build_list(items)
end
changes = helper.changes_by_category
+
+# Ignore any files that are known but uncategoried. Prompt for any unknown files
+changes.delete(:none)
categories = changes.keys - [:unknown]
unless changes.empty?
diff --git a/db/migrate/20180209115333_create_chatops_tables.rb b/db/migrate/20180209115333_create_chatops_tables.rb
new file mode 100644
index 00000000000..2cfb71e1007
--- /dev/null
+++ b/db/migrate/20180209115333_create_chatops_tables.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+class CreateChatopsTables < ActiveRecord::Migration[4.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ create_table :ci_pipeline_chat_data, id: :bigserial do |t|
+ t.integer :pipeline_id, null: false
+ t.references :chat_name, foreign_key: { on_delete: :cascade }, null: false
+ t.text :response_url, null: false
+
+ # A pipeline can only contain one row in this table, hence this index is
+ # unique.
+ t.index :pipeline_id, unique: true
+
+ t.index :chat_name_id
+ end
+
+ # rubocop:disable Migration/AddConcurrentForeignKey
+ add_foreign_key :ci_pipeline_chat_data, :ci_pipelines,
+ column: :pipeline_id,
+ on_delete: :cascade
+ end
+end
diff --git a/db/migrate/limits_to_mysql.rb b/db/migrate/limits_to_mysql.rb
index 87992b541b1..33cb19aff9e 100644
--- a/db/migrate/limits_to_mysql.rb
+++ b/db/migrate/limits_to_mysql.rb
@@ -2,19 +2,6 @@ class LimitsToMysql < ActiveRecord::Migration[4.2]
def up
return unless ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/
- # These columns were removed in 10.3, but this is called from two places:
- # 1. A migration run after they were added, but before they were removed.
- # 2. A rake task which can be run at any time.
- #
- # Because of item 2, we need these checks.
- if column_exists?(:merge_request_diffs, :st_commits)
- change_column :merge_request_diffs, :st_commits, :text, limit: 2147483647
- end
-
- if column_exists?(:merge_request_diffs, :st_diffs)
- change_column :merge_request_diffs, :st_diffs, :text, limit: 2147483647
- end
-
change_column :snippets, :content, :text, limit: 2147483647
change_column :notes, :st_diff, :text, limit: 2147483647
end
diff --git a/db/post_migrate/20181101091005_steal_digest_column.rb b/db/post_migrate/20181101091005_steal_digest_column.rb
new file mode 100644
index 00000000000..58ea710c18a
--- /dev/null
+++ b/db/post_migrate/20181101091005_steal_digest_column.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class StealDigestColumn < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ Gitlab::BackgroundMigration.steal('DigestColumn')
+ end
+
+ def down
+ # raise ActiveRecord::IrreversibleMigration
+ end
+end
diff --git a/db/post_migrate/20181101091124_remove_token_from_personal_access_tokens.rb b/db/post_migrate/20181101091124_remove_token_from_personal_access_tokens.rb
new file mode 100644
index 00000000000..415373068d5
--- /dev/null
+++ b/db/post_migrate/20181101091124_remove_token_from_personal_access_tokens.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class RemoveTokenFromPersonalAccessTokens < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ remove_column :personal_access_tokens, :token, :string
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 0f7e9ad4996..1651a24f412 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -421,6 +421,14 @@ ActiveRecord::Schema.define(version: 20190204115450) do
t.index ["project_id"], name: "index_ci_job_artifacts_on_project_id", using: :btree
end
+ create_table "ci_pipeline_chat_data", id: :bigserial, force: :cascade do |t|
+ t.integer "pipeline_id", null: false
+ t.integer "chat_name_id", null: false
+ t.text "response_url", null: false
+ t.index ["chat_name_id"], name: "index_ci_pipeline_chat_data_on_chat_name_id", using: :btree
+ t.index ["pipeline_id"], name: "index_ci_pipeline_chat_data_on_pipeline_id", unique: true, using: :btree
+ end
+
create_table "ci_pipeline_schedule_variables", force: :cascade do |t|
t.string "key", null: false
t.text "value"
@@ -1507,7 +1515,6 @@ ActiveRecord::Schema.define(version: 20190204115450) do
create_table "personal_access_tokens", force: :cascade do |t|
t.integer "user_id", null: false
- t.string "token"
t.string "name", null: false
t.boolean "revoked", default: false
t.date "expires_at"
@@ -1516,7 +1523,6 @@ ActiveRecord::Schema.define(version: 20190204115450) do
t.string "scopes", default: "--- []\n", null: false
t.boolean "impersonation", default: false, null: false
t.string "token_digest"
- t.index ["token"], name: "index_personal_access_tokens_on_token", unique: true, using: :btree
t.index ["token_digest"], name: "index_personal_access_tokens_on_token_digest", unique: true, using: :btree
t.index ["user_id"], name: "index_personal_access_tokens_on_user_id", using: :btree
end
@@ -2345,6 +2351,8 @@ ActiveRecord::Schema.define(version: 20190204115450) do
add_foreign_key "ci_group_variables", "namespaces", column: "group_id", name: "fk_33ae4d58d8", on_delete: :cascade
add_foreign_key "ci_job_artifacts", "ci_builds", column: "job_id", on_delete: :cascade
add_foreign_key "ci_job_artifacts", "projects", on_delete: :cascade
+ add_foreign_key "ci_pipeline_chat_data", "chat_names", on_delete: :cascade
+ add_foreign_key "ci_pipeline_chat_data", "ci_pipelines", column: "pipeline_id", on_delete: :cascade
add_foreign_key "ci_pipeline_schedule_variables", "ci_pipeline_schedules", column: "pipeline_schedule_id", name: "fk_41c35fda51", on_delete: :cascade
add_foreign_key "ci_pipeline_schedules", "projects", name: "fk_8ead60fcc4", on_delete: :cascade
add_foreign_key "ci_pipeline_schedules", "users", column: "owner_id", name: "fk_9ea99f58d2", on_delete: :nullify
diff --git a/doc/README.md b/doc/README.md
index f87ff925e94..14e8d2edb90 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -284,6 +284,7 @@ The following documentation relates to the DevOps **Configure** stage:
| [Auto DevOps](topics/autodevops/index.md) | Automatically employ a complete DevOps lifecycle. |
| [Easy creation of Kubernetes<br/>clusters on GKE](user/project/clusters/index.md#adding-and-creating-a-new-gke-cluster-via-gitlab) | Use Google Kubernetes Engine and GitLab. |
| [Executable Runbooks](user/project/clusters/runbooks/index.md) | Documented procedures that explain how to carry out particular processes. |
+| [GitLab ChatOps](ci/chatops/README.md) | Interact with CI/CD jobs through chat services. |
| [Installing Applications](user/project/clusters/index.md#installing-applications) | Deploy Helm, Ingress, and Prometheus on Kubernetes. |
| [Mattermost slash commands](user/project/integrations/mattermost_slash_commands.md) | Enable and use slash commands from within Mattermost. |
| [Protected variables](ci/variables/README.md#protected-variables) | Restrict variables to protected branches and tags. |
diff --git a/doc/administration/auth/authentiq.md b/doc/administration/auth/authentiq.md
index 772e55cef07..94a8803fff1 100644
--- a/doc/administration/auth/authentiq.md
+++ b/doc/administration/auth/authentiq.md
@@ -50,7 +50,6 @@ Authentiq will generate a Client ID and the accompanying Client Secret for you t
}
```
-
1. The `scope` is set to request the user's name, email (required and signed), and permission to send push notifications to sign in on subsequent visits.
See [OmniAuth Authentiq strategy](https://github.com/AuthentiqID/omniauth-authentiq/wiki/Scopes,-callback-url-configuration-and-responses) for more information on scopes and modifiers.
diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md
index 0ac73c55580..37e596f198f 100644
--- a/doc/administration/auth/ldap.md
+++ b/doc/administration/auth/ldap.md
@@ -448,7 +448,6 @@ ldapsearch -H ldaps://$host:$port -D "$bind_dn" -y bind_dn_password.txt -b "$ba
port.
- We are assuming the password for the bind_dn user is in bind_dn_password.txt.
-
### Invalid credentials when logging in
- Make sure the user you are binding with has enough permissions to read the user's
diff --git a/doc/administration/auth/okta.md b/doc/administration/auth/okta.md
index ae38094391b..3136923fa96 100644
--- a/doc/administration/auth/okta.md
+++ b/doc/administration/auth/okta.md
@@ -140,7 +140,6 @@ Now that the Okta app is configured, it's time to enable it in GitLab.
}
```
-
1. [Reconfigure][reconf] or [restart] GitLab for Omnibus and installations
from source respectively for the changes to take effect.
diff --git a/doc/administration/container_registry.md b/doc/administration/container_registry.md
index 60f1911fa2a..a1ac4a2a57c 100644
--- a/doc/administration/container_registry.md
+++ b/doc/administration/container_registry.md
@@ -542,7 +542,6 @@ Read more about the Container Registry notifications config options in the
>**Note:**
Multiple endpoints can be configured for the Container Registry.
-
**Omnibus GitLab installations**
To configure a notification endpoint in Omnibus:
diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md
index 987a0b9f350..55d85ad86e6 100644
--- a/doc/administration/high_availability/redis.md
+++ b/doc/administration/high_availability/redis.md
@@ -359,6 +359,12 @@ following section assumes you are using Omnibus GitLab Enterprise Edition.
For the Omnibus Community Edition and installations from source, follow the
[Redis HA source install](redis_source.md) guide.
+NOTE: **Note:** If you are using an external Redis Sentinel instance, be sure
+to exclude the `requirepass` parameter from the Sentinel
+configuration. This parameter will cause clients to report `NOAUTH
+Authentication required.`. [Redis Sentinel 3.2.x does not support
+password authentication](https://github.com/antirez/redis/issues/3279).
+
Now that the Redis servers are all set up, let's configure the Sentinel
servers.
@@ -855,7 +861,6 @@ To make sure your configuration is correct:
You should see a different port after a few seconds delay
(the failover/reconnect time).
-
## Changelog
Changes to Redis HA over time.
diff --git a/doc/administration/incoming_email.md b/doc/administration/incoming_email.md
index 05873e01a08..658b2f55d30 100644
--- a/doc/administration/incoming_email.md
+++ b/doc/administration/incoming_email.md
@@ -95,244 +95,249 @@ for a real-world example of this exploit.
### Omnibus package installations
-1. Find the `incoming_email` section in `/etc/gitlab/gitlab.rb`, enable the
- feature and fill in the details for your specific IMAP server and email account:
+1. Find the `incoming_email` section in `/etc/gitlab/gitlab.rb`, enable the feature
+ and fill in the details for your specific IMAP server and email account (see [examples](#config-examples) below).
- Configuration for Postfix mail server, assumes mailbox
- incoming@gitlab.example.com
+1. Reconfigure GitLab for the changes to take effect:
- ```ruby
- gitlab_rails['incoming_email_enabled'] = true
+ ```sh
+ sudo gitlab-ctl reconfigure
+ sudo gitlab-ctl restart
+ ```
- # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
- # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
- gitlab_rails['incoming_email_address'] = "incoming+%{key}@gitlab.example.com"
+1. Verify that everything is configured correctly:
- # Email account username
- # With third party providers, this is usually the full email address.
- # With self-hosted email servers, this is usually the user part of the email address.
- gitlab_rails['incoming_email_email'] = "incoming"
- # Email account password
- gitlab_rails['incoming_email_password'] = "[REDACTED]"
+ ```sh
+ sudo gitlab-rake gitlab:incoming_email:check
+ ```
- # IMAP server host
- gitlab_rails['incoming_email_host'] = "gitlab.example.com"
- # IMAP server port
- gitlab_rails['incoming_email_port'] = 143
- # Whether the IMAP server uses SSL
- gitlab_rails['incoming_email_ssl'] = false
- # Whether the IMAP server uses StartTLS
- gitlab_rails['incoming_email_start_tls'] = false
+Reply by email should now be working.
- # The mailbox where incoming mail will end up. Usually "inbox".
- gitlab_rails['incoming_email_mailbox_name'] = "inbox"
- # The IDLE command timeout.
- gitlab_rails['incoming_email_idle_timeout'] = 60
+### Installations from source
+
+1. Go to the GitLab installation directory:
+
+ ```sh
+ cd /home/git/gitlab
```
- Configuration for Gmail / Google Apps, assumes mailbox
- gitlab-incoming@gmail.com
+1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature
+ and fill in the details for your specific IMAP server and email account (see [examples](#config-examples) below).
- ```ruby
- gitlab_rails['incoming_email_enabled'] = true
+1. Enable `mail_room` in the init script at `/etc/default/gitlab`:
+
+ ```sh
+ sudo mkdir -p /etc/default
+ echo 'mail_room_enabled=true' | sudo tee -a /etc/default/gitlab
+ ```
+
+1. Restart GitLab:
+
+ ```sh
+ sudo service gitlab restart
+ ```
+
+1. Verify that everything is configured correctly:
+
+ ```sh
+ sudo -u git -H bundle exec rake gitlab:incoming_email:check RAILS_ENV=production
+ ```
+
+Reply by email should now be working.
+
+### Config examples
+
+#### Postfix
+
+Example configuration for Postfix mail server. Assumes mailbox incoming@gitlab.example.com.
+
+Example for Omnibus installs:
+
+```ruby
+gitlab_rails['incoming_email_enabled'] = true
+
+# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
+# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
+gitlab_rails['incoming_email_address'] = "incoming+%{key}@gitlab.example.com"
+
+# Email account username
+# With third party providers, this is usually the full email address.
+# With self-hosted email servers, this is usually the user part of the email address.
+gitlab_rails['incoming_email_email'] = "incoming"
+# Email account password
+gitlab_rails['incoming_email_password'] = "[REDACTED]"
+
+# IMAP server host
+gitlab_rails['incoming_email_host'] = "gitlab.example.com"
+# IMAP server port
+gitlab_rails['incoming_email_port'] = 143
+# Whether the IMAP server uses SSL
+gitlab_rails['incoming_email_ssl'] = false
+# Whether the IMAP server uses StartTLS
+gitlab_rails['incoming_email_start_tls'] = false
+
+# The mailbox where incoming mail will end up. Usually "inbox".
+gitlab_rails['incoming_email_mailbox_name'] = "inbox"
+# The IDLE command timeout.
+gitlab_rails['incoming_email_idle_timeout'] = 60
+```
+
+Example for source installs:
+
+```yaml
+incoming_email:
+ enabled: true
# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
- gitlab_rails['incoming_email_address'] = "gitlab-incoming+%{key}@gmail.com"
+ address: "incoming+%{key}@gitlab.example.com"
# Email account username
# With third party providers, this is usually the full email address.
# With self-hosted email servers, this is usually the user part of the email address.
- gitlab_rails['incoming_email_email'] = "gitlab-incoming@gmail.com"
+ user: "incoming"
# Email account password
- gitlab_rails['incoming_email_password'] = "[REDACTED]"
+ password: "[REDACTED]"
# IMAP server host
- gitlab_rails['incoming_email_host'] = "imap.gmail.com"
+ host: "gitlab.example.com"
# IMAP server port
- gitlab_rails['incoming_email_port'] = 993
+ port: 143
# Whether the IMAP server uses SSL
- gitlab_rails['incoming_email_ssl'] = true
+ ssl: false
# Whether the IMAP server uses StartTLS
- gitlab_rails['incoming_email_start_tls'] = false
+ start_tls: false
# The mailbox where incoming mail will end up. Usually "inbox".
- gitlab_rails['incoming_email_mailbox_name'] = "inbox"
+ mailbox: "inbox"
# The IDLE command timeout.
- gitlab_rails['incoming_email_idle_timeout'] = 60
- ```
+ idle_timeout: 60
+```
+
+#### Gmail
+
+Example configuration for Gmail/G Suite. Assumes mailbox gitlab-incoming@gmail.com.
+
+Example for Omnibus installs:
+
+```ruby
+gitlab_rails['incoming_email_enabled'] = true
- Configuration for Microsoft Exchange mail server w/ IMAP enabled, assumes the
- catch-all mailbox incoming@exchange.example.com
+# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
+# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
+gitlab_rails['incoming_email_address'] = "gitlab-incoming+%{key}@gmail.com"
- ```ruby
- gitlab_rails['incoming_email_enabled'] = true
+# Email account username
+# With third party providers, this is usually the full email address.
+# With self-hosted email servers, this is usually the user part of the email address.
+gitlab_rails['incoming_email_email'] = "gitlab-incoming@gmail.com"
+# Email account password
+gitlab_rails['incoming_email_password'] = "[REDACTED]"
+
+# IMAP server host
+gitlab_rails['incoming_email_host'] = "imap.gmail.com"
+# IMAP server port
+gitlab_rails['incoming_email_port'] = 993
+# Whether the IMAP server uses SSL
+gitlab_rails['incoming_email_ssl'] = true
+# Whether the IMAP server uses StartTLS
+gitlab_rails['incoming_email_start_tls'] = false
+
+# The mailbox where incoming mail will end up. Usually "inbox".
+gitlab_rails['incoming_email_mailbox_name'] = "inbox"
+# The IDLE command timeout.
+gitlab_rails['incoming_email_idle_timeout'] = 60
+```
+
+Example for source installs:
+
+```yaml
+incoming_email:
+ enabled: true
# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
- # Exchange does not support sub-addressing, so a catch-all mailbox must be used.
- gitlab_rails['incoming_email_address'] = "incoming-%{key}@exchange.example.com"
+ address: "gitlab-incoming+%{key}@gmail.com"
# Email account username
- # Typically this is the userPrincipalName (UPN)
- gitlab_rails['incoming_email_email'] = "incoming@ad-domain.example.com"
+ # With third party providers, this is usually the full email address.
+ # With self-hosted email servers, this is usually the user part of the email address.
+ user: "gitlab-incoming@gmail.com"
# Email account password
- gitlab_rails['incoming_email_password'] = "[REDACTED]"
+ password: "[REDACTED]"
# IMAP server host
- gitlab_rails['incoming_email_host'] = "exchange.example.com"
+ host: "imap.gmail.com"
# IMAP server port
- gitlab_rails['incoming_email_port'] = 993
+ port: 993
# Whether the IMAP server uses SSL
- gitlab_rails['incoming_email_ssl'] = true
- ```
-
-1. Reconfigure GitLab for the changes to take effect:
+ ssl: true
+ # Whether the IMAP server uses StartTLS
+ start_tls: false
- ```sh
- sudo gitlab-ctl reconfigure
- sudo gitlab-ctl restart
- ```
+ # The mailbox where incoming mail will end up. Usually "inbox".
+ mailbox: "inbox"
+ # The IDLE command timeout.
+ idle_timeout: 60
+```
-1. Verify that everything is configured correctly:
+#### MS Exchange
- ```sh
- sudo gitlab-rake gitlab:incoming_email:check
- ```
+Example configuration for Microsoft Exchange mail server with IMAP enabled. Assumes the
+catch-all mailbox incoming@exchange.example.com.
-1. Reply by email should now be working.
+Example for Omnibus installs:
-### Installations from source
+```ruby
+gitlab_rails['incoming_email_enabled'] = true
-1. Go to the GitLab installation directory:
+# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
+# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
+# Exchange does not support sub-addressing, so a catch-all mailbox must be used.
+gitlab_rails['incoming_email_address'] = "incoming-%{key}@exchange.example.com"
- ```sh
- cd /home/git/gitlab
- ```
+# Email account username
+# Typically this is the userPrincipalName (UPN)
+gitlab_rails['incoming_email_email'] = "incoming@ad-domain.example.com"
+# Email account password
+gitlab_rails['incoming_email_password'] = "[REDACTED]"
-1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature
- and fill in the details for your specific IMAP server and email account:
+# IMAP server host
+gitlab_rails['incoming_email_host'] = "exchange.example.com"
+# IMAP server port
+gitlab_rails['incoming_email_port'] = 993
+# Whether the IMAP server uses SSL
+gitlab_rails['incoming_email_ssl'] = true
+```
- ```sh
- sudo editor config/gitlab.yml
- ```
+Example for source installs:
- Configuration for Postfix mail server, assumes mailbox
- incoming@gitlab.example.com
-
- ```yaml
- incoming_email:
- enabled: true
-
- # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
- # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
- address: "incoming+%{key}@gitlab.example.com"
-
- # Email account username
- # With third party providers, this is usually the full email address.
- # With self-hosted email servers, this is usually the user part of the email address.
- user: "incoming"
- # Email account password
- password: "[REDACTED]"
-
- # IMAP server host
- host: "gitlab.example.com"
- # IMAP server port
- port: 143
- # Whether the IMAP server uses SSL
- ssl: false
- # Whether the IMAP server uses StartTLS
- start_tls: false
-
- # The mailbox where incoming mail will end up. Usually "inbox".
- mailbox: "inbox"
- # The IDLE command timeout.
- idle_timeout: 60
- ```
+```yaml
+incoming_email:
+ enabled: true
- Configuration for Gmail / Google Apps, assumes mailbox
- gitlab-incoming@gmail.com
-
- ```yaml
- incoming_email:
- enabled: true
-
- # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
- # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
- address: "gitlab-incoming+%{key}@gmail.com"
-
- # Email account username
- # With third party providers, this is usually the full email address.
- # With self-hosted email servers, this is usually the user part of the email address.
- user: "gitlab-incoming@gmail.com"
- # Email account password
- password: "[REDACTED]"
-
- # IMAP server host
- host: "imap.gmail.com"
- # IMAP server port
- port: 993
- # Whether the IMAP server uses SSL
- ssl: true
- # Whether the IMAP server uses StartTLS
- start_tls: false
-
- # The mailbox where incoming mail will end up. Usually "inbox".
- mailbox: "inbox"
- # The IDLE command timeout.
- idle_timeout: 60
- ```
-
- Configuration for Microsoft Exchange mail server w/ IMAP enabled, assumes the
- catch-all mailbox incoming@exchange.example.com
-
- ```yaml
- incoming_email:
- enabled: true
-
- # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
- # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
- # Exchange does not support sub-addressing, so a catch-all mailbox must be used.
- address: "incoming-%{key}@exchange.example.com"
-
- # Email account username
- # Typically this is the userPrincipalName (UPN)
- user: "incoming@ad-domain.example.com"
- # Email account password
- password: "[REDACTED]"
-
- # IMAP server host
- host: "exchange.example.com"
- # IMAP server port
- port: 993
- # Whether the IMAP server uses SSL
- ssl: true
- # Whether the IMAP server uses StartTLS
- start_tls: false
-
- # The mailbox where incoming mail will end up. Usually "inbox".
- mailbox: "inbox"
- # The IDLE command timeout.
- idle_timeout: 60
- ```
-
-1. Enable `mail_room` in the init script at `/etc/default/gitlab`:
-
- ```sh
- sudo mkdir -p /etc/default
- echo 'mail_room_enabled=true' | sudo tee -a /etc/default/gitlab
- ```
-
-1. Restart GitLab:
-
- ```sh
- sudo service gitlab restart
- ```
+ # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
+ # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
+ # Exchange does not support sub-addressing, so a catch-all mailbox must be used.
+ address: "incoming-%{key}@exchange.example.com"
-1. Verify that everything is configured correctly:
+ # Email account username
+ # Typically this is the userPrincipalName (UPN)
+ user: "incoming@ad-domain.example.com"
+ # Email account password
+ password: "[REDACTED]"
- ```sh
- sudo -u git -H bundle exec rake gitlab:incoming_email:check RAILS_ENV=production
- ```
+ # IMAP server host
+ host: "exchange.example.com"
+ # IMAP server port
+ port: 993
+ # Whether the IMAP server uses SSL
+ ssl: true
+ # Whether the IMAP server uses StartTLS
+ start_tls: false
-1. Reply by email should now be working.
+ # The mailbox where incoming mail will end up. Usually "inbox".
+ mailbox: "inbox"
+ # The IDLE command timeout.
+ idle_timeout: 60
+```
diff --git a/doc/administration/index.md b/doc/administration/index.md
index 12fec2753bf..a27c11c4f64 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -4,24 +4,27 @@ description: 'Learn how to install, configure, update, and maintain your GitLab
# Administrator documentation **[CORE ONLY]**
-Learn how to administer your GitLab instance (Community Edition and
-Enterprise Edition).
-Regular users don't have access to GitLab administration tools and settings.
+Learn how to administer your self-managed GitLab instance.
-GitLab has two product distributions: the open source
-[GitLab Community Edition (CE)](https://gitlab.com/gitlab-org/gitlab-ce),
-and the open core [GitLab Enterprise Edition (EE)](https://gitlab.com/gitlab-org/gitlab-ee),
-available through [different subscriptions](https://about.gitlab.com/pricing/).
+GitLab has two product distributions available through [different subscriptions](https://about.gitlab.com/pricing/):
-You can [install GitLab CE or GitLab EE](https://about.gitlab.com/installation/ce-or-ee/),
-but the features you'll have access to depend on the subscription you choose
-(Core, Starter, Premium, or Ultimate). GitLab Community Edition installations
-only have access to Core features.
+- The open source [GitLab Community Edition (CE)](https://gitlab.com/gitlab-org/gitlab-ce).
+- The open core [GitLab Enterprise Edition (EE)](https://gitlab.com/gitlab-org/gitlab-ee).
+
+You can [install either GitLab CE or GitLab EE](https://about.gitlab.com/installation/ce-or-ee/).
+However, the features you'll have access to depend on the subscription you choose
+(Core, Starter, Premium, or Ultimate).
+
+NOTE: **Note:**
+GitLab Community Edition installations only have access to Core features.
GitLab.com is administered by GitLab, Inc., therefore, only GitLab team members have
access to its admin configurations. If you're a GitLab.com user, please check the
[user documentation](../user/index.html).
+NOTE: **Note:**
+Non-administrator users don’t have access to GitLab administration tools and settings.
+
## Installing and maintaining GitLab
Learn how to install, configure, update, and maintain your GitLab instance.
@@ -48,7 +51,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Third party offers](../user/admin_area/settings/third_party_offers.md)
- [Compliance](compliance.md): A collection of features from across the application that you may configure to help ensure that your GitLab instance and DevOps workflow meet compliance standards.
- [Diff limits](../user/admin_area/diff_limits.md): Configure the diff rendering size limits of branch comparison pages.
-- [Merge request diffs](merge_request_diffs.md): Configure the diffs shown on merge requests
+- [Merge request diffs storage](merge_request_diffs.md): Configure merge requests diffs external storage.
- [Broadcast Messages](../user/admin_area/broadcast_messages.md): Send messages to GitLab users through the UI.
#### Customizing GitLab's appearance
@@ -127,7 +130,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Job traces](job_traces.md): Information about the job traces (logs).
- [Register Shared and specific Runners](../ci/runners/README.md#registering-a-shared-runner): Learn how to register and configure Shared and specific Runners to your own instance.
- [Shared Runners pipelines quota](../user/admin_area/settings/continuous_integration.md#shared-runners-pipeline-minutes-quota): Limit the usage of pipeline minutes for Shared Runners.
-- [Enable/disable Auto DevOps](../topics/autodevops/index.md#enabling-auto-devops): Enable or disable Auto DevOps for your instance.
+- [Enable/disable Auto DevOps](../topics/autodevops/index.md#enablingdisabling-auto-devops): Enable or disable Auto DevOps for your instance.
## Git configuration options
diff --git a/doc/administration/integration/plantuml.md b/doc/administration/integration/plantuml.md
index b61c5409a56..d383d1efe70 100644
--- a/doc/administration/integration/plantuml.md
+++ b/doc/administration/integration/plantuml.md
@@ -52,7 +52,6 @@ http://localhost:8080/plantuml
you can change these defaults by editing the `/etc/tomcat7/server.xml` file.
-
## GitLab
You need to enable PlantUML integration from Settings under Admin Area. To do
diff --git a/doc/administration/merge_request_diffs.md b/doc/administration/merge_request_diffs.md
index 94620c3d3a0..c34a9519ace 100644
--- a/doc/administration/merge_request_diffs.md
+++ b/doc/administration/merge_request_diffs.md
@@ -1,7 +1,6 @@
-# Merge request diffs administration
+# Merge request diffs storage **[CORE ONLY]**
-> **Notes:**
-> - External merge request diffs introduced in GitLab 11.8
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/52568) in GitLab 11.8.
Merge request diffs are size-limited copies of diffs associated with merge
requests. When viewing a merge request, diffs are sourced from these copies
@@ -16,9 +15,7 @@ large, in which case, switching to external storage is recommended.
Merge request diffs can be stored on disk, or in object storage. In general, it
is better to store the diffs in the database than on disk.
-To enable external storage of merge request diffs:
-
----
+To enable external storage of merge request diffs, follow the instructions below.
**In Omnibus installations:**
@@ -29,17 +26,15 @@ To enable external storage of merge request diffs:
```
1. _The external diffs will be stored in in
- `/var/opt/gitlab/gitlab-rails/shared/external-diffs`._ To change the path,
- for example to `/mnt/storage/external-diffs`, edit `/etc/gitlab/gitlab.rb`
+ `/var/opt/gitlab/gitlab-rails/shared/external-diffs`._ To change the path,
+ for example, to `/mnt/storage/external-diffs`, edit `/etc/gitlab/gitlab.rb`
and add the following line:
```ruby
gitlab_rails['external_diffs_storage_path'] = "/mnt/storage/external-diffs"
```
-1. Save the file and [reconfigure GitLab][] for the changes to take effect.
-
----
+1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
**In installations from source:**
@@ -52,7 +47,7 @@ To enable external storage of merge request diffs:
```
1. _The external diffs will be stored in
- `/home/git/gitlab/shared/external-diffs`._ To change the path, for example
+ `/home/git/gitlab/shared/external-diffs`._ To change the path, for example,
to `/mnt/storage/external-diffs`, edit `/home/git/gitlab/config/gitlab.yml`
and add or amend the following lines:
@@ -62,18 +57,18 @@ To enable external storage of merge request diffs:
storage_path: /mnt/storage/external-diffs
```
-1. Save the file and [restart GitLab][] for the changes to take effect.
+1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect.
### Using object storage
-Instead of storing the external diffs on disk, we recommended you use an object
+Instead of storing the external diffs on disk, we recommended the use of an object
store like AWS S3 instead. This configuration relies on valid AWS credentials to
be configured already.
### Object Storage Settings
For source installations, these settings are nested under `external_diffs:` and
-then `object_store:`. On omnibus installs, they are prefixed by
+then `object_store:`. On Omnibus installations, they are prefixed by
`external_diffs_object_store_`.
| Setting | Description | Default |
@@ -118,7 +113,7 @@ The connection settings match those provided by [Fog](https://github.com/fog), a
}
```
- NOTE: if you are using AWS IAM profiles, be sure to omit the
+ Note that, if you are using AWS IAM profiles, be sure to omit the
AWS access key and secret access key/value pairs. For example:
```ruby
@@ -129,9 +124,7 @@ The connection settings match those provided by [Fog](https://github.com/fog), a
}
```
-1. Save the file and [reconfigure GitLab][] for the changes to take effect.
-
----
+1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
**In installations from source:**
@@ -151,4 +144,4 @@ The connection settings match those provided by [Fog](https://github.com/fog), a
region: eu-central-1
```
-1. Save the file and [restart GitLab][] for the changes to take effect.
+1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect.
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index 6ea0ac0d495..08cd23682d1 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -17,10 +17,7 @@ GitLab monitors its own internal service metrics, and makes them available at th
`/-/metrics` endpoint. Unlike other [Prometheus] exporters, in order to access
it, the client IP needs to be [included in a whitelist][whitelist].
-Currently the embedded Prometheus server is not automatically configured to
-collect metrics from this endpoint. We recommend setting up another Prometheus
-server, because the embedded server configuration is overwritten once every
-[reconfigure of GitLab][reconfigure]. In the future this will not be required.
+For Omnibus and Chart installations, these metrics are automatically enabled and collected as of [GitLab 9.4](https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/1702). For source installations or earlier verisons, these metrics will need to be enabled manually and collected by a Prometheus server.
## Unicorn Metrics available
diff --git a/doc/administration/operations/cleaning_up_redis_sessions.md b/doc/administration/operations/cleaning_up_redis_sessions.md
index 3a35aff8366..b45ca99fd80 100644
--- a/doc/administration/operations/cleaning_up_redis_sessions.md
+++ b/doc/administration/operations/cleaning_up_redis_sessions.md
@@ -20,7 +20,6 @@ configuration settings if you have used the advanced Redis
settings outlined in
[Configuration Files Documentation](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/README.md).
-
First we define a shell function with the proper Redis connection details.
```
diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md
index 51e1518d73f..4934aaf39f7 100644
--- a/doc/administration/repository_storage_types.md
+++ b/doc/administration/repository_storage_types.md
@@ -41,7 +41,6 @@ Registry, etc.
## Hashed Storage
-
Hashed Storage is the new storage behavior we rolled out with 10.0. Instead
of coupling project URL and the folder structure where the repository will be
stored on disk, we are coupling a hash, based on the project's ID. This makes
diff --git a/doc/administration/restart_gitlab.md b/doc/administration/restart_gitlab.md
index 7d245b3effd..cbc3fbd9473 100644
--- a/doc/administration/restart_gitlab.md
+++ b/doc/administration/restart_gitlab.md
@@ -137,7 +137,6 @@ If you are using other init systems, like systemd, you can check the
[GitLab Recipes][gl-recipes] repository for some unofficial services. These are
**not** officially supported so use them at your own risk.
-
[omnibus-dl]: https://about.gitlab.com/downloads/ "Download the Omnibus packages"
[install]: ../install/installation.md "Documentation to install GitLab from source"
[mailroom]: reply_by_email.md "Used for replying by email in GitLab issues and merge requests"
diff --git a/doc/api/README.md b/doc/api/README.md
index 3b43d195390..48d9ad8f30d 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -1,101 +1,140 @@
# GitLab API
-Automate GitLab via a simple and powerful API. All definitions can be found
-under [`/lib/api`](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/api).
+Automate GitLab via a simple and powerful API.
The main GitLab API is a [REST](https://en.wikipedia.org/wiki/Representational_state_transfer) API. Therefore, documentation in this section assumes knowledge of REST concepts.
-## API Resources
-
-The following API resources are available:
-
-- [Applications](applications.md)
-- [Avatar](avatar.md)
-- [Award emoji](award_emoji.md)
-- [Branches](branches.md)
-- [Broadcast messages](broadcast_messages.md)
-- [Code snippets](snippets.md)
-- [Commits](commits.md)
-- [Container Registry](container_registry.md)
-- [Custom attributes](custom_attributes.md)
-- [Deploy keys](deploy_keys.md), and [deploy keys for multiple projects](deploy_key_multiple_projects.md)
-- [Deployments](deployments.md)
-- [Discussions](discussions.md) (threaded comments)
-- [Environments](environments.md)
-- [Events](events.md)
-- [Feature flags](features.md)
-- Group-related resources, including:
- - [Groups](groups.md)
- - [Group access requests](access_requests.md)
- - [Group badges](group_badges.md)
- - [Group issue boards](group_boards.md)
- - [Group labels](group_labels.md)
- - [Group-level variables](group_level_variables.md)
- - [Group members](members.md)
- - [Group milestones](group_milestones.md)
-- [Issues](issues.md)
-- [Issue boards](boards.md)
-- [Jobs](jobs.md)
-- [Keys](keys.md)
-- [Labels](labels.md)
-- [Markdown](markdown.md)
-- [Merge requests](merge_requests.md)
-- [Namespaces](namespaces.md)
-- [Notes](notes.md) (comments)
-- [Notification settings](notification_settings.md)
-- [Pages domains](pages_domains.md)
-- [Pipelines](pipelines.md)
-- [Pipeline schedules](pipeline_schedules.md)
-- [Pipeline triggers](pipeline_triggers.md) and [triggering pipelines](../ci/triggers/README.md)
-- Project-related resources, including:
- - [Projects](projects.md) including setting Webhooks
- - [Project access requests](access_requests.md)
- - [Project badges](project_badges.md)
- - [Project clusters](project_clusters.md)
- - [Project-level variables](project_level_variables.md)
- - [Project import/export](project_import_export.md)
- - [Project import from GitHub](import.md)
- - [Project members](members.md)
- - [Project milestones](milestones.md)
- - [Project snippets](project_snippets.md)
- - [Project templates](project_templates.md) (see also [Templates API Resources](#templates-api-resources))
-- [Protected branches](protected_branches.md)
-- [Protected tags](protected_tags.md)
-- [Repositories](repositories.md)
-- [Repository files](repository_files.md)
-- [Repository submodules](repository_submodules.md)
-- [Resource label events](resource_label_events.md)
-- [Runners](runners.md)
-- [Search](search.md)
-- [Services](services.md)
-- [Settings](settings.md)
-- [Sidekiq metrics](sidekiq_metrics.md)
-- [System hooks](system_hooks.md)
-- [Tags](tags.md)
-- [Releases](releases/index.md)
-- Release Assets
- - [Links](releases/links.md)
-- [Todos](todos.md)
-- [Users](users.md)
-- [Validate CI configuration](lint.md) (linting)
-- [Version](version.md)
-- [Wikis](wikis.md)
-
-See also [V3 to V4](v3_to_v4.md).
-
-### Templates API Resources
+## API resources
+
+Available API resources can be grouped in the following contexts:
+
+- [Projects](#project-resources).
+- [Groups](#group-resources).
+- [Standalone](#standalone-resources).
+
+See also:
+
+- [V3 to V4](v3_to_v4.md).
+- Adding [deploy keys for multiple projects](deploy_key_multiple_projects.md).
+
+### Project resources
+
+The following API resources are available in the project context:
+
+| Resource | Available endpoints |
+|:------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| [Access requests](access_requests.md) | `/projects/:id/access_requests` (also available for groups) |
+| [Award emoji](award_emoji.md) | `/projects/:id/issues/.../award_emoji`, `/projects/:id/merge_requests/.../award_emoji`, `/projects/:id/snippets/.../award_emoji` |
+| [Branches](branches.md) | `/projects/:id/repository/branches/`, `/projects/:id/repository/merged_branches` |
+| [Commits](commits.md) | `/projects/:id/repository/commits`, `/projects/:id/statuses` |
+| [Container Registry](container_registry.md) | `/projects/:id/registry/repositories` |
+| [Custom attributes](custom_attributes.md) | `/projects/:id/custom_attributes` (also available for groups and users) |
+| [Deploy keys](deploy_keys.md) | `/projects/:id/deploy_keys` (also available standalone) |
+| [Deployments](deployments.md) | `/projects/:id/deployments` |
+| [Discussions](discussions.md) (threaded comments) | `/projects/:id/issues/.../discussions`, `/projects/:id/snippets/.../discussions`, `/projects/:id/merge_requests/.../discussions`, `/projects/:id/commits/.../discussions` |
+| [Environments](environments.md) | `/projects/:id/environments` |
+| [Events](events.md) | `/projects/:id/events` (also available for users and standalone) |
+| [Issues](issues.md) | `/projects/:id/issues` (also available for groups and standalone) |
+| [Issue boards](boards.md) | `/projects/:id/boards` |
+| [Jobs](jobs.md) | `/projects/:id/jobs`, `/projects/:id/pipelines/.../jobs` |
+| [Labels](labels.md) | `/projects/:id/labels` |
+| [Members](members.md) | `/projects/:id/members` (also available for groups) |
+| [Merge requests](merge_requests.md) | `/projects/:id/merge_requests` (also available for groups and standalone) |
+| [Notes](notes.md) (comments) | `/projects/:id/issues/.../notes`, `/projects/:id/snippets/.../notes`, `/projects/:id/merge_requests/.../notes` |
+| [Notification settings](notification_settings.md) | `/projects/:id/notification_settings` (also available for groups and standalone) |
+| [Pages domains](pages_domains.md) | `/projects/:id/pages` (also available standalone) |
+| [Pipelines](pipelines.md) | `/projects/:id/pipelines` |
+| [Pipeline schedules](pipeline_schedules.md) | `/projects/:id/pipeline_schedules` |
+| [Pipeline triggers](pipeline_triggers.md) | `/projects/:id/triggers` |
+| [Projects](projects.md) including setting Webhooks | `/projects`, `/projects/:id/hooks` (also available for users) |
+| [Project badges](project_badges.md) | `/projects/:id/badges` |
+| [Project clusters](project_clusters.md) | `/projects/:id/clusters` |
+| [Project-level variables](project_level_variables.md) | `/projects/:id/variables` |
+| [Project import/export](project_import_export.md) | `/projects/:id/export`, `/projects/import`, `/projects/:id/import` |
+| [Project milestones](milestones.md) | `/projects/:id/milestones` |
+| [Project snippets](project_snippets.md) | `/projects/:id/snippets` |
+| [Project templates](project_templates.md) | `/projects/:id/templates` |
+| [Protected branches](protected_branches.md) | `/projects/:id/protected_branches` |
+| [Protected tags](protected_tags.md) | `/projects/:id/protected_tags` |
+| [Releases](releases/index.md) | `/projects/:id/releases` |
+| [Release links](releases/links.md) | `/projects/:id/releases/.../assets/links` |
+| [Repositories](repositories.md) | `/projects/:id/repository` |
+| [Repository files](repository_files.md) | `/projects/:id/repository/files` |
+| [Repository submodules](repository_submodules.md) | `/projects/:id/repository/submodules` |
+| [Resource label events](resource_label_events.md) | `/projects/:id/issues/.../resource_label_events`, `/projects/:id/merge_requests/.../resource_label_events` |
+| [Runners](runners.md) | `/projects/:id/runners` (also available standalone) |
+| [Search](search.md) | `/projects/:id/search` (also available for groups and standalone) |
+| [Services](services.md) | `/projects/:id/services` |
+| [Tags](tags.md) | `/projects/:id/repository/tags` |
+| [Wikis](wikis.md) | `/projects/:id/wikis` |
+
+### Group resources
+
+The following API resources are available in the group context:
+
+| Resource | Available endpoints |
+|:--------------------------------------------------|:---------------------------------------------------------------------------------|
+| [Access requests](access_requests.md) | `/groups/:id/access_requests/` (also available for projects) |
+| [Custom attributes](custom_attributes.md) | `/groups/:id/custom_attributes` (also available for projects and users) |
+| [Groups](groups.md) | `/groups`, `/groups/.../subgroups` |
+| [Group badges](group_badges.md) | `/groups/:id/badges` |
+| [Group issue boards](group_boards.md) | `/groups/:id/boards` |
+| [Group labels](group_labels.md) | `/groups/:id/labels` |
+| [Group-level variables](group_level_variables.md) | `/groups/:id/variables` |
+| [Group milestones](group_milestones.md) | `/groups/:id/milestones` |
+| [Issues](issues.md) | `/groups/:id/issues` (also available for projects and standalone) |
+| [Members](members.md) | `/groups/:id/members` (also available for projects) |
+| [Merge requests](merge_requests.md) | `/groups/:id/merge_requests` (also available for projects and standalone) |
+| [Notification settings](notification_settings.md) | `/groups/:id/notification_settings` (also available for projects and standalone) |
+| [Search](search.md) | `/groups/:id/search` (also available for projects and standalone) |
+
+### Standalone resources
+
+The following API resources are available outside of project and group contexts (including `/users`):
+
+| Resource | Available endpoints |
+|:--------------------------------------------------|:------------------------------------------------------------------------|
+| [Applications](applications.md) | `/applications` |
+| [Avatar](avatar.md) | `/avatar` |
+| [Broadcast messages](broadcast_messages.md) | `/broadcast_messages` |
+| [Code snippets](snippets.md) | `/snippets` |
+| [Custom attributes](custom_attributes.md) | `/users/:id/custom_attributes` (also available for groups and projects) |
+| [Deploy keys](deploy_keys.md) | `/deploy_keys` (also available for projects) |
+| [Events](events.md) | `/events`, `/users/:id/events` (also available for projects) |
+| [Feature flags](features.md) | `/features` |
+| [Import repository from GitHub](import.md) | `/import/github` |
+| [Issues](issues.md) | `/issues` (also available for groups and projects) |
+| [Keys](keys.md) | `/keys` |
+| [Markdown](markdown.md) | `/markdown` |
+| [Merge requests](merge_requests.md) | `/merge_requests` (also available for groups and projects) |
+| [Namespaces](namespaces.md) | `/namespaces` |
+| [Notification settings](notification_settings.md) | `/notification_settings` (also available for groups and projects) |
+| [Pages domains](pages_domains.md) | `/pages/domains` (also available for projects) |
+| [Projects](projects.md) | `/users/:id/projects` (also available for projects) |
+| [Runners](runners.md) | `/runners` (also available for projects) |
+| [Search](search.md) | `/search` (also available for groups and projects) |
+| [Settings](settings.md) | `/application/settings` |
+| [Sidekiq metrics](sidekiq_metrics.md) | `/sidekiq` |
+| [Suggestions](suggestions.md) | `/suggestions` |
+| [System hooks](system_hooks.md) | `/hooks` |
+| [Todos](todos.md) | `/todos` |
+| [Users](users.md) | `/users` |
+| [Validate `.gitlab-ci.yml` file](lint.md) | `/lint` |
+| [Version](version.md) | `/version` |
+
+### Templates API resources
Endpoints are available for:
- [Dockerfile templates](templates/dockerfiles.md).
-- [gitignore templates](templates/gitignores.md).
+- [`.gitignore` templates](templates/gitignores.md).
- [GitLab CI YAML templates](templates/gitlab_ci_ymls.md).
- [Open source license templates](templates/licenses.md).
## Road to GraphQL
Going forward, we will start on moving to
-[GraphQL](http://graphql.org/learn/best-practices/) and deprecate the use of
+[GraphQL](graphql/index.md) and deprecate the use of
controller-specific endpoints. GraphQL has a number of benefits:
1. We avoid having to maintain two different APIs.
@@ -110,7 +149,7 @@ have been resolved to our satisfaction by the relicensing of the reference
implementations under MIT, and the use of the OWF license for the GraphQL
specification.
-## Compatibility Guidelines
+## Compatibility guidelines
The HTTP API is versioned using a single number, the current one being 4. This
number symbolizes the same as the major version number as described by
diff --git a/doc/api/award_emoji.md b/doc/api/award_emoji.md
index 92936a277ac..1d0e39e6bbf 100644
--- a/doc/api/award_emoji.md
+++ b/doc/api/award_emoji.md
@@ -31,7 +31,7 @@ Parameters:
| Attribute | Type | Required | Description |
|:---------------|:---------------|:---------|:-----------------------------------------------------------------------------|
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). |
-| `awardable_id` | integer | yes | ID (`iid` for merge requests/issues, `id` for snippets) of an awardable. |
+| `issue_iid`/`merge_request_iid`/`snippet_id` | integer | yes | ID (`iid` for merge requests/issues, `id` for snippets) of an awardable. |
Example request:
@@ -93,7 +93,7 @@ Parameters:
| Attribute | Type | Required | Description |
|:---------------|:---------------|:---------|:-----------------------------------------------------------------------------|
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). |
-| `awardable_id` | integer | yes | ID (`iid` for merge requests/issues, `id` for snippets) of an awardable. |
+| `issue_iid`/`merge_request_iid`/`snippet_id` | integer | yes | ID (`iid` for merge requests/issues, `id` for snippets) of an awardable. |
| `award_id` | integer | yes | ID of the award emoji. |
Example request:
@@ -138,7 +138,7 @@ Parameters:
| Attribute | Type | Required | Description |
|:---------------|:---------------|:---------|:-----------------------------------------------------------------------------|
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). |
-| `awardable_id` | integer | yes | ID (`iid` for merge requests/issues, `id` for snippets) of an awardable. |
+| `issue_iid`/`merge_request_iid`/`snippet_id` | integer | yes | ID (`iid` for merge requests/issues, `id` for snippets) of an awardable. |
| `name` | string | yes | Name of the emoji without colons. |
```sh
@@ -184,7 +184,7 @@ Parameters:
| Attribute | Type | Required | Description |
|:---------------|:---------------|:---------|:-----------------------------------------------------------------------------|
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). |
-| `awardable_id` | integer | yes | ID (`iid` for merge requests/issues, `id` for snippets) of an awardable. |
+| `issue_iid`/`merge_request_iid`/`snippet_id` | integer | yes | ID (`iid` for merge requests/issues, `id` for snippets) of an awardable. |
| `award_id` | integer | yes | ID of an award emoji. |
```sh
@@ -197,7 +197,8 @@ Comments (also known as notes) are a sub-resource of issues, merge requests, and
NOTE: **Note:**
The examples below describe working with award emoji on comments for an issue, but can be
-easily adapted for comments on a merge request.
+easily adapted for comments on a merge request or on a snippet. Therefore, you have to replace
+`issue_iid` either with `merge_request_iid` or with the `snippet_id`.
### List a comment's award emoji
diff --git a/doc/api/boards.md b/doc/api/boards.md
index 2a2622736c3..28c73db6b98 100644
--- a/doc/api/boards.md
+++ b/doc/api/boards.md
@@ -37,7 +37,7 @@ Example response:
"web_url": "http://example.com/diaspora/diaspora-project-site"
},
"milestone": {
- "id": 12
+ "id": 12,
"title": "10.0"
},
"lists" : [
@@ -95,7 +95,7 @@ Example response:
```json
{
"id": 1,
- "name:": "project issue board",
+ "name": "project issue board",
"project": {
"id": 5,
"name": "Diaspora Project Site",
@@ -106,7 +106,7 @@ Example response:
"web_url": "http://example.com/diaspora/diaspora-project-site"
},
"milestone": {
- "id": 12
+ "id": 12,
"title": "10.0"
},
"lists" : [
diff --git a/doc/api/commits.md b/doc/api/commits.md
index 7bfea0498e4..8d36ae7d559 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -18,7 +18,6 @@ GET /projects/:id/repository/commits
| `all` | boolean | no | Retrieve every commit from the repository |
| `with_stats` | boolean | no | Stats about each commit will be added to the response |
-
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/repository/commits"
```
@@ -81,7 +80,6 @@ POST /projects/:id/repository/commits
| `author_name` | string | no | Specify the commit author's name |
| `stats` | boolean | no | Include commit stats. Default is true |
-
| `actions[]` Attribute | Type | Required | Description |
| --------------------- | ---- | -------- | ----------- |
| `action` | string | yes | The action to perform, `create`, `delete`, `move`, `update`, `chmod`|
@@ -601,7 +599,6 @@ GET /projects/:id/repository/commits/:sha/merge_requests
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
| `sha` | string | yes | The commit SHA
-
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/repository/commits/af5b13261899fb2c0db30abdd0af8b07cb44fdc5/merge_requests"
```
diff --git a/doc/api/container_registry.md b/doc/api/container_registry.md
index c77ed39e4dc..1f17af1f1e9 100644
--- a/doc/api/container_registry.md
+++ b/doc/api/container_registry.md
@@ -16,7 +16,6 @@ GET /projects/:id/registry/repositories
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
-
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/registry/repositories"
```
diff --git a/doc/api/discussions.md b/doc/api/discussions.md
index 79090ea5254..7d68d0ae744 100644
--- a/doc/api/discussions.md
+++ b/doc/api/discussions.md
@@ -641,7 +641,6 @@ Parameters:
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/merge_requests/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7?resolved=true
```
-
### Add note to existing merge request discussion
Adds a new note to the discussion.
diff --git a/doc/api/features.md b/doc/api/features.md
index 47f104e1f20..6ecd4ec14b9 100644
--- a/doc/api/features.md
+++ b/doc/api/features.md
@@ -60,10 +60,11 @@ POST /features/:name
| `value` | integer/string | yes | `true` or `false` to enable/disable, or an integer for percentage of time |
| `feature_group` | string | no | A Feature group name |
| `user` | string | no | A GitLab username |
+| `group` | string | no | A GitLab group's path, for example 'gitlab-org' |
| `project` | string | no | A projects path, for example 'gitlab-org/gitlab-ce' |
Note that you can enable or disable a feature for a `feature_group`, a `user`,
-and a `project` in a single API call.
+a `group`, and a `project` in a single API call.
```bash
curl --data "value=30" --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/features/new_library
diff --git a/doc/api/group_milestones.md b/doc/api/group_milestones.md
index 7be01ce9c6d..eb974267084 100644
--- a/doc/api/group_milestones.md
+++ b/doc/api/group_milestones.md
@@ -48,7 +48,6 @@ Example Response:
]
```
-
## Get single milestone
Gets a single group milestone.
diff --git a/doc/api/import.md b/doc/api/import.md
index 9f8e0d232c6..1deb26e8388 100644
--- a/doc/api/import.md
+++ b/doc/api/import.md
@@ -15,7 +15,6 @@ POST /import/github
| `new_name` | string | no | New repo name |
| `target_namespace` | string | yes | Namespace to import repo into |
-
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "personal_access_token=abc123&repo_id=12345&target_namespace=root" https://gitlab.example.com/api/v4/import/github
```
@@ -30,4 +29,3 @@ Example response:
"full_name": "Administrator / my-repo"
}
```
-
diff --git a/doc/api/issues.md b/doc/api/issues.md
index ed3165d95df..0571f280d2a 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -110,6 +110,7 @@ Example response:
"labels" : [],
"upvotes": 4,
"downvotes": 0,
+ "merge_requests_count": 0,
"user_notes_count": 1,
"due_date": "2016-07-22",
"web_url": "http://example.com/example/example/issues/6",
@@ -168,7 +169,6 @@ GET /groups/:id/issues?my_reaction_emoji=star
| `updated_after` | datetime | no | Return issues updated on or after the given time |
| `updated_before` | datetime | no | Return issues updated on or before the given time |
-
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/4/issues
```
@@ -220,6 +220,7 @@ Example response:
"labels" : [],
"upvotes": 4,
"downvotes": 0,
+ "merge_requests_count": 0,
"id" : 41,
"title" : "Ut commodi ullam eos dolores perferendis nihil sunt.",
"updated_at" : "2016-01-04T15:31:46.176Z",
@@ -335,6 +336,7 @@ Example response:
"labels" : [],
"upvotes": 4,
"downvotes": 0,
+ "merge_requests_count": 0,
"id" : 41,
"title" : "Ut commodi ullam eos dolores perferendis nihil sunt.",
"updated_at" : "2016-01-04T15:31:46.176Z",
@@ -431,6 +433,7 @@ Example response:
"labels" : [],
"upvotes": 4,
"downvotes": 0,
+ "merge_requests_count": 0,
"id" : 41,
"title" : "Ut commodi ullam eos dolores perferendis nihil sunt.",
"updated_at" : "2016-01-04T15:31:46.176Z",
@@ -506,6 +509,7 @@ Example response:
],
"upvotes": 4,
"downvotes": 0,
+ "merge_requests_count": 0,
"author" : {
"name" : "Alexandra Bashirian",
"avatar_url" : null,
@@ -568,7 +572,6 @@ PUT /projects/:id/issues/:issue_iid
| `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` |
| `discussion_locked` | boolean | no | Flag indicating if the issue's discussion is locked. If the discussion is locked only project members can add or edit comments. |
-
```bash
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/issues/85?state_event=close
```
@@ -606,6 +609,7 @@ Example response:
],
"upvotes": 4,
"downvotes": 0,
+ "merge_requests_count": 0,
"id" : 85,
"assignees" : [],
"assignee" : null,
@@ -692,6 +696,7 @@ Example response:
"labels": [],
"upvotes": 4,
"downvotes": 0,
+ "merge_requests_count": 0,
"milestone": null,
"assignees": [{
"name": "Miss Monserrate Beier",
@@ -776,6 +781,7 @@ Example response:
"labels": [],
"upvotes": 4,
"downvotes": 0,
+ "merge_requests_count": 0,
"milestone": null,
"assignees": [{
"name": "Miss Monserrate Beier",
@@ -822,7 +828,6 @@ Example response:
**Note**: `assignee` column is deprecated, now we show it as a single-sized array `assignees` to conform to the GitLab EE API.
-
**Note**: The `closed_by` attribute was [introduced in GitLab 10.6][ce-17042]. This value will only be present for issues which were closed after GitLab 10.6 and when the user account that closed the issue still exists.
## Unsubscribe from an issue
@@ -859,6 +864,7 @@ Example response:
"labels": [],
"upvotes": 4,
"downvotes": 0,
+ "merge_requests_count": 0,
"milestone": null,
"assignee": {
"name": "Edwardo Grady",
@@ -976,6 +982,7 @@ Example response:
"user_notes_count": 7,
"upvotes": 0,
"downvotes": 0,
+ "merge_requests_count": 0,
"due_date": null,
"web_url": "http://example.com/example/example/issues/110",
"confidential": false,
@@ -990,7 +997,6 @@ Example response:
**Note**: `assignee` column is deprecated, now we show it as a single-sized array `assignees` to conform to the GitLab EE API.
-
**Note**: The `closed_by` attribute was [introduced in GitLab 10.6][ce-17042]. This value will only be present for issues which were closed after GitLab 10.6 and when the user account that closed the issue still exists.
## Set a time estimate for an issue
@@ -1288,7 +1294,6 @@ Example response:
]
```
-
## Participants on issues
```
@@ -1327,7 +1332,6 @@ Example response:
]
```
-
## Comments on issues
Comments are done via the [notes](notes.md) resource.
diff --git a/doc/api/labels.md b/doc/api/labels.md
index eec55bff294..9d10d383bf9 100644
--- a/doc/api/labels.md
+++ b/doc/api/labels.md
@@ -161,7 +161,6 @@ PUT /projects/:id/labels
| `description` | string | no | The new description of the label |
| `priority` | integer | no | The new priority of the label. Must be greater or equal than zero or `null` to remove the priority. |
-
```bash
curl --request PUT --data "name=documentation&new_name=docs&color=#8E44AD&description=Documentation" --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/labels"
```
diff --git a/doc/api/lint.md b/doc/api/lint.md
index a0307f7081d..71c09d35b8c 100644
--- a/doc/api/lint.md
+++ b/doc/api/lint.md
@@ -2,7 +2,7 @@
> [Introduced][ce-5953] in GitLab 8.12.
-Checks if your .gitlab-ci.yml file is valid.
+Checks if your `.gitlab-ci.yml` file is valid.
```
POST /lint
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index ffeceabc680..e176cdffc5f 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -160,7 +160,6 @@ will be the same. In the case of a merge request from a fork,
`target_project_id` and `project_id` will be the same and
`source_project_id` will be the fork project's ID.
-
Parameters:
| Attribute | Type | Required | Description |
@@ -531,7 +530,6 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `merge_request_iid` (required) - The internal ID of the merge request
-
```json
[
{
@@ -566,7 +564,6 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `merge_request_iid` (required) - The internal ID of the merge request
-
```json
[
{
diff --git a/doc/api/oauth2.md b/doc/api/oauth2.md
index 6e156a14b25..dfe62554852 100644
--- a/doc/api/oauth2.md
+++ b/doc/api/oauth2.md
@@ -206,4 +206,3 @@ or you can put the token to the Authorization header:
```
curl --header "Authorization: Bearer OAUTH-TOKEN" https://gitlab.example.com/api/v4/user
```
-
diff --git a/doc/api/pages_domains.md b/doc/api/pages_domains.md
index 4c41350dcdb..70fbe24099f 100644
--- a/doc/api/pages_domains.md
+++ b/doc/api/pages_domains.md
@@ -8,7 +8,7 @@ The GitLab Pages feature must be enabled to use these endpoints. Find out more a
Get a list of all pages domains. The user must have admin permissions.
-```http
+```text
GET /pages/domains
```
@@ -34,7 +34,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/ap
Get a list of project pages domains. The user must have permissions to view pages domains.
-```http
+```text
GET /projects/:id/pages/domains
```
@@ -69,7 +69,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/ap
Get a single project pages domain. The user must have permissions to view pages domains.
-```http
+```text
GET /projects/:id/pages/domains/:domain
```
@@ -110,7 +110,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/ap
Creates a new pages domain. The user must have permissions to create new pages domains.
-```http
+```text
POST /projects/:id/pages/domains
```
@@ -146,7 +146,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --form "domain
Updates an existing project pages domain. The user must have permissions to change an existing pages domains.
-```http
+```text
PUT /projects/:id/pages/domains/:domain
```
@@ -182,7 +182,7 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --form "certifi
Deletes an existing project pages domain.
-```http
+```text
DELETE /projects/:id/pages/domains/:domain
```
diff --git a/doc/api/project_import_export.md b/doc/api/project_import_export.md
index fc91c5741da..5155e996158 100644
--- a/doc/api/project_import_export.md
+++ b/doc/api/project_import_export.md
@@ -1,8 +1,8 @@
# Project import/export API
-[Introduced][ce-41899] in GitLab 10.6
+> [Introduced][ce-41899] in GitLab 10.6.
-[See also the project import/export documentation](../user/project/settings/import_export.md)
+See also the [project import/export documentation](../user/project/settings/import_export.md).
## Schedule an export
@@ -16,7 +16,7 @@ data file uploads to the final server.
If the `upload` params is present, `upload[url]` param is required.
(**Note:** This feature was introduced in GitLab 10.7)
-```http
+```text
POST /projects/:id/export
```
@@ -28,8 +28,7 @@ POST /projects/:id/export
| `upload[url]` | string | yes | The URL to upload the project |
| `upload[http_method]` | string | no | The HTTP method to upload the exported project. Only `PUT` and `POST` methods allowed. Default is `PUT` |
-
-```console
+```sh
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/1/export \
--data "upload[http_method]=PUT" \
--data-urlencode "upload[url]=https://example-bucket.s3.eu-west-3.amazonaws.com/backup?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIMBJHN2O62W8IELQ%2F20180312%2Feu-west-3%2Fs3%2Faws4_request&X-Amz-Date=20180312T110328Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=8413facb20ff33a49a147a0b4abcff4c8487cc33ee1f7e450c46e8f695569dbd"
@@ -45,7 +44,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab
Get the status of export.
-```http
+```text
GET /projects/:id/export
```
@@ -53,7 +52,7 @@ GET /projects/:id/export
| --------- | -------------- | -------- | ---------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
-```console
+```sh
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/1/export
```
@@ -86,7 +85,7 @@ to a web server, etc.
Download the finished export.
-```http
+```text
GET /projects/:id/export/download
```
@@ -94,18 +93,18 @@ GET /projects/:id/export/download
| --------- | -------------- | -------- | ---------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
-```console
+```sh
curl --header "PRIVATE-TOKEN: <your_access_token>" --remote-header-name --remote-name https://gitlab.example.com/api/v4/projects/5/export/download
```
-```console
+```sh
ls *export.tar.gz
2017-12-05_22-11-148_namespace_project_export.tar.gz
```
## Import a file
-```http
+```text
POST /projects/import
```
@@ -124,7 +123,7 @@ cURL to post data using the header `Content-Type: multipart/form-data`.
The `file=` parameter must point to a file on your file system and be preceded
by `@`. For example:
-```console
+```sh
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --form "path=api-project" --form "file=@/path/to/file" https://gitlab.example.com/api/v4/projects/import
```
@@ -168,7 +167,7 @@ requests.post(url, headers=headers, data=data, files=files)
Get the status of an import.
-```http
+```text
GET /projects/:id/import
```
@@ -176,7 +175,7 @@ GET /projects/:id/import
| --------- | -------------- | -------- | ---------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
-```console
+```sh
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/1/import
```
diff --git a/doc/api/project_snippets.md b/doc/api/project_snippets.md
index 8f4640fcbd6..f02674adfe2 100644
--- a/doc/api/project_snippets.md
+++ b/doc/api/project_snippets.md
@@ -124,7 +124,6 @@ Parameters:
> **Notes:**
> [Introduced][ce-29508] in GitLab 9.4.
-
Available only for admins.
```
diff --git a/doc/api/project_templates.md b/doc/api/project_templates.md
index ef98205cd68..3b5b12c8da3 100644
--- a/doc/api/project_templates.md
+++ b/doc/api/project_templates.md
@@ -101,7 +101,6 @@ GET /projects/:id/templates/:type/:key
Example response (Dockerfile):
-
```json
{
"name": "Binary",
diff --git a/doc/api/protected_branches.md b/doc/api/protected_branches.md
index fa04680d406..a261bb75be5 100644
--- a/doc/api/protected_branches.md
+++ b/doc/api/protected_branches.md
@@ -5,6 +5,7 @@
**Valid access levels**
The access levels are defined in the `ProtectedRefAccess.allowed_access_levels` method. Currently, these levels are recognized:
+
```
0 => No access
30 => Developer access
diff --git a/doc/api/releases/index.md b/doc/api/releases/index.md
index 61f4ec130fa..e7f79a0d359 100644
--- a/doc/api/releases/index.md
+++ b/doc/api/releases/index.md
@@ -2,7 +2,7 @@
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/41766) in GitLab 11.7.
> - Using this API you can manipulate GitLab's [Release](../../user/project/releases/index.md) entries.
-> - For manipulating links as a release asset, see [Release Links API](links.md)
+> - For manipulating links as a release asset, see [Release Links API](links.md).
## List Releases
diff --git a/doc/api/search.md b/doc/api/search.md
index aaaed7d9956..aa601648b2c 100644
--- a/doc/api/search.md
+++ b/doc/api/search.md
@@ -281,7 +281,6 @@ Example response:
]
```
-
## Group Search API
Search within the specified group.
@@ -520,7 +519,6 @@ Search the expression within the specified scope. Currently these scopes are sup
The response depends on the requested scope.
-
### Scope: issues
```bash
diff --git a/doc/api/services.md b/doc/api/services.md
index 5d5aa3e5b3e..9275a1ccda8 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -522,6 +522,7 @@ Parameters:
| `project_key` | string | yes | The short identifier for your JIRA project, all uppercase, e.g., `PROJ`. |
| `username` | string | yes | The username of the user created to be used with GitLab/JIRA. |
| `password` | string | yes | The password of the user created to be used with GitLab/JIRA. |
+| `active` | boolean | no | Activates or deactivates the service. Defaults to false (deactivated). |
| `jira_issue_transition_id` | integer | no | The ID of a transition that moves issues to a closed state. You can find this number under the JIRA workflow administration (**Administration > Issues > Workflows**) by selecting **View** under **Operations** of the desired workflow of your project. The ID of each state can be found inside the parenthesis of each transition name under the **Transitions (id)** column ([see screenshot][trans]). By default, this ID is set to `2`. |
### Delete JIRA service
diff --git a/doc/api/sidekiq_metrics.md b/doc/api/sidekiq_metrics.md
index 95dcf2d5277..5f2202fa51d 100644
--- a/doc/api/sidekiq_metrics.md
+++ b/doc/api/sidekiq_metrics.md
@@ -149,4 +149,3 @@ Example response:
}
}
```
-
diff --git a/doc/api/tags.md b/doc/api/tags.md
index 23dbf2d9ff7..3177fec618f 100644
--- a/doc/api/tags.md
+++ b/doc/api/tags.md
@@ -163,7 +163,6 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `tag_name` (required) - The name of a tag
-
## Create a new release
Add release notes to the existing git tag. If there
diff --git a/doc/api/templates/gitignores.md b/doc/api/templates/gitignores.md
index 3804855129c..bf6a914e120 100644
--- a/doc/api/templates/gitignores.md
+++ b/doc/api/templates/gitignores.md
@@ -1,8 +1,8 @@
-# Gitignores API
+# `.gitignore` API
-## List gitignore templates
+## List `.gitignore` templates
-Get all gitignore templates.
+Get all `.gitignore` templates.
```
GET /templates/gitignores
@@ -99,9 +99,9 @@ Example response:
]
```
-## Single gitignore template
+## Single `.gitignore` template
-Get a single gitignore template.
+Get a single `.gitignore` template.
```
GET /templates/gitignores/:key
@@ -109,7 +109,7 @@ GET /templates/gitignores/:key
| Attribute | Type | Required | Description |
| ---------- | ------ | -------- | ----------- |
-| `key` | string | yes | The key of the gitignore template |
+| `key` | string | yes | The key of the `.gitignore` template |
```bash
curl https://gitlab.example.com/api/v4/templates/gitignores/Ruby
diff --git a/doc/api/users.md b/doc/api/users.md
index fd8778abb17..b0977810120 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -1043,7 +1043,6 @@ Will return `201 OK` on success, `404 User Not Found` is user cannot be found or
Please refer to the [Events API documentation](events.md#get-user-contribution-events)
-
## Get all impersonation tokens of a user
> Requires admin permissions.
diff --git a/doc/api/wikis.md b/doc/api/wikis.md
index 436d06cfd3a..12f048ac09b 100644
--- a/doc/api/wikis.md
+++ b/doc/api/wikis.md
@@ -119,7 +119,6 @@ PUT /projects/:id/wikis/:slug
| `format` | string | no | The format of the wiki page. Available formats are: `markdown` (default), `rdoc`, and `asciidoc` |
| `slug` | string | yes | The slug (a unique string) of the wiki page |
-
```bash
curl --request PUT --data "format=rdoc&content=documentation&title=Docs" --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/wikis/foo"
```
diff --git a/doc/articles/openshift_and_gitlab/index.md b/doc/articles/openshift_and_gitlab/index.md
index 76fdb2eb00a..822d012aa3d 100644
--- a/doc/articles/openshift_and_gitlab/index.md
+++ b/doc/articles/openshift_and_gitlab/index.md
@@ -1,4 +1,3 @@
---
redirect_to: '../../install/openshift_and_gitlab/index.html'
---
-
diff --git a/doc/ci/README.md b/doc/ci/README.md
index 4e066a0df97..4c3c9920b93 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -5,131 +5,194 @@ description: "Learn how to use GitLab CI/CD, the GitLab built-in Continuous Inte
# GitLab Continuous Integration (GitLab CI/CD)
-![Pipeline graph](img/cicd_pipeline_infograph.png)
+GitLab provides tools for continuously integrating and delivering code.
+
+Within the [entire DevOps lifecycle](../README.md#the-entire-devops-lifecycle), GitLab CI/CD spans
+the [Verify (CI)](../README.md#verify) and [Release (CD)](../README.md#release) stages.
-The benefits of Continuous Integration are huge when automation plays an
-integral part of your workflow. GitLab comes with built-in Continuous
-Integration, Continuous Deployment, and Continuous Delivery support
-to build, test, and deploy your application.
+## Overview
-Here's some info we've gathered to get you started.
+CI/CD is a vast area, so GitLab provides documentation for all levels of expertise. Consult the following table to find the right documentation for you:
-## Getting started
+| Level of expertise | Resource |
+|:------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------|
+| New to the concepts of CI and CD | For a high-level overview, see the [GitLab Continuous Integration & Delivery](https://about.gitlab.com/product/continuous-integration/) product page. |
+| Familiar with the purpose of CI/CD | Delve into GitLab CI/CD by continuing down the page, starting with our [introduction](#introduction). |
+| Familiar with GitLab CI/CD concepts | After getting familiar with GitLab CI/CD, let us walk you through a simple example in our [quick start guide](quick_start/README.md). |
+| A GitLab CI/CD expert | Jump straight to our [`.gitlab.yml`](yaml/README.md) reference. |
-The first steps towards your GitLab CI/CD journey.
+## Introduction
-- [Getting started with GitLab CI/CD](quick_start/README.md): understand how GitLab CI/CD works.
-- [GitLab CI/CD configuration file: `.gitlab-ci.yml`](yaml/README.md) - Learn all about the ins and outs of `.gitlab-ci.yml`.
-- [Pipelines and jobs](pipelines.md): configure your GitLab CI/CD pipelines to build, test, and deploy your application.
-- Runners: The [GitLab Runner](https://docs.gitlab.com/runner/) is responsible by running the jobs in your CI/CD pipeline. On GitLab.com, Shared Runners are enabled by default, so
-you don't need to set up anything to start to use them with GitLab CI/CD.
+The following introduces the process of continuous integration (CI) and continuous delivery (CD):
-### Introduction to GitLab CI/CD
+![Pipeline graph](img/cicd_pipeline_infograph.png)
-- Article (2016-08-05): [Continuous Integration, Delivery, and Deployment with GitLab - Intro to CI/CD](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/)
-- Article (2015-12-14): [Getting started with GitLab and GitLab CI - Intro to CI](https://about.gitlab.com/2015/12/14/getting-started-with-gitlab-and-gitlab-ci/)
-- Article (2017-07-13): [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/)
-- Article (2017-05-22): [Fast and Natural Continuous Integration with GitLab CI](https://about.gitlab.com/2017/05/22/fast-and-natural-continuous-integration-with-gitlab-ci/)
-- **Videos:**
- - Demo (Streamed live on Jul 17, 2017): [GitLab CI/CD Deep Dive](https://youtu.be/pBe4t1CD8Fc?t=195)
- - Demo (March, 2017): [How to get started using CI/CD with GitLab](https://about.gitlab.com/2017/03/13/ci-cd-demo/)
- - Webcast (April, 2016): [Getting started with CI in GitLab](https://about.gitlab.com/2016/04/20/webcast-recording-and-slides-introduction-to-ci-in-gitlab/)
-- **Third-party videos:**
- - [Intégration continue avec GitLab (September, 2016)](https://www.youtube.com/watch?v=URcMBXjIr24&t=13s)
- - [GitLab CI for Minecraft Plugins (July, 2016)](https://www.youtube.com/watch?v=Z4pcI9F8yf8)
+In this illustration:
-### Why GitLab CI/CD?
+- New code is combined with existing code through a commit to a project's [repository](../user/project/repository/index.md).
+- The newly combined code is sent to a CI [pipeline](pipelines.md) where:
+ - The code is [built](../user/project/pipelines/job_artifacts.md).
+ - Unit and integration tests are run over the built code.
+- Assuming the build and tests are successful, a CD pipeline is triggered to allow for:
+ - Review using [Review Apps](review_apps/index.md).
+ - Deploying to configured [environments](environments.md).
- - Article (2016-10-17): [Why We Chose GitLab CI for our CI/CD Solution](https://about.gitlab.com/2016/10/17/gitlab-ci-oohlala/)
- - Article (2016-07-22): [Building our web-app on GitLab CI: 5 reasons why Captain Train migrated from Jenkins to GitLab CI](https://about.gitlab.com/2016/07/22/building-our-web-app-on-gitlab-ci/)
+The benefits of CI/CD are vast, allowing automation to be an integral part of your workflow for testing, building, deploying, and monitoring your code.
-## Exploring GitLab CI/CD
+Because CI and CD with GitLab is broad topic with many possibilities, the rest of this section provides
+links to topics and resources needed to make use of GitLab CI/CD.
-- [CI/CD Variables](variables/README.md) - Learn how to use variables defined in
- your `.gitlab-ci.yml` or the ones defined in your project's settings
- - [Where variables can be used](variables/where_variables_can_be_used.md) - A
- deeper look on where and how the CI/CD variables can be used
-- **The permissions model** - Learn about the access levels a user can have for
- performing certain CI actions
- - [User permissions](../user/permissions.md#gitlab-ci)
- - [Job permissions](../user/permissions.md#job-permissions)
-- [Configure a Runner, the application that runs your jobs](runners/README.md)
-- Article (2016-03-01): [Setting up GitLab Runner For Continuous Integration](https://about.gitlab.com/2016/03/01/gitlab-runner-with-docker/)
-- Article (2016-07-29): [GitLab CI: Run jobs sequentially, in parallel, or build a custom pipeline](https://about.gitlab.com/2016/07/29/the-basics-of-gitlab-ci/)
-- Article (2016-08-26): [GitLab CI: Deployment & environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/)
-- Article (2016-05-23): [Introduction to GitLab Container Registry](https://about.gitlab.com/2016/05/23/gitlab-container-registry/)
+## Essentials
-## Advanced use
+The following documentation provides the minimum required knowledge for making use of GitLab CI/CD:
-Once you get familiar with the basics of GitLab CI/CD, it's time to dive in and
-learn how to leverage its potential even more.
+| Topic | Description |
+|:------------------------------------------------------------------------|:---------------------------------------------------------|
+| [Getting started with GitLab CI/CD](quick_start/README.md) | Outlines the first steps for configuring GitLab CI/CD. |
+| [Introduction to pipelines and jobs](pipelines.md) | Provides an overview of GitLab CI/CD and jobs. |
+| [Configuration of your pipelines with `.gitlab-ci.yml`](yaml/README.md) | A comprehensive reference for the `.gitlab-ci.yml` file. |
-- [Environments and deployments](environments.md): Separate your jobs into
- environments and use them for different purposes like testing, building and
- deploying
-- [Job artifacts](../user/project/pipelines/job_artifacts.md)
-- [Caching dependencies](caching/index.md)
-- [Git submodules](git_submodules.md) - How to run your CI jobs when Git
- submodules are involved
-- [Pipelines for merge requests](merge_request_pipelines/index.md)
-- [Use SSH keys in your build environment](ssh_keys/README.md)
-- [Trigger pipelines through the GitLab API](triggers/README.md)
-- [Trigger pipelines on a schedule](../user/project/pipelines/schedules.md)
-- [Kubernetes clusters](../user/project/clusters/index.md) - Integrate one or
- more Kubernetes clusters to your project
-- [Interactive web terminal](interactive_web_terminal/index.md) - Open an interactive
- web terminal to debug the running jobs
+NOTE: **Note:**
+Familiarity with [GitLab Runner](https://docs.gitlab.com/runner/) is useful because it is
+responsible for running the jobs in your CI/CD pipeline. On GitLab.com, shared Runners are enabled
+by default so you don't need to set up anything to get started.
-## GitLab CI/CD for Docker
+### Auto DevOps
-Leverage the power of Docker to run your CI pipelines.
+An alternative to manually configuring CI/CD, GitLab supports [Auto DevOps](../topics/autodevops/index.md),
+which:
-- [Use Docker images with GitLab Runner](docker/using_docker_images.md)
-- [Use CI to build Docker images](docker/using_docker_build.md)
-- [CI services (linked Docker containers)](services/README.md)
-- Article (2016-03-01): [Setting up GitLab Runner For Continuous Integration](https://about.gitlab.com/2016/03/01/gitlab-runner-with-docker/)
+- Provides simplified setup and execution of CI/CD.
+- Allows GitLab to automatically detect, build, test, deploy, and monitor your applications.
-## Review Apps
+## Basic usage
-- [Review Apps documentation](review_apps/index.md)
-- Article (2016-11-22): [Introducing Review Apps](https://about.gitlab.com/2016/11/22/introducing-review-apps/)
-- [Example project that shows how to use Review Apps](https://gitlab.com/gitlab-examples/review-apps-nginx/)
+With basic knowledge of how GitLab CI/CD works, the following documentation extends your knowledge
+into more features:
-## Auto DevOps
+| Topic | Description |
+|:-------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------|
+| [CI/CD Variables](variables/README.md) | How environment variables can be configured and made available in pipelines. |
+| [Where variables can be used](variables/where_variables_can_be_used.md) | A deeper look into where and how CI/CD variables can be used. |
+| [User](../user/permissions.md#gitlab-ci) and [job](../user/permissions.md#job-permissions) permissions | Learn about the access levels a user can have for performing certain CI actions. |
+| [Configuring GitLab Runners](runners/README.md) | Documentation for configuring [GitLab Runner](https://docs.gitlab.com/runner/). |
-- [Auto DevOps](../topics/autodevops/index.md): Auto DevOps automatically detects, builds, tests, deploys, and monitors your applications.
+## Advanced usage
-## GitLab CI for GitLab Pages
+Once you get familiar with the basics of GitLab CI/CD, consult the following documentation to make
+use of advanced features:
-See the documentation on [GitLab Pages](../user/project/pages/index.md).
+| Topic | Description |
+|:---------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------|
+| [Introduction to environments and deployments](environments.md) | Learn how to separate your jobs into environments and use them for different purposes like testing, building and, deploying. |
+| [Job artifacts](../user/project/pipelines/job_artifacts.md) | Learn about the output of jobs. |
+| [Cache dependencies in GitLab CI/CD](caching/index.md) | Discover how to speed up pipelines using caching. |
+| [Using Git submodules with GitLab CI](git_submodules.md) | How to run your CI jobs when using Git submodules. |
+| [Pipelines for merge requests](merge_request_pipelines/index.md) | Create pipelines specifically for merge requests. |
+| [Using SSH keys with GitLab CI/CD](ssh_keys/README.md) | Use SSH keys in your build environment. |
+| [Triggering pipelines through the API](triggers/README.md) | Use the GitLab API to trigger a pipeline. |
+| [Pipeline schedules](../user/project/pipelines/schedules.md) | Trigger pipelines on a schedule. |
+| [Connecting GitLab with a Kubernetes cluster](../user/project/clusters/index.md) | Integrate one or more Kubernetes clusters to your project. |
+| [ChatOps](chatops/README.md) | Trigger CI jobs from chat, with results sent back to the channel. |
+| [Interactive web terminals](interactive_web_terminal/index.md) | Open an interactive web terminal to debug the running jobs. |
+
+### GitLab Pages
+
+GitLab CI/CD can be used to build and host static websites. For more information, see the
+documentation on [GitLab Pages](../user/project/pages/index.md).
## Examples
-Check the [GitLab CI/CD examples](examples/README.md) for a collection of tutorials and guides on setting up your CI/CD pipeline for various programming languages, frameworks,
-and operating systems.
+Check out the [GitLab CI/CD examples](examples/README.md) for a collection of tutorials and guides on
+setting up your CI/CD pipeline for various programming languages, frameworks, and operating systems.
+
+## Administration
+
+As a GitLab administrator, you can change the default behavior of GitLab CI/CD for:
+
+- An [entire GitLab instance](../user/admin_area/settings/continuous_integration.md).
+- Specific projects, using [pipelines settings](../user/project/pipelines/settings.md).
+
+See also:
+
+- [How to enable or disable GitLab CI/CD](enable_or_disable_ci.md).
+- Other [CI administration settings](../administration/index.md#continuous-integration-settings).
+
+## Using Docker
+
+Docker is commonly used with GitLab CI/CD. Learn more about how to to accomplish this with the following
+documentation:
+
+| Topic | Description |
+|:-------------------------------------------------------------------------|:-------------------------------------------------------------------------|
+| [Using Docker images](docker/using_docker_images.md) | Use GitLab and GitLab Runner with Docker to build and test applications. |
+| [Building Docker images with GitLab CI/CD](docker/using_docker_build.md) | Maintain Docker-based projects using GitLab CI/CD. |
+
+Related topics include:
-## Integrations
+- [Docker integration](docker/README.md).
+- [CI services (linked Docker containers)](services/README.md).
+- [Setting up GitLab Runner For Continuous Integration](https://about.gitlab.com/2016/03/01/gitlab-runner-with-docker/) (article).
-- Article (2016-06-09): [Continuous Delivery with GitLab and Convox](https://about.gitlab.com/2016/06/09/continuous-delivery-with-gitlab-and-convox/)
-- Article (2016-05-05): [Getting Started with GitLab and Shippable Continuous Integration](https://about.gitlab.com/2016/05/05/getting-started-gitlab-and-shippable/)
-- Article (2016-04-19): [GitLab Partners with DigitalOcean to make Continuous Integration faster, safer, and more affordable](https://about.gitlab.com/2016/04/19/gitlab-partners-with-digitalocean-to-make-continuous-integration-faster-safer-and-more-affordable/)
+## Further resources
-## Special configuration (GitLab admin)
+This section provides further resources to help you get familiar with GitLab CI/CD.
-As a GitLab administrator, you can change the default behavior of GitLab CI/CD in
-your whole GitLab instance as well as in each project.
+### Articles
-- [Continuous Integration admin settings](../administration/index.md#continuous-integration-settings)
-- **Project specific:**
- - [Pipelines settings](../user/project/pipelines/settings.md)
- - [Learn how to enable or disable GitLab CI](enable_or_disable_ci.md)
-- **Affecting the whole GitLab instance:**
- - [Continuous Integration admin settings](../user/admin_area/settings/continuous_integration.md)
+The following table provides a list of articles about CI/CD, sorted in reverse chronological order of publish date:
+
+| Publish Date | Article |
+|:-------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| 2017-07-13 | [Making CI easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/). |
+| 2017-05-22 | [Fast and natural continuous integration with GitLab CI](https://about.gitlab.com/2017/05/22/fast-and-natural-continuous-integration-with-gitlab-ci/). |
+| 2016-11-22 | [Introducing Review Apps](https://about.gitlab.com/2016/11/22/introducing-review-apps/). |
+| 2016-08-26 | [GitLab CI: Deployment & Environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/). |
+| 2016-08-05 | [Continuous Integration, Delivery, and Deployment with GitLab](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/). |
+| 2016-07-29 | [GitLab CI: Run jobs sequentially, in parallel or build a custom pipeline](https://about.gitlab.com/2016/07/29/the-basics-of-gitlab-ci/). |
+| 2016-06-09 | [Continuous Delivery with GitLab and Convox](https://about.gitlab.com/2016/06/09/continuous-delivery-with-gitlab-and-convox/) |
+| 2016-05-23 | [GitLab Container Registry](https://about.gitlab.com/2016/05/23/gitlab-container-registry/). |
+| 2016-05-05 | [Getting Started with GitLab and Shippable Continuous Integration](https://about.gitlab.com/2016/05/05/getting-started-gitlab-and-shippable/) |
+| 2016-04-19 | [GitLab Partners with DigitalOcean to make Continuous Integration faster, safer, and more affordable](https://about.gitlab.com/2016/04/19/gitlab-partners-with-digitalocean-to-make-continuous-integration-faster-safer-and-more-affordable/) |
+| 2015-03-01 | [Setting up GitLab Runner For Continuous Integration](https://about.gitlab.com/2016/03/01/gitlab-runner-with-docker/). |
+| 2015-12-14 | [Getting started with GitLab and GitLab CI](https://about.gitlab.com/2015/12/14/getting-started-with-gitlab-and-gitlab-ci/). |
+
+### Videos
+
+The following table provides a list of videos about CI/CD, sorted in reverse chronological order of publish date:
+
+| Publish Date | Video |
+|:-------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| 2017-07-17 | [GitLab CI/CD Deep Dive](https://youtu.be/pBe4t1CD8Fc?t=195). |
+| 2017-03-13 | [Demo: CI/CD with GitLab in action](https://about.gitlab.com/2017/03/13/ci-cd-demo/). |
+| 2016-04-20 | [Webcast Recording and Slides: Getting started with CI in GitLab](https://about.gitlab.com/2016/04/20/webcast-recording-and-slides-introduction-to-ci-in-gitlab/). |
+
+In addition, the following third-party videos are available:
+
+- [Intégration continue avec GitLab (September 2016)](https://www.youtube.com/watch?v=URcMBXjIr24&t=13s).
+- [GitLab CI for Minecraft Plugins (July 2016)](https://www.youtube.com/watch?v=Z4pcI9F8yf8).
+
+### Example Projects
+
+[`review-apps-nginx`](https://gitlab.com/gitlab-examples/review-apps-nginx/) provides an example of using Review Apps.
+
+Other example projects are available at the [`gitlab-examples`](https://gitlab.com/gitlab-examples) group.
+
+### Why GitLab CI/CD?
+
+The following articles explain reasons why you might use GitLab CI/CD for your CI/CD infrastructure:
+
+- [Why we chose GitLab CI for our CI/CD solution](https://about.gitlab.com/2016/10/17/gitlab-ci-oohlala/).
+- [Building our web-app on GitLab CI](https://about.gitlab.com/2016/07/22/building-our-web-app-on-gitlab-ci/).
+
+See also the [Why CI/CD?](https://docs.google.com/presentation/d/1OGgk2Tcxbpl7DJaIOzCX4Vqg3dlwfELC3u2jEeCBbDk) presentation.
## Breaking changes
-- [CI variables renaming for GitLab 9.0](variables/README.md#9-0-renaming) Read about the
+As GitLab CI/CD has evolved, certain breaking changes have been necessary. These are:
+
+- [CI variables renaming for GitLab 9.0](variables/README.md#gitlab-90-renaming). Read about the
deprecated CI variables and what you should use for GitLab 9.0+.
-- [New CI job permissions model](../user/project/new_ci_build_permissions_model.md)
- Read about what changed in GitLab 8.12 and how that affects your jobs.
+- [New CI job permissions model](../user/project/new_ci_build_permissions_model.md).
+ See what changed in GitLab 8.12 and how that affects your jobs.
There's a new way to access your Git submodules and LFS objects in jobs.
diff --git a/doc/ci/caching/index.md b/doc/ci/caching/index.md
index 8b2ce425cf5..e079483e2b5 100644
--- a/doc/ci/caching/index.md
+++ b/doc/ci/caching/index.md
@@ -46,14 +46,13 @@ needed to compile the project:
with stages and shared artifacts before investing time in changes to the
setup.
-
It's sometimes confusing because the name artifact sounds like something that
is only useful outside of the job, like for downloading a final image. But
artifacts are also available in between stages within a pipeline. So if you
build your application by downloading all the required modules, you might want
to declare them as artifacts so that each subsequent stage can depend on them
being there. There are some optimizations like declaring an
-[expiry time](../yaml/README.md#artifacts-expire_in) so you don't keep artifacts
+[expiry time](../yaml/README.md#artifactsexpire_in) so you don't keep artifacts
around too long, and using [dependencies](../yaml/README.md#dependencies) to
control exactly where artifacts are passed around.
@@ -88,7 +87,7 @@ cache, when declaring `cache` in your jobs, use one or a mix of the following:
that share their cache.
- [Use sticky Runners](../runners/README.md#locking-a-specific-runner-from-being-enabled-for-other-projects)
that will be only available to a particular project.
-- [Use a `key`](../yaml/README.md#cache-key) that fits your workflow (e.g.,
+- [Use a `key`](../yaml/README.md#cachekey) that fits your workflow (e.g.,
different caches on each branch). For that, you can take advantage of the
[CI/CD predefined variables](../variables/README.md#predefined-environment-variables).
@@ -170,7 +169,7 @@ job:
```
For more fine tuning, read also about the
-[`cache: policy`](../yaml/README.md#cache-policy).
+[`cache: policy`](../yaml/README.md#cachepolicy).
## Common use cases
diff --git a/doc/ci/chatops/README.md b/doc/ci/chatops/README.md
new file mode 100644
index 00000000000..6ad1df7bb2a
--- /dev/null
+++ b/doc/ci/chatops/README.md
@@ -0,0 +1,61 @@
+# GitLab ChatOps
+
+> **Notes:**
+>
+> * [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4466) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.6. [Moved](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24780) to [GitLab Core](https://about.gitlab.com/pricing/) in 11.9.
+>
+> * ChatOps is currently in alpha, with some important features missing like access control.
+
+GitLab ChatOps provides a method to interact with CI/CD jobs through chat services like Slack. Many organizations' discussion, collaboration, and troubleshooting is taking place in chat services these days, and having a method to run CI/CD jobs with output posted back to the channel can significantly augment a team's workflow.
+
+## How it works
+
+GitLab ChatOps is built upon two existing features, [GitLab CI/CD](../README.md) and [Slack Slash Commmands](../../user/project/integrations/slack_slash_commands.md).
+
+A new `run` action has been added to the [slash commands](../../integration/slash_commands.md), which takes two arguments: a `<job name>` to execute and the `<job arguments>`. When executed, ChatOps will look up the specified job name and attempt to match it to a corresponding job in [.gitlab-ci.yml](../yaml/README.md). If a matching job is found on `master`, a pipeline containing just that job is scheduled. Two additional [CI/CD variables](../variables/README.html#predefined-variables-environment-variables) are passed to the job: `CHAT_INPUT` contains any additional arguments, and `CHAT_CHANNEL` is set to the name of channel the action was triggered in.
+
+After the job has finished, its output is sent back to Slack provided it has completed within 30 minutes. If a job takes more than 30 minutes to run it must use the Slack API to manually send data back to a channel.
+
+[Developer access and above](../../user/permissions.html#project-members-permissions) is required to use the `run` command. If a job should not be able to be triggered from chat, it can be set to `except: [chat]`.
+
+## Creating a ChatOps CI job
+
+Since ChatOps is built upon GitLab CI/CD, the job has all the same features and functions available. There a few best practices to consider however when creating ChatOps jobs:
+
+* It is strongly recommended to set `only: [chat]` so the job does not run as part of the standard CI pipeline.
+* If the job is set to `when: manual`, the pipeline will be created however the job will wait to be started.
+* It is important to keep in mind that there is very limited support for access control. If the user who triggered the slash command is a developer in the project, the job will run. The job itself can utilize existing [CI/CD variables](../variables/README.html#predefined-environment-variables) like `GITLAB_USER_ID` to perform additional rights validation, however these variables can be [overridden](https://docs.gitlab.com/ce/ci/variables/README.html#priority-of-variables).
+
+### Controlling the ChatOps reply
+
+For jobs with a single command, its output is automatically sent back to the channel as a reply. For example the chat reply of the following job is simply `Hello World.`
+
+```yaml
+hello-world:
+ stage: chatops
+ only: [chat]
+ script:
+ - echo "Hello World."
+```
+
+Jobs that contain multiple commands, or have a `before_script`, include additional content in the chat reply. In these cases both the commands and their output are included, with the commands wrapped in ANSI colors codes.
+
+To selectively reply with the output of one command, its output must be bounded by the `chat_reply` section. For example, the following job will list the files in the current directory.
+
+```yaml
+ls:
+ stage: chatops
+ only: [chat]
+ script:
+ - echo "This command will not be shown."
+ - echo -e "section_start:$( date +%s ):chat_reply\r\033[0K\n$( ls -la )\nsection_end:$( date +%s ):chat_reply\r\033[0K"
+```
+
+## GitLab ChatOps icon
+
+Say Hi to our ChatOps bot.
+You can find and download the official GitLab ChatOps icon here.
+
+![GitLab ChatOps bot icon](img/gitlab-chatops-icon-small.png)
+
+[Download bigger image](img/gitlab-chatops-icon.png)
diff --git a/doc/ci/chatops/img/gitlab-chatops-icon-small.png b/doc/ci/chatops/img/gitlab-chatops-icon-small.png
new file mode 100644
index 00000000000..71cc5dba5cf
--- /dev/null
+++ b/doc/ci/chatops/img/gitlab-chatops-icon-small.png
Binary files differ
diff --git a/doc/ci/chatops/img/gitlab-chatops-icon.png b/doc/ci/chatops/img/gitlab-chatops-icon.png
new file mode 100644
index 00000000000..3ba8bd308e3
--- /dev/null
+++ b/doc/ci/chatops/img/gitlab-chatops-icon.png
Binary files differ
diff --git a/doc/ci/docker/README.md b/doc/ci/docker/README.md
index 8ae80b2bc02..446f5b54f0c 100644
--- a/doc/ci/docker/README.md
+++ b/doc/ci/docker/README.md
@@ -4,6 +4,8 @@ comments: false
# Docker integration
-- [Using Docker Images](using_docker_images.md)
-- [Using Docker Build](using_docker_build.md)
-- [Using kaniko](using_kaniko.md)
+The following documentation is available for using GitLab CI/CD with Docker:
+
+- [Using Docker images](using_docker_images.md).
+- [Building Docker images with GitLab CI/CD](using_docker_build.md).
+- [Building images with kaniko and GitLab CI/CD](using_kaniko.md).
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index 959271d8abc..3314c76d234 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -391,9 +391,9 @@ CI jobs:
from `Dockerfile` that may be overridden in `.gitlab-ci.yml`)
1. The Runner attaches itself to a running container.
1. The Runner prepares a script (the combination of
- [`before_script`](../yaml/README.md#before_script),
+ [`before_script`](../yaml/README.md#before_script-and-after_script),
[`script`](../yaml/README.md#script),
- and [`after_script`](../yaml/README.md#after_script)).
+ and [`after_script`](../yaml/README.md#before_script-and-after_script)).
1. The Runner sends the script to the container's shell STDIN and receives the
output.
diff --git a/doc/ci/environments.md b/doc/ci/environments.md
index 6a9917f6430..fe66f7e3c28 100644
--- a/doc/ci/environments.md
+++ b/doc/ci/environments.md
@@ -103,7 +103,7 @@ the Git SHA and environment name.
To sum up, with the above `.gitlab-ci.yml` we have achieved that:
- All branches will run the `test` and `build` jobs.
-- The `deploy_staging` job will run [only](yaml/README.md#only) on the `master`
+- The `deploy_staging` job will run [only](yaml/README.md#only-and-except-simplified) on the `master`
branch which means all merge requests that are created from branches don't
get to deploy to the staging server
- When a merge request is merged, all jobs will run and the `deploy_staging`
@@ -401,7 +401,7 @@ Let's briefly see where URL that's defined in the environments is exposed.
## Making use of the environment URL
-The [environment URL](yaml/README.md#environments-url) is exposed in a few
+The [environment URL](yaml/README.md#environmenturl) is exposed in a few
places within GitLab.
| In a merge request widget as a link | In the Environments view as a button | In the Deployments view as a button |
@@ -619,9 +619,9 @@ Below are some links you may find interesting:
[deployments]: #deployments
[permissions]: ../user/permissions.md
[variables]: variables/README.md
-[env-name]: yaml/README.md#environment-name
-[only]: yaml/README.md#only-and-except
-[onstop]: yaml/README.md#environment-on_stop
+[env-name]: yaml/README.md#environmentname
+[only]: yaml/README.md#only-and-except-simplified
+[onstop]: yaml/README.md#environmenton_stop
[ce-7015]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7015
[gitlab-flow]: ../workflow/gitlab_flow.md
[gitlab runner]: https://docs.gitlab.com/runner/
diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md
index a8c119edaa0..87e86bef44b 100644
--- a/doc/ci/examples/README.md
+++ b/doc/ci/examples/README.md
@@ -40,6 +40,7 @@ There's also a collection of repositories with [example projects](https://gitlab
### Miscellaneous
+- [End-to-end testing with GitLab CI/CD and WebdriverIO](end_to_end_testing_webdriverio/index.md)
- [Using `dpl` as deployment tool](deployment/README.md)
- [The `.gitlab-ci.yml` file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml)
diff --git a/doc/ci/examples/deployment/composer-npm-deploy.md b/doc/ci/examples/deployment/composer-npm-deploy.md
index 36358515b84..4758ccad5aa 100644
--- a/doc/ci/examples/deployment/composer-npm-deploy.md
+++ b/doc/ci/examples/deployment/composer-npm-deploy.md
@@ -4,7 +4,6 @@ This guide covers the building dependencies of a PHP project while compiling ass
While is possible to create your own image with custom PHP and Node JS versions, for brevity, we will use an existing [Docker image](https://hub.docker.com/r/tetraweb/php/) that contains both PHP and NodeJS installed.
-
```yaml
image: tetraweb/php
```
diff --git a/doc/ci/examples/end_to_end_testing_webdriverio/img/deployed_dependency_update.png b/doc/ci/examples/end_to_end_testing_webdriverio/img/deployed_dependency_update.png
new file mode 100644
index 00000000000..c45d70d7f7a
--- /dev/null
+++ b/doc/ci/examples/end_to_end_testing_webdriverio/img/deployed_dependency_update.png
Binary files differ
diff --git a/doc/ci/examples/end_to_end_testing_webdriverio/index.md b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
new file mode 100644
index 00000000000..9f3b8d9ad14
--- /dev/null
+++ b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
@@ -0,0 +1,251 @@
+---
+author: Vincent Tunru
+author_gitlab: Vinnl
+level: advanced
+article_type: user guide
+date: 2019-02-18
+description: 'Confidence checking your entire app every time a new feature is added can quickly become repetitive. Learn how to automate it with GitLab CI/CD.'
+---
+
+# End-to-end testing with GitLab CI/CD and WebdriverIO
+
+[Review Apps](../../review_apps/index.md) are great: for every merge request
+(or branch, for that matter), the new code can be copied and deployed to a fresh production-like live
+environment, making it incredibly low-effort to assess the impact of the changes. Thus, when we use a dependency manager like
+[Dependencies.io](https://www.dependencies.io/), it can submit a merge request with an updated dependency,
+and it will immediately be clear that the application can still be properly built and deployed. After all, you can _see_ it
+running!
+
+<img src="img/deployed_dependency_update.png" alt="dependencies.io" class="image-noshadow">
+
+However, looking at the freshly deployed code to check whether it still looks and behaves as
+expected is repetitive manual work, which means it is a prime candidate for automation. This is
+where automated [end-to-end testing](https://martinfowler.com/bliki/BroadStackTest.html) comes in:
+having the computer run through a few simple scenarios that requires the proper functioning of all
+layers of your application, from the frontend to the database. In this article, we will discuss how
+to write such end-to-end tests, and how to set up GitLab CI/CD to automatically run these tests
+against your new code, on a branch-by-branch basis. For the scope of this article, we will walk you
+through the process of setting up GitLab CI/CD for end-to-end testing Javascript-based applications
+with WebdriverIO, but the general strategy should carry over to other languages.
+We assume you are familiar with GitLab, [GitLab CI/CD](../../README.md), [Review Apps](../../review_apps/index.md), and running your app locally, e.g., on `localhost:8000`.
+
+### What to test
+
+In the widely-used [testing pyramid strategy](https://martinfowler.com/bliki/TestPyramid.html), end-to-end tests act more like a
+safeguard: [most of your code should be covered by
+unit tests](https://vincenttunru.com/100-percent-coverage/) that allow you to easily identify the source of a problem, should one occur. Rather, you
+will likely want to
+[limit the number of end-to-end tests](https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html)
+to just enough to give you the confidence that the deployment went as intended, that your
+infrastructure is up and running, and that your units of code work well together.
+
+### Selenium and WebdriverIO
+
+[Selenium](http://www.seleniumhq.org/) is a piece of software that can control web browsers, e.g., to make them
+visit a specific URL or interact with elements on the page. It can be programmatically controlled
+from a variety of programming languages. In this article we're going to be using the
+[WebdriverIO](http://webdriver.io/) Javascript bindings, but the general concept should carry over
+pretty well to
+[other programming languages supported by Selenium](http://docs.seleniumhq.org/about/platforms.jsp#programming-languages).
+
+## Writing tests
+
+You can write tests using
+[several testing frameworks supported by WebdriverIO](http://webdriver.io/guide/testrunner/frameworks.html).
+We will be using [Jasmine](https://jasmine.github.io/) here:
+
+```javascript
+describe('A visitor without account', function(){
+ it('should be able to navigate to the homepage from the 404 page', function(){
+ browser.url('/page-that-does-not-exist');
+
+ expect(browser.getUrl()).toMatch('page-that-does-not-exist');
+
+ browser.element('.content a[href="/"]').click();
+
+ expect(browser.getUrl()).not.toMatch('page-that-does-not-exist');
+ });
+});
+```
+
+The functions `describe`, `it`, and `browser` are provided by WebdriverIO. Let's break them down one by one.
+
+The function `describe` allows you to group related tests. This can be useful if, for example, you want to
+run the same initialization commands (using [`beforeEach`](https://jasmine.github.io/api/2.9/global.html#beforeEach)) for
+multiple tests, such as making sure you are logged in.
+
+The function `it` defines an individual test.
+
+[The `browser` object](http://webdriver.io/guide/testrunner/browserobject.html) is WebdriverIO's
+special sauce. It provides most of [the WebdriverIO API methods](http://webdriver.io/api.html) that are the key to
+steering the browser. In this case, we can use
+[`browser.url`](http://webdriver.io/api/protocol/url.html) to visit `/page-that-does-not-exist` to
+hit our 404 page. We can then use [`browser.getUrl`](http://webdriver.io/api/property/getUrl.html)
+to verify that the current page is indeed at the location we specified. To interact with the page,
+we can simply pass CSS selectors to
+[`browser.element`](http://webdriver.io/api/protocol/element.html) to get access to elements on the
+page and to interact with them - for example, to click on the link back to the home page.
+
+The simple test shown above
+can already give us a lot of confidence if it passes: we know our deployment has succeeded, that the
+elements are visible on the page and that actual browsers can interact with it, and that routing
+works as expected. And all that in just 10 lines with gratituous whitespace! Add to that succeeding
+unit tests and a successfully completed pipeline, and you can be fairly confident that the
+dependency upgrade did not break anything without even having to look at your website.
+
+## Running locally
+
+We'll get to running the above test in CI/CD in a moment. When writing tests, however, it helps if
+you do not have to wait for your pipelines to succeed in order to check whether they do what you
+expect them to do. In other words, let's get it to run locally.
+
+Make sure that your app is running locally. If you use Webpack,
+you can use [the Webpack Dev Server WebdriverIO plugin](https://www.npmjs.com/package/wdio-webpack-dev-server-service)
+that automatically starts a development server before executing the tests.
+
+The WebdriverIO documentation has
+[an overview of all configuration options](http://webdriver.io/guide/getstarted/configuration.html), but the
+easiest way to get started is to start with
+[WebdriverIO's default configuration](http://webdriver.io/guide/testrunner/configurationfile.html), which
+provides an overview of all available options. The two options that are going to be most relevant now are the
+`specs` option, which is an array of paths to your tests, and the `baseUrl` option, which points to where your app is
+running. And finally, we will need to tell WebdriverIO in which browsers we would like to run our
+tests. This can be configured through the `capabilities` option, which is an array of browser names (e.g.
+`firefox` or `chrome`). It is recommended to install
+[selenium-assistant](https://googlechromelabs.github.io/selenium-assistant/) to detect all installed
+browsers:
+
+```javascript
+ const seleniumAssistant = require('selenium-assistant');
+ const browsers = seleniumAssistant.getLocalBrowsers();
+ config.capabilities = browsers.map(browser => ({ browserName: browser.getId() }));
+```
+
+But of course, a simple configuration of `config.capabilities = ['firefox']` would work as well.
+
+If you've installed WebdriverIO as a dependency
+(`npm install --save-dev webdriverio`), you can add a line to the `scripts` property in your
+`package.json` that runs `wdio` with the path to your configuration file as value, e.g.:
+
+```javascript
+ "confidence-check": "wdio wdio.conf.js",
+```
+
+You can then execute the tests using `npm run confidence-check`, after which you will actually see a
+new browser window interacting with your app as you specified.
+
+## Configuring GitLab CI/CD
+
+Which brings us to the exciting part: how do we run this in GitLab CI/CD? There are two things we
+need to do for this:
+
+1. Set up [CI/CD jobs](../../yaml/README.md#jobs) that actually have a browser available.
+2. 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
+image](../../docker/using_docker_images.html). However, WebdriverIO fires up actual browsers
+to interact with your application, so we need to install and run them.
+Furthermore, WebdriverIO uses Selenium as a common interface to control different browsers,
+so we need to install and run Selenium as well. Luckily, the Selenium project provides the Docker images
+[standalone-firefox](https://hub.docker.com/r/selenium/standalone-firefox/) and
+[standalone-chrome](https://hub.docker.com/r/selenium/standalone-chrome/) that provide just that for
+Firefox and Chrome, respectively. (Since Safari and Internet Explorer/Edge are not open source and
+not available for Linux, we are unfortunately unable to use those in GitLab CI/CD).
+
+GitLab CI/CD makes it a breeze to link these images to our `confidence-check` jobs using the
+`service` property, which makes the Selenium server available under a hostname based on the image
+name. Our job configuration then looks something like this:
+
+```yaml
+e2e:firefox:
+ stage: confidence-check
+ services:
+ - selenium/standalone-firefox
+ script:
+ - npm run confidence-check --host=selenium__standalone-firefox
+```
+
+And likewise for Chrome:
+
+```yaml
+e2e:chrome:
+ stage: confidence-check
+ services:
+ - selenium/standalone-chrome
+ script:
+ - npm run confidence-check --host=selenium__standalone-chrome
+```
+
+Now that we have a job to run the end-to-end tests in, we need to tell WebdriverIO how to connect to
+the Selenium servers running alongside it. We've already cheated a bit above by
+passing the value of the [`host`](http://webdriver.io/guide/getstarted/configuration.html#host)
+option as an argument to `npm run confidence-check` on the command line.
+However, we still need to tell WebdriverIO which browser is available for it to use.
+
+[GitLab CI/CD makes
+a number of variables available](../../variables/README.html#predefined-variables-environment-variables)
+with information about the current CI job. We can use this information to dynamically set
+up our WebdriverIO configuration according to the job that is running. More specifically, we can
+tell WebdriverIO what browser to execute the test on depending on the name of the currently running
+job. We can do so in WebdriverIO's configuration file, which we named `wdio.conf.js` above:
+
+```javascript
+if(process.env.CI_JOB_NAME) {
+ dynamicConfig.capabilities = [
+ { browserName: process.env.CI_JOB_NAME === 'e2e:chrome' ? 'chrome' : 'firefox' },
+ ];
+}
+```
+
+Likewise, we can tell WebdriverIO where the review app is running - in this example's case, it's on
+`<branch name>.flockademic.com`:
+
+```javascript
+if(process.env.CI_COMMIT_REF_SLUG) {
+ dynamicConfig.baseUrl = `https://${process.env.CI_COMMIT_REF_SLUG}.flockademic.com`;
+}
+```
+
+And we can make sure our local-specific configuration is only used when _not_ running in CI using
+`if (!process.env.CI)`. That's basically all the ingredients you need to run your end-to-end tests
+on GitLab CI/CD!
+
+To recap, our `.gitlab-ci.yml` configuration file looks something like this:
+
+```yaml
+image: node:8.10
+stages:
+ - deploy
+ - confidence-check
+deploy_terraform:
+ stage: deploy
+ script:
+ # Your Review App deployment scripts - for a working example please check https://gitlab.com/Flockademic/Flockademic/blob/5a45f1c2412e93810fab50e2dab8949e2d0633c7/.gitlab-ci.yml#L315
+e2e:firefox:
+ stage: confidence-check
+ services:
+ - selenium/standalone-firefox
+ script:
+ - npm run confidence-check --host=selenium__standalone-firefox
+e2e:chrome:
+ stage: confidence-check
+ services:
+ - selenium/standalone-chrome
+ script:
+ - npm run confidence-check --host=selenium__standalone-chrome
+```
+
+## What's next
+
+If you are setting this up for yourself and want to peek at the working configuration of a
+production project, see:
+
+- [Flockademic's `wdio.conf.js`](https://gitlab.com/Flockademic/Flockademic/blob/dev/wdio.conf.js)
+- [Flockademic's `.gitlab-ci.yml`](https://gitlab.com/Flockademic/Flockademic/blob/dev/.gitlab-ci.yml)
+- [Flockademic's tests](https://gitlab.com/Flockademic/Flockademic/tree/dev/__e2e__)
+
+There's plenty more that WebdriverIO can do. For example, you can configure a [`screenshotPath`](http://webdriver.io/guide/getstarted/configuration.html#screenshotPath) to tell WebdriverIO to take
+a screenshot when tests are failing. Then tell GitLab CI/CD to store those
+[artifacts](../../yaml/README.md#artifacts), and you'll be able to see what went
+wrong within GitLab.
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
index b1ccce744d8..d3b6650b0f4 100644
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
+++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
@@ -68,7 +68,6 @@ Since we have our app up and running locally, it's time to push the codebase to
Let's create [a new project](../../../gitlab-basics/create-project.md) in GitLab named `laravel-sample`.
After that, follow the command line instructions displayed on the project's homepage to initiate the repository on our machine and push the first commit.
-
```bash
cd laravel-sample
git init
@@ -127,7 +126,6 @@ We'll use this variable in the `.gitlab-ci.yml` later, to easily connect to our
We also need to add the public key to **Project** > **Settings** > **Repository** as a [Deploy Key](../../../ssh/README.md#deploy-keys), which gives us the ability to access our repository from the server through [SSH protocol](../../../gitlab-basics/command-line-commands.md#start-working-on-your-project).
-
```bash
# As the deployer user on the server
#
@@ -186,7 +184,6 @@ To use Envoy, we should first install it on our local machine [using the given i
The pros of Envoy is that it doesn't require Blade engine, it just uses Blade syntax to define tasks.
To start, we create an `Envoy.blade.php` in the root of our app with a simple task to test Envoy.
-
```php
@servers(['web' => 'remote_username@remote_host'])
@@ -220,7 +217,6 @@ Our deployment plan is to clone the latest release from GitLab repository, insta
The first step of our deployment process is to define a set of variables within [@setup](https://laravel.com/docs/envoy/#setup) directive.
You may change the `app` to your application's name:
-
```php
...
@@ -246,7 +242,6 @@ You may change the `app` to your application's name:
The [@story](https://laravel.com/docs/envoy/#stories) directive allows us define a list of tasks that can be run as a single task.
Here we have three tasks called `clone_repository`, `run_composer`, `update_symlinks`. These variables are usable to making our task's codes more cleaner:
-
```php
...
@@ -618,7 +613,7 @@ Lastly, `when: manual` is used to turn the job from running automatically to a m
deploy_production:
script:
- # Add the private SSH key to the build environment
+ # Add the private SSH key to the build environment
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- ssh-add <(echo "$SSH_PRIVATE_KEY")
diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md
index c41f3b7e82d..4f3106c6dc6 100644
--- a/doc/ci/pipelines.md
+++ b/doc/ci/pipelines.md
@@ -197,7 +197,7 @@ stage has a job with a manual action.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21767) in GitLab 11.4.
-When you do not want to run a job immediately, you can [delay the job to run after a certain period](yaml/README.md#when-delayed).
+When you do not want to run a job immediately, you can [delay the job to run after a certain period](yaml/README.md#whendelayed).
This is especially useful for timed incremental rollout that new code is rolled out gradually.
For example, if you start rolling out new code and users do not experience trouble, GitLab automatically completes the deployment from 0% to 100%.
Alternatively, if you start rolling out and you noticed that a few users experience trouble with the version,
diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md
index c742dc61368..ce55b231666 100644
--- a/doc/ci/runners/README.md
+++ b/doc/ci/runners/README.md
@@ -240,21 +240,64 @@ shared Runners will [only run the jobs they are equipped to run](../yaml/README.
For instance, at GitLab we have Runners tagged with "rails" if they contain
the appropriate dependencies to run Rails test suites.
-### Preventing Runners with tags from picking jobs without tags
+### Allowing Runners with tags to pick jobs without tags
-You can configure a Runner to prevent it from picking
-[jobs with tags](../yaml/README.md#tags) when the Runner does not have tags
-assigned. This setting can be enabled the first
-time you [register a Runner][register] and can be changed afterwards under
-each Runner's settings.
+When you [register a Runner][register], its default behavior is to **only pick**
+[tagged jobs](../yaml/README.md#tags).
-To make a Runner pick tagged/untagged jobs:
+NOTE: **Note:**
+Maintainer [permissions](../../user/permissions.md) are required to change the
+Runner settings.
-1. Visit your project's **Settings âž” CI/CD**
-1. Find the Runner you wish and make sure it's enabled
-1. Click the pencil button
-1. Check the **Run untagged jobs** option
-1. Click **Save changes** for the changes to take effect
+To make a Runner pick untagged jobs:
+
+1. Visit your project's **Settings > CI/CD > Runners**.
+1. Find the Runner you want to pick untagged jobs and make sure it's enabled.
+1. Click the pencil button.
+1. Check the **Run untagged jobs** option.
+1. Click the **Save changes** button for the changes to take effect.
+
+NOTE: **Note:**
+The Runner tags list can not be empty when it's not allowed to pick untagged jobs.
+
+Below are some example scenarios of different variations.
+
+#### Runner runs only tagged jobs
+
+The following examples illustrate the potential impact of the Runner being set
+to run only tagged jobs.
+
+Example 1:
+
+1. The Runner is configured to run only tagged jobs and has the `docker` tag.
+1. A job that has a `hello` tag is executed and stuck.
+
+Example 2:
+
+1. The Runner is configured to run only tagged jobs and has the `docker` tag.
+1. A job that has a `docker` tag is executed and run.
+
+Example 3:
+
+1. The Runner is configured to run only tagged jobs and has the `docker` tag.
+1. A job that has no tags defined is executed and stuck.
+
+#### Runner is allowed to run untagged jobs
+
+The following examples illustrate the potential impact of the Runner being set
+to run tagged and untagged jobs.
+
+Example 1:
+
+1. The Runner is configured to run untagged jobs and has the `docker` tag.
+1. A job that has no tags defined is executed and run.
+1. A second job that has a `docker` tag defined is executed and run.
+
+Example 2:
+
+1. The Runner is configured to run untagged jobs and has no tags defined.
+1. A job that has no tags defined is executed and run.
+1. A second job that has a `docker` tag defined is stuck.
### Setting maximum job timeout for a Runner
diff --git a/doc/ci/ssh_keys/README.md b/doc/ci/ssh_keys/README.md
index 0c3b0bf6990..ff63d0bd69f 100644
--- a/doc/ci/ssh_keys/README.md
+++ b/doc/ci/ssh_keys/README.md
@@ -92,7 +92,7 @@ to access it. This is where an SSH key pair comes in handy.
```
NOTE: **Note:**
- The [`before_script`](../yaml/README.md#before-script) can be set globally
+ The [`before_script`](../yaml/README.md#before_script-and-after_script) can be set globally
or per-job.
1. Make sure the private server's [SSH host keys are verified](#verifying-the-ssh-host-keys).
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 7498617ed2c..ca668ac526f 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -53,6 +53,8 @@ future GitLab releases.**
| Variable | GitLab | Runner | Description |
|-------------------------------------------|--------|--------|-------------|
| **ARTIFACT_DOWNLOAD_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to download artifacts running a job |
+| **CHAT_INPUT** | 10.6 | all | Additional arguments passed in the [ChatOps](../chatops/README.md) command |
+| **CHAT_CHANNEL** | 10.6 | all | Source chat channel which triggered the [ChatOps](../chatops/README.md) command |
| **CI** | all | 0.4 | Mark that job is executed in CI environment |
| **CI_COMMIT_BEFORE_SHA** | 11.2 | all | The previous latest commit present on a branch before a push request. |
| **CI_COMMIT_DESCRIPTION** | 10.8 | all | The description of the commit: the message without first line, if the title is shorter than 100 characters; full message in other case. |
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 8c8a31e9323..e5668c140fa 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -1,7 +1,7 @@
-# Configuration of your jobs with .gitlab-ci.yml
+# Configuration of your pipelines with .gitlab-ci.yml
This document describes the usage of `.gitlab-ci.yml`, the file that is used by
-GitLab Runner to manage your project's jobs.
+GitLab Runner to manage your project's pipelines.
From version 7.12, GitLab CI uses a [YAML](https://en.wikipedia.org/wiki/YAML)
file (`.gitlab-ci.yml`) for the project configuration. It is placed in the root
@@ -96,9 +96,9 @@ This can be an array or a multi-line string.
jobs, including failed ones. This has to be an array or a multi-line string.
The `before_script` and the main `script` are concatenated and run in a single context/container.
-The `after_script` is run separately, so depending on the executor, changes done
-outside of the working tree might not be visible, e.g. software installed in the
-`before_script`.
+The `after_script` is run separately. The current working directory is set back to
+default. Depending on the executor, changes done outside of the working tree might
+not be visible, e.g. software installed in the `before_script`.
It's possible to overwrite the globally defined `before_script` and `after_script`
if you set it per-job:
@@ -1206,7 +1206,7 @@ job:
`expire_in` allows you to specify how long artifacts should live before they
expire and therefore deleted, counting from the time they are uploaded and
stored on GitLab. If the expiry time is not defined, it defaults to the
-[instance wide setting](../../user/admin_area/settings/continuous_integration.md#default-artifacts-expiration)
+[instance wide setting](../../user/admin_area/settings/continuous_integration.md#default-artifacts-expiration-core-only)
(30 days by default, forever on GitLab.com).
You can use the **Keep** button on the job page to override expiration and
@@ -1245,7 +1245,7 @@ this with [JUnit reports](#artifactsreportsjunit).
NOTE: **Note:**
The test reports are collected regardless of the job results (success or failure).
-You can use [`artifacts:expire_in`](#artifacts-expire_in) to set up an expiration
+You can use [`artifacts:expire_in`](#artifactsexpire_in) to set up an expiration
date for their artifacts.
NOTE: **Note:**
@@ -1425,7 +1425,7 @@ deploy:
> Introduced in GitLab 10.3.
If the artifacts of the job that is set as a dependency have been
-[expired](#artifacts-expire_in) or
+[expired](#artifactsexpire_in) or
[erased](../../user/project/pipelines/job_artifacts.md#erasing-artifacts), then
the dependent job will fail.
diff --git a/doc/customization/help_message.md b/doc/customization/help_message.md
new file mode 100644
index 00000000000..c2e592d03bf
--- /dev/null
+++ b/doc/customization/help_message.md
@@ -0,0 +1,13 @@
+# GitLab Help custom text
+
+In larger organizations it is useful to have information about who has the responsibility of maintaining the company GitLab server.
+
+1. Navigate to the admin area, click on **Preferences** and expand **Help page**.
+
+1. Under **Help text** fill in the required information about the person(s) administering GitLab or any other information relevant to your needs.
+
+ ![help message](help_message/help_text.png)
+
+1. After saving the page this information will be shown on the GitLab login page and on the GitLab `/help` page (e.g., <https://gitlab.com/help>).
+
+ ![help text on help page](help_message/help_text_on_help_page.png)
diff --git a/doc/customization/help_message/help_text.png b/doc/customization/help_message/help_text.png
new file mode 100644
index 00000000000..99697a106bf
--- /dev/null
+++ b/doc/customization/help_message/help_text.png
Binary files differ
diff --git a/doc/customization/help_message/help_text_on_help_page.png b/doc/customization/help_message/help_text_on_help_page.png
new file mode 100644
index 00000000000..288b4b8c1eb
--- /dev/null
+++ b/doc/customization/help_message/help_text_on_help_page.png
Binary files differ
diff --git a/doc/customization/index.md b/doc/customization/index.md
new file mode 100644
index 00000000000..71e87b3f111
--- /dev/null
+++ b/doc/customization/index.md
@@ -0,0 +1,18 @@
+---
+description: Learn how to customize GitLab's appearance for self-managed installations.
+---
+
+# Customizing GitLab's appearance **[CORE ONLY]**
+
+For GitLab self-managed instances, it's possible to customize
+a few pages.
+
+Read through the following documents to adjust GitLab's
+look and feel to meet your needs:
+
+- [Custom login page](branded_login_page.md)
+- [Custom header and email logo](branded_page_and_email_header.md)
+- [Custom favicon](favicon.md)
+- [Libravatar](libravatar.md)
+- [New project page](new_project_page.md)
+- [Custom `/help` message](help_message.md) \ No newline at end of file
diff --git a/doc/customization/libravatar.md b/doc/customization/libravatar.md
index 9bd22d3966d..18aaeb5a712 100644
--- a/doc/customization/libravatar.md
+++ b/doc/customization/libravatar.md
@@ -38,7 +38,6 @@ For example, you host a service on `http://libravatar.example.com` the `plain_ur
`http://libravatar.example.com/avatar/%{hash}?s=%{size}&d=identicon`
-
## Omnibus-gitlab example
In `/etc/gitlab/gitlab.rb`:
@@ -57,10 +56,8 @@ gitlab_rails['gravatar_enabled'] = true
gitlab_rails['gravatar_ssl_url'] = "https://seccdn.libravatar.org/avatar/%{hash}?s=%{size}&d=identicon"
```
-
Run `sudo gitlab-ctl reconfigure` for changes to take effect.
-
## Default URL for missing images
[Libravatar supports different sets](https://wiki.libravatar.org/api/) of `missing images` for emails not found on the Libravatar service.
@@ -68,7 +65,6 @@ Run `sudo gitlab-ctl reconfigure` for changes to take effect.
In order to use a different set other than `identicon`, replace `&d=identicon` portion of the URL with another supported set.
For example, you can use `retro` set in which case the URL would look like: `plain_url: "http://cdn.libravatar.org/avatar/%{hash}?s=%{size}&d=retro"`
-
## Usage examples
#### For Microsoft Office 365
diff --git a/doc/customization/welcome_message.md b/doc/customization/welcome_message.md
index 0aef0bf5abb..9194f847cdf 100644
--- a/doc/customization/welcome_message.md
+++ b/doc/customization/welcome_message.md
@@ -1,12 +1 @@
-# Customize the complete sign-in page
-
-Please see [Branded login page](branded_login_page.md)
-
-# Add a welcome message to the sign-in page (GitLab Community Edition)
-
-It is possible to add a markdown-formatted welcome message to your GitLab
-sign-in page. Users of GitLab Enterprise Edition should use the [branded login
-page feature](branded_login_page.md) instead.
-
-The welcome message (extra_sign_in_text) can now be set/changed in the Admin UI.
-Admin area > Settings
+This document was moved to [another location](branded_login_page.md).
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index e65c5f05505..63574b28edc 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -35,7 +35,7 @@ run: redis: (pid 30560) 14274s; run: log: (pid 13807) 2432047s
run: redis-exporter: (pid 30946) 14205s; run: log: (pid 13869) 2432045s
run: sidekiq: (pid 30953) 14205s; run: log: (pid 13810) 2432047s
run: unicorn: (pid 30960) 14204s; run: log: (pid 13809) 2432047s
-```
+```
### Layers
@@ -51,11 +51,11 @@ GitLab can be considered to have two layers from a process perspective:
- Omnibus configuration options
- Layer: Monitoring
-[Alert manager](https://prometheus.io/docs/alerting/alertmanager/) is a tool provided by prometheus that _"handles alerts sent by client applications such as the Prometheus server. It takes care of deduplicating, grouping, and routing them to the correct receiver integration such as email, PagerDuty, or OpsGenie. It also takes care of silencing and inhibition of alerts."_ You can read more in [issue gitlab-ce#45740](https://gitlab.com/gitlab-org/gitlab-ce/issues/45740) about what we will be alerting on.
+[Alert manager](https://prometheus.io/docs/alerting/alertmanager/) is a tool provided by prometheus that _"handles alerts sent by client applications such as the Prometheus server. It takes care of deduplicating, grouping, and routing them to the correct receiver integration such as email, PagerDuty, or OpsGenie. It also takes care of silencing and inhibition of alerts."_ You can read more in [issue gitlab-ce#45740](https://gitlab.com/gitlab-org/gitlab-ce/issues/45740) about what we will be alerting on.
### gitaly
-- [Omnibus configuration options](https://gitlab.com/gitlab-org/gitaly/tree/master/doc/configuration)
+- [Omnibus configuration options](https://gitlab.com/gitlab-org/gitaly/tree/master/doc/configuration)
- Layer: Core Service (Data)
Gitaly is a service designed by GitLab to remove our need for NFS for Git storage in distributed deployments of GitLab. (Think GitLab.com or High Availablity Deployments) As of 11.3.0, This service handles all Git level access in GitLab. You can read more about the project [in the project's readme](https://gitlab.com/gitlab-org/gitaly).
@@ -63,7 +63,7 @@ Gitaly is a service designed by GitLab to remove our need for NFS for Git storag
### gitlab-monitor
- Omnibus configuration options
-- Layer: Monitoring
+- Layer: Monitoring
GitLab Monitor is a process disigned in house that allows us to export metrics about GitLab application internals to prometheus. You can read more [in the project's readme](https://gitlab.com/gitlab-org/gitlab-monitor)
@@ -100,14 +100,14 @@ Nginx as an ingress port for all HTTP requests and routes them to the approriate
- [Omnibus configuration options](https://docs.gitlab.com/ee/administration/monitoring/prometheus/postgres_exporter.html)
- Layer: Monitoring
-[Postgres-exporter](https://github.com/wrouesnel/postgres_exporter) is the community provided Prometheus exporter that will deliver data about Postgres to prometheus for use in Grafana Dashboards.
+[Postgres-exporter](https://github.com/wrouesnel/postgres_exporter) is the community provided Prometheus exporter that will deliver data about Postgres to prometheus for use in Grafana Dashboards.
### postgresql
- [Omnibus configuration options](https://docs.gitlab.com/omnibus/settings/database.html)
- Layer: Core Service (Data)
-GitLab packages the popular Database to provide storage for Application meta data and user information.
+GitLab packages the popular Database to provide storage for Application meta data and user information.
### prometheus
@@ -121,11 +121,11 @@ Prometheus is a time-series tool that helps GitLab administrators expose metrics
- [Omnibus configuration options](https://docs.gitlab.com/omnibus/settings/redis.html)
- Layer: Core Service (Data)
-Redis is packaged to provide a place to store:
+Redis is packaged to provide a place to store:
- session data
- temporary cache information
-- background job queues.
+- background job queues.
### redis-exporter
@@ -146,7 +146,7 @@ Sidekiq is a Ruby background job processor that pulls jobs from the redis queue
- [Omnibus configuration options](https://docs.gitlab.com/omnibus/settings/unicorn.html)
- Layer: Core Service (Processor)
-[Unicorn](https://bogomips.org/unicorn/) is a Ruby application server that is used to run the core Rails Application that provides the user facing features in GitLab. Often process output you will see this as `bundle` or `config.ru` depending on the GitLab version.
+[Unicorn](https://bogomips.org/unicorn/) is a Ruby application server that is used to run the core Rails Application that provides the user facing features in GitLab. Often process output you will see this as `bundle` or `config.ru` depending on the GitLab version.
### Additional Processes
@@ -176,7 +176,6 @@ When making a request to an HTTP Endpoint (Think `/users/sign_in`) the request w
- unicorn - Since this is a web request, and it needs to access the application it will go to Unicorn.
- Postgres/Gitaly/Redis - Depending on the type of request, it may hit these services to store or retreive data.
-
### GitLab Git Request Cycle
Below we describe the different pathing that HTTP vs. SSH Git requests will take. There is some overlap with the Web Request Cycle but also some differences.
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index 4e5fb47e331..1b591c7c322 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -137,6 +137,14 @@ If a developer who happens to also be a maintainer was involved in a merge reque
as a domain expert and/or reviewer, it is recommended that they are not also picked
as the maintainer to ultimately approve and merge it.
+Try to review in a timely manner; doing so allows everyone involved in the merge
+request to iterate faster as the context is fresh in memory. Further, this
+improves contributors' experiences significantly. Reviewers should aim to review
+within two working days from the date they were assigned the merge request. If
+you don't think you'll be able to review a merge request within that time, let
+the author know as soon as possible. When the author of the merge request has not
+heard anything after two days, a new reviewer should be assigned.
+
Maintainers should check before merging if the merge request is approved by the
required approvers.
diff --git a/doc/development/database_debugging.md b/doc/development/database_debugging.md
index b2c804b2ff0..f00c5ccb9e9 100644
--- a/doc/development/database_debugging.md
+++ b/doc/development/database_debugging.md
@@ -13,7 +13,6 @@ Available `RAILS_ENV`
- `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):
@@ -36,7 +35,6 @@ If your test DB is giving you problems, it is safe to nuke it because it doesn't
- `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
Access the database via one of these commands (they all get you to the same place)
@@ -54,7 +52,6 @@ bundle exec rails db RAILS_ENV=development
- `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
### `ActiveRecord::PendingMigrationError` with Spring
diff --git a/doc/development/diffs.md b/doc/development/diffs.md
index 43fc125c21d..56e869c21f8 100644
--- a/doc/development/diffs.md
+++ b/doc/development/diffs.md
@@ -59,28 +59,24 @@ Gitlab::Git::DiffCollection.collection_limits[:safe_max_files] = Gitlab::Git::Di
File diffs will be collapsed (but be expandable) if 100 files have already been rendered.
-
```ruby
Gitlab::Git::DiffCollection.collection_limits[:safe_max_lines] = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines] = 5000
```
File diffs will be collapsed (but be expandable) if 5000 lines have already been rendered.
-
```ruby
Gitlab::Git::DiffCollection.collection_limits[:safe_max_bytes] = Gitlab::Git::DiffCollection.collection_limits[:safe_max_files] * 5.kilobytes = 500.kilobytes
```
File diffs will be collapsed (but be expandable) if 500 kilobytes have already been rendered.
-
```ruby
Gitlab::Git::DiffCollection.collection_limits[:max_files] = Commit::DIFF_HARD_LIMIT_FILES = 1000
```
No more files will be rendered at all if 1000 files have already been rendered.
-
```ruby
Gitlab::Git::DiffCollection.collection_limits[:max_lines] = Commit::DIFF_HARD_LIMIT_LINES = 50000
```
@@ -129,4 +125,3 @@ Diff Viewers, which can be found on `models/diff_viewer/*` are classes used to m
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.
-
diff --git a/doc/development/documentation/feature-change-workflow.md b/doc/development/documentation/feature-change-workflow.md
index b5b325683a3..eb479b85083 100644
--- a/doc/development/documentation/feature-change-workflow.md
+++ b/doc/development/documentation/feature-change-workflow.md
@@ -2,111 +2,178 @@
description: How to add docs for new or enhanced GitLab features.
---
-# Documentation process at GitLab
+# Documentation process for feature changes
At GitLab, developers contribute new or updated documentation along with their code, but product managers and technical writers also have essential roles in the process.
- **Developers**: Author/update documentation in the same MR as their code, and
merge it by the feature freeze for the assigned milestone. Request technical writer
-assistance if needed.
+assistance if needed. Other developers typically act as reviewers.
- **Product Managers** (PMs): In the issue for all new and enhanced features,
confirm the documentation requirements, plus the mentioned feature description
and use cases, which can be reused in docs. They can bring in a technical
-writer for discussion or help, and can be called upon themselves as a doc reviewer.
+writer for discussion or collaboration, and can be called upon themselves as a doc reviewer.
- **Technical Writers**: Review doc requirements in issues, track issues and MRs
that contain docs changes, help with any questions throughout the authoring/editing process,
-and review all new and updated docs content after it's merged (unless a pre-merge
-review request is made).
+work on special projects related to the documentation, and review all new and updated
+docs content, whether before or after it is merged.
-Beyond this process, any member of the GitLab community can also author documentation
+Beyond this process, any member of the GitLab community can also author or request documentation
improvements that are not associated with a new or changed feature. See the [Documentation improvement workflow](improvement-workflow.md).
## When documentation is required
Documentation must be delivered whenever:
-- A new or enhanced feature is shipped that impacts the user/admin experience
-- There are changes to the UI or API
-- A process, workflow, or previously documented feature is changed
-- A feature is deprecated or removed
+- A new or enhanced feature is shipped that impacts the user/admin experience.
+- There are changes to the UI or API.
+- A process, workflow, or previously documented feature is changed.
+- A feature is deprecated or removed.
+
+For example, a UI restyling that offers no difference in functionality may require
+documentation updates if screenshots are now needed, or need to be updated.
Documentation is not required when a feature is changed on the backend
-only and does not directly affect the way that any user or
-administrator would interact with GitLab. For example, a UI restyling that offers
-no difference in functionality may require documentation updates if screenshots
-are now needed, or need to be updated.
+only and does not directly affect the way that any user or administrator would
+interact with GitLab.
NOTE: **Note:**
When revamping documentation, if unrelated to the feature change, this should be submitted
in its own MR (using the [documentation improvement workflow](improvement-workflow.md))
so that we can ensure the more time-sensitive doc updates are merged with code by the freeze.
+## Documentation requirements in feature issues
+
+Requirements for the documentation of a feature should be included as part of the
+issue for planning that feature, in a Documentation section within the issue description.
+
+This section is provided as part of the Feature Proposal template and should be added
+to the issue if it is not already present.
+
+Anyone can add these details, but the product manager who assigns the issue to a specific release
+milestone will ensure these details are present and finalized by the time of that milestone's kickoff.
+Developers, technical writers, and others may help further refine this plan at any time.
+
+### Details to include
+
+- What concepts and procedures should the docs guide and enable the user to understand or accomplish?
+- To this end, what new page(s) are needed, if any? What pages/subsections need updates? Consider user, admin, and API doc changes and additions.
+- For any guide or instruction set, should it help address a single use case, or be flexible to address a certain range of use cases?
+- Do we need to update a previously recommended workflow? Should we link the new feature from various relevant locations? Consider all ways documentation should be affected.
+- Are there any key terms or task descriptions that should be included so that the docs are found in relevant searches?
+- Include suggested titles of any pages or subsections, if applicable.
+
## Documenting a new or changed feature
To follow a consistent workflow every month, documentation changes
involve the Product Managers, the developer who shipped the feature,
-and the Technical Writing team. Each role is described below.
+and the technical writer for the DevOps stage. Each role is described below.
+
+The Documentation items in the GitLab CE/EE [Feature Proposal issue template](https://gitlab.com/gitlab-org/gitlab-ce/raw/template-improvements-for-documentation/.gitlab/issue_templates/Feature%20proposal.md)
+and default merge request template will assist you with following this process.
+
+### Product Manager role
-### 1. Product Manager's role
+For issues requiring any new or updated documentation, the Product Manager (PM)
+must:
-The Product Manager (PM) should confirm or add the following items in the issue:
+- Add the `Documentation` label.
+- Confirm or add the [documentation requirements](#documentation-requirements).
+- Ensure the issue contains any new or updated feature name, overview/description,
+and use cases, as required per the [documentation structure and template](structure.md), when applicable.
-- New or updated feature name, overview/description, and use cases, all required per the [Documentation structure and template](structure.md).
-- The documentation requirements for the developer working on the docs.
- - What new page, new subsection of an existing page, or other update to an existing page/subsection is needed.
- - Just one page/section/update or multiple (perhaps there's an end user and admin change needing docs, or we need to update a previously recommended workflow, or we want to link the new feature from various places; consider and mention all ways documentation should be affected.
- - Suggested title of any page or subsection, if applicable.
-- Label the issue with `Documentation` and `docs:P1` in addition to the `Deliverable` label and correct milestone.
+Everyone is encouraged to draft the requirements in the issue, but a product manager will
+do the following:
-Anyone is welcome to draft the items above in the issue, but a product manager must review and update them whenever the issue is assigned a specific milestone.
+- When the issue is assigned a release milestone, review and update the Documentation details.
+- By the kickoff, finalizie the Documentation details.
-### 2. Developer's role
+### Developer and maintainer roles
+
+#### Authoring
As a developer, you must ship the documentation with the code of the feature that
you are creating or updating. The documentation is an essential part of the product.
+Technical writers are happy to help, as requested and planned on an issue-by-issue basis.
+
+Follow the process below unless otherwise agreed with the product manager and technical writer for a given issue:
-- New and edited docs should be included in the MR introducing the code, and planned
-in the issue that proposed the feature. However, if the new or changed doc requires
-extensive collaboration or conversation, a separate, linked issue can be used for the planning process.
+- Include any new and edited docs in the MR introducing the code.
+- Use the Documentation requirements confirmed by the Product Manager in the
+issue and discuss any further doc plans or ideas as needed.
+ - If the new or changed doc requires extensive collaboration or conversation, a separate,
+linked issue can be used for the planning process.
+ - We are trying to avoid using a separate MR, so that the docs stay with the code, but the
+Technical Writing team is interested in discussing any potential exceptions that may be suggested.
- Use the [Documentation guidelines](index.md), as well as other resources linked from there,
-including the [Structure and template](structure.md) page, [Style Guide](styleguide.md), and [Markdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/).
+including the Documentation [Structure and template](structure.md) page, [Style Guide](styleguide.md), and [Markdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/).
- If you need any help to choose the correct place for a doc, discuss a documentation
idea or outline, or request any other help, ping the Technical Writer for the relevant
[DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages)
in your issue or MR, or write within `#docs` on the GitLab Slack.
- The docs must be merged with the code **by the feature freeze date**, otherwise
-- the feature cannot be included with the release.<!-- TODO: Policy/process for feature-flagged issues -->
+the feature cannot be included with the release. A policy for documenting feature-flagged
+issues is forthcoming and you are welcome to join the [discussion](https://gitlab.com/gitlab-org/gitlab-ce/issues/56813).
-Prior to merge, documentation changes commited by the developer must be reviewed by:
-* the person reviewing the code and merging the MR.
-* optionally: others involved in the work (such as other devs, the PM, or a technical writer), if requested.
+#### Reviews and merging
-After merging, documentation changing are reviewed by:
-* a technical writer (for clarity, structure, grammar, etc).
-* optionally: by the PM (for accuracy and to ensure it's consistent with the vision for how the product will be used).
+All reviewers can help ensure accuracy, clarity, completeness, and adherence to the plans in the issue, as well as the [Documentation Guidelines](https://docs.gitlab.com/ee/development/documentation/) and [Style Guide](https://docs.gitlab.com/ee/development/documentation/styleguide.html).
+
+- **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.
+
+- 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 mainitainer 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).
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.
-### 3. Technical Writer's role
+### Technical Writer role
+
+#### Planning
+
+- The technical writer monitors the documentation needs of issues assigned to the current and next milestone
+for their DevOps stage(s), and participates in any needed discussion on docs planning and requirements refinement
+with the dev, PM, and others.
+- The technical writer will review these requirements again upon the kickoff and provide feedback, as needed.
+This is not a blocking review and developers should not wait to work on docs.
+
+#### Collaboration
+
+By default, the developer will work on documentation changes independently, but
+the developer, PM, or technicial writer can propose a broader collaboration for
+any given issue.
+
+Additionally, technical writers are available for questions at any time.
-**Planning**
-- Once an issue contains a Documentation label and an upcoming milestone, a
-technical writer reviews the listed documentation requirements, which should have
-already been reviewed by the PM. (These are non-blocking reviews; developers should
-not wait to work on docs.)
-- Monitor the documentation needs of issues assigned to the current and next milestone,
-and participate in any needed discussion on docs planning with the dev, PM, and others.
+#### Review
-**Review**
- Techncial writers provide non-blocking reviews of all documentation changes,
-typically after the change is merged. However, if the docs are ready in the MR while
-we are awaiting other work in order to merge, the technical writer's review can commence early.
+before or after the change is merged. However, if the docs are ready in the MR while
+there's time before the freeze, the technical writer's review can commence early, on request.
- The technical writer will confirm that the doc is clear, grammatically correct,
and discoverable, while avoiding redundancy, bad file locations, typos, broken links,
etc. The technical writer will review the documentation for the following, which
the developer and code reviewer should have already made a good-faith effort to ensure:
- Clarity.
- - Relevance (make sure the content is appropriate given the impact of the feature).
- - Location (make sure the doc is in the correct dir and has the correct name).
+ - Adherence to the plans and goals in the issue.
+ - Location (make sure the docs are in the correct directorkes and has the correct name).
- Syntax, typos, and broken links.
- Improvements to the content.
- - Accordance to the [Documentation Style Guide](styleguide.md) and [structure/template](structure.md).
+ - Accordance with the [Documentation Style Guide](styleguide.md), and [Structure and Template](structure.md) doc.
diff --git a/doc/development/documentation/improvement-workflow.md b/doc/development/documentation/improvement-workflow.md
index ef6392c6f7f..c97d8966b8c 100644
--- a/doc/development/documentation/improvement-workflow.md
+++ b/doc/development/documentation/improvement-workflow.md
@@ -9,26 +9,30 @@ Anyone can contribute a merge request or create an issue for GitLab's documentat
This page covers the process for any contributions to GitLab's docs that are
not part of feature development. If you are looking for information on updating
GitLab's docs as is required with the development and release of a new feature
-or feature enhancement, see the [feature-change documentation workflow](feature-change-workflow.md).
+or feature enhancement, see the [documentation process for feature changes](feature-change-workflow.md).
## Who updates the docs
Anyone can contribute! You can create a merge request with documentation
when you find errors or other room for improvement in an existing doc, or when you
-have an idea for all-new documentation that would help a GitLab user or admin
-to achieve or improve their DevOps workflows.
+have an idea for all-new documentation that would help a GitLab user or administrator
+to accomplish their work with GitLab.
## How to update the docs
-- Follow the described standards and processes listed on the [GitLab Documentation guidelines](index.md) page,
-including linked resources: the [Structure and template](structure.md) page, [Style Guide](styleguide.md), and [Markdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/).
-- Follow GitLab's [Merge Request Guidelines](../contributing/merge_request_workflow.md#merge-request-guidelines).
-- If you need any help to choose the correct place for a doc, discuss a documentation
+1. Click "Edit this Page" at the bottom of any page on docs.gitlab.com, or navigate to
+one of the repositories and doc paths listed on the [GitLab Documentation guidelines](index.md) page.
+Work in a fork if you do not have developer access to the GitLab project.
+1. Follow the described standards and processes listed on that Guidelines page,
+including the linked resources: the [Structure and template](structure.md) page, [Style Guide](styleguide.md), and [Markdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/).
+1. Follow GitLab's [Merge Request Guidelines](../contributing/merge_request_workflow.md#merge-request-guidelines).
+
+If you need any help to choose the correct place for a doc, discuss a documentation
idea or outline, or request any other help, ping the Technical Writer for the relevant
[DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages)
in your issue or MR, or write within `#docs` if you are a member of GitLab's Slack workspace.
-## Merging
+## Review and merging
Anyone with master access to the affected GitLab project can merge documentation changes.
This person must make a good-faith effort to ensure that the content is clear
@@ -38,12 +42,22 @@ that it meets the [Documentation Guidelines](index.md) and [Style Guide](stylegu
If the author or reviewer has any questions, or would like a techncial writer's review
before merging, mention the writer who is assigned to the relevant [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages).
-## Technical Writer review
+The process can involve the following parties/phases, and is replicated in the `Documentation` MR template for GitLab CE and EE, to help with following the process.
+
+**1. Primary Reviewer** - Review by a [code reviewer](https://about.gitlab.com/handbook/engineering/projects/) or other appropriate colleague to confirm accuracy, clarity, and completeness. This can be skipped for minor fixes without substantive content changes.
+
+**2. Technical Writer** - Optional - If not requested for this MR, must be scheduled post-merge. To request a pre-merge review, assign the writer listed for the applicable [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages).
+To request a post-merge 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.
+
+**3. Maintainer**
-The technical writing team reviews changes after they are merged, unless a prior
-review is requested.
+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.
## Other ways to help
If you have ideas for further documentation resources that would be best
-considered/handled by technical writers, devs, and other SMEs, please create an issue.
+considered/handled by technical writers, devs, and other SMEs, please [create an issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issuable_template=Documentation)
+using the Documentation template.
diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md
index 287a472d0d8..652fe3ea711 100644
--- a/doc/development/documentation/index.md
+++ b/doc/development/documentation/index.md
@@ -10,8 +10,8 @@ In addition to this page, the following resources to help craft and contribute d
- [Style Guide](styleguide.md) - What belongs in the docs, language guidelines, and more.
- [Structure and template](structure.md) - Learn the typical parts of a doc page and how to write each one.
-- [Workflow](workflow.md) - A landing page for our key workflows:
- - [Feature-change documentation workflow](feature-change-workflow.md) - Adding required documentation when developing a GitLab feature.
+- [Workflows](workflow.md) - A landing page for our key workflows:
+ - [Documentation process for feature changes](feature-change-workflow.md) - Adding required documentation when developing a GitLab feature.
- [Documentation improvement workflow](improvement-workflow.md) - New content not associated with a new feature.
- [Markdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/) - A reference for the markdown implementation used by GitLab's documentation site and about.gitlab.com.
- [Site architecture](site_architecture/index.md) - How docs.gitlab.com is built.
diff --git a/doc/development/documentation/site_architecture/global_nav.md b/doc/development/documentation/site_architecture/global_nav.md
index 62ca7d6c805..0aa3c41a225 100644
--- a/doc/development/documentation/site_architecture/global_nav.md
+++ b/doc/development/documentation/site_architecture/global_nav.md
@@ -234,7 +234,7 @@ Examples:
```yaml
- category_title: Issues
category_url: 'user/project/issues/'
- # note that the above URL does not start with a slash and
+ # note that the above URL does not start with a slash and
# does not include index.html at the end
docs:
@@ -295,7 +295,6 @@ On the other hand, if the user is looking at `/ce/` docs,
all the links in the CE nav should link internally to `/ce/`
files, except for [`ee-only` docs](#ee-only-docs).
-
```html
<% if dir != 'ce' %>
<a href="/ee/<%= sec[:section_url] %>">...</a>
diff --git a/doc/development/documentation/workflow.md b/doc/development/documentation/workflow.md
index 7c32c92b147..0abfe4b82a4 100644
--- a/doc/development/documentation/workflow.md
+++ b/doc/development/documentation/workflow.md
@@ -2,9 +2,9 @@
description: Learn the processes for contributing to GitLab's documentation.
---
-# Documentation workflows at GitLab
+# Documentation workflows
-Documentation workflows at GitLab differ depending on the reason for the change. The two types of documentation changes are:
+Documentation workflows at GitLab differ depending on the reason for the change:
-- [Feature-change documentation workflow](feature-change-workflow.md) - The documentation is being created or updated as part of the development and release of a new or enhanced feature. This process involves the developer of the feature (who includes new/updated documentation files as part of the same merge request containing the feature's code) and also involves the product manager and technical writer who are listed for the feature's [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages).
+- [Documentation process for feature changes](feature-change-workflow.md) - The documentation is being created or updated as part of the development and release of a new or enhanced feature. This process involves the developer of the feature (who includes new/updated documentation files as part of the same merge request containing the feature's code) and also involves the product manager and technical writer who are listed for the feature's [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages).
- [Documentation improvement workflow](improvement-workflow.md) - All documentation additions not associated with a feature release. Documentation is being created or updated to improve accuracy, completeness, ease of use, or any reason other than a feature change. Anyone (and everyone) can contribute a merge request for this type of change at any time.
diff --git a/doc/development/fe_guide/accessibility.md b/doc/development/fe_guide/accessibility.md
index 366b220cbb2..df32242a522 100644
--- a/doc/development/fe_guide/accessibility.md
+++ b/doc/development/fe_guide/accessibility.md
@@ -8,6 +8,5 @@ are useful for testing for potential accessibility problems in GitLab.
Accessibility best-practices and more in-depth information is available on
[the Audit Rules page][audit-rules] for the Chrome Accessibility Developer Tools.
-
[chrome-accessibility-developer-tools]: https://github.com/GoogleChrome/accessibility-developer-tools
[audit-rules]: https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules
diff --git a/doc/development/fe_guide/design_patterns.md b/doc/development/fe_guide/design_patterns.md
index e05887a19af..0342d16a87c 100644
--- a/doc/development/fe_guide/design_patterns.md
+++ b/doc/development/fe_guide/design_patterns.md
@@ -74,5 +74,4 @@ new Foo({ container: '.my-element' });
```
You can find an example of the above in this [class][container-class-example];
-
[container-class-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/mini_pipeline_graph_dropdown.js
diff --git a/doc/development/fe_guide/droplab/droplab.md b/doc/development/fe_guide/droplab/droplab.md
index e6aa0be671f..2f8c79abde1 100644
--- a/doc/development/fe_guide/droplab/droplab.md
+++ b/doc/development/fe_guide/droplab/droplab.md
@@ -90,7 +90,6 @@ const list = document.getElementById('list');
droplab.addHook(trigger, list);
```
-
### Dynamic data
Adding `data-dynamic` to your dropdown element will enable dynamic list rendering.
diff --git a/doc/development/fe_guide/droplab/plugins/input_setter.md b/doc/development/fe_guide/droplab/plugins/input_setter.md
index 8e28a41f32e..e229103e462 100644
--- a/doc/development/fe_guide/droplab/plugins/input_setter.md
+++ b/doc/development/fe_guide/droplab/plugins/input_setter.md
@@ -13,7 +13,6 @@ to update the `input` element with.
You can also set the `InputSetter` config to an array of objects, which will allow you to update multiple elements.
-
```html
<input id="input" value="">
<div id="div" data-selected-id=""></div>
diff --git a/doc/development/fe_guide/performance.md b/doc/development/fe_guide/performance.md
index e5a383c25f5..0aba38aca8c 100644
--- a/doc/development/fe_guide/performance.md
+++ b/doc/development/fe_guide/performance.md
@@ -169,7 +169,6 @@ General tips:
- [Profiling with Chrome DevTools][google-devtools-profiling]
- [Browser Diet][browser-diet] is a community-built guide that catalogues practical tips for improving web page performance.
-
[web-page-test]: http://www.webpagetest.org/
[pagespeed-insights]: https://developers.google.com/speed/pagespeed/insights/
[google-devtools-profiling]: https://developers.google.com/web/tools/chrome-devtools/profile/?hl=en
diff --git a/doc/development/fe_guide/security.md b/doc/development/fe_guide/security.md
index 19e72c1d368..83bb449e54d 100644
--- a/doc/development/fe_guide/security.md
+++ b/doc/development/fe_guide/security.md
@@ -77,7 +77,6 @@ Inline styles should be avoided in almost all cases, they should only be used
when no alternatives can be found. This allows reusability of styles as well as
readability.
-
[observatory-cli]: https://github.com/mozilla/http-observatory-cli
[qualys-ssl]: https://www.ssllabs.com/ssltest/analyze.html
[secure_headers]: https://github.com/twitter/secureheaders
diff --git a/doc/development/fe_guide/vue.md b/doc/development/fe_guide/vue.md
index 9c614e3468a..60f322c6e75 100644
--- a/doc/development/fe_guide/vue.md
+++ b/doc/development/fe_guide/vue.md
@@ -229,7 +229,6 @@ One should apply to be a Vue.js expert by opening an MR when the Merge Request's
- Vuex code follows the [documented pattern](./vuex.md#actions-pattern-request-and-receive-namespaces)
- Knowledge about the existing Vue and Vuex applications and existing reusable components
-
[vue-docs]: http://vuejs.org/guide/index.html
[issue-boards]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/assets/javascripts/boards
[environments-table]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/assets/javascripts/environments
diff --git a/doc/development/feature_flags.md b/doc/development/feature_flags.md
index b6161cd6163..67356a12eba 100644
--- a/doc/development/feature_flags.md
+++ b/doc/development/feature_flags.md
@@ -124,4 +124,3 @@ Feature.enable(:feature_flag_name)
## Enabling a feature flag (in production)
Check how to [roll out changes using feature flags](rolling_out_changes_using_feature_flags.md).
-
diff --git a/doc/development/file_storage.md b/doc/development/file_storage.md
index 597812c8c49..18e4dc2ca0c 100644
--- a/doc/development/file_storage.md
+++ b/doc/development/file_storage.md
@@ -20,7 +20,6 @@ There are many places where file uploading is used, according to contexts:
- LFS Objects
- Merge request diffs
-
## Disk storage
GitLab started saving everything on local disk. While directory location changed from previous versions,
diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md
index ac910e80a89..6054873cb46 100644
--- a/doc/development/i18n/proofreader.md
+++ b/doc/development/i18n/proofreader.md
@@ -85,7 +85,7 @@ are very appreciative of the work done by translators and proofreaders!
- Spanish
- Pedro Garcia - [GitLab](https://gitlab.com/pedgarrod), [Crowdin](https://crowdin.com/profile/breaking_pitt)
- Turkish
- - Proofreaders needed.
+ - Ali DemirtaÅŸ - [GitLab](https://gitlab.com/alidemirtas), [Crowdin](https://crowdin.com/profile/alidemirtas)
- Ukrainian
- Volodymyr Sobotovych - [GitLab](https://gitlab.com/wheleph), [Crowdin](https://crowdin.com/profile/wheleph)
- Andrew Vityuk - [GitLab](https://gitlab.com/3_1_3_u), [Crowdin](https://crowdin.com/profile/andruwa13)
diff --git a/doc/development/import_export.md b/doc/development/import_export.md
index 71db1abb201..e2108605d54 100644
--- a/doc/development/import_export.md
+++ b/doc/development/import_export.md
@@ -60,12 +60,12 @@ class StuckImportJobsWorker
Marked stuck import jobs as failed. JIDs: xyz
```
-```
+```
+-----------+ +-----------------------------------+
|Export Job |--->| Calls ActiveRecord `as_json` and |
+-----------+ | `to_json` on all project models |
+-----------------------------------+
-
+
+-----------+ +-----------------------------------+
|Import Job |--->| Loads all JSON in memory, then |
+-----------+ | inserts into the DB in batches |
@@ -109,13 +109,13 @@ The `AttributeCleaner` removes any prohibited keys:
# Removes all `_ids` and other prohibited keys
class AttributeCleaner
ALLOWED_REFERENCES = RelationFactory::PROJECT_REFERENCES + RelationFactory::USER_REFERENCES + ['group_id']
-
+
def clean
@relation_hash.reject do |key, _value|
prohibited_key?(key) || !@relation_class.attribute_method?(key) || excluded_key?(key)
end.except('id')
end
-
+
...
```
@@ -133,7 +133,7 @@ The `AttributeConfigurationSpec` checks and confirms the addition of new columns
SAFE_MODEL_ATTRIBUTES: #{File.expand_path(safe_attributes_file)}
IMPORT_EXPORT_CONFIG: #{Gitlab::ImportExport.config_file}
-MSG
+MSG
```
The `ModelConfigurationSpec` checks and confirms the addition of new models:
@@ -157,7 +157,7 @@ The `ExportFileSpec` detects encrypted or sensitive columns:
```ruby
# ExportFileSpec
<<-MSG
- Found a new sensitive word <#{key_found}>, which is part of the hash #{parent.inspect}
+ Found a new sensitive word <#{key_found}>, which is part of the hash #{parent.inspect}
If you think this information shouldn't get exported, please exclude the model or attribute in
IMPORT_EXPORT_CONFIG.
@@ -214,7 +214,6 @@ We do not need to bump the version up in any of the following cases:
- Remove a column or model (unless there is a DB constraint)
- Export new things (such as a new type of upload)
-
Every time we bump the version, the integration specs will fail and can be fixed with:
```bash
@@ -231,7 +230,7 @@ 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
+2. Remove it from `RelationRenameService` in X.Y + 1
3. Bump Import/Export version in X.Y + 1
```ruby
@@ -270,8 +269,8 @@ included_attributes:
user:
- :id
- :email
- ...
-
+ ...
+
```
Do not include the following attributes for the models specified:
@@ -319,7 +318,7 @@ module Gitlab
ensure
remove_import_file
end
-
+
def restorers
[repo_restorer, wiki_restorer, project_tree, avatar_restorer,
uploads_restorer, lfs_restorer, statistics_restorer]
@@ -346,7 +345,7 @@ module Projects
end
def save_services
- [version_saver, avatar_saver, project_tree_saver, uploads_saver, repo_saver,
+ [version_saver, avatar_saver, project_tree_saver, uploads_saver, repo_saver,
wiki_repo_saver, lfs_saver].all?(&:save)
end
```
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index 98b54684d39..bb40c0d32b4 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -48,7 +48,6 @@ various database operations, such as
and whether they require downtime and how to work around that whenever possible.
-
## Downtime Tagging
Every migration must specify if it requires downtime or not, and if it should
diff --git a/doc/development/new_fe_guide/development/testing.md b/doc/development/new_fe_guide/development/testing.md
index d74f141f08f..8441089418e 100644
--- a/doc/development/new_fe_guide/development/testing.md
+++ b/doc/development/new_fe_guide/development/testing.md
@@ -339,7 +339,6 @@ afterEach(() => {
Some regressions only affect a specific browser version. We can install and test in particular browsers with either Firefox or Browserstack using the following steps:
-
### Browserstack
[Browserstack](https://www.browserstack.com/) allows you to test more than 1200 mobile devices and browsers.
diff --git a/doc/development/performance.md b/doc/development/performance.md
index 972c93be817..2a287611bdf 100644
--- a/doc/development/performance.md
+++ b/doc/development/performance.md
@@ -336,7 +336,6 @@ the same method won't end up retrieving data from Redis upon every call. When
memoizing cached data in an instance variable, make sure to also reset the
instance variable when flushing the cache. An example:
-
```ruby
def first_branch
@first_branch ||= cache.fetch(:first_branch) { branches.first }
@@ -411,6 +410,21 @@ there's nothing stopping somebody from doing this elsewhere in the code:
SOME_CONSTANT = 'bar'
```
+## How to seed a database with millions of rows
+
+You might want millions of project rows in your local database, for example,
+in order to compare relative query performance, or to reproduce a bug. You could
+do this by hand with SQL commands, but since you have ActiveRecord models, you
+might find using these gems more convenient:
+
+- [BulkInsert gem](https://github.com/jamis/bulk_insert)
+- [ActiveRecord::PgGenerateSeries gem](https://github.com/ryu39/active_record-pg_generate_series)
+
+### Examples
+
+You may find some useful examples in this snippet:
+https://gitlab.com/gitlab-org/gitlab-ce/snippets/33946
+
[#15607]: https://gitlab.com/gitlab-org/gitlab-ce/issues/15607
[yorickpeterse]: https://gitlab.com/yorickpeterse
[anti-pattern]: https://en.wikipedia.org/wiki/Anti-pattern
diff --git a/doc/development/policies.md b/doc/development/policies.md
index 97424d90fb5..6a3b90f3604 100644
--- a/doc/development/policies.md
+++ b/doc/development/policies.md
@@ -8,7 +8,6 @@ The policy used is based on the subject's class name - so `Ability.allowed?(user
Permissions are broken into two parts: `conditions` and `rules`. Conditions are boolean expressions that can access the database and the environment, while rules are statically configured combinations of expressions and other rules that enable or prevent certain abilities. For an ability to be allowed, it must be enabled by at least one rule, and not prevented by any.
-
### Conditions
Conditions are defined by the `condition` method, and are given a name and a block. The block will be executed in the context of the policy object - so it can access `@user` and `@subject`, as well as call any methods defined on the policy. Note that `@user` may be nil (in the anonymous case), but `@subject` is guaranteed to be a real instance of the subject class.
@@ -66,7 +65,51 @@ Within the rule DSL, you can use:
To see how the rules get evaluated into a judgment, it is useful in a console to use `policy.debug(:some_ability)`. This will print the rules in the order they are evaluated.
-When a policy is asked whether a particular ability is allowed (`policy.allowed?(:some_ability)`), it does not necessarily have to compute all the conditions on the policy. First, only the rules relevant to that particular ability are selected. Then, the execution model takes advantage of short-circuiting, and attempts to sort rules based on a heuristic of how expensive they will be to calculate. The sorting is dynamic and cache-aware, so that previously calculated conditions will be considered first, before computing other conditions.
+For example, let's say you wanted to debug `IssuePolicy`. You might run
+the debugger in this way:
+
+```ruby
+user = User.find_by(username: 'john')
+issue = Issue.first
+policy = IssuePolicy.new(user, issue)
+policy.debug(:read_issue)
+```
+
+An example debug output would look as follows:
+
+```ruby
+- [0] prevent when all?(confidential, ~can_read_confidential) ((@john : Issue/1))
+- [0] prevent when archived ((@john : Project/4))
+- [0] prevent when issues_disabled ((@john : Project/4))
+- [0] prevent when all?(anonymous, ~public_project) ((@john : Project/4))
++ [32] enable when can?(:reporter_access) ((@john : Project/4))
+```
+
+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
+and subject for that rule.
+
+Here you can see that the first four rules were evaluated `false` for
+which user and subject. For example, you can see in the last line that
+the rule was activated because the user `root` had at reporter access to
+the `Project/4`.
+
+When a policy is asked whether a particular ability is allowed
+(`policy.allowed?(:some_ability)`), it does not necessarily have to
+compute all the conditions on the policy. First, only the rules relevant
+to that particular ability are selected. Then, the execution model takes
+advantage of short-circuiting, and attempts to sort rules based on a
+heuristic of how expensive they will be to calculate. The sorting is
+dynamic and cache-aware, so that previously calculated conditions will
+be considered first, before computing other conditions.
+
+Note that the score is chosen by a developer via the `score:` parameter
+in a `condition` to denote how expensive evaluating this rule would be
+relative to other rules.
## Scope
diff --git a/doc/development/profiling.md b/doc/development/profiling.md
index b7d9f640a3f..f41d635de43 100644
--- a/doc/development/profiling.md
+++ b/doc/development/profiling.md
@@ -74,7 +74,6 @@ Gitlab::Profiler.print_by_total_time(result, max_percent: 60, min_percent: 2)
To print the profile in HTML format, use the following example:
-
```ruby
result = Gitlab::Profiler.profile('/my-user')
@@ -92,7 +91,6 @@ Redash to Looker](https://gitlab.com/gitlab-com/Product/issues/5#note_121194467)
We are [currently investigating how to make this data
public](https://gitlab.com/meltano/looker/issues/294).
-
## Sherlock
Sherlock is a custom profiling tool built into GitLab. Sherlock is _only_
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index ae9bf863419..4cc500ed1b6 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -151,7 +151,6 @@ following:
bundle exec rake gemojione:digests
```
-
This will update the file `fixtures/emojis/digests.json` based on the currently
available Emoji.
diff --git a/doc/development/shell_commands.md b/doc/development/shell_commands.md
index 73893f9dd46..7bdf676be58 100644
--- a/doc/development/shell_commands.md
+++ b/doc/development/shell_commands.md
@@ -190,7 +190,7 @@ A check like this could have avoided CVE-2013-4583.
## Properly anchor regular expressions to the start and end of strings
-When using regular expressions to validate user input that is passed as an argument to a shell command, make sure to use the `\A` and `\z` anchors that designate the start and end of the string, rather than `^` and `$`, or no anchors at all.
+When using regular expressions to validate user input that is passed as an argument to a shell command, make sure to use the `\A` and `\z` anchors that designate the start and end of the string, rather than `^` and `$`, or no anchors at all.
If you don't, an attacker could use this to execute commands with potentially harmful effect.
@@ -198,7 +198,7 @@ For example, when a project's `import_url` is validated like below, the user cou
```ruby
validates :import_url, format: { with: URI.regexp(%w(ssh git http https)) }
-# URI.regexp(%w(ssh git http https)) roughly evaluates to /(ssh|git|http|https):(something_that_looks_like_a_url)/
+# URI.regexp(%w(ssh git http https)) roughly evaluates to /(ssh|git|http|https):(something_that_looks_like_a_url)/
```
Suppose the user submits the following as their import URL:
@@ -211,7 +211,6 @@ Since there are no anchors in the used regular expression, the `git:/tmp/lol` in
When importing, GitLab would execute the following command, passing the `import_url` as an argument:
-
```sh
git clone file://git:/tmp/lol
```
diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md
index 4cc3812b0f0..2c8d488877b 100644
--- a/doc/development/testing_guide/best_practices.md
+++ b/doc/development/testing_guide/best_practices.md
@@ -358,16 +358,11 @@ range of inputs, might look like this:
describe "#==" do
using RSpec::Parameterized::TableSyntax
- let(:project1) { create(:project) }
- let(:project2) { create(:project) }
where(:a, :b, :result) do
1 | 1 | true
1 | 2 | false
true | true | true
true | false | false
- project1 | project1 | true
- project2 | project2 | true
- project 1 | project2 | false
end
with_them do
@@ -380,6 +375,11 @@ describe "#==" do
end
```
+CAUTION: **Caution:**
+Only use simple values as input in the `where` block. Using procs, stateful
+objects, FactoryBot-created objects etc. can lead to
+[unexpected results](https://github.com/tomykaira/rspec-parameterized/issues/8).
+
### Prometheus tests
Prometheus metrics may be preserved from one test run to another. To ensure that metrics are
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index 6012f1080ab..0470a071d39 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -141,14 +141,15 @@ module. GitLab has a custom `spyOnDependency` method which utilizes
[babel-plugin-rewire](https://github.com/speedskater/babel-plugin-rewire) to
achieve this. It can be used like so:
-```javascript
+```js
// my_module.js
import { visitUrl } from '~/lib/utils/url_utility';
export default function doSomething() {
visitUrl('/foo/bar');
}
-
+```
+```js
// my_module_spec.js
import doSomething from '~/my_module';
diff --git a/doc/development/testing_guide/review_apps.md b/doc/development/testing_guide/review_apps.md
index 85b47f88cc4..703e342fc13 100644
--- a/doc/development/testing_guide/review_apps.md
+++ b/doc/development/testing_guide/review_apps.md
@@ -70,9 +70,8 @@ subgraph GCP `gitlab-review-apps` project
you get a dedicated environment for your branch that's very close to what
it would look in production.
1. Once the [`review-deploy`][review-deploy] job succeeds, you should be able to
- use your Review App thanks to the direct link to it from the MR widget. The
- default username is `root` and its password can be found in the 1Password
- secure note named **gitlab-{ce,ee} Review App's root password**.
+ use your Review App thanks to the direct link to it from the MR widget. To log
+ into the Review App, see "Log into my Review App?" below.
**Additional notes:**
@@ -98,6 +97,17 @@ Note that both jobs first wait for the `review-deploy` job to be finished.
## How to?
+### Log into my Review App?
+
+The default username is `root` and its password can be found in the 1Password
+secure note named **gitlab-{ce,ee} Review App's root password**.
+
+### Enable a feature flag for my Review App?
+
+1. Open your Review App and log in as documented above.
+1. Create a personal access token.
+1. Enable the feature flag using the [Feature flag API](../../api/features.md).
+
### Find my Review App slug?
1. Open the `review-deploy` job.
diff --git a/doc/development/testing_guide/testing_levels.md b/doc/development/testing_guide/testing_levels.md
index 070b6477a7a..a7a3459719b 100644
--- a/doc/development/testing_guide/testing_levels.md
+++ b/doc/development/testing_guide/testing_levels.md
@@ -159,6 +159,10 @@ Every new feature should come with a [test plan].
> See [end-to-end tests](end_to_end_tests.md) for more information.
+Note that `qa/spec` contains unit tests of the QA framework itself, not to be
+confused with the application's [unit tests](#unit-tests) or
+[end-to-end tests](#black-box-tests-at-the-system-level-aka-end-to-end-tests).
+
[multiple pieces]: ../architecture.md#components
[GitLab Shell]: https://gitlab.com/gitlab-org/gitlab-shell
[GitLab Workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse
diff --git a/doc/gitlab-basics/create-your-ssh-keys.md b/doc/gitlab-basics/create-your-ssh-keys.md
index 881629c3bfd..8c2f48fb1e2 100644
--- a/doc/gitlab-basics/create-your-ssh-keys.md
+++ b/doc/gitlab-basics/create-your-ssh-keys.md
@@ -27,7 +27,6 @@
![SSH key single page](img/profile_settings_ssh_keys_single_key.png)
-
>**Note:**
Once you add a key, you cannot edit it, only remove it. In case the paste
didn't work, you will have to remove the offending key and re-add it.
diff --git a/doc/install/aws/index.md b/doc/install/aws/index.md
index e209a00b38c..51d2a232dc0 100644
--- a/doc/install/aws/index.md
+++ b/doc/install/aws/index.md
@@ -471,7 +471,6 @@ gitlab_rails['redis_port'] = 6379
Finally, reconfigure GitLab for the change to take effect:
-
```sh
sudo gitlab-ctl reconfigure
```
diff --git a/doc/install/azure/index.md b/doc/install/azure/index.md
index 19a6e46f969..be14858f4c8 100644
--- a/doc/install/azure/index.md
+++ b/doc/install/azure/index.md
@@ -1,5 +1,5 @@
---
-description: 'Learn how to spin up a
+description: 'Learn how to spin up a
pre-configured GitLab VM on Microsoft Azure and have your very own private GitLab instance up and running in around 30 minutes.'
---
@@ -9,10 +9,10 @@ pre-configured GitLab VM on Microsoft Azure and have your very own private GitLa
>
> _Ported to the GitLab documentation and updated on 2017-08-24 by [Ian Scorer](https://gitlab.com/iscorer)._
-Azure is Microsoft's business cloud and GitLab is a pre-configured offering on the Azure Marketplace.
-Hopefully, you aren't surprised to hear that Microsoft and Azure have embraced open source software
-like Ubuntu, Red Hat Enterprise Linux, and of course - GitLab! This means that you can spin up a
-pre-configured GitLab VM and have your very own private GitLab up and running in around 30 minutes.
+Azure is Microsoft's business cloud and GitLab is a pre-configured offering on the Azure Marketplace.
+Hopefully, you aren't surprised to hear that Microsoft and Azure have embraced open source software
+like Ubuntu, Red Hat Enterprise Linux, and of course - GitLab! This means that you can spin up a
+pre-configured GitLab VM and have your very own private GitLab up and running in around 30 minutes.
Let's get started.
## Getting started
@@ -20,40 +20,40 @@ Let's get started.
First, you'll need an account on Azure. There are three ways to do this:
- If your company (or you) already has an account, then you are ready to go!
-- You can also open your own Azure account for free. _At time of writing_, you get $200
-of credit to spend on Azure services for 30 days. You can use this credit to try out paid Azure
+- You can also open your own Azure account for free. _At time of writing_, you get $200
+of credit to spend on Azure services for 30 days. You can use this credit to try out paid Azure
services, exploring Microsoft's cloud for free. Even after the first 30 days, you never have to pay
-anything unless you decide to transition to paid services with a Pay-As-You-Go Azure subscription.
-This is a great way to try out Azure and cloud computing, and you can
+anything unless you decide to transition to paid services with a Pay-As-You-Go Azure subscription.
+This is a great way to try out Azure and cloud computing, and you can
[read more in their comprehensive FAQ][Azure-Free-Account-FAQ].
-- If you have an MSDN subscription, you can activate your Azure subscriber benefits. Your MSDN
-subscription gives you recurring Azure credits every month, so why not put those credits to use and
+- If you have an MSDN subscription, you can activate your Azure subscriber benefits. Your MSDN
+subscription gives you recurring Azure credits every month, so why not put those credits to use and
try out GitLab right now?
## Working with Azure
-Once you have an Azure account, you can get started. Login to Azure using
+Once you have an Azure account, you can get started. Login to Azure using
[portal.azure.com](https://portal.azure.com) and the first thing you will see is the Dashboard:
![Azure Dashboard](img/azure-dashboard.png)
-The Dashboard gives you a quick overview of Azure resources, and from here you can build VMs,
+The Dashboard gives you a quick overview of Azure resources, and from here you can build VMs,
create SQL Databases, author websites, and perform lots of other cloud tasks.
## Create New VM
-The [Azure Marketplace][Azure-Marketplace] is an online store for pre-configured applications and
+The [Azure Marketplace][Azure-Marketplace] is an online store for pre-configured applications and
services which have been optimized for the cloud by software vendors like GitLab,
available on the Azure Marketplace as pre-configured solutions. In this tutorial
we will install GitLab Community Edition, but for GitLab Enterprise Edition you
can follow the same process.
-To begin creating a new GitLab VM, click on the **+ New** icon, type "GitLab" into the search
+To begin creating a new GitLab VM, click on the **+ New** icon, type "GitLab" into the search
box, and then click the **"GitLab Community Edition"** search result:
![Azure - New - Search for 'GitLab'](img/azure-new-search-gitlab.png)
-A new "blade" window will pop-out, where you can read more about the **"GitLab Community Edition"**
+A new "blade" window will pop-out, where you can read more about the **"GitLab Community Edition"**
offering which is freely available under the MIT Expat License:
![Azure - New - Select 'GitLab Community Edition'](img/azure-new-gitlab-ce.png)
@@ -70,18 +70,18 @@ The first items we need to configure are the basic settings of the underlying vi
1. Select a `VM disk type` - either **HDD** _(slower, lower cost)_ or **SSD** _(faster, higher cost)_
1. Enter a `User name` - e.g. **"gitlab-admin"**
1. Select an `Authentication type`, either **SSH public key** or **Password**:
-
+
> **Note:** if you're unsure which authentication type to use, select **Password**
- 1. If you chose **SSH public key** - enter your `SSH public key` into the field provided
- _(read the [SSH documentation][GitLab-Docs-SSH] to learn more about how to set up SSH
+ 1. If you chose **SSH public key** - enter your `SSH public key` into the field provided
+ _(read the [SSH documentation][GitLab-Docs-SSH] to learn more about how to set up SSH
public keys)_
- 1. If you chose **Password** - enter the password you wish to use _(this is the password that you
+ 1. If you chose **Password** - enter the password you wish to use _(this is the password that you
will use later in this tutorial to [SSH] into the VM, so make sure it's a strong password/passphrase)_
1. Choose the appropriate `Subscription` tier for your Azure account
1. Choose an existing `Resource Group` or create a new one - e.g. **"GitLab-CE-Azure"**
- > **Note:** a "Resource group" is a way to group related resources together for easier administration.
+ > **Note:** a "Resource group" is a way to group related resources together for easier administration.
> We chose "GitLab-CE-Azure", but your resource group can have the same name as your VM.
1. Choose a `Location` - if you're unsure, select the default location
@@ -94,23 +94,23 @@ Check the settings you have entered, and then click **"OK"** when you're ready t
## Size
-Next, you need to choose the size of your VM - selecting features such as the number of CPU cores,
+Next, you need to choose the size of your VM - selecting features such as the number of CPU cores,
the amount of RAM, the size of storage (and its speed), etc.
-> **Note:** in common with other cloud vendors, Azure operates a resource/usage pricing model, i.e.
-the more resources your VM consumes the more it will cost you to run, so make your selection
+> **Note:** in common with other cloud vendors, Azure operates a resource/usage pricing model, i.e.
+the more resources your VM consumes the more it will cost you to run, so make your selection
carefully. You'll see that Azure provides an _estimated_ monthly cost beneath each VM Size to help
guide your selection.
-The default size - the lowest cost **"DS1_V2 Standard"** VM - meets the minimum system requirements
-to run a small GitLab environment for testing and evaluation purposes, and so we're going to go
+The default size - the lowest cost **"DS1_V2 Standard"** VM - meets the minimum system requirements
+to run a small GitLab environment for testing and evaluation purposes, and so we're going to go
ahead and select this one, but please choose the size which best meets your own requirements:
![Azure - Create Virtual Machine - Size](img/azure-create-virtual-machine-size.png)
-> **Note:** be aware that whilst your VM is active (known as "allocated"), it will incur
-"compute charges" which, ultimately, you will be billed for. So, even if you're using the
-free trial credits, you'll likely want to learn
+> **Note:** be aware that whilst your VM is active (known as "allocated"), it will incur
+"compute charges" which, ultimately, you will be billed for. So, even if you're using the
+free trial credits, you'll likely want to learn
[how to properly shutdown an Azure VM to save money][Azure-Properly-Shutdown-VM].
Go ahead and click your chosen size, then click **"Select"** when you're ready to proceed to the
@@ -118,8 +118,8 @@ next step.
## Settings
-On the next blade, you're asked to configure the Storage, Network and Extension settings.
-We've gone with the default settings as they're sufficient for test-driving GitLab, but please
+On the next blade, you're asked to configure the Storage, Network and Extension settings.
+We've gone with the default settings as they're sufficient for test-driving GitLab, but please
choose the settings which best meet your own requirements:
![Azure - Create Virtual Machine - Settings](img/azure-create-virtual-machine-settings.png)
@@ -128,80 +128,80 @@ Review the settings and then click **"OK"** when you're ready to proceed to the
## Purchase
-The Purchase page is the last step and here you will be presented with the price per hour for your
-new VM. You'll be billed only for the VM itself (e.g. "Standard DS1 v2") because the
+The Purchase page is the last step and here you will be presented with the price per hour for your
+new VM. You'll be billed only for the VM itself (e.g. "Standard DS1 v2") because the
**"GitLab Community Edition"** marketplace solution is free to use at 0 USD/hr:
![Azure - Create Virtual Machine - Purchase](img/azure-create-virtual-machine-purchase.png)
-> **Note:** at this stage, you can review and modify the any of the settings you have made during all
+> **Note:** at this stage, you can review and modify the any of the settings you have made during all
previous steps, just click on any of the four steps to re-open them.
When you have read and agreed to the terms of use and are ready to proceed, click **"Purchase"**.
## Deployment
-At this point, Azure will begin deploying your new VM. The deployment process will take a few
+At this point, Azure will begin deploying your new VM. The deployment process will take a few
minutes to complete, with progress displayed on the **"Deployment"** blade:
![Azure - Create Virtual Machine - Deployment](img/azure-create-virtual-machine-deployment.png)
-Once the deployment process is complete, the new VM and its associated resources will be displayed
+Once the deployment process is complete, the new VM and its associated resources will be displayed
on the Azure Dashboard (you may need to refresh the page):
![Azure - Dashboard - All resources](img/azure-dashboard-running-resources.png)
-The new VM can also be accessed by clicking the `All resources` or `Virtual machines` icons in the
+The new VM can also be accessed by clicking the `All resources` or `Virtual machines` icons in the
Azure Portal sidebar navigation menu.
## Set up a domain name
-The VM will have a public IP address (static by default), but Azure allows us to assign a friendly
+The VM will have a public IP address (static by default), but Azure allows us to assign a friendly
DNS name to the VM, so let's go ahead and do that.
-From the Dashboard, click on the **"GitLab-CE"** tile to open the management blade for the new VM.
+From the Dashboard, click on the **"GitLab-CE"** tile to open the management blade for the new VM.
The public IP address that the VM uses is shown in the 'Essentials' section:
![Azure - VM - Management - Public IP Address](img/azure-vm-management-public-ip.png)
-Click on the public IP address - which should open the **"Public IP address - Configuration"** blade,
-then click on **"Configuration"** (under "Settings"). Now enter a friendly DNS name for your instance
+Click on the public IP address - which should open the **"Public IP address - Configuration"** blade,
+then click on **"Configuration"** (under "Settings"). Now enter a friendly DNS name for your instance
in the `DNS name label` field:
![Azure - VM - Domain Name](img/azure-vm-domain-name.png)
-In the screenshot above, you'll see that we've set the `DNS name label` to **"gitlab-ce-test"**.
-This will make our VM accessible at `gitlab-ce-test.centralus.cloudapp.azure.com`
-_(the full domain name of your own VM will be different, of course)_.
+In the screenshot above, you'll see that we've set the `DNS name label` to **"gitlab-ce-test"**.
+This will make our VM accessible at `gitlab-ce-test.centralus.cloudapp.azure.com`
+_(the full domain name of your own VM will be different, of course)_.
Click **"Save"** for the changes to take effect.
-> **Note:** if you want to use your own domain name, you will need to add a DNS `A` record at your
-domain registrar which points to the public IP address of your Azure VM. If you do this, you'll need
-to make sure your VM is configured to use a _static_ public IP address (i.e. not a _dynamic_ one)
-or you will have to reconfigure the DNS `A` record each time Azure reassigns your VM a new public IP
+> **Note:** if you want to use your own domain name, you will need to add a DNS `A` record at your
+domain registrar which points to the public IP address of your Azure VM. If you do this, you'll need
+to make sure your VM is configured to use a _static_ public IP address (i.e. not a _dynamic_ one)
+or you will have to reconfigure the DNS `A` record each time Azure reassigns your VM a new public IP
address. Read [IP address types and allocation methods in Azure][Azure-IP-Address-Types] to learn more.
## Let's open some ports!
-At this stage you should have a running and fully operational VM. However, none of the services on
-your VM (e.g. GitLab) will be publicly accessible via the internet until you have opened up the
+At this stage you should have a running and fully operational VM. However, none of the services on
+your VM (e.g. GitLab) will be publicly accessible via the internet until you have opened up the
necessary ports to enable access to those services.
-Ports are opened by adding _security rules_ to the **"Network security group"** (NSG) which our VM
-has been assigned to. If you followed the process above, then Azure will have automatically created
-an NSG named `GitLab-CE-nsg` and assigned the `GitLab-CE` VM to it.
+Ports are opened by adding _security rules_ to the **"Network security group"** (NSG) which our VM
+has been assigned to. If you followed the process above, then Azure will have automatically created
+an NSG named `GitLab-CE-nsg` and assigned the `GitLab-CE` VM to it.
-> **Note:** if you gave your VM a different name then the NSG automatically created by Azure will
+> **Note:** if you gave your VM a different name then the NSG automatically created by Azure will
also have a different name - the name you have your VM, with `-nsg` appended to it.
-You can navigate to the NSG settings via many different routes in the Azure Portal, but one of the
-simplest ways is to go to the Azure Dashboard, and then click on the Network Security Group listed
+You can navigate to the NSG settings via many different routes in the Azure Portal, but one of the
+simplest ways is to go to the Azure Dashboard, and then click on the Network Security Group listed
in the **"All resources"** tile:
![Azure - Dashboard - All resources - Network security group](img/azure-dashboard-highlight-nsg.png)
-With the **"Network security group"** blade open, click on **"Inbound security rules"** under
+With the **"Network security group"** blade open, click on **"Inbound security rules"** under
**"Settings"**:
![Azure - Network security group - Inbound security rules](img/azure-nsg-inbound-sec-rules-highlight.png)
@@ -212,18 +212,18 @@ Next, click **"Add"**:
### Which ports to open?
-Like all servers, our VM will be running many services. However, we want to open up the correct
+Like all servers, our VM will be running many services. However, we want to open up the correct
ports to enable public internet access to two services in particular:
-1. **HTTP** (port 80) - opening port 80 will enable our VM to respond to HTTP requests, allowing
+1. **HTTP** (port 80) - opening port 80 will enable our VM to respond to HTTP requests, allowing
public access to the instance of GitLab running on our VM.
-1. **SSH** (port 22) - opening port 22 will enable our VM to respond to SSH connection requests,
-allowing public access (with authentication) to remote terminal sessions
+1. **SSH** (port 22) - opening port 22 will enable our VM to respond to SSH connection requests,
+allowing public access (with authentication) to remote terminal sessions
_(you'll see why we need [SSH] access to our VM [later on in this tutorial](#maintaining-your-gitlab-instance))_
### Open HTTP on Port 80
-In the **"Add inbound security rule"** blade, let's open port 80 so that our VM will accept HTTP
+In the **"Add inbound security rule"** blade, let's open port 80 so that our VM will accept HTTP
connections:
![Azure - Add inbound security rules - HTTP](img/azure-add-inbound-sec-rule-http.png)
@@ -235,7 +235,7 @@ connections:
### Open SSH on Port 22
-Repeat the above process, adding a second Inbound security rule to open port 22, enabling our VM to
+Repeat the above process, adding a second Inbound security rule to open port 22, enabling our VM to
accept [SSH] connections:
![Azure - Add inbound security rules - SSH](img/azure-add-inbound-sec-rule-ssh.png)
@@ -245,16 +245,15 @@ accept [SSH] connections:
1. Make sure the `Action` is set to **Allow**
1. Click **"OK"**
-
-It will take a moment for Azure to add each new Inbound Security Rule (and you may need to click on
-**"Inbound security rules"** to refresh the list), but once completed, you should see the two new
+It will take a moment for Azure to add each new Inbound Security Rule (and you may need to click on
+**"Inbound security rules"** to refresh the list), but once completed, you should see the two new
rules in the list:
![Azure - Inbound security rules - List](img/azure-inbound-sec-rules-list.png)
## Connecting to GitLab
-Use the domain name you set up earlier (or the public IP address) to visit your new GitLab instance
-in your browser. If everything has gone according to plan you should be presented with the
+Use the domain name you set up earlier (or the public IP address) to visit your new GitLab instance
+in your browser. If everything has gone according to plan you should be presented with the
following page, asking you to set a _new_ password for the administrator account automatically
created by GitLab:
@@ -262,26 +261,26 @@ created by GitLab:
Enter your _new_ password into both form fields, and then click **"Change your password"**.
-Once you have changed the password you will be redirected to the GitLab login page. Use `root` as
+Once you have changed the password you will be redirected to the GitLab login page. Use `root` as
the username, enter the new password you set in the previous step, and then click **"Sign in"**:
![GitLab - Login](img/gitlab-login.png)
### Success?
-After signing in successfully, you should see the GitLab Projects page displaying a
+After signing in successfully, you should see the GitLab Projects page displaying a
**"Welcome to GitLab!"** message:
![GitLab - Projects Page](img/gitlab-home.png)
-If so, you now have a working GitLab instance on your own private Azure VM. **Congratulations!**
+If so, you now have a working GitLab instance on your own private Azure VM. **Congratulations!**
## Creating your first GitLab project
-You can skip this section if you are familiar with Git and GitLab. Otherwise, let's create our first
+You can skip this section if you are familiar with Git and GitLab. Otherwise, let's create our first
project. From the Welcome page, click **"New Project"**.
-Let's give our project a name and a description, and then accept the default values for everything
+Let's give our project a name and a description, and then accept the default values for everything
else:
1. Enter **"demo"** into the `Project path` project name field
@@ -290,12 +289,12 @@ else:
![GitLab - New Project](img/gitlab-new-project.png)
-Once the new project has been created (which should only take a moment), you'll be redirected to
+Once the new project has been created (which should only take a moment), you'll be redirected to
homepage for the project:
![GitLab - Empty Project](img/gitlab-project-home-empty.png)
-If you scroll further down the project's home page, you'll see some basic instructions on how to
+If you scroll further down the project's home page, you'll see some basic instructions on how to
set up a local clone of your new repository and push and pull from it:
![GitLab - Empty Project - Basic Instructions](img/gitlab-project-home-instructions.png)
@@ -304,50 +303,50 @@ set up a local clone of your new repository and push and pull from it:
## Maintaining your GitLab instance
-It's important to keep your GitLab environment up-to-date. The GitLab team is constantly making
-enhancements and occasionally you may need to update for security reasons. So let's review how to
-update GitLab.
+It's important to keep your GitLab environment up-to-date. The GitLab team is constantly making
+enhancements and occasionally you may need to update for security reasons. So let's review how to
+update GitLab.
### Checking our current version
To check which version of GitLab we're currently running, click on the "Admin Area" link - it's the
-the wrench icon displayed in the top-right, next to the search box.
+the wrench icon displayed in the top-right, next to the search box.
-In the following screenshot you can see an **"update asap"** notification message in the top-right.
-This particular message indicates that there is a newer version of GitLab available which contains
+In the following screenshot you can see an **"update asap"** notification message in the top-right.
+This particular message indicates that there is a newer version of GitLab available which contains
one or more security fixes:
![GitLab - update asap](img/gitlab-admin-area.png)
-Under the **"Components"** section, we can see that our VM is currently running version `8.6.5` of
-GitLab. This is the version of GitLab which was contained in the Azure Marketplace
-**"GitLab Community Edition"** offering we used to build the VM when we wrote this tutorial.
+Under the **"Components"** section, we can see that our VM is currently running version `8.6.5` of
+GitLab. This is the version of GitLab which was contained in the Azure Marketplace
+**"GitLab Community Edition"** offering we used to build the VM when we wrote this tutorial.
-> **Note:** The version of GitLab in your own VM instance may well be different, but the update
+> **Note:** The version of GitLab in your own VM instance may well be different, but the update
process will still be the same.
### Connect via SSH
-To perform an update, we need to connect directly to our Azure VM instance and run some commands
-from the terminal. Our Azure VM is actually a server running Linux (Ubuntu), so we'll need to
-connect to it using SSH ([Secure Shell][SSH]).
+To perform an update, we need to connect directly to our Azure VM instance and run some commands
+from the terminal. Our Azure VM is actually a server running Linux (Ubuntu), so we'll need to
+connect to it using SSH ([Secure Shell][SSH]).
-If you're running Windows, you'll need to connect using [PuTTY] or an equivalent Windows SSH client.
-If you're running Linux or macOS, then you already have an SSH client installed.
+If you're running Windows, you'll need to connect using [PuTTY] or an equivalent Windows SSH client.
+If you're running Linux or macOS, then you already have an SSH client installed.
-> **Note:**
-> - Remember that you will need to login with the username and password you specified
+> **Note:**
+> - Remember that you will need to login with the username and password you specified
> [when you created](#basics) your Azure VM
-> - If you need to reset your VM password, read
+> - If you need to reset your VM password, read
> [how to reset SSH credentials for a user on an Azure VM][Azure-Troubleshoot-SSH-Connection].
#### SSH from the command-line
-If you're running [SSH] from the command-line (terminal), then type in the following command to
-connect to your VM, substituting `username` and `your-azure-domain-name.com` for the correct values.
+If you're running [SSH] from the command-line (terminal), then type in the following command to
+connect to your VM, substituting `username` and `your-azure-domain-name.com` for the correct values.
-Again, remember that your Azure VM domain name will be the one you
-[set up previously in the tutorial](#set-up-a-domain-name). If you didn't set up a domain name for
+Again, remember that your Azure VM domain name will be the one you
+[set up previously in the tutorial](#set-up-a-domain-name). If you didn't set up a domain name for
your VM, you can use the IP address in its place in the following command:
```bash
@@ -357,7 +356,7 @@ Provide your password at the prompt to authenticate.
#### SSH from Windows (PuTTY)
-If you're using [PuTTY] in Windows as your [SSH] client, then you might want to take a quick
+If you're using [PuTTY] in Windows as your [SSH] client, then you might want to take a quick
read on [using PuTTY in Windows][Using-SSH-In-Putty].
### Updating GitLab
@@ -369,8 +368,8 @@ version:
sudo apt-get update && sudo apt-get install gitlab-ce
```
-This command will update GitLab and its associated components to the latest versions, so it will
-take a little time to complete. You'll see various update tasks being completed in your SSH
+This command will update GitLab and its associated components to the latest versions, so it will
+take a little time to complete. You'll see various update tasks being completed in your SSH
terminal window:
![GitLab updating](img/gitlab-ssh-update-in-progress.png)
@@ -387,23 +386,23 @@ before anything else.
#### Check out your updated GitLab
-Refresh your GitLab instance in the browser and navigate to the Admin Area. You should now have an
-up-to-date GitLab instance.
+Refresh your GitLab instance in the browser and navigate to the Admin Area. You should now have an
+up-to-date GitLab instance.
-When we wrote this tutorial our Azure VM GitLab instance was updated to the latest version at time
-of writing (`9.4.0`). You can see that the message which was previously displaying **"update asap"**
+When we wrote this tutorial our Azure VM GitLab instance was updated to the latest version at time
+of writing (`9.4.0`). You can see that the message which was previously displaying **"update asap"**
is now showing **"up-to-date"**:
![GitLab up to date](img/gitlab-admin-area-9.4.0.png)
## Conclusion
-Naturally, we believe that GitLab is a great git repository tool. However, GitLab is a whole lot
-more than that too. GitLab unifies issues, code review, CI and CD into a single UI, helping you to
-move faster from idea to production, and in this tutorial we showed you how quick and easy it is to
-set up and run your own instance of GitLab on Azure, Microsoft's cloud service.
+Naturally, we believe that GitLab is a great git repository tool. However, GitLab is a whole lot
+more than that too. GitLab unifies issues, code review, CI and CD into a single UI, helping you to
+move faster from idea to production, and in this tutorial we showed you how quick and easy it is to
+set up and run your own instance of GitLab on Azure, Microsoft's cloud service.
-Azure is a great way to experiment with GitLab, and if you decide (as we hope) that GitLab is for
+Azure is a great way to experiment with GitLab, and if you decide (as we hope) that GitLab is for
you, you can continue to use Azure as your secure, scalable cloud provider or of course run GitLab
on any cloud service you choose.
diff --git a/doc/install/kubernetes/gitlab_chart.md b/doc/install/kubernetes/gitlab_chart.md
index 26ced45de7b..9db246b3eb3 100644
--- a/doc/install/kubernetes/gitlab_chart.md
+++ b/doc/install/kubernetes/gitlab_chart.md
@@ -5,7 +5,7 @@ This is the official way to install GitLab on a cloud native environment.
NOTE: **Kubernetes experience required:**
Our Helm charts are recommended for those who are familiar with Kubernetes.
If you're not sure if Kubernetes is for you, our
-[Omnibus GitLab packages](../README.md#install-gitlab-using-the-omnibus-gitlab-package-recommended)
+[Omnibus GitLab packages](../README.md#installing-gitlab-using-the-omnibus-gitlab-package-recommended)
are mature, scalable, support [high availability](../../administration/high_availability/README.md)
and are used today on GitLab.com.
It is not necessary to have GitLab installed on Kubernetes in order to use [GitLab Kubernetes integration](https://docs.gitlab.com/ee/user/project/clusters/index.html).
diff --git a/doc/install/kubernetes/gitlab_omnibus.md b/doc/install/kubernetes/gitlab_omnibus.md
index 2ae5485869e..c0cb7694e91 100644
--- a/doc/install/kubernetes/gitlab_omnibus.md
+++ b/doc/install/kubernetes/gitlab_omnibus.md
@@ -4,7 +4,7 @@ CAUTION: **Caution:**
This chart is **deprecated**. We recommend using the [`gitlab` chart](gitlab_chart.md)
instead. A comparison of the two charts is available in [this video](https://youtu.be/Z6jWR8Z8dv8).
-For more information on available GitLab Helm Charts, see the [charts overview](index.md#chart-overview).
+For more information on available GitLab Helm Charts, see [Installing GitLab on Kubernetes](index.md).
- This GitLab-Omnibus chart has been tested on Google Kubernetes Engine and Azure Container Service.
- This work is based partially on: <https://github.com/lwolf/kubernetes-gitlab/>. GitLab would like to thank Sergey Nuzhdin for his work.
@@ -39,7 +39,7 @@ The deployment includes:
- _At least_ 4 GB of RAM available on your cluster. 41GB of storage and 2 CPU are also required.
- Kubernetes 1.4+ with Beta APIs enabled
- [Persistent Volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) provisioner support in the underlying infrastructure
-- A [wildcard DNS entry](#networking-prerequisites), which resolves to the external IP address
+- A [wildcard DNS entry](#networking-requirements), which resolves to the external IP address
- The `kubectl` CLI installed locally and authenticated for the cluster
- The [Helm client](https://github.com/kubernetes/helm/blob/master/docs/quickstart.md) installed locally on your machine
@@ -52,7 +52,7 @@ and [Mattermost](https://docs.gitlab.com/omnibus/gitlab-mattermost/).
To support the GitLab services and dynamic environments, a wildcard DNS entry
is required which resolves to the [load balancer](#load-balancer-ip) or
-[external IP](#external-ip). Configuration of the DNS entry will depend upon
+[external IP](#external-ip-recommended). Configuration of the DNS entry will depend upon
the DNS service being used.
#### External IP (recommended)
@@ -84,13 +84,13 @@ to configure the Wildcard DNS entry. For more information on creating a wildcard
DNS entry, consult the documentation for the DNS server you are using.
For production deployments of GitLab, we strongly recommend using a
-[external IP](#external-ip).
+[external IP](#external-ip-recommended).
## Configuring and Installing GitLab
For most installations, two parameters are required:
-- `baseDomain`: the [base domain](#networking-prerequisites) of the wildcard host entry. For example, `mycompany.io` if the wild card entry is `*.mycompany.io`.
+- `baseDomain`: the [base domain](#networking-requirements) of the wildcard host entry. For example, `mycompany.io` if the wild card entry is `*.mycompany.io`.
- `legoEmail`: Email address to use when requesting new SSL certificates from Let's Encrypt.
Other common configuration options:
@@ -105,7 +105,7 @@ For additional configuration options, consult the
### Choosing a different GitLab release version
-The version of GitLab installed is based on the `gitlab` setting (see [section](#choosing-gitlab-edition) above), and
+The version of GitLab installed is based on the `gitlab` setting (see [section](#configuring-and-installing-gitLab) above), and
the value of the corresponding helm setting: `gitlabCEImage` or `gitabEEImage`.
```yaml
diff --git a/doc/install/kubernetes/gitlab_runner_chart.md b/doc/install/kubernetes/gitlab_runner_chart.md
index 3c2f883f29d..68b2a146115 100644
--- a/doc/install/kubernetes/gitlab_runner_chart.md
+++ b/doc/install/kubernetes/gitlab_runner_chart.md
@@ -11,7 +11,7 @@ This chart configures the Runner to:
- For each new job it receives from [GitLab CI](https://about.gitlab.com/features/gitlab-ci-cd/), it will provision a
new pod within the specified namespace to run it.
-For more information on available GitLab Helm Charts, please see our [overview](index.md#chart-overview).
+For more information on available GitLab Helm Charts, please see our [overview](index.md).
## Prerequisites
@@ -33,7 +33,7 @@ In order for GitLab Runner to function, your config file **must** specify the fo
- `gitlabUrl` - the GitLab Server URL (with protocol) to register the runner against
- `runnerRegistrationToken` - The Registration Token for adding new Runners to the GitLab Server. This must be
- retrieved from your GitLab Instance. See the [GitLab Runner Documentation](../../ci/runners/README.md#creating-and-registering-a-runner) for more information.
+ retrieved from your GitLab Instance. See the [GitLab Runner Documentation](../../ci/runners/README.md) for more information.
Unless you need to specify additional configuration, you are [ready to install](#installing-gitlab-runner-using-the-helm-chart).
@@ -51,7 +51,7 @@ gitlabUrl: http://gitlab.your-domain.com/
## The Registration Token for adding new Runners to the GitLab Server. This must
## be retrieved from your GitLab Instance.
-## ref: https://docs.gitlab.com/ce/ci/runners/README.html#creating-and-registering-a-runner
+## ref: https://docs.gitlab.com/ee/ci/runners/README.html
##
runnerRegistrationToken: ""
@@ -227,7 +227,7 @@ helm repo add gitlab https://charts.gitlab.io
helm init
```
-Once you [have configured](#configuration) GitLab Runner in your `values.yml` file,
+Once you [have configured](#configuring-gitlab-runner-using-the-helm-chart) GitLab Runner in your `values.yml` file,
run the following:
```bash
@@ -236,7 +236,7 @@ helm install --namespace <NAMESPACE> --name gitlab-runner -f <CONFIG_VALUES_FILE
- `<NAMESPACE>` is the Kubernetes namespace where you want to install the GitLab Runner.
- `<CONFIG_VALUES_FILE>` is the path to values file containing your custom configuration. See the
- [Configuration](#configuration) section to create it.
+ [Configuring GitLab Runner using the Helm Chart](#configuring-gitlab-runner-using-the-helm-chart) section to create it.
## Updating GitLab Runner using the Helm Chart
@@ -247,11 +247,12 @@ helm upgrade --namespace <NAMESPACE> -f <CONFIG_VALUES_FILE> <RELEASE-NAME> gitl
```
Where:
+
- `<NAMESPACE>` is the Kubernetes namespace where GitLab Runner is installed
- `<CONFIG_VALUES_FILE>` is the path to values file containing your custom configuration. See the
- [Configuration](#configuration) section to create it.
+ [Configuring GitLab Runner using the Helm Chart](#configuring-gitlab-runner-using-the-helm-chart) section to create it.
- `<RELEASE-NAME>` is the name you gave the chart when installing it.
- In the [Install section](#installing) we called it `gitlab-runner`.
+ In the [Installing GitLab Runner using the Helm Chart](#installing-gitlab-runner-using-the-helm-chart) section, we called it `gitlab-runner`.
## Uninstalling GitLab Runner using the Helm Chart
@@ -265,4 +266,4 @@ where:
- `<NAMESPACE>` is the Kubernetes namespace where GitLab Runner is installed
- `<RELEASE-NAME>` is the name you gave the chart when installing it.
- In the [Install section](#installing) we called it `gitlab-runner`.
+ In the [Installing GitLab Runner using the Helm Chart](#installing-gitlab-runner-using-the-helm-chart) section, we called it `gitlab-runner`.
diff --git a/doc/install/kubernetes/index.md b/doc/install/kubernetes/index.md
index 281630174e7..ecc956d04e9 100644
--- a/doc/install/kubernetes/index.md
+++ b/doc/install/kubernetes/index.md
@@ -7,7 +7,7 @@ description: 'Read through the different methods to deploy GitLab on Kubernetes.
NOTE: **Kubernetes experience required:**
Our Helm charts are recommended for those who are familiar with Kubernetes.
If you're not sure if Kubernetes is for you, our
-[Omnibus GitLab packages](../README.md#install-gitlab-using-the-omnibus-gitlab-package-recommended)
+[Omnibus GitLab packages](../README.md#installing-gitlab-using-the-omnibus-gitlab-package-recommended)
are mature, scalable, support [high availability](../../administration/high_availability/README.md)
and are used today on GitLab.com.
It is not necessary to have GitLab installed on Kubernetes in order to use [GitLab Kubernetes integration](https://docs.gitlab.com/ee/user/project/clusters/index.html).
diff --git a/doc/install/kubernetes/preparation/eks.md b/doc/install/kubernetes/preparation/eks.md
index c40177c4302..647b856d54e 100644
--- a/doc/install/kubernetes/preparation/eks.md
+++ b/doc/install/kubernetes/preparation/eks.md
@@ -26,7 +26,7 @@ With EKS, there are a few important details to keep in mind:
The easiest way to solve this and still utilize dynamic provisioning is to utilize, or create, a Storage Class that is locked to a specific zone.
-> **Note**: Restricting volumes to specific zone will cause GitLab and any other application using this Storage Class to only reside in that zone. For multiple zone support, utilize [manually provisioned volumes](#manual-provisioning-of-volumes).
+> **Note**: Restricting volumes to specific zone will cause GitLab and any other application using this Storage Class to only reside in that zone. For multiple zone support, utilize [manually provisioned volumes](#manual-provisioning-of-volumes-recommended).
To create the storage class, download and edit Amazon EKS's [sample Storage Class](https://docs.aws.amazon.com/eks/latest/userguide/storage-classes.html) and add the following parameter:
diff --git a/doc/install/kubernetes/preparation/networking.md b/doc/install/kubernetes/preparation/networking.md
index 34a6130de27..b9fb9a7399f 100644
--- a/doc/install/kubernetes/preparation/networking.md
+++ b/doc/install/kubernetes/preparation/networking.md
@@ -12,7 +12,7 @@ To support the GitLab services and dynamic environments, a wildcard DNS entry is
To provision an external IP on GCP and Azure, simply request a new address from the Networking section. Ensure that the region matches the region your container cluster is created in. Note, it is important that the IP is not assigned at this point in time. It will be automatically assigned once the Helm chart is installed, to the Load Balancer.
-Set `global.hosts.externalIP` to this IP address when [deploying GitLab](../gitlab_chart.md#configuring-and-installing-gitlab).
+Set `global.hosts.externalIP` to this IP address when [deploying GitLab](../gitlab_chart.md#installing-gitlab-using-the-helm-chart).
Then, create a [wildcard DNS record](#wildcard-dns-entry) which resolves to this IP address.
@@ -35,4 +35,4 @@ Please consult the documentation for your DNS service for more information on cr
- [Google Domains](https://support.google.com/domains/answer/3290350?hl=en)
- [GoDaddy](https://www.godaddy.com/help/add-an-a-record-19238)
-Set `global.hosts.domain` to this DNS name when [deploying GitLab](../gitlab_chart.md#configuring-and-installing-gitlab).
+Set `global.hosts.domain` to this DNS name when [deploying GitLab](../gitlab_chart.md#installing-gitlab-using-the-helm-chart).
diff --git a/doc/install/kubernetes/preparation/tools_installation.md b/doc/install/kubernetes/preparation/tools_installation.md
index 210bc2f9e58..d2f7a69a0af 100644
--- a/doc/install/kubernetes/preparation/tools_installation.md
+++ b/doc/install/kubernetes/preparation/tools_installation.md
@@ -16,4 +16,4 @@ You can get Helm from the project's [releases page](https://github.com/kubernete
# Next steps
-Once installed, proceed to the next [installation step](../gitlab_chart.md#prerequisites).
+Once installed, proceed to the next [installation step](../gitlab_chart.md#installing-gitlab-using-the-helm-chart).
diff --git a/doc/integration/akismet.md b/doc/integration/akismet.md
index 35024a78fca..4f7be70baf2 100644
--- a/doc/integration/akismet.md
+++ b/doc/integration/akismet.md
@@ -34,7 +34,6 @@ To use Akismet:
![Screenshot of Akismet settings](img/akismet_settings.png)
-
## Training
> *Note:* Training the Akismet filter is only available in 8.11 and above.
diff --git a/doc/integration/cas.md b/doc/integration/cas.md
index f757edf0bc2..c6178fa44f0 100644
--- a/doc/integration/cas.md
+++ b/doc/integration/cas.md
@@ -38,7 +38,6 @@ To enable the CAS OmniAuth provider you must register your application with your
}
]
```
-
For installations from source:
@@ -65,4 +64,3 @@ On the sign in page there should now be a CAS tab in the sign in form.
[reconfigure]: ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure
[restart GitLab]: ../administration/restart_gitlab.md#installations-from-source
-
diff --git a/doc/integration/github.md b/doc/integration/github.md
index 9bb3579dd84..bee68688ace 100644
--- a/doc/integration/github.md
+++ b/doc/integration/github.md
@@ -93,7 +93,6 @@ To get the credentials (a pair of Client ID and Client Secret), you must registe
args: { scope: 'user:email' } }
```
-
For GitHub Enterprise:
```
diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md
index f5f1f9486c2..4db986197f3 100644
--- a/doc/integration/omniauth.md
+++ b/doc/integration/omniauth.md
@@ -231,7 +231,6 @@ In order to enable/disable an OmniAuth provider, go to Admin Area -> Settings ->
![Enabled OAuth Sign-In sources](img/enabled-oauth-sign-in-sources.png)
-
## Disabling Omniauth
Starting from version 11.4 of GitLab, Omniauth is enabled by default. This only
diff --git a/doc/integration/saml.md b/doc/integration/saml.md
index bb3cd9a005f..8ee07a7fcdc 100644
--- a/doc/integration/saml.md
+++ b/doc/integration/saml.md
@@ -249,7 +249,6 @@ If you want some SAML authentication methods to count as 2FA on a per session ba
1. Save the file and [restart GitLab][] for the changes ot take effect
-
In addition to the changes in GitLab, make sure that your Idp is returning the
`AuthnContext`. For example:
diff --git a/doc/integration/slash_commands.md b/doc/integration/slash_commands.md
index 7d73026a6c6..cd755089be8 100644
--- a/doc/integration/slash_commands.md
+++ b/doc/integration/slash_commands.md
@@ -1,5 +1,7 @@
# Slash Commands
+> The `run` command was [introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4466) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.6. [Moved](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24780) to [GitLab Core](https://about.gitlab.com/pricing/) in 11.9.
+
Slash commands in Mattermost and Slack allow you to control GitLab and view GitLab content right inside your chat client, without having to leave it. For Slack, this requires a [project service configuration](../user/project/integrations/slack_slash_commands.md). Simply type the command as a message in your chat client to activate it.
Commands are scoped to a project, with a trigger term that is specified during configuration.
@@ -8,7 +10,6 @@ We suggest you use the project name as the trigger term for simplicity and clari
Taking the trigger term as `project-name`, the commands are:
-
| Command | Effect |
| ------- | ------ |
| `/project-name help` | Shows all available slash commands |
@@ -17,6 +18,7 @@ Taking the trigger term as `project-name`, the commands are:
| `/project-name issue search <query>` | Shows up to 5 issues matching `<query>` |
| `/project-name issue move <id> to <project>` | Moves issue ID `<id>` to `<project>` |
| `/project-name deploy <from> to <to>` | Deploy from the `<from>` environment to the `<to>` environment |
+| `/project-name run <job name> <arguments>` | Execute [ChatOps](../ci/chatops/README.md) job `<job name>` on `master` |
Note that if you are using the [GitLab Slack application](https://docs.gitlab.com/ee/user/project/integrations/gitlab_slack_application.html) for
your GitLab.com projects, you need to [add the `gitlab` keyword at the beginning of the command](https://docs.gitlab.com/ee/user/project/integrations/gitlab_slack_application.html#usage).
diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md
index 81ee7338e4e..8601551e3bd 100644
--- a/doc/public_access/public_access.md
+++ b/doc/public_access/public_access.md
@@ -1,6 +1,6 @@
# Public access
-GitLab allows you to change your projects' visibility in order be accessed
+GitLab allows [Owners](../user/permissions.md) to change a projects' visibility in order to be accessed
**publicly** or **internally**.
Projects with either of these visibility levels will be listed in the
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index 037b71a27b9..2514ca94775 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -442,7 +442,6 @@ backups will be copied to, and will be created if it does not exist. If the
directory that you want to copy the tarballs to is the root of your mounted
directory, just use `.` instead.
-
For Omnibus GitLab packages:
1. Edit `/etc/gitlab/gitlab.rb`:
@@ -564,7 +563,6 @@ For installations from source:
1. [Restart GitLab] for the changes to take effect.
-
```sh
sudo -u git crontab -e # Edit the crontab for the git user
```
@@ -806,9 +804,22 @@ If you have failed to [back up the secrets file](#storing-configuration-files),
then users with 2FA enabled will not be able to log into GitLab. In that case,
you need to [disable 2FA for everyone](../security/two_factor_authentication.md#disabling-2fa-for-everyone).
-In the case of CI/CD, if your project has secure variables set, you might experience
-some weird behavior, like stuck jobs or 500 errors. In that case, you can try
-deleting the `ci_variables` table from the database.
+The secrets file is also responsible for storing the encryption key for several
+columns containing sensitive information. If the key is lost, GitLab will be
+unable to decrypt those columns. This will break a wide range of functionality,
+including (but not restricted to):
+
+* [CI/CD variables](../ci/variables/README.md)
+* [Kubernetes / GCP integration](../user/project/clusters/index.md)
+* [Custom Pages domains](../user/project/pages/getting_started_part_three.md)
+* [Project error tracking](../user/project/operations/error_tracking.md)
+* [Runner authentication](../ci/runners/README.md)
+* [Project mirroring](../workflow/repository_mirroring.md)
+* [Web hooks](../user/project/integrations/webhooks.md)
+
+In the case of CI/CD, variables, you might experience some weird behavior, like
+stuck jobs or 500 errors. In that case, you can try removing contents of the
+`ci_group_variables` and `ci_project_variables` tables from the database.
CAUTION: **Warning:**
Use the following commands at your own risk, and make sure you've taken a
@@ -828,9 +839,10 @@ backup beforehand.
sudo -u git -H bundle exec rails dbconsole RAILS_ENV=production
```
-1. Check the `ci_variables` table:
+1. Check the `ci_group_variables` and `ci_variables` tables:
```sql
+ SELECT * FROM public."ci_group_variables";
SELECT * FROM public."ci_variables";
```
@@ -839,6 +851,7 @@ backup beforehand.
1. Drop the table:
```sql
+ DELETE FROM ci_group_variables;
DELETE FROM ci_variables;
```
@@ -848,5 +861,9 @@ backup beforehand.
You should now be able to visit your project, and the jobs will start
running again.
+A similar strategy can be employed for the remaining features - by removing the
+data that cannot be decrypted, GitLab can be brought back into working order,
+and the lost data can be manually replaced.
+
[reconfigure GitLab]: ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure
[restart GitLab]: ../administration/restart_gitlab.md#installations-from-source
diff --git a/doc/raketasks/user_management.md b/doc/raketasks/user_management.md
index e1b1912ed47..571e784e530 100644
--- a/doc/raketasks/user_management.md
+++ b/doc/raketasks/user_management.md
@@ -52,7 +52,6 @@ bundle exec rake gitlab:import:all_users_to_all_groups RAILS_ENV=production
- Enable this setting to keep new users blocked until they have been cleared by the admin (default: false).
-
```
block_auto_created_users: false
```
@@ -77,7 +76,6 @@ GitLab stores the secret data enabling 2FA to work in an encrypted database
column. The encryption key for this data is known as `otp_key_base`, and is
stored in `config/secrets.yml`.
-
If that file is leaked, but the individual 2FA secrets have not, it's possible
to re-encrypt those secrets with a new encryption key. This allows you to change
the leaked key without forcing all users to change their 2FA details.
diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md
index 55b678d6af5..a3698d60f6d 100644
--- a/doc/system_hooks/system_hooks.md
+++ b/doc/system_hooks/system_hooks.md
@@ -147,7 +147,7 @@ Please refer to `group_rename` and `user_rename` for that case.
"user_name": "John Smith",
"user_username": "johnsmith",
"user_id": 41,
- "project_visibility": "private"
+ "project_visibility": "visibilitylevel|private"
}
```
@@ -158,7 +158,7 @@ Please refer to `group_rename` and `user_rename` for that case.
"created_at": "2012-07-21T07:30:56Z",
"updated_at": "2012-07-21T07:38:22Z",
"event_name": "user_remove_from_team",
- "project_access": "Maintainer",
+ "access_level": "Maintainer",
"project_id": 74,
"project_name": "StoreCloud",
"project_path": "storecloud",
@@ -167,7 +167,7 @@ Please refer to `group_rename` and `user_rename` for that case.
"user_name": "John Smith",
"user_username": "johnsmith",
"user_id": 41,
- "project_visibility": "private"
+ "project_visibility": "visibilitylevel|private"
}
```
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index b72138ef24f..1ab0372fb67 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -13,7 +13,7 @@ Starting with GitLab 11.3, the Auto DevOps pipeline is enabled by default for al
projects. If it has not been explicitly enabled for the project, Auto DevOps will be automatically
disabled on the first pipeline failure. Your project will continue to use an alternative
[CI/CD configuration file](../../ci/yaml/README.md) if one is found. A GitLab
-administrator can [change this setting](../../user/admin_area/settings/continuous_integration.html#auto-devops)
+administrator can [change this setting](../../user/admin_area/settings/continuous_integration.html#auto-devops-core-only)
in the admin area.
With Auto DevOps, the software development process becomes easier to set up
@@ -114,7 +114,7 @@ To make full use of Auto DevOps, you will need:
will need Prometheus installed somewhere (inside or outside your cluster) and
configured to scrape your Kubernetes cluster. To get response metrics
(in addition to system metrics), you need to
- [configure Prometheus to monitor NGINX](../../user/project/integrations/prometheus_library/nginx_ingress.md#configuring-prometheus-to-monitor-for-nginx-ingress-metrics).
+ [configure Prometheus to monitor NGINX](../../user/project/integrations/prometheus_library/nginx_ingress.md#configuring-nginx-ingress-monitoring).
The [Prometheus service](../../user/project/integrations/prometheus.md)
integration needs to be enabled for the project, or enabled as a
[default service template](../../user/project/integrations/services_templates.md)
@@ -166,7 +166,7 @@ them to the Kubernetes pods that run your application(s).
When using Auto DevOps, you may want to deploy different environments to
different Kubernetes clusters. This is possible due to the 1:1 connection that
-[exists between them](../../user/project/clusters/index.md#multiple-kubernetes-clusters).
+[exists between them](../../user/project/clusters/index.md#multiple-kubernetes-clusters-premium).
In the [Auto DevOps template](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml)
(used behind the scenes by Auto DevOps), there are currently 3 defined environment names that you need to know:
@@ -179,7 +179,7 @@ Those environments are tied to jobs that use [Auto Deploy](#auto-deploy), so
except for the environment scope, they would also need to have a different
domain they would be deployed to. This is why you need to define a separate
`KUBE_INGRESS_BASE_DOMAIN` variable for all the above
-[based on the environment](../../ci/variables/README.md#limiting-environment-scopes-of-variables).
+[based on the environment](https://docs.gitlab.com/ee/ci/variables/index.html#limiting-environment-scopes-of-variables-premium).
The following table is an example of how the three different clusters would
be configured.
@@ -188,7 +188,7 @@ be configured.
| ------------ | -------------- | ----------------------------- | ------------- | ------ |
| review | `review/*` | `review.example.com` | `review/*` | The review cluster which will run all [Review Apps](../../ci/review_apps/index.md). `*` is a wildcard, which means it will be used by every environment name starting with `review/`. |
| staging | `staging` | `staging.example.com` | `staging` | (Optional) The staging cluster which will run the deployments of the staging environments. You need to [enable it first](#deploy-policy-for-staging-and-production-environments). |
-| production | `production` | `example.com` | `production` | The production cluster which will run the deployments of the production environment. You can use [incremental rollouts](#incremental-rollout-to-production). |
+| production | `production` | `example.com` | `production` | The production cluster which will run the deployments of the production environment. You can use [incremental rollouts](#incremental-rollout-to-production-premium). |
To add a different cluster for each environment:
@@ -269,12 +269,12 @@ The available options are:
- **Continuous deployment to production**: Enables [Auto Deploy](#auto-deploy)
with `master` branch directly deployed to production.
- **Continuous deployment to production using timed incremental rollout**: Sets the
- [`INCREMENTAL_ROLLOUT_MODE`](#timed-incremental-rollout-to-production) variable
+ [`INCREMENTAL_ROLLOUT_MODE`](#timed-incremental-rollout-to-production-premium) variable
to `timed`, and production deployment will be executed with a 5 minute delay between
each increment in rollout.
- **Automatic deployment to staging, manual deployment to production**: Sets the
[`STAGING_ENABLED`](#deploy-policy-for-staging-and-production-environments) and
- [`INCREMENTAL_ROLLOUT_MODE`](#incremental-rollout-to-production) variables
+ [`INCREMENTAL_ROLLOUT_MODE`](#incremental-rollout-to-production-premium) variables
to `1` and `manual`. This means:
- `master` branch is directly deployed to staging.
@@ -503,7 +503,7 @@ Auto Deploy doesn't include deployments to staging or canary by default, but the
[Auto DevOps template] contains job definitions for these tasks if you want to
enable them.
-You can make use of [environment variables](#helm-chart-variables) to automatically
+You can make use of [environment variables](#environment-variables) to automatically
scale your pod replicas.
It's important to note that when a project is deployed to a Kubernetes cluster,
@@ -553,7 +553,7 @@ Herokuish](https://github.com/gliderlabs/herokuish#paths)
> [Introduced][ce-19507] in GitLab 11.0.
-For internal and private projects a [GitLab Deploy Token](../../user/project/deploy_tokens/index.md###gitlab-deploy-token)
+For internal and private projects a [GitLab Deploy Token](../../user/project/deploy_tokens/index.md#gitlab-deploy-token)
will be automatically created, when Auto DevOps is enabled and the Auto DevOps settings are saved. This Deploy Token
can be used for permanent access to the registry.
@@ -581,7 +581,7 @@ The metrics include:
In order to make use of monitoring you need to:
-1. [Deploy Prometheus](../../user/project/integrations/prometheus.md#configuring-your-own-prometheus-server-within-kubernetes) into your Kubernetes cluster
+1. [Deploy Prometheus](../../user/project/integrations/prometheus.md) into your Kubernetes cluster
1. If you would like response metrics, ensure you are running at least version
0.9.0 of NGINX Ingress and
[enable Prometheus metrics](https://github.com/kubernetes/ingress-nginx/blob/master/docs/examples/customization/custom-vts-metrics-prometheus/nginx-vts-metrics-conf.yaml).
@@ -684,7 +684,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| **Variable** | **Description** |
| ------------ | --------------- |
-| `AUTO_DEVOPS_DOMAIN` | The [Auto DevOps domain](#auto-devops-domain). By default, set automatically by the [Auto DevOps setting](#enabling-auto-devops). This variable is deprecated and [is scheduled to be removed](https://gitlab.com/gitlab-org/gitlab-ce/issues/56959). Use `KUBE_INGRESS_BASE_DOMAIN` instead. |
+| `AUTO_DEVOPS_DOMAIN` | The [Auto DevOps domain](#auto-devops-base-domain). By default, set automatically by the [Auto DevOps setting](#enablingdisabling-auto-devops). This variable is deprecated and [is scheduled to be removed](https://gitlab.com/gitlab-org/gitlab-ce/issues/56959). Use `KUBE_INGRESS_BASE_DOMAIN` instead. |
| `AUTO_DEVOPS_CHART` | The Helm Chart used to deploy your apps; defaults to the one [provided by GitLab](https://gitlab.com/charts/auto-deploy-app). |
| `AUTO_DEVOPS_CHART_REPOSITORY` | The Helm Chart repository used to search for charts; defaults to `https://charts.gitlab.io`. |
| `REPLICAS` | The number of replicas to deploy; defaults to 1. |
@@ -703,8 +703,8 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `DB_INITIALIZE` | From GitLab 11.4, this variable can be used to specify the command to run to initialize the application's PostgreSQL database. It runs inside the application pod. |
| `DB_MIGRATE` | From GitLab 11.4, this variable can be used to specify the command to run to migrate the application's PostgreSQL database. It runs inside the application pod. |
| `STAGING_ENABLED` | From GitLab 10.8, this variable can be used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). |
-| `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments). |
-| `INCREMENTAL_ROLLOUT_MODE`| From GitLab 11.4, this variable, if present, can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment.<br/>Set to: <ul><li>`manual`, for manual deployment jobs.</li><li>`timed`, for automatic rollout deployments with a 5 minute delay each one.</li></ul> |
+| `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments-premium). |
+| `INCREMENTAL_ROLLOUT_MODE`| From GitLab 11.4, this variable, if present, can be used to enable an [incremental rollout](#incremental-rollout-to-production-premium) of your application for the production environment.<br/>Set to: <ul><li>`manual`, for manual deployment jobs.</li><li>`timed`, for automatic rollout deployments with a 5 minute delay each one.</li></ul> |
| `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. |
| `CODE_QUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. |
| `SAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. |
@@ -938,7 +938,7 @@ TIP: **Tip:**
You can also set this inside your [project's settings](#deployment-strategy).
This configuration based on
-[incremental rollout to production](#incremental-rollout-to-production).
+[incremental rollout to production](#incremental-rollout-to-production-premium).
Everything behaves the same way, except:
diff --git a/doc/topics/autodevops/quick_start_guide.md b/doc/topics/autodevops/quick_start_guide.md
index 9749bd63f2b..367e192b85a 100644
--- a/doc/topics/autodevops/quick_start_guide.md
+++ b/doc/topics/autodevops/quick_start_guide.md
@@ -159,15 +159,15 @@ In the **test** stage, GitLab runs various checks on the application:
- The `test` job runs unit and integration tests by detecting the language and
framework ([Auto Test](index.md#auto-test))
- The `code_quality` job checks the code quality and is allowed to fail
- ([Auto Code Quality](index.md#auto-code-quality)) **[STARTER]**
+ ([Auto Code Quality](index.md#auto-code-quality-starter)) **[STARTER]**
- The `container_scanning` job checks the Docker container if it has any
vulnerabilities and is allowed to fail ([Auto Container Scanning](index.md#auto-container-scanning))
- The `dependency_scanning` job checks if the application has any dependencies
- susceptible to vulnerabilities and is allowed to fail ([Auto Dependency Scanning](index.md#auto-dependency-scanning)) **[ULTIMATE]**
+ susceptible to vulnerabilities and is allowed to fail ([Auto Dependency Scanning](index.md#auto-dependency-scanning-ultimate)) **[ULTIMATE]**
- The `sast` job runs static analysis on the current code to check for potential
- security issues and is allowed to fail([Auto SAST](index.md#auto-sast)) **[ULTIMATE]**
+ security issues and is allowed to fail([Auto SAST](index.md#auto-sast-ultimate)) **[ULTIMATE]**
- The `license_management` job searches the application's dependencies to determine each of their
- licenses and is allowed to fail ([Auto License Management](index.md#auto-license-management)) **[ULTIMATE]**
+ licenses and is allowed to fail ([Auto License Management](index.md#auto-license-management-ultimate)) **[ULTIMATE]**
NOTE: **Note:**
As you might have noticed, all jobs except `test` are allowed to fail in the
@@ -178,7 +178,7 @@ deploys the application in Kubernetes ([Auto Deploy](index.md#auto-deploy)).
Lastly, in the **performance** stage, some performance tests will run
on the deployed application
-([Auto Browser Performance Testing](index.md#auto-browser-performance-testing)). **[PREMIUM]**
+([Auto Browser Performance Testing](index.md#auto-browser-performance-testing-premium)). **[PREMIUM]**
---
@@ -285,8 +285,8 @@ all within GitLab. Despite its automatic nature, Auto DevOps can also be configu
and customized to fit your workflow. Here are some helpful resources for further reading:
1. [Auto DevOps](index.md)
-1. [Multiple Kubernetes clusters](index.md#using-multiple-kubernetes-clusters) **[PREMIUM]**
-1. [Incremental rollout to production](index.md#incremental-rollout-to-production) **[PREMIUM]**
+1. [Multiple Kubernetes clusters](index.md#using-multiple-kubernetes-clusters-premium) **[PREMIUM]**
+1. [Incremental rollout to production](index.md#incremental-rollout-to-production-premium) **[PREMIUM]**
1. [Disable jobs you don't need with environment variables](index.md#environment-variables)
1. [Use a static IP for your cluster](../../user/project/clusters/index.md#using-a-static-ip)
1. [Use your own buildpacks to build your application](index.md#custom-buildpacks)
diff --git a/doc/topics/git/numerous_undo_possibilities_in_git/index.md b/doc/topics/git/numerous_undo_possibilities_in_git/index.md
index 7195b0f0f04..8a8021dc36d 100644
--- a/doc/topics/git/numerous_undo_possibilities_in_git/index.md
+++ b/doc/topics/git/numerous_undo_possibilities_in_git/index.md
@@ -45,7 +45,6 @@ Here's what we'll cover in this tutorial:
- [How to modify history](#how-modifying-history-is-done)
- [How to remove sensitive information from repository](#deleting-sensitive-information-from-commits)
-
### Branching strategy
[Git][git-official] is a de-centralized version control system, which means that beside regular
@@ -64,14 +63,12 @@ prevent that anything is lost or out of sync when feature is complete. You can a
read through this blog post on [Git Tips & Tricks][gitlab-git-tips-n-tricks]
to learn how to easily **do** things in Git.
-
## Undo local changes
Until you push your changes to any remote repository, they will only affect you.
That broadens your options on how to handle undoing them. Still, local changes
can be on various stages and each stage has a different approach on how to tackle them.
-
### Unstaged local changes (before you commit)
When a change is made, but it is not added to the staged tree, Git itself
@@ -315,7 +312,6 @@ In case you want to modify something introduced in commit `B`.
You can find some more examples in [below section where we explain how to modify
history](#how-modifying-history-is-done)
-
### Redoing the Undo
Sometimes you realize that the changes you undid were useful and you want them
@@ -396,8 +392,8 @@ a nicer history of your contribution.
Keep in mind that this also removes the comments attached to certain commits
in merge requests, so if you need to retain traceability in GitLab, then
modifying history is not acceptable.
-A feature-branch of a merge request is a public branch and might be used by
-other developers, but project process and rules might allow or require
+A feature-branch of a merge request is a public branch and might be used by
+other developers, but project process and rules might allow or require
you to use `git rebase` (command that changes history) to reduce number of
displayed commits on target branch after reviews are done (for example
GitLab). There is a `git merge --squash` command which does exactly that
diff --git a/doc/university/glossary/README.md b/doc/university/glossary/README.md
index cf148a424db..254e234a22c 100644
--- a/doc/university/glossary/README.md
+++ b/doc/university/glossary/README.md
@@ -706,4 +706,3 @@ Files that have been modified but are not committed. Check them by using the com
### YAML
A human-readable data serialization [language](http://www.yaml.org/about.html) that takes concepts from programming languages such as C, Perl, and Python, and ideas from XML and the data format of electronic mail.
-
diff --git a/doc/university/high-availability/aws/README.md b/doc/university/high-availability/aws/README.md
index 77a1892b656..b4ba5b7a24e 100644
--- a/doc/university/high-availability/aws/README.md
+++ b/doc/university/high-availability/aws/README.md
@@ -80,7 +80,6 @@ our newly created VPC.
![Route Table](img/route_table.png)
-
### Internet Gateway
Now still on the same dashboard head over to Internet Gateways and
@@ -238,7 +237,6 @@ running reconfigure we need to make sure all our services are tied down
so just leave the reconfigure command until after we edit our gitlab.rb
file.
-
### Extension for PostgreSQL
Connect to your new RDS instance to verify access and to install
@@ -277,8 +275,6 @@ username, and password.
Next, we only need to configure the Redis section by adding the host and
uncommenting the port.
-
-
The last configuration step is to [change the default file locations ](http://docs.gitlab.com/ee/administration/high_availability/nfs.html)
to make the EFS integration easier to manage.
@@ -344,7 +340,6 @@ text area that allows us to add a lot of custom configurations which
allows you to add a custom script for when launching an instance. Let's
add the following script to the User Data section:
-
#cloud-config
package_upgrade: true
packages:
diff --git a/doc/university/support/README.md b/doc/university/support/README.md
index 805af253367..feb90ae9bad 100644
--- a/doc/university/support/README.md
+++ b/doc/university/support/README.md
@@ -2,7 +2,6 @@
comments: false
---
-
# Support Boot Camp
**Goal:** Prepare new Service Engineers at GitLab
diff --git a/doc/university/training/end-user/README.md b/doc/university/training/end-user/README.md
index 53578a34d1c..60c0eadc572 100644
--- a/doc/university/training/end-user/README.md
+++ b/doc/university/training/end-user/README.md
@@ -2,7 +2,6 @@
comments: false
---
-
# Training
This training material is the markdown used to generate training slides
@@ -205,7 +204,6 @@ git push origin squash_some_bugs
- Anyone can comment, not just the assignee
- Push corrections to the same branch
-
---
### Merge request example
@@ -395,7 +393,6 @@ git revert vs git reset
Reset removes the commit while revert removes the changes but leaves the commit
Revert is safer considering we can revert a revert
-
# Changed file
git commit -am "bug introduced"
git revert HEAD
diff --git a/doc/university/training/topics/git_log.md b/doc/university/training/topics/git_log.md
index 127fdf4d44a..763ef802a04 100644
--- a/doc/university/training/topics/git_log.md
+++ b/doc/university/training/topics/git_log.md
@@ -40,7 +40,6 @@ Git log lists commit history. It allows searching and filtering.
git log --since=1.month.ago --until=3.weeks.ago
```
-
----------
## Git Log Workflow
diff --git a/doc/university/training/topics/rollback_commits.md b/doc/university/training/topics/rollback_commits.md
index ca3a64e4347..96b89e3319a 100644
--- a/doc/university/training/topics/rollback_commits.md
+++ b/doc/university/training/topics/rollback_commits.md
@@ -51,7 +51,6 @@ comments: false
1. Pull for updates
1. Push changes
-
----------
## Commands
diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md
index 7c6a14cb104..1f4b5fbffa7 100644
--- a/doc/update/mysql_to_postgresql.md
+++ b/doc/update/mysql_to_postgresql.md
@@ -97,7 +97,6 @@ Now, you can use pgloader to migrate the data from MySQL to PostgreSQL:
1. Once the migration finishes, you should see a summary table that looks like
the following:
-
```
table name read imported errors total time
----------------------------------------------- --------- --------- --------- --------------
@@ -245,7 +244,6 @@ Now, you can use pgloader to migrate the data from MySQL to PostgreSQL:
1. Once the migration finishes, you should see a summary table that looks like
the following:
-
```
table name read imported errors total time
----------------------------------------------- --------- --------- --------- --------------
@@ -286,4 +284,3 @@ If you experience 500 errors after the migration, try to clear the cache:
``` bash
sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
```
-
diff --git a/doc/update/restore_after_failure.md b/doc/update/restore_after_failure.md
index 01c52aae7f5..faa3a6e65cb 100644
--- a/doc/update/restore_after_failure.md
+++ b/doc/update/restore_after_failure.md
@@ -80,4 +80,3 @@ exit
Once the migration is successfully marked, run the rake `db:migrate` task again.
You will likely have to repeat this process several times until all failed
migrations are marked complete.
-
diff --git a/doc/update/upgrading_postgresql_using_slony.md b/doc/update/upgrading_postgresql_using_slony.md
index d2e2cf439b5..835166837a8 100644
--- a/doc/update/upgrading_postgresql_using_slony.md
+++ b/doc/update/upgrading_postgresql_using_slony.md
@@ -270,7 +270,6 @@ sudo -u gitlab-psql /opt/gitlab/embedded/bin/slon_start 1 --conf /var/opt/gitlab
If all went well this will produce output such as:
-
```
Invoke slon for node 1 - /opt/gitlab/embedded/bin/slon -p /var/run/slony1/slony_replication_node1.pid -s 1000 -d2 slony_replication 'host=192.168.0.7 dbname=gitlabhq_production user=slony port=5432 password=hieng8ezohHuCeiqu0leeghai4aeyahp' > /var/log/gitlab/slony/node1/gitlabhq_production-2016-10-06.log 2>&1 &
Slon successfully started for cluster slony_replication, node node1
diff --git a/doc/user/abuse_reports.md b/doc/user/abuse_reports.md
index 1f4f598b121..41ee7e62b2c 100644
--- a/doc/user/abuse_reports.md
+++ b/doc/user/abuse_reports.md
@@ -25,7 +25,6 @@ To report abuse from a user's comment:
1. Complete an abuse report.
1. Click the **Send report** button.
-
NOTE: **Note:**
A URL to the reported user's comment will be
pre-filled in the abuse report's **Message** field.
diff --git a/doc/user/admin_area/monitoring/health_check.md b/doc/user/admin_area/monitoring/health_check.md
index 43b1190fb48..c22982ac190 100644
--- a/doc/user/admin_area/monitoring/health_check.md
+++ b/doc/user/admin_area/monitoring/health_check.md
@@ -28,7 +28,6 @@ With default whitelist settings, the probes can be accessed from localhost:
- `http://localhost/-/readiness`
- `http://localhost/-/liveness`
-
The first endpoint, `/-/health/`, only checks whether the application server is running. It does
-not verify the database or other services are running. A successful response will return
a 200 status code with the following message:
diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md
index d4853a5842e..01979f12a01 100644
--- a/doc/user/admin_area/settings/continuous_integration.md
+++ b/doc/user/admin_area/settings/continuous_integration.md
@@ -20,7 +20,7 @@ From now on, every existing project and newly created ones that don't have a
`.gitlab-ci.yml`, will use the Auto DevOps pipelines.
If you want to disable it for a specific project, you can do so in
-[its settings](../../../topics/autodevops/index.md#enabling-auto-devops).
+[its settings](../../../topics/autodevops/index.md##enablingdisabling-auto-devops).
## Maximum artifacts size **[CORE ONLY]**
@@ -38,7 +38,7 @@ To change it:
The default expiration time of the [job artifacts](../../../administration/job_artifacts.md)
can be set in the Admin area of your GitLab instance. The syntax of duration is
-described in [`artifacts:expire_in`](../../../ci/yaml/README.md#artifacts-expire_in)
+described in [`artifacts:expire_in`](../../../ci/yaml/README.md#artifactsexpire_in)
and the default value is `30 days`. On GitLab.com they
[never expire](../../gitlab_com/index.md#gitlab-ci-cd).
@@ -47,7 +47,7 @@ and the default value is `30 days`. On GitLab.com they
1. Hit **Save changes** for the changes to take effect.
This setting is set per job and can be overridden in
-[`.gitlab-ci.yml`](../../../ci/yaml/README.md#artifacts-expire_in).
+[`.gitlab-ci.yml`](../../../ci/yaml/README.md#artifactsexpire_in).
To disable the expiration, set it to `0`. The default unit is in seconds.
## Archive jobs **[CORE ONLY]**
diff --git a/doc/user/admin_area/settings/index.md b/doc/user/admin_area/settings/index.md
index 93767aefb51..8358fe64f18 100644
--- a/doc/user/admin_area/settings/index.md
+++ b/doc/user/admin_area/settings/index.md
@@ -14,6 +14,10 @@ include:
- [Usage statistics](usage_statistics.md)
- [Visibility and access controls](visibility_and_access_controls.md)
+NOTE: **Note:**
+You can change the [first day of the week](../../profile/preferences.md) for the entire GitLab instance
+in the **Localization** section of **Admin area > Settings > Preferences**.
+
## GitLab.com admin area settings
Most of the settings under the admin area change the behavior of the whole
diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md
index 84f4b0b3922..d202b1260ad 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -253,8 +253,9 @@ to newer issues or merge requests.
- The people participating in the discussion are trolling, abusive, or otherwise
being unproductive.
-In these cases, a user with Maintainer permissions or higher in the project can lock (and unlock)
-an issue or a merge request, using the "Lock" section in the sidebar:
+In these cases, a user with Developer permissions or higher in the project can lock (and unlock)
+an issue or a merge request, using the "Lock" section in the sidebar. For issues,
+a user with Reporter permissions can lock (and unlock).
| Unlock | Lock |
| :-----------: | :----------: |
diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index c1c9b8bf43c..762cf911fcf 100644
--- a/doc/user/gitlab_com/index.md
+++ b/doc/user/gitlab_com/index.md
@@ -60,7 +60,7 @@ Below are the current settings regarding [GitLab CI/CD](../../ci/README.md).
| Setting | GitLab.com | Default |
| ----------- | ----------------- | ------------- |
| Artifacts maximum size | 1G | 100M |
-| Artifacts [expiry time](../../ci/yaml/README.md#artifacts-expire_in) | kept forever | deleted after 30 days unless otherwise specified |
+| Artifacts [expiry time](../../ci/yaml/README.md#artifactsexpire_in) | kept forever | deleted after 30 days unless otherwise specified |
## Repository size limit
diff --git a/doc/user/group/clusters/index.md b/doc/user/group/clusters/index.md
index 9fc50741407..8d223d1c5f0 100644
--- a/doc/user/group/clusters/index.md
+++ b/doc/user/group/clusters/index.md
@@ -95,7 +95,6 @@ For example, let's say we have the following Kubernetes clusters:
| Test | `test` | Group |
| Development| `*` | Group |
-
And the following environments are set in [`.gitlab-ci.yml`](../../../ci/yaml/README.md):
```yaml
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index 9a01625f3ff..a7a87773eec 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -288,7 +288,6 @@ On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/he
Ubuntu 18.04 (like many modern Linux distros) has this font installed by default.
```
-
Sometimes you want to <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/monkey.png" width="20px" height="20px"> around a bit and add some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/star2.png" width="20px" height="20px"> to your <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/speech_balloon.png" width="20px" height="20px">. Well we have a gift for you:
<img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/zap.png" width="20px" height="20px">You can use emoji anywhere GFM is supported. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/v.png" width="20px" height="20px">
@@ -453,14 +452,14 @@ Color written inside backticks will be followed by a color "chip".
Examples:
- `#F00`
- `#F00A`
- `#FF0000`
- `#FF0000AA`
- `RGB(0,255,0)`
- `RGB(0%,100%,0%)`
- `RGBA(0,255,0,0.7)`
- `HSL(540,70%,50%)`
+ `#F00`
+ `#F00A`
+ `#FF0000`
+ `#FF0000AA`
+ `RGB(0,255,0)`
+ `RGB(0%,100%,0%)`
+ `RGBA(0,255,0,0.7)`
+ `HSL(540,70%,50%)`
`HSLA(540,70%,50%,0.7)`
Becomes:
@@ -987,7 +986,6 @@ while the equation for the theory of relativity is E = mc<sup>2</sup>.
The formula for water is H<sub>2</sub>O while the equation for the theory of relativity is E = mc<sup>2</sup>.
-
## Wiki-specific Markdown
The following examples show how links inside wikis behave.
diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md
index efb031b8239..ba756c4b793 100644
--- a/doc/user/profile/index.md
+++ b/doc/user/profile/index.md
@@ -89,7 +89,6 @@ To enable private profile:
1. Check the "Private profile" option.
1. Hit **Update profile settings**.
-
NOTE: **Note:**
You and GitLab admins can see your the abovementioned information on your profile even if it is private.
diff --git a/doc/user/profile/preferences.md b/doc/user/profile/preferences.md
index 7387d1810ca..db68510c46d 100644
--- a/doc/user/profile/preferences.md
+++ b/doc/user/profile/preferences.md
@@ -93,6 +93,12 @@ You can choose between 3 options:
## Localization
+### Language
+
+Select your preferred language from a list of supported languages.
+
+*This feature is experimental and translations are not complete yet.*
+
### First day of the week
The first day of the week can be customised for calendar views and date pickers.
diff --git a/doc/user/project/clusters/eks_and_gitlab/index.md b/doc/user/project/clusters/eks_and_gitlab/index.md
index 0e6d4bce153..ea09f9f1547 100644
--- a/doc/user/project/clusters/eks_and_gitlab/index.md
+++ b/doc/user/project/clusters/eks_and_gitlab/index.md
@@ -253,7 +253,7 @@ With RBAC disabled and services deployed,
[Auto DevOps](../../../../topics/autodevops/index.md) can now be leveraged
to build, test, and deploy the app.
-[Enable Auto DevOps](../../../../topics/autodevops/index.md##enablingdisabling-auto-devops-at-the-project-level)
+[Enable Auto DevOps](../../../../topics/autodevops/index.md#enablingdisabling-auto-devops-at-the-project-level)
if not already enabled. If a wildcard DNS entry was created resolving to the
Load Balancer, enter it in the `domain` field under the Auto DevOps settings.
Otherwise, the deployed app will not be externally available outside of the cluster.
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index 85a4af24dc5..42aa6840609 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -307,6 +307,28 @@ you should be careful as GitLab cannot detect it. In this case, installing
Tiller via the applications will result in the cluster having it twice, which
can lead to confusion during deployments.
+### Upgrading applications
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24789)
+in GitLab 11.8.
+
+Users can perform a one-click upgrade for the GitLab Runner application,
+when there is an upgrade available.
+
+To upgrade the GitLab Runner application:
+
+1. Navigate to your project's **Operations > Kubernetes**.
+1. Select your cluster.
+1. Click the **Upgrade** button for the Runnner application.
+
+The **Upgrade** button will not be shown if there is no upgrade
+available.
+
+NOTE: **Note:**
+Upgrades will reset values back to the values built into the `runner`
+chart plus the values set by
+[`values.yaml`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/vendor/runner/values.yaml)
+
## Getting the external IP address
NOTE: **Note:**
diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md
index aa1e165e3a2..fc3a4a757d0 100644
--- a/doc/user/project/clusters/serverless/index.md
+++ b/doc/user/project/clusters/serverless/index.md
@@ -138,7 +138,6 @@ Follow these steps to deploy a function using the Node.js runtime to your Knativ
FUNCTION: echo
```
-
The `serverless.yml` file references both an `echo` directory (under `buildargs`) and an `echo` file (under `handler`),
which is a reference to `echo.js` in the [repository](https://gitlab.com/knative-examples/functions). Additionally, it
contains three sections with distinct parameters:
@@ -150,7 +149,6 @@ contains three sections with distinct parameters:
| `service` | Name for the Knative service which will serve the function. |
| `description` | A short description of the `service`. |
-
### `provider`
| Parameter | Description |
diff --git a/doc/user/project/container_registry.md b/doc/user/project/container_registry.md
index 638b73bfcb6..dec6eac2508 100644
--- a/doc/user/project/container_registry.md
+++ b/doc/user/project/container_registry.md
@@ -47,7 +47,6 @@ to enable it.
> - To move or rename a repository with a container registry you will have to
> delete all existing images.
-
If you visit the **Registry** link under your project's menu, you can see the
explicit instructions to login to the Container Registry using your GitLab
credentials.
diff --git a/doc/user/project/cycle_analytics.md b/doc/user/project/cycle_analytics.md
index b98221bea61..529a82ded9e 100644
--- a/doc/user/project/cycle_analytics.md
+++ b/doc/user/project/cycle_analytics.md
@@ -46,7 +46,7 @@ You can see that there are seven stages in total:
## How the data is measured
Cycle Analytics records cycle time and data based on the project issues with the
-exception of the staging and production stages, where only data deployed to
+exception of the staging and production stages, where only data deployed to
production are measured.
Specifically, if your CI is not set up and you have not defined a `production`
@@ -86,7 +86,6 @@ So, the Cycle Analytics dashboard won't present any data:
- For staging and production stages, if the project has no `production` or `production/*`
environment.
-
## Example workflow
Below is a simple fictional workflow of a single cycle that happens in a
@@ -159,7 +158,6 @@ Learn more about Cycle Analytics in the following resources:
- [Cycle Analytics feature preview](https://about.gitlab.com/2016/09/16/feature-preview-introducing-cycle-analytics/)
- [Cycle Analytics feature highlight](https://about.gitlab.com/2016/09/21/cycle-analytics-feature-highlight/)
-
[board]: issue_board.md#creating-a-new-list
[ce-5986]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5986
[ce-20975]: https://gitlab.com/gitlab-org/gitlab-ce/issues/20975
diff --git a/doc/user/project/description_templates.md b/doc/user/project/description_templates.md
index 7c94f4ef4d8..08647caaf07 100644
--- a/doc/user/project/description_templates.md
+++ b/doc/user/project/description_templates.md
@@ -93,4 +93,3 @@ Possible fixes
[ce-4981]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4981
[gitlab-ce-templates]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/.gitlab
-
diff --git a/doc/user/project/integrations/bamboo.md b/doc/user/project/integrations/bamboo.md
index 48aabd02438..3206a39dc08 100644
--- a/doc/user/project/integrations/bamboo.md
+++ b/doc/user/project/integrations/bamboo.md
@@ -59,4 +59,3 @@ Bamboo under 'Trigger IP addresses'.
> **Note:**
> - Starting with GitLab 8.14.0, builds are triggered on push events.
-
diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md
index 60a4c6aaf64..cbf08a4f30a 100644
--- a/doc/user/project/integrations/webhooks.md
+++ b/doc/user/project/integrations/webhooks.md
@@ -1228,6 +1228,15 @@ by uncommenting or adding the following setting to your `/etc/gitlab/gitlab.rb`:
gitlab_rails['webhook_timeout'] = 10
```
+### Troubleshooting: "Unable to get local issuer certificate"
+
+When SSL verification is enabled, this error indicates that GitLab isn't able to verify the SSL certificate of the webhook endpoint.
+Typically, this is because the root certificate isn't issued by a trusted certification authority as
+determined by [CAcert.org](http://www.cacert.org/).
+
+Should that not be the case, consider using [SSL Checker](https://www.sslshopper.com/ssl-checker.html) to identify faults.
+Missing intermediate certificates are a common point of verification failure.
+
## Example webhook receiver
If you want to see GitLab's webhooks in action for testing purposes you can use
diff --git a/doc/user/project/pages/introduction.md b/doc/user/project/pages/introduction.md
index fa65a206273..23eb88fd305 100644
--- a/doc/user/project/pages/introduction.md
+++ b/doc/user/project/pages/introduction.md
@@ -151,7 +151,7 @@ Depending on how you plan to publish your website, the steps defined in the
Be aware that Pages are by default branch/tag agnostic and their deployment
relies solely on what you specify in `.gitlab-ci.yml`. If you don't limit the
-`pages` job with the [`only` parameter](../../../ci/yaml/README.md#only-and-except),
+`pages` job with the [`only` parameter](../../../ci/yaml/README.md#only-and-except-simplified),
whenever a new commit is pushed to whatever branch or tag, the Pages will be
overwritten. In the example below, we limit the Pages to be deployed whenever
a commit is pushed only on the `master` branch:
@@ -252,7 +252,7 @@ get you started.
Remember that GitLab Pages are by default branch/tag agnostic and their
deployment relies solely on what you specify in `.gitlab-ci.yml`. You can limit
-the `pages` job with the [`only` parameter](../../../ci/yaml/README.md#only-and-except),
+the `pages` job with the [`only` parameter](../../../ci/yaml/README.md#only-and-except-simplified),
whenever a new commit is pushed to a branch that will be used specifically for
your pages.
diff --git a/doc/user/project/pipelines/job_artifacts.md b/doc/user/project/pipelines/job_artifacts.md
index 4ab66063dcf..bf939dbdaa3 100644
--- a/doc/user/project/pipelines/job_artifacts.md
+++ b/doc/user/project/pipelines/job_artifacts.md
@@ -195,5 +195,5 @@ artifacts and the job's trace.
In order to retrieve a job artifact of a different project, you might need to use a private token in order to [authenticate and download](../../../api/jobs.md#get-job-artifacts) the artifacts.
-[expiry date]: ../../../ci/yaml/README.md#artifacts-expire_in
+[expiry date]: ../../../ci/yaml/README.md#artifactsexpire_in
[ce-14399]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14399 \ No newline at end of file
diff --git a/doc/user/project/pipelines/schedules.md b/doc/user/project/pipelines/schedules.md
index ec8b8444d99..58a0fbc97cd 100644
--- a/doc/user/project/pipelines/schedules.md
+++ b/doc/user/project/pipelines/schedules.md
@@ -1,4 +1,4 @@
-# Pipeline Schedules
+# Pipeline schedules
> **Notes**:
> - This feature was introduced in 9.1 as [Trigger Schedule][ce-10533].
diff --git a/doc/user/project/protected_tags.md b/doc/user/project/protected_tags.md
index 3d8fff9f733..26bec323f02 100644
--- a/doc/user/project/protected_tags.md
+++ b/doc/user/project/protected_tags.md
@@ -10,7 +10,6 @@ This feature evolved out of [Protected Branches](protected_branches.md)
Protected tags will prevent anyone from updating or deleting the tag, as and will prevent creation of matching tags based on the permissions you have selected. By default, anyone without Maintainer permission will be prevented from creating tags.
-
## Configuring protected tags
To protect a tag, you need to have at least Maintainer permission level.
@@ -43,7 +42,6 @@ matching the wildcard. For example:
| `*gitlab*` | `gitlab`, `gitlab/v1` |
| `*` | `v1.0.1rc2`, `accidental-tag` |
-
Two different wildcards can potentially match the same tag. For example,
`*-stable` and `production-*` would both match a `production-stable` tag.
In that case, if _any_ of these protected tags have a setting like
@@ -54,7 +52,6 @@ all matching tags:
![Protected tag matches](img/protected_tag_matches.png)
-
---
[ce-10356]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10356 "Protected Tags"
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index 85a03d125dd..392e72dcc5c 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -55,7 +55,6 @@ discussions, and descriptions:
| `/merge` | Merge (when pipeline succeeds) | | ✓ |
| `/create_merge_request <branch name>` | Create a new merge request starting from the current issue | ✓ | |
-
## Quick actions for commit messages
The following quick actions are applicable for commit messages:
diff --git a/doc/user/project/repository/gpg_signed_commits/index.md b/doc/user/project/repository/gpg_signed_commits/index.md
index db19370b97c..6495ede42e0 100644
--- a/doc/user/project/repository/gpg_signed_commits/index.md
+++ b/doc/user/project/repository/gpg_signed_commits/index.md
@@ -190,7 +190,6 @@ key to use.
Replace `30F2B65B9246B6CA` with your GPG key ID.
-
1. (Optional) If Git is using `gpg` and you get errors like `secret key not available`
or `gpg: signing failed: secret key not available`, run the following command to
change to `gpg2`:
diff --git a/doc/user/project/repository/web_editor.md b/doc/user/project/repository/web_editor.md
index 57e8437697b..ce9d23bf911 100644
--- a/doc/user/project/repository/web_editor.md
+++ b/doc/user/project/repository/web_editor.md
@@ -112,7 +112,6 @@ screenshot above will yield a branch named
Since GitLab 9.0, when you click the `New branch` in an empty repository project, GitLab automatically creates the master branch, commits a blank `README.md` file to it and creates and redirects you to a new branch based on the issue title.
If your [project is already configured with a deployment service][project-services-doc] (e.g. Kubernetes), GitLab takes one step further and prompts you to set up [auto deploy][auto-deploy-doc] by helping you create a `.gitlab-ci.yml` file.
-
After the branch is created, you can edit files in the repository to fix
the issue. When a merge request is created based on the newly created branch,
the description field will automatically display the [issue closing pattern]
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index b09a3f927d1..d5f4a7fd4ac 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -121,7 +121,7 @@ NOTE: **Note:**
GitLab administrators can use the admin interface to move any project to any
namespace if needed.
-[permissions]: ../../permissions.md##project-members-permissions
+[permissions]: ../../permissions.md#project-members-permissions
## Operations settings
diff --git a/doc/workflow/forking_workflow.md b/doc/workflow/forking_workflow.md
index 733d079bd4a..02be0ad191d 100644
--- a/doc/workflow/forking_workflow.md
+++ b/doc/workflow/forking_workflow.md
@@ -10,7 +10,6 @@ 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.
diff --git a/jest.config.js b/jest.config.js
index 4dab7c2891a..97979a38074 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -13,10 +13,11 @@ if (process.env.CI) {
// eslint-disable-next-line import/no-commonjs
module.exports = {
- testMatch: ['<rootDir>/spec/frontend/**/*_spec.js'],
+ testMatch: ['<rootDir>/spec/frontend/**/*_spec.js', '<rootDir>/ee/spec/frontend/**/*_spec.js'],
moduleFileExtensions: ['js', 'json', 'vue'],
moduleNameMapper: {
'^~(.*)$': '<rootDir>/app/assets/javascripts$1',
+ '^ee(.*)$': '<rootDir>/ee/app/assets/javascripts$1',
'^helpers(.*)$': '<rootDir>/spec/frontend/helpers$1',
},
collectCoverageFrom: ['<rootDir>/app/assets/javascripts/**/*.{js,vue}'],
@@ -31,4 +32,5 @@ module.exports = {
'^.+\\.js$': 'babel-jest',
'^.+\\.vue$': 'vue-jest',
},
+ transformIgnorePatterns: ['node_modules/(?!(@gitlab/ui)/)'],
};
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 27da2c2e5ed..9199f898ea0 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -557,6 +557,15 @@ module API
expose :time_stats, using: 'API::Entities::IssuableTimeStats' do |issue|
issue
end
+
+ expose :merge_requests_count do |issue, options|
+ if options[:issuable_metadata]
+ # Avoids an N+1 query when metadata is included
+ options[:issuable_metadata][issue.id].merge_requests_count
+ else
+ issue.merge_requests_closing_issues.count
+ end
+ end
end
class Issue < IssueBasic
diff --git a/lib/api/features.rb b/lib/api/features.rb
index 835aac05905..4dc1834c644 100644
--- a/lib/api/features.rb
+++ b/lib/api/features.rb
@@ -42,6 +42,7 @@ module API
requires :value, type: String, desc: '`true` or `false` to enable/disable, an integer for percentage of time'
optional :feature_group, type: String, desc: 'A Feature group name'
optional :user, type: String, desc: 'A GitLab username'
+ optional :group, type: String, desc: "A GitLab group's path, such as 'gitlab-org'"
optional :project, type: String, desc: 'A projects path, like gitlab-org/gitlab-ce'
end
post ':name' do
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 2eb7b04711a..54cd4cd9cdb 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -299,6 +299,12 @@ module API
items.search(text)
end
+ def order_options_with_tie_breaker
+ order_options = { params[:order_by] => params[:sort] }
+ order_options['id'] ||= 'desc'
+ order_options
+ end
+
# error helpers
def forbidden!(reason = nil)
@@ -393,7 +399,7 @@ module API
# rubocop: disable CodeReuse/ActiveRecord
def reorder_projects(projects)
- projects.reorder(params[:order_by] => params[:sort])
+ projects.reorder(order_options_with_tie_breaker)
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb
index 4eaaca96b49..fe78049af87 100644
--- a/lib/api/helpers/internal_helpers.rb
+++ b/lib/api/helpers/internal_helpers.rb
@@ -81,6 +81,14 @@ module API
Gitlab::GlRepository.gl_repository(project, wiki?)
end
+ def gl_project_path
+ if wiki?
+ project.wiki.full_path
+ else
+ project.full_path
+ end
+ end
+
# Return the repository depending on whether we want the wiki or the
# regular repository
def repository
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index 9488b3469d9..70b32f7d758 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -77,6 +77,7 @@ module API
when ::Gitlab::GitAccessResult::Success
payload = {
gl_repository: gl_repository,
+ gl_project_path: gl_project_path,
gl_id: Gitlab::GlId.gl_id(user),
gl_username: user&.username,
git_config_options: [],
@@ -117,13 +118,7 @@ module API
raise ActiveRecord::RecordNotFound.new("No key_id or user_id passed!")
end
- token_handler = Gitlab::LfsToken.new(actor)
-
- {
- username: token_handler.actor_name,
- lfs_token: token_handler.token,
- repository_http_path: project.http_url_to_repo
- }
+ Gitlab::LfsToken.new(actor).authentication_payload(project.http_url_to_repo)
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index b3636c98550..94ed9ac6fb1 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -29,8 +29,7 @@ module API
issues = IssuesFinder.new(current_user, args).execute
.preload(:assignees, :labels, :notes, :timelogs, :project, :author, :closed_by)
-
- issues.reorder(args[:order_by] => args[:sort])
+ issues.reorder(order_options_with_tie_breaker)
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -304,19 +303,14 @@ module API
get ':id/issues/:issue_iid/related_merge_requests' do
issue = find_project_issue(params[:issue_iid])
- merge_request_iids = ::Issues::ReferencedMergeRequestsService.new(user_project, current_user)
+ merge_requests = ::Issues::ReferencedMergeRequestsService.new(user_project, current_user)
.execute(issue)
.flatten
- .map(&:iid)
-
- merge_requests =
- if merge_request_iids.present?
- MergeRequestsFinder.new(current_user, project_id: user_project.id, iids: merge_request_iids).execute
- else
- MergeRequest.none
- end
- present paginate(merge_requests), with: Entities::MergeRequestBasic, current_user: current_user, project: user_project
+ present paginate(::Kaminari.paginate_array(merge_requests)),
+ with: Entities::MergeRequestBasic,
+ current_user: current_user,
+ project: user_project
end
desc 'List merge requests closing issue' do
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index df46b4446ff..f8d2ba49d2f 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -38,7 +38,7 @@ module API
args[:scope] = args[:scope].underscore if args[:scope]
merge_requests = MergeRequestsFinder.new(current_user, args).execute
- .reorder(args[:order_by] => args[:sort])
+ .reorder(order_options_with_tie_breaker)
merge_requests = paginate(merge_requests)
.preload(:source_project, :target_project)
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index 1bdf7aeb119..f7bd092ce50 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -39,7 +39,7 @@ module API
# at the DB query level (which we cannot in that case), the current
# page can have less elements than :per_page even if
# there's more than one page.
- raw_notes = noteable.notes.with_metadata.reorder(params[:order_by] => params[:sort])
+ raw_notes = noteable.notes.with_metadata.reorder(order_options_with_tie_breaker)
notes =
# paginate() only works with a relation. This could lead to a
# mismatch between the pagination headers info and the actual notes
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 163c7505a65..145897516a0 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -431,7 +431,7 @@ module API
{
required: false,
name: :jira_issue_transition_id,
- type: Integer,
+ type: String,
desc: 'The ID of a transition that moves issues to a closed state. You can find this number under the JIRA workflow administration (**Administration > Issues > Workflows**) by selecting **View** under **Operations** of the desired workflow of your project. The ID of each state can be found inside the parenthesis of each transition name under the **Transitions (id)** column ([see screenshot][trans]). By default, this ID is set to `2`'
}
],
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 8ce09a8881b..7d88880d412 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -26,7 +26,7 @@ module API
# rubocop: disable CodeReuse/ActiveRecord
def reorder_users(users)
if params[:order_by] && params[:sort]
- users.reorder(params[:order_by] => params[:sort])
+ users.reorder(order_options_with_tie_breaker)
else
users
end
diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb
index 9c14b26c65a..fbd451efb23 100644
--- a/lib/bitbucket_server/connection.rb
+++ b/lib/bitbucket_server/connection.rb
@@ -77,6 +77,7 @@ module BitbucketServer
private
def check_errors!(response)
+ return if ActionDispatch::Response::NO_CONTENT_CODES.include?(response.code)
raise ConnectionError, "Response is not valid JSON" unless response.parsed_response.is_a?(Hash)
return if response.code >= 200 && response.code < 300
diff --git a/lib/feature.rb b/lib/feature.rb
index e59cd70f822..749c861d740 100644
--- a/lib/feature.rb
+++ b/lib/feature.rb
@@ -111,11 +111,11 @@ class Feature
end
def gate_specified?
- %i(user project feature_group).any? { |key| params.key?(key) }
+ %i(user project group feature_group).any? { |key| params.key?(key) }
end
def targets
- [feature_group, user, project].compact
+ [feature_group, user, project, group].compact
end
private
@@ -139,5 +139,11 @@ class Feature
Project.find_by_full_path(params[:project])
end
+
+ def group
+ return unless params.key?(:group)
+
+ Group.find_by_full_path(params[:group])
+ end
end
end
diff --git a/lib/gitlab/chat.rb b/lib/gitlab/chat.rb
new file mode 100644
index 00000000000..23d4fb36b66
--- /dev/null
+++ b/lib/gitlab/chat.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Chat
+ # Returns `true` if Chatops is available for the current instance.
+ def self.available?
+ ::Feature.enabled?(:chatops, default_enabled: true)
+ end
+ end
+end
diff --git a/lib/gitlab/chat/command.rb b/lib/gitlab/chat/command.rb
new file mode 100644
index 00000000000..49b7dcf4bbe
--- /dev/null
+++ b/lib/gitlab/chat/command.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Chat
+ # Class for scheduling chat pipelines.
+ #
+ # A Command takes care of creating a `Ci::Pipeline` with all the data
+ # necessary to execute a chat command. This includes data such as the chat
+ # data (e.g. the response URL) and any environment variables that should be
+ # exposed to the chat command.
+ class Command
+ include Utils::StrongMemoize
+
+ attr_reader :project, :chat_name, :name, :arguments, :response_url,
+ :channel
+
+ # project - The Project to schedule the command for.
+ # chat_name - The ChatName belonging to the user that scheduled the
+ # command.
+ # name - The name of the chat command to run.
+ # arguments - The arguments (as a String) to pass to the command.
+ # channel - The channel the message was sent from.
+ # response_url - The URL to send the response back to.
+ def initialize(project:, chat_name:, name:, arguments:, channel:, response_url:)
+ @project = project
+ @chat_name = chat_name
+ @name = name
+ @arguments = arguments
+ @channel = channel
+ @response_url = response_url
+ end
+
+ # Tries to create a new pipeline.
+ #
+ # This method will return a pipeline that _may_ be persisted, or `nil` if
+ # the pipeline could not be created.
+ def try_create_pipeline
+ return unless valid?
+
+ create_pipeline
+ end
+
+ def create_pipeline
+ service = ::Ci::CreatePipelineService.new(
+ project,
+ chat_name.user,
+ ref: branch,
+ sha: commit,
+ chat_data: {
+ chat_name_id: chat_name.id,
+ command: name,
+ arguments: arguments,
+ response_url: response_url
+ }
+ )
+
+ service.execute(:chat) do |pipeline|
+ build_environment_variables(pipeline)
+ build_chat_data(pipeline)
+ end
+ end
+
+ # pipeline - The `Ci::Pipeline` to create the environment variables for.
+ def build_environment_variables(pipeline)
+ pipeline.variables.build(
+ [{ key: 'CHAT_INPUT', value: arguments },
+ { key: 'CHAT_CHANNEL', value: channel }]
+ )
+ end
+
+ # pipeline - The `Ci::Pipeline` to create the chat data for.
+ def build_chat_data(pipeline)
+ pipeline.build_chat_data(
+ chat_name_id: chat_name.id,
+ response_url: response_url
+ )
+ end
+
+ def valid?
+ branch && commit
+ end
+
+ def branch
+ strong_memoize(:branch) { project.default_branch }
+ end
+
+ def commit
+ strong_memoize(:commit) do
+ project.commit(branch)&.id if branch
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/chat/output.rb b/lib/gitlab/chat/output.rb
new file mode 100644
index 00000000000..411b1555a7d
--- /dev/null
+++ b/lib/gitlab/chat/output.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Chat
+ # Class for gathering and formatting the output of a `Ci::Build`.
+ class Output
+ attr_reader :build
+
+ MissingBuildSectionError = Class.new(StandardError)
+
+ # The primary trace section to look for.
+ PRIMARY_SECTION = 'chat_reply'
+
+ # The backup trace section in case the primary one could not be found.
+ FALLBACK_SECTION = 'build_script'
+
+ # build - The `Ci::Build` to obtain the output from.
+ def initialize(build)
+ @build = build
+ end
+
+ # Returns a `String` containing the output of the build.
+ #
+ # The output _does not_ include the command that was executed.
+ def to_s
+ offset, length = read_offset_and_length
+
+ trace.read do |stream|
+ stream.seek(offset)
+
+ output = stream
+ .stream
+ .read(length)
+ .force_encoding(Encoding.default_external)
+
+ without_executed_command_line(output)
+ end
+ end
+
+ # Returns the offset to seek to and the number of bytes to read relative
+ # to the offset.
+ def read_offset_and_length
+ section = find_build_trace_section(PRIMARY_SECTION) ||
+ find_build_trace_section(FALLBACK_SECTION)
+
+ unless section
+ raise(
+ MissingBuildSectionError,
+ "The build_script trace section could not be found for build #{build.id}"
+ )
+ end
+
+ length = section[:byte_end] - section[:byte_start]
+
+ [section[:byte_start], length]
+ end
+
+ # Removes the line containing the executed command from the build output.
+ #
+ # output - A `String` containing the output of a trace section.
+ def without_executed_command_line(output)
+ # If `output.split("\n")` produces an empty Array then the slicing that
+ # follows it will produce a nil. For example:
+ #
+ # "\n".split("\n") # => []
+ # "\n".split("\n")[1..-1] # => nil
+ #
+ # To work around this we only "join" if we're given an Array.
+ if (converted = output.split("\n")[1..-1])
+ converted.join("\n")
+ else
+ ''
+ end
+ end
+
+ # Returns the trace section for the given name, or `nil` if the section
+ # could not be found.
+ #
+ # name - The name of the trace section to find.
+ def find_build_trace_section(name)
+ trace_sections.find { |s| s[:name] == name }
+ end
+
+ def trace_sections
+ @trace_sections ||= trace.extract_sections
+ end
+
+ def trace
+ @trace ||= build.trace
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/chat/responder.rb b/lib/gitlab/chat/responder.rb
new file mode 100644
index 00000000000..6267fbc20e2
--- /dev/null
+++ b/lib/gitlab/chat/responder.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Chat
+ module Responder
+ # Returns an instance of the responder to use for generating chat
+ # responses.
+ #
+ # This method will return `nil` if no formatter is available for the given
+ # build.
+ #
+ # build - A `Ci::Build` that executed a chat command.
+ def self.responder_for(build)
+ service = build.pipeline.chat_data&.chat_name&.service
+
+ if (responder = service.try(:chat_responder))
+ responder.new(build)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/chat/responder/base.rb b/lib/gitlab/chat/responder/base.rb
new file mode 100644
index 00000000000..f1ad0e36793
--- /dev/null
+++ b/lib/gitlab/chat/responder/base.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Chat
+ module Responder
+ class Base
+ attr_reader :build
+
+ # build - The `Ci::Build` that was executed.
+ def initialize(build)
+ @build = build
+ end
+
+ def pipeline
+ build.pipeline
+ end
+
+ def project
+ pipeline.project
+ end
+
+ def success(*)
+ raise NotImplementedError, 'You must implement #success(output)'
+ end
+
+ def failure
+ raise NotImplementedError, 'You must implement #failure'
+ end
+
+ def send_response(output)
+ raise NotImplementedError, 'You must implement #send_response(output)'
+ end
+
+ def scheduled_output
+ raise NotImplementedError, 'You must implement #scheduled_output'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/chat/responder/slack.rb b/lib/gitlab/chat/responder/slack.rb
new file mode 100644
index 00000000000..0cf02c92a67
--- /dev/null
+++ b/lib/gitlab/chat/responder/slack.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Chat
+ module Responder
+ class Slack < Responder::Base
+ SUCCESS_COLOR = '#B3ED8E'
+ FAILURE_COLOR = '#FF5640'
+ RESPONSE_TYPE = :in_channel
+
+ # Slack breaks messages apart if they're around 4 KB in size. We use a
+ # slightly smaller limit here to account for user mentions.
+ MESSAGE_SIZE_LIMIT = 3.5.kilobytes
+
+ # Sends a response back to Slack
+ #
+ # output - The output to send back to Slack, as a Hash.
+ def send_response(output)
+ Gitlab::HTTP.post(
+ pipeline.chat_data.response_url,
+ {
+ headers: { Accept: 'application/json' },
+ body: output.to_json
+ }
+ )
+ end
+
+ # Sends the output for a build that completed successfully.
+ #
+ # output - The output produced by the chat command.
+ def success(output)
+ return if output.empty?
+
+ send_response(
+ text: message_text(limit_output(output)),
+ response_type: RESPONSE_TYPE
+ )
+ end
+
+ # Sends the output for a build that failed.
+ def failure
+ send_response(
+ text: message_text("<#{build_url}|Sorry, the build failed!>"),
+ response_type: RESPONSE_TYPE
+ )
+ end
+
+ # Returns the output to send back after a command has been scheduled.
+ def scheduled_output
+ # We return an empty message so that Slack still shows the input
+ # command, without polluting the channel with standard "The job has
+ # been scheduled" (or similar) responses.
+ { text: '' }
+ end
+
+ private
+
+ def limit_output(output)
+ if output.bytesize <= MESSAGE_SIZE_LIMIT
+ output
+ else
+ "<#{build_url}|The output is too large to be sent back directly!>"
+ end
+ end
+
+ def mention_user
+ "<@#{pipeline.chat_data.chat_name.chat_id}>"
+ end
+
+ def message_text(output)
+ "#{mention_user}: #{output}"
+ end
+
+ def build_url
+ ::Gitlab::Routing.url_helpers.project_build_url(project, build)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/pipeline/chain/command.rb b/lib/gitlab/ci/pipeline/chain/command.rb
index e62d547d862..e4ed1424865 100644
--- a/lib/gitlab/ci/pipeline/chain/command.rb
+++ b/lib/gitlab/ci/pipeline/chain/command.rb
@@ -10,7 +10,8 @@ module Gitlab
:origin_ref, :checkout_sha, :after_sha, :before_sha,
:trigger_request, :schedule, :merge_request,
:ignore_skip_ci, :save_incompleted,
- :seeds_block, :variables_attributes, :push_options
+ :seeds_block, :variables_attributes, :push_options,
+ :chat_data
) do
include Gitlab::Utils::StrongMemoize
diff --git a/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs.rb b/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs.rb
index 0f687a4ce9b..1e09b417311 100644
--- a/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs.rb
+++ b/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs.rb
@@ -6,7 +6,13 @@ module Gitlab
module Chain
class RemoveUnwantedChatJobs < Chain::Base
def perform!
- # to be overriden in EE
+ return unless pipeline.config_processor && pipeline.chat?
+
+ # When scheduling a chat pipeline we only want to run the build
+ # that matches the chat command.
+ pipeline.config_processor.jobs.select! do |name, _|
+ name.to_s == command.chat_data[:command].to_s
+ end
end
def break?
diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
index e369d26f22f..7c1182d4293 100644
--- a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
@@ -49,11 +49,13 @@ variables:
POSTGRES_ENABLED: "true"
POSTGRES_DB: $CI_ENVIRONMENT_SLUG
- KUBERNETES_VERSION: 1.11.6
- HELM_VERSION: 2.12.2
+ KUBERNETES_VERSION: 1.11.7
+ HELM_VERSION: 2.12.3
DOCKER_DRIVER: overlay2
+ ROLLOUT_RESOURCE_TYPE: deployment
+
stages:
- build
- test
@@ -740,7 +742,7 @@ rollout 100%:
chart/
fi
- kubectl rollout status -n "$KUBE_NAMESPACE" -w "deployment/$name"
+ kubectl rollout status -n "$KUBE_NAMESPACE" -w "$ROLLOUT_RESOURCE_TYPE/$name"
}
function scale() {
@@ -826,7 +828,7 @@ rollout 100%:
# Function to ensure backwards compatibility with AUTO_DEVOPS_DOMAIN
function ensure_kube_ingress_base_domain() {
- if [ -z ${KUBE_INGRESS_BASE_DOMAIN+x} ]; then
+ if [ -z ${KUBE_INGRESS_BASE_DOMAIN+x} ] && [ -n "$AUTO_DEVOPS_DOMAIN" ] ; then
export KUBE_INGRESS_BASE_DOMAIN=$AUTO_DEVOPS_DOMAIN
fi
}
diff --git a/lib/gitlab/danger/helper.rb b/lib/gitlab/danger/helper.rb
index b8428637e19..d3c86fdb629 100644
--- a/lib/gitlab/danger/helper.rb
+++ b/lib/gitlab/danger/helper.rb
@@ -95,6 +95,7 @@ module Gitlab
CATEGORY_LABELS = {
docs: "~Documentation",
+ none: "",
qa: "~QA"
}.freeze
@@ -105,13 +106,13 @@ module Gitlab
%r{\A(ee/)?app/(assets|views)/} => :frontend,
%r{\A(ee/)?public/} => :frontend,
- %r{\A(ee/)?spec/javascripts/} => :frontend,
+ %r{\A(ee/)?spec/(javascripts|frontend)/} => :frontend,
%r{\A(ee/)?vendor/assets/} => :frontend,
%r{\A(jest\.config\.js|package\.json|yarn\.lock)\z} => :frontend,
%r{\A(ee/)?app/(?!assets|views)[^/]+} => :backend,
%r{\A(ee/)?(bin|config|danger|generator_templates|lib|rubocop|scripts)/} => :backend,
- %r{\A(ee/)?spec/(?!javascripts)[^/]+} => :backend,
+ %r{\A(ee/)?spec/(?!javascripts|frontend)[^/]+} => :backend,
%r{\A(ee/)?vendor/(?!assets)[^/]+} => :backend,
%r{\A(ee/)?vendor/(languages\.yml|licenses\.csv)\z} => :backend,
%r{\A(Dangerfile|Gemfile|Gemfile.lock|Procfile|Rakefile|\.gitlab-ci\.yml)\z} => :backend,
@@ -120,6 +121,9 @@ module Gitlab
%r{\A(ee/)?db/} => :database,
%r{\A(ee/)?qa/} => :qa,
+ # Files that don't fit into any category are marked with :none
+ %r{\A(ee/)?changelogs/} => :none,
+
# Fallbacks in case the above patterns miss anything
%r{\.rb\z} => :backend,
%r{\.(md|txt)\z} => :docs,
diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb
index e410d5a8333..c9d89d56884 100644
--- a/lib/gitlab/diff/file.rb
+++ b/lib/gitlab/diff/file.rb
@@ -293,6 +293,10 @@ module Gitlab
end
end
+ def viewer
+ rich_viewer || simple_viewer
+ end
+
def simple_viewer
@simple_viewer ||= simple_viewer_class.new(self)
end
diff --git a/lib/gitlab/etag_caching/router.rb b/lib/gitlab/etag_caching/router.rb
index 08e30214b46..0891f79198d 100644
--- a/lib/gitlab/etag_caching/router.rb
+++ b/lib/gitlab/etag_caching/router.rb
@@ -52,6 +52,14 @@ module Gitlab
Gitlab::EtagCaching::Router::Route.new(
%r(^(?!.*(#{RESERVED_WORDS_REGEX})).*/environments\.json\z),
'environments'
+ ),
+ Gitlab::EtagCaching::Router::Route.new(
+ %r(^(?!.*(#{RESERVED_WORDS_REGEX})).*/import/github/realtime_changes\.json\z),
+ 'realtime_changes_import_github'
+ ),
+ Gitlab::EtagCaching::Router::Route.new(
+ %r(^(?!.*(#{RESERVED_WORDS_REGEX})).*/import/gitea/realtime_changes\.json\z),
+ 'realtime_changes_import_gitea'
)
].freeze
diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml
index 099677a791c..7f8c6d56627 100644
--- a/lib/gitlab/import_export/import_export.yml
+++ b/lib/gitlab/import_export/import_export.yml
@@ -133,7 +133,6 @@ excluded_attributes:
- :external_diff
- :stored_externally
- :external_diff_store
- - :st_diffs
merge_request_diff_files:
- :diff
- :external_diff_offset
diff --git a/lib/gitlab/kubernetes/helm.rb b/lib/gitlab/kubernetes/helm.rb
index bbac15c7710..42c4745ff98 100644
--- a/lib/gitlab/kubernetes/helm.rb
+++ b/lib/gitlab/kubernetes/helm.rb
@@ -3,8 +3,8 @@
module Gitlab
module Kubernetes
module Helm
- HELM_VERSION = '2.12.2'.freeze
- KUBECTL_VERSION = '1.11.0'.freeze
+ HELM_VERSION = '2.12.3'.freeze
+ KUBECTL_VERSION = '1.11.7'.freeze
NAMESPACE = 'gitlab-managed-apps'.freeze
SERVICE_ACCOUNT = 'tiller'.freeze
CLUSTER_ROLE_BINDING = 'tiller-admin'.freeze
diff --git a/lib/gitlab/lfs_token.rb b/lib/gitlab/lfs_token.rb
index 26b81847d37..31e6fc9d8c7 100644
--- a/lib/gitlab/lfs_token.rb
+++ b/lib/gitlab/lfs_token.rb
@@ -30,8 +30,8 @@ module Gitlab
end
end
- def token(expire_time: DEFAULT_EXPIRE_TIME)
- HMACToken.new(actor).token(expire_time)
+ def token
+ HMACToken.new(actor).token(DEFAULT_EXPIRE_TIME)
end
def token_valid?(token_to_check)
@@ -47,6 +47,15 @@ module Gitlab
user? ? :lfs_token : :lfs_deploy_token
end
+ def authentication_payload(repository_http_path)
+ {
+ username: actor_name,
+ lfs_token: token,
+ repository_http_path: repository_http_path,
+ expires_in: DEFAULT_EXPIRE_TIME
+ }
+ end
+
private # rubocop:disable Lint/UselessAccessModifier
class HMACToken
diff --git a/lib/gitlab/slash_commands/application_help.rb b/lib/gitlab/slash_commands/application_help.rb
new file mode 100644
index 00000000000..0ea7554ba64
--- /dev/null
+++ b/lib/gitlab/slash_commands/application_help.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module SlashCommands
+ class ApplicationHelp < BaseCommand
+ def initialize(params)
+ @params = params
+ end
+
+ def execute
+ Gitlab::SlashCommands::Presenters::Help.new(commands).present(trigger, params[:text])
+ end
+
+ private
+
+ def trigger
+ "#{params[:command]} [project name or alias]"
+ end
+
+ def commands
+ Gitlab::SlashCommands::Command.commands
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/slash_commands/command.rb b/lib/gitlab/slash_commands/command.rb
index 474c09b9c4d..7c963fcf38a 100644
--- a/lib/gitlab/slash_commands/command.rb
+++ b/lib/gitlab/slash_commands/command.rb
@@ -9,7 +9,8 @@ module Gitlab
Gitlab::SlashCommands::IssueNew,
Gitlab::SlashCommands::IssueSearch,
Gitlab::SlashCommands::IssueMove,
- Gitlab::SlashCommands::Deploy
+ Gitlab::SlashCommands::Deploy,
+ Gitlab::SlashCommands::Run
]
end
diff --git a/lib/gitlab/slash_commands/presenters/error.rb b/lib/gitlab/slash_commands/presenters/error.rb
new file mode 100644
index 00000000000..442f8796338
--- /dev/null
+++ b/lib/gitlab/slash_commands/presenters/error.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module SlashCommands
+ module Presenters
+ class Error < Presenters::Base
+ def initialize(message)
+ @message = message
+ end
+
+ def message
+ ephemeral_response(text: @message)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/slash_commands/presenters/run.rb b/lib/gitlab/slash_commands/presenters/run.rb
new file mode 100644
index 00000000000..c4bbc231464
--- /dev/null
+++ b/lib/gitlab/slash_commands/presenters/run.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module SlashCommands
+ module Presenters
+ class Run < Presenters::Base
+ # rubocop: disable CodeReuse/ActiveRecord
+ def present(pipeline)
+ build = pipeline.builds.take
+
+ if build && (responder = Chat::Responder.responder_for(build))
+ in_channel_response(responder.scheduled_output)
+ else
+ unsupported_chat_service
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def unsupported_chat_service
+ ephemeral_response(text: 'Sorry, this chat service is currently not supported by GitLab ChatOps.')
+ end
+
+ def failed_to_schedule(command)
+ ephemeral_response(
+ text: 'The command could not be scheduled. Make sure that your ' \
+ 'project has a .gitlab-ci.yml that defines a job with the ' \
+ "name #{command.inspect}"
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/slash_commands/run.rb b/lib/gitlab/slash_commands/run.rb
new file mode 100644
index 00000000000..10a545e28ac
--- /dev/null
+++ b/lib/gitlab/slash_commands/run.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module SlashCommands
+ # Slash command for triggering chatops jobs.
+ class Run < BaseCommand
+ def self.match(text)
+ /\Arun\s+(?<command>\S+)(\s+(?<arguments>.+))?\z/.match(text)
+ end
+
+ def self.help_message
+ 'run <command> <arguments>'
+ end
+
+ def self.available?(project)
+ Chat.available? && project.builds_enabled?
+ end
+
+ def self.allowed?(project, user)
+ can?(user, :create_pipeline, project)
+ end
+
+ def execute(match)
+ command = Chat::Command.new(
+ project: project,
+ chat_name: chat_name,
+ name: match[:command],
+ arguments: match[:arguments],
+ channel: params[:channel_id],
+ response_url: params[:response_url]
+ )
+
+ presenter = Gitlab::SlashCommands::Presenters::Run.new
+ pipeline = command.try_create_pipeline
+
+ if pipeline&.persisted?
+ presenter.present(pipeline)
+ else
+ presenter.failed_to_schedule(command.name)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index a65f4a8639c..0101ccc046a 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -64,12 +64,12 @@ module Gitlab
group_clusters_disabled: count(::Clusters::Cluster.disabled.group_type),
clusters_platforms_gke: count(::Clusters::Cluster.gcp_installed.enabled),
clusters_platforms_user: count(::Clusters::Cluster.user_provided.enabled),
- clusters_applications_helm: count(::Clusters::Applications::Helm.installed),
- clusters_applications_ingress: count(::Clusters::Applications::Ingress.installed),
- clusters_applications_cert_managers: count(::Clusters::Applications::CertManager.installed),
- clusters_applications_prometheus: count(::Clusters::Applications::Prometheus.installed),
- clusters_applications_runner: count(::Clusters::Applications::Runner.installed),
- clusters_applications_knative: count(::Clusters::Applications::Knative.installed),
+ clusters_applications_helm: count(::Clusters::Applications::Helm.available),
+ clusters_applications_ingress: count(::Clusters::Applications::Ingress.available),
+ clusters_applications_cert_managers: count(::Clusters::Applications::CertManager.available),
+ clusters_applications_prometheus: count(::Clusters::Applications::Prometheus.available),
+ clusters_applications_runner: count(::Clusters::Applications::Runner.available),
+ clusters_applications_knative: count(::Clusters::Applications::Knative.available),
in_review_folder: count(::Environment.in_review_folder),
groups: count(Group),
issues: count(Issue),
diff --git a/lib/sentry/client.rb b/lib/sentry/client.rb
index 4187014d49e..49ec196b103 100644
--- a/lib/sentry/client.rb
+++ b/lib/sentry/client.rb
@@ -54,7 +54,7 @@ module Sentry
def handle_response(response)
unless response.code == 200
- raise Client::Error, "Sentry response error: #{response.code}"
+ raise Client::Error, "Sentry response status code: #{response.code}"
end
response.as_json
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 7cbba0779f7..fa680cd5ef0 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -117,6 +117,9 @@ msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -528,6 +531,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "All"
msgstr ""
@@ -1323,6 +1329,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1389,9 +1398,6 @@ msgstr ""
msgid "Choose the top-level group for your repository imports."
msgstr ""
-msgid "Choose which repositories you want to import."
-msgstr ""
-
msgid "CiStatusLabel|canceled"
msgstr ""
@@ -1536,9 +1542,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "ClusterIntegration| %{custom_domain_start}More information%{custom_domain_end}."
msgstr ""
@@ -3289,7 +3292,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3459,7 +3462,7 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -3486,9 +3489,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -3582,6 +3591,9 @@ msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -3953,6 +3965,21 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "In order to enable instance-level analytics, please ask an admin to enable %{usage_ping_link_start}usage ping%{usage_ping_link_end}."
msgstr ""
@@ -4046,6 +4073,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -4064,6 +4094,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -4082,7 +4115,7 @@ msgstr ""
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "It must have a header row and at least two columns: the first column is the issue title and the second column is the issue description. The separator is automatically detected."
@@ -4238,6 +4271,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -4306,6 +4342,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -4428,6 +4467,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -4796,6 +4838,9 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
+msgid "New Password"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr ""
@@ -4862,6 +4907,9 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -4970,6 +5018,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -5449,6 +5500,9 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -5458,6 +5512,9 @@ msgstr ""
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -5476,6 +5533,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -5668,9 +5728,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -5812,6 +5869,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -6320,6 +6380,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SSH Keys"
msgstr ""
@@ -6362,6 +6425,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -6578,6 +6644,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -6596,6 +6665,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -6934,6 +7006,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -7345,6 +7423,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -7360,6 +7453,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -8597,6 +8693,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
diff --git a/package.json b/package.json
index 21edd0dcf41..1a6538cf791 100644
--- a/package.json
+++ b/package.json
@@ -29,8 +29,8 @@
"@babel/plugin-syntax-import-meta": "^7.2.0",
"@babel/preset-env": "^7.3.1",
"@gitlab/csslab": "^1.8.0",
- "@gitlab/svgs": "^1.52.0",
- "@gitlab/ui": "^2.0.2",
+ "@gitlab/svgs": "^1.53.0",
+ "@gitlab/ui": "^2.0.4",
"apollo-boost": "^0.1.20",
"apollo-client": "^2.4.5",
"autosize": "^4.0.0",
diff --git a/qa/Gemfile b/qa/Gemfile
index 873eac1013f..f29006617ed 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -8,3 +8,4 @@ gem 'rspec', '~> 3.7'
gem 'selenium-webdriver', '~> 3.12'
gem 'airborne', '~> 0.2.13'
gem 'nokogiri', '~> 1.10.1'
+gem 'rspec-retry', '~> 0.6.1'
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 9f84bdc3828..c3d9f558c23 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -76,6 +76,8 @@ GEM
rspec-mocks (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
+ rspec-retry (0.6.1)
+ rspec-core (> 3.3)
rspec-support (3.7.0)
rubyzip (1.2.2)
selenium-webdriver (3.141.0)
@@ -101,6 +103,7 @@ DEPENDENCIES
pry-byebug (~> 3.5.1)
rake (~> 12.3.0)
rspec (~> 3.7)
+ rspec-retry (~> 0.6.1)
selenium-webdriver (~> 3.12)
BUNDLED WITH
diff --git a/qa/README.md b/qa/README.md
index 5e32496ea9f..7709db36f8e 100644
--- a/qa/README.md
+++ b/qa/README.md
@@ -1,21 +1,25 @@
-# GitLab QA - Integration tests for GitLab
+# GitLab QA - End-to-end tests for GitLab
-This directory contains integration tests for GitLab.
+This directory contains [end-to-end tests](doc/development/testing_guide/end_to_end_tests.md)
+for GitLab. It includes the test framework and the tests themselves.
+
+The tests can be found in `qa/specs/features` (not to be confused with the unit
+tests for the test framework, which are in `spec/`).
It is part of the [GitLab QA project](https://gitlab.com/gitlab-org/gitlab-qa).
## What is it?
-GitLab QA is an integration tests suite for GitLab.
+GitLab QA is an end-to-end tests suite for GitLab.
-These are black-box and entirely click-driven integration tests you can run
+These are black-box and entirely click-driven end-to-end tests you can run
against any existing instance.
## How does it work?
1. When we release a new version of GitLab, we build a Docker images for it.
1. Along with GitLab Docker Images we also build and publish GitLab QA images.
-1. GitLab QA project uses these images to execute integration tests.
+1. GitLab QA project uses these images to execute end-to-end tests.
## Validating GitLab views / partials / selectors in merge requests
diff --git a/qa/qa.rb b/qa/qa.rb
index 8c5411d3f6b..2b3ffabbbaa 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -260,6 +260,10 @@ module QA
autoload :Sidebar, 'qa/page/issuable/sidebar'
end
+ module Alert
+ autoload :AutoDevopsAlert, 'qa/page/alert/auto_devops_alert'
+ end
+
module Layout
autoload :Banner, 'qa/page/layout/banner'
end
@@ -367,6 +371,7 @@ module QA
end
autoload :Api, 'qa/support/api'
autoload :Waiter, 'qa/support/waiter'
+ autoload :Retrier, 'qa/support/retrier'
end
end
diff --git a/qa/qa/page/alert/auto_devops_alert.rb b/qa/qa/page/alert/auto_devops_alert.rb
new file mode 100644
index 00000000000..8f66c805b77
--- /dev/null
+++ b/qa/qa/page/alert/auto_devops_alert.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Alert
+ class AutoDevopsAlert < Page::Base
+ view 'app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml' do
+ element :auto_devops_banner
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index 01ac161d26d..05e38aba77d 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -39,18 +39,9 @@ module QA
false
end
- def retry_on_exception(max_attempts: 3, reload: false, sleep_interval: 0.0)
- attempts = 0
-
- begin
+ def retry_on_exception(max_attempts: 3, reload: false, sleep_interval: 0.5)
+ QA::Support::Retrier.retry_on_exception(max_attempts: max_attempts, reload_page: (reload && self), sleep_interval: sleep_interval) do
yield
- rescue StandardError
- sleep sleep_interval
- refresh if reload
- attempts += 1
-
- retry if attempts < max_attempts
- raise
end
end
@@ -162,6 +153,10 @@ module QA
click_link text
end
+ def click_body
+ find('body').click
+ end
+
def self.path
raise NotImplementedError
end
diff --git a/qa/qa/page/label/index.rb b/qa/qa/page/label/index.rb
index f0d323ca3b4..de0cfa9f293 100644
--- a/qa/qa/page/label/index.rb
+++ b/qa/qa/page/label/index.rb
@@ -14,6 +14,10 @@ module QA
element :label_svg
end
+ view 'app/views/shared/empty_states/_priority_labels.html.haml' do
+ element :label_svg
+ end
+
def go_to_new_label
# The 'labels.svg' takes a fraction of a second to load after which the "New label" button shifts up a bit
# This can cause webdriver to miss the hit so we wait for the svg to load (implicitly with has_element?)
diff --git a/qa/qa/page/project/import/github.rb b/qa/qa/page/project/import/github.rb
index a3cde73d3f2..488157d9878 100644
--- a/qa/qa/page/project/import/github.rb
+++ b/qa/qa/page/project/import/github.rb
@@ -10,12 +10,11 @@ module QA
element :list_repos_button, "submit_tag _('List your GitHub repositories')" # rubocop:disable QA/ElementWithPattern
end
- view 'app/views/import/_githubish_status.html.haml' do
- element :project_import_row, 'data: { qa: { repo_path: repo.full_name } }' # rubocop:disable QA/ElementWithPattern
+ view 'app/assets/javascripts/import_projects/components/provider_repo_table_row.vue' do
+ element :project_import_row
element :project_namespace_select
- element :project_namespace_field, 'select_tag :namespace_id' # rubocop:disable QA/ElementWithPattern
- element :project_path_field, 'text_field_tag :path, sanitize_project_name(repo.name)' # rubocop:disable QA/ElementWithPattern
- element :import_button, "_('Import')" # rubocop:disable QA/ElementWithPattern
+ element :project_path_field
+ element :import_button
end
def add_personal_access_token(personal_access_token)
diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb
index 1028cc045a0..1c25be5fd0c 100644
--- a/qa/qa/page/project/issue/show.rb
+++ b/qa/qa/page/project/issue/show.rb
@@ -37,18 +37,25 @@ module QA
end
def select_comments_only_filter
- click_element :discussion_filter
- find_element(:filter_options, "Show comments only").click
+ select_filter_with_text('Show comments only')
end
def select_history_only_filter
- click_element :discussion_filter
- find_element(:filter_options, "Show history only").click
+ select_filter_with_text('Show history only')
end
def select_all_activities_filter
- click_element :discussion_filter
- find_element(:filter_options, "Show all activity").click
+ select_filter_with_text('Show all activity')
+ end
+
+ private
+
+ def select_filter_with_text(text)
+ retry_on_exception do
+ click_body
+ click_element :discussion_filter
+ find_element(:filter_options, text).click
+ end
end
end
end
diff --git a/qa/qa/page/project/job/show.rb b/qa/qa/page/project/job/show.rb
index 49c676c01f2..9c218f4ed8b 100644
--- a/qa/qa/page/project/job/show.rb
+++ b/qa/qa/page/project/job/show.rb
@@ -4,10 +4,6 @@ module QA::Page
COMPLETED_STATUSES = %w[passed failed canceled blocked skipped manual].freeze # excludes created, pending, running
PASSED_STATUS = 'passed'.freeze
- view 'app/assets/javascripts/jobs/components/job_app.vue' do
- element :loading_animation
- end
-
view 'app/assets/javascripts/jobs/components/job_log.vue' do
element :build_trace
end
@@ -20,22 +16,13 @@ module QA::Page
element :pipeline_path
end
- def completed?
- COMPLETED_STATUSES.include?(status_badge)
- end
-
def successful?(timeout: 60)
- wait(reload: false, max: timeout) do
- completed? && !trace_loading?
- end
+ raise "Timed out waiting for the build trace to load" unless loaded?
+ raise "Timed out waiting for the status to be a valid completed state" unless completed?(timeout: timeout)
status_badge == PASSED_STATUS
end
- def trace_loading?
- has_element?(:loading_animation)
- end
-
# Reminder: You may wish to wait for a particular job status before checking output
def output
find_element(:build_trace).text
@@ -43,6 +30,16 @@ module QA::Page
private
+ def loaded?(wait: 60)
+ has_element?(:build_trace, wait: wait)
+ end
+
+ def completed?(timeout: 60)
+ wait(reload: false, max: timeout) do
+ COMPLETED_STATUSES.include?(status_badge)
+ end
+ end
+
def status_badge
find_element(:status_badge).text
end
diff --git a/qa/qa/page/project/operations/kubernetes/show.rb b/qa/qa/page/project/operations/kubernetes/show.rb
index 98ac5c32d91..d4e1679b6bf 100644
--- a/qa/qa/page/project/operations/kubernetes/show.rb
+++ b/qa/qa/page/project/operations/kubernetes/show.rb
@@ -14,6 +14,11 @@ module QA
element :ingress_ip_address, 'id="ingress-ip-address"' # rubocop:disable QA/ElementWithPattern
end
+ view 'app/views/clusters/clusters/_form.html.haml' do
+ element :base_domain
+ element :save_domain
+ end
+
def install!(application_name)
within(".js-cluster-application-row-#{application_name}") do
page.has_button?('Install', wait: 30)
@@ -32,6 +37,14 @@ module QA
# ip address is assigned for the ingress controller
page.find('#ingress-ip-address', wait: 1200).value
end
+
+ def set_domain(domain)
+ fill_element :base_domain, domain
+ end
+
+ def save_domain
+ click_element :save_domain
+ end
end
end
end
diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb
index f192f1fc64b..22b0a168964 100644
--- a/qa/qa/page/project/pipeline/show.rb
+++ b/qa/qa/page/project/pipeline/show.rb
@@ -18,6 +18,10 @@ module QA::Page
element :status_icon, 'ci-status-icon-${status}' # rubocop:disable QA/ElementWithPattern
end
+ view 'app/views/projects/pipelines/_info.html.haml' do
+ element :pipeline_badges
+ end
+
def running?
within('.ci-header-container') do
page.has_content?('running')
@@ -32,6 +36,12 @@ module QA::Page
end
end
+ def has_tag?(tag_name)
+ within_element(:pipeline_badges) do
+ has_selector?('.badge', text: tag_name)
+ end
+ end
+
def go_to_job(job_name)
find_element(:job_link, job_name).click
end
diff --git a/qa/qa/resource/kubernetes_cluster.rb b/qa/qa/resource/kubernetes_cluster.rb
index 986b31da528..93a06be6818 100644
--- a/qa/qa/resource/kubernetes_cluster.rb
+++ b/qa/qa/resource/kubernetes_cluster.rb
@@ -12,10 +12,6 @@ module QA
Page::Project::Operations::Kubernetes::Show.perform(&:ingress_ip)
end
- attribute :domain do
- "#{ingress_ip}.nip.io"
- end
-
def fabricate!
@project.visit!
@@ -53,6 +49,12 @@ module QA
page.await_installed(:ingress) if @install_ingress
page.await_installed(:prometheus) if @install_prometheus
page.await_installed(:runner) if @install_runner
+
+ if @install_ingress
+ populate(:ingress_ip)
+ page.set_domain("#{ingress_ip}.nip.io")
+ page.save_domain
+ end
end
end
end
diff --git a/qa/qa/runtime/namespace.rb b/qa/qa/runtime/namespace.rb
index 704c65467e0..9d7c1aea508 100644
--- a/qa/qa/runtime/namespace.rb
+++ b/qa/qa/runtime/namespace.rb
@@ -8,7 +8,9 @@ module QA
end
def name
- Runtime::Env.namespace_name || "qa-test-#{time.strftime('%Y-%m-%d-%H-%M-%S')}"
+ # If any changes are made to the name tag, following script has to be considered:
+ # https://ops.gitlab.net/gitlab-com/gl-infra/traffic-generator/blob/master/bin/janitor.bash
+ @name ||= Runtime::Env.namespace_name || "qa-test-#{time.strftime('%Y-%m-%d-%H-%M-%S')}-#{SecureRandom.hex(8)}"
end
def path
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 2fb8402edd8..6632c2977ef 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
@@ -1,8 +1,7 @@
# frozen_string_literal: true
module QA
- # Failure issue: https://gitlab.com/gitlab-org/quality/nightly/issues/72
- context 'Manage', :smoke, :quarantine do
+ context 'Manage', :smoke do
describe 'Project creation' do
it 'user creates a new project' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb
index 60836a7b98d..f146636c49a 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb
@@ -2,7 +2,7 @@
module QA
# Failure issue: https://gitlab.com/gitlab-org/quality/staging/issues/31
- context 'Create', :quarantine do
+ context 'Create' do
describe 'Merge request squashing' do
it 'user squashes commits while merging' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb
index 7d96da32423..243f0b83b77 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_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/37
+ context 'Create', :quarantine do
describe 'push after setting the file size limit via admin/application_settings' do
before(:all) do
push = Resource::Repository::ProjectPush.fabricate! do |p|
diff --git a/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb b/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
index 2375aa4ce91..3f65eabc756 100644
--- a/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
+++ b/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
@@ -95,7 +95,7 @@ module QA
Page::Project::Pipeline::Show.perform(&:go_to_first_job)
Page::Project::Job::Show.perform do |job|
- expect(job).to be_successful, "Job status did not become \"passed\"."
+ expect(job).to be_successful
expect(job.output).to include(sha1sum)
end
end
diff --git a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
index 8cd353fa250..02bd378acfc 100644
--- a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
+++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
@@ -4,13 +4,13 @@ require 'pathname'
module QA
# Transient failure issue: https://gitlab.com/gitlab-org/quality/nightly/issues/68
- context 'Configure', :orchestrated, :kubernetes, :quarantine do
- describe 'Auto DevOps support' do
- def login
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
- end
+ context 'Configure' do
+ def login
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.perform(&:sign_in_using_credentials)
+ end
+ describe 'Auto DevOps support', :orchestrated, :kubernetes, :quarantine do
[true, false].each do |rbac|
context "when rbac is #{rbac ? 'enabled' : 'disabled'}" do
before(:all) do
@@ -40,7 +40,7 @@ module QA
# Create and connect K8s cluster
@cluster = Service::KubernetesCluster.new(rbac: rbac).create!
- kubernetes_cluster = Resource::KubernetesCluster.fabricate! do |cluster|
+ Resource::KubernetesCluster.fabricate! do |cluster|
cluster.project = @project
cluster.cluster = @cluster
cluster.install_helm_tiller = true
@@ -49,14 +49,11 @@ module QA
cluster.install_runner = true
end
- kubernetes_cluster.populate(:ingress_ip)
@project.visit!
Page::Project::Menu.perform(&:click_ci_cd_settings)
Page::Project::Settings::CICD.perform do |p|
p.enable_auto_devops
end
-
- kubernetes_cluster.populate(:domain)
end
after(:all) do
@@ -76,7 +73,7 @@ module QA
pipeline.go_to_job('build')
end
Page::Project::Job::Show.perform do |job|
- expect(job).to be_sucessful(timeout: 600), "Job did not pass"
+ expect(job).to be_successful(timeout: 600)
job.click_element(:pipeline_path)
end
@@ -85,7 +82,7 @@ module QA
pipeline.go_to_job('test')
end
Page::Project::Job::Show.perform do |job|
- expect(job).to be_sucessful(timeout: 600), "Job did not pass"
+ expect(job).to be_successful(timeout: 600)
job.click_element(:pipeline_path)
end
@@ -94,7 +91,7 @@ module QA
pipeline.go_to_job('production')
end
Page::Project::Job::Show.perform do |job|
- expect(job).to be_sucessful(timeout: 1200), "Job did not pass"
+ expect(job).to be_successful(timeout: 1200)
job.click_element(:pipeline_path)
end
@@ -137,7 +134,7 @@ module QA
pipeline.go_to_job('build')
end
Page::Project::Job::Show.perform do |job|
- expect(job).to be_sucessful(timeout: 600), "Job did not pass"
+ expect(job).to be_successful(timeout: 600)
job.click_element(:pipeline_path)
end
@@ -146,7 +143,7 @@ module QA
pipeline.go_to_job('test')
end
Page::Project::Job::Show.perform do |job|
- expect(job).to be_sucessful(timeout: 600), "Job did not pass"
+ expect(job).to be_successful(timeout: 600)
job.click_element(:pipeline_path)
end
@@ -155,7 +152,7 @@ module QA
pipeline.go_to_job('production')
end
Page::Project::Job::Show.perform do |job|
- expect(job).to be_sucessful(timeout: 1200), "Job did not pass"
+ expect(job).to be_successful(timeout: 1200)
job.click_element(:pipeline_path)
end
@@ -176,5 +173,38 @@ module QA
end
end
end
+
+ describe 'Auto DevOps', :smoke do
+ it 'enables AutoDevOps by default' do
+ login
+
+ project = Resource::Project.fabricate! do |p|
+ p.name = Runtime::Env.auto_devops_project_name || 'project-with-autodevops'
+ p.description = 'Project with AutoDevOps'
+ end
+
+ project.visit!
+
+ Page::Alert::AutoDevopsAlert.perform do |alert|
+ expect(alert).to have_text(/.*The Auto DevOps pipeline has been enabled.*/)
+ end
+
+ # Create AutoDevOps repo
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.directory = Pathname
+ .new(__dir__)
+ .join('../../../../../fixtures/auto_devops_rack')
+ push.commit_message = 'Create AutoDevOps compatible Project'
+ end
+
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ Page::Project::Pipeline::Index.perform(&:go_to_latest_pipeline)
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ expect(pipeline).to have_tag('Auto DevOps')
+ end
+ end
+ end
end
end
diff --git a/qa/qa/support/retrier.rb b/qa/qa/support/retrier.rb
new file mode 100644
index 00000000000..8be4e9f5365
--- /dev/null
+++ b/qa/qa/support/retrier.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module QA
+ module Support
+ module Retrier
+ module_function
+
+ def retry_on_exception(max_attempts: 3, reload_page: nil, sleep_interval: 0.5)
+ QA::Runtime::Logger.debug("with retry_on_exception: max_attempts #{max_attempts}; sleep_interval #{sleep_interval}")
+
+ attempts = 0
+
+ begin
+ QA::Runtime::Logger.debug("Attempt number #{attempts + 1}")
+ yield
+ rescue StandardError, RSpec::Expectations::ExpectationNotMetError
+ sleep sleep_interval
+ reload_page.refresh if reload_page
+ attempts += 1
+
+ retry if attempts < max_attempts
+ QA::Runtime::Logger.debug("Raising exception after #{max_attempts} attempts")
+ raise
+ end
+ end
+ end
+ end
+end
diff --git a/qa/spec/spec_helper.rb b/qa/spec/spec_helper.rb
index 20a153f3f63..cbdd6e881b1 100644
--- a/qa/spec/spec_helper.rb
+++ b/qa/spec/spec_helper.rb
@@ -1,4 +1,5 @@
require_relative '../qa'
+require 'rspec/retry'
%w[helpers shared_examples].each do |d|
Dir[::File.join(__dir__, d, '**', '*.rb')].each { |f| require f }
@@ -31,6 +32,17 @@ RSpec.configure do |config|
config.profile_examples = 10
config.order = :random
Kernel.srand config.seed
+
+ # show retry status in spec process
+ config.verbose_retry = true
+
+ # show exception that triggers a retry if verbose_retry is set to true
+ config.display_try_failure_messages = true
+
+ config.around do |example|
+ retry_times = example.metadata.keys.include?(:quarantine) ? 1 : 3
+ example.run_with_retry retry: retry_times
+ end
end
# Skip tests in quarantine unless we explicitly focus on them.
diff --git a/qa/spec/spec_helper_spec.rb b/qa/spec/spec_helper_spec.rb
index 2427999e110..27ec1ec80fe 100644
--- a/qa/spec/spec_helper_spec.rb
+++ b/qa/spec/spec_helper_spec.rb
@@ -28,6 +28,22 @@ describe 'rspec config tests' do
end
end
+ let(:group_2) do
+ RSpec.describe do
+ before(:all) do
+ @expectations = [1, 2, 3]
+ end
+
+ example 'not in quarantine' do
+ expect(@expectations.shift).to be(3)
+ end
+
+ example 'in quarantine', :quarantine do
+ expect(@expectations.shift).to be(3)
+ end
+ end
+ end
+
context 'with no tags focussed' do
before do
group.run
@@ -301,4 +317,39 @@ describe 'rspec config tests' do
end
end
end
+
+ context 'rspec retry' do
+ context 'in an untagged context' do
+ before do
+ group_2.run
+ end
+
+ it 'should run example :retry times' do
+ examples = group_2.descendant_filtered_examples
+ ex = examples.find { |e| e.description == 'not in quarantine' }
+ expect(ex.execution_result.status).to eq(:passed)
+ end
+ end
+
+ context 'with :quarantine focussed' do
+ before do
+ RSpec.configure do |config|
+ config.inclusion_filter = :quarantine
+ end
+ group_2.run
+ end
+
+ after do
+ RSpec.configure do |config|
+ config.inclusion_filter.clear
+ end
+ end
+
+ it 'should run example once only' do
+ examples = group_2.descendant_filtered_examples
+ ex = examples.find { |e| e.description == 'in quarantine' }
+ expect(ex.execution_result.status).to eq(:failed)
+ end
+ end
+ end
end
diff --git a/scripts/build_assets_image b/scripts/build_assets_image
index 4e5ef977161..9afada244c8 100755
--- a/scripts/build_assets_image
+++ b/scripts/build_assets_image
@@ -22,6 +22,8 @@ mkdir -p assets_container.build/public
cp -r public/assets assets_container.build/public/
cp Dockerfile.assets assets_container.build/
docker build -t ${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_SLUG} -f assets_container.build/Dockerfile.assets assets_container.build/
+docker tag ${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_SLUG} ${ASSETS_IMAGE_PATH}:${CI_COMMIT_SHA}
docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY}
-docker push ${ASSETS_IMAGE_PATH}
+docker push ${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_SLUG}
+docker push ${ASSETS_IMAGE_PATH}:${CI_COMMIT_SHA}
diff --git a/scripts/lint-changelog-yaml b/scripts/lint-changelog-yaml
index 6553e02ffca..06d502c4676 100755
--- a/scripts/lint-changelog-yaml
+++ b/scripts/lint-changelog-yaml
@@ -3,7 +3,7 @@
require 'yaml'
invalid_changelogs = Dir['changelogs/**/*'].reject do |changelog|
- next true if changelog =~ /(archive\.md|unreleased(-ee)?)$/
+ next true if changelog =~ /((README|archive)\.md|unreleased(-ee)?)$/
next false unless changelog.end_with?('.yml')
begin
diff --git a/scripts/lint-doc.sh b/scripts/lint-doc.sh
index 848364b4a9b..bc73225c1bf 100755
--- a/scripts/lint-doc.sh
+++ b/scripts/lint-doc.sh
@@ -35,7 +35,7 @@ fi
# Do not use 'README.md', instead use 'index.md'
# Number of 'README.md's as of 2018-03-26
-NUMBER_READMES_CE=42
+NUMBER_READMES_CE=43
NUMBER_READMES_EE=46
FIND_READMES=$(find doc/ -name "README.md" | wc -l)
echo '=> Checking for new README.md files...'
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index 6b66cbd2651..c934db9e237 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -8,6 +8,17 @@ describe Admin::UsersController do
sign_in(admin)
end
+ describe 'GET :id' do
+ it 'finds a user case-insensitively' do
+ user = create(:user, username: 'CaseSensitive')
+
+ get :show, params: { id: user.username.downcase }
+
+ expect(response).to be_redirect
+ expect(response.location).to end_with(user.username)
+ end
+ end
+
describe 'DELETE #user with projects' do
let(:project) { create(:project, namespace: user.namespace) }
let!(:issue) { create(:issue, author: user) }
diff --git a/spec/controllers/dashboard/milestones_controller_spec.rb b/spec/controllers/dashboard/milestones_controller_spec.rb
index 8b176e07bc8..4b164d0aa6b 100644
--- a/spec/controllers/dashboard/milestones_controller_spec.rb
+++ b/spec/controllers/dashboard/milestones_controller_spec.rb
@@ -3,11 +3,9 @@ require 'spec_helper'
describe Dashboard::MilestonesController do
let(:project) { create(:project) }
let(:group) { create(:group) }
- let(:public_group) { create(:group, :public) }
let(:user) { create(:user) }
let(:project_milestone) { create(:milestone, project: project) }
let(:group_milestone) { create(:milestone, group: group) }
- let!(:public_milestone) { create(:milestone, group: public_group) }
let(:milestone) do
DashboardMilestone.build(
[project],
@@ -45,6 +43,9 @@ describe Dashboard::MilestonesController do
end
describe "#index" do
+ let(:public_group) { create(:group, :public) }
+ let!(:public_milestone) { create(:milestone, group: public_group) }
+
render_views
it 'returns group and project milestones to which the user belongs' do
@@ -74,10 +75,10 @@ describe Dashboard::MilestonesController do
expect(response.body).not_to include(project_milestone.title)
end
- it 'should contain group and project milestones to which the user belongs to' do
+ it 'should show counts of 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\">3</span>")
+ 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>")
end
end
diff --git a/spec/controllers/groups/clusters_controller_spec.rb b/spec/controllers/groups/clusters_controller_spec.rb
index 360030102e0..ef23ffaa843 100644
--- a/spec/controllers/groups/clusters_controller_spec.rb
+++ b/spec/controllers/groups/clusters_controller_spec.rb
@@ -453,7 +453,7 @@ describe Groups::ClustersController do
end
context 'when domain is invalid' do
- let(:domain) { 'not-a-valid-domain' }
+ let(:domain) { 'http://not-a-valid-domain' }
it 'should not update cluster attributes' do
go
diff --git a/spec/controllers/import/gitea_controller_spec.rb b/spec/controllers/import/gitea_controller_spec.rb
index 5ba64ab3eed..8cbec79095f 100644
--- a/spec/controllers/import/gitea_controller_spec.rb
+++ b/spec/controllers/import/gitea_controller_spec.rb
@@ -40,4 +40,12 @@ describe Import::GiteaController do
end
end
end
+
+ describe "GET realtime_changes" do
+ it_behaves_like 'a GitHub-ish import controller: GET realtime_changes' do
+ before do
+ assign_host_url
+ end
+ end
+ end
end
diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb
index bca5f3f6589..162dff98ec5 100644
--- a/spec/controllers/import/github_controller_spec.rb
+++ b/spec/controllers/import/github_controller_spec.rb
@@ -60,4 +60,8 @@ describe Import::GithubController do
describe "POST create" do
it_behaves_like 'a GitHub-ish import controller: POST create'
end
+
+ describe "GET realtime_changes" do
+ it_behaves_like 'a GitHub-ish import controller: GET realtime_changes'
+ end
end
diff --git a/spec/controllers/profiles/preferences_controller_spec.rb b/spec/controllers/profiles/preferences_controller_spec.rb
index 760c0fab130..ee881f85233 100644
--- a/spec/controllers/profiles/preferences_controller_spec.rb
+++ b/spec/controllers/profiles/preferences_controller_spec.rb
@@ -43,7 +43,8 @@ describe Profiles::PreferencesController do
color_scheme_id: '1',
dashboard: 'stars',
theme_id: '2',
- first_day_of_week: '1'
+ first_day_of_week: '1',
+ preferred_language: 'jp'
}.with_indifferent_access
expect(user).to receive(:assign_attributes).with(ActionController::Parameters.new(prefs).permit!)
diff --git a/spec/controllers/projects/autocomplete_sources_controller_spec.rb b/spec/controllers/projects/autocomplete_sources_controller_spec.rb
new file mode 100644
index 00000000000..4bc72042710
--- /dev/null
+++ b/spec/controllers/projects/autocomplete_sources_controller_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Projects::AutocompleteSourcesController do
+ set(:group) { create(:group) }
+ set(:project) { create(:project, namespace: group) }
+ set(:issue) { create(:issue, project: project) }
+ set(:user) { create(:user) }
+
+ describe 'GET members' do
+ before do
+ group.add_owner(user)
+ sign_in(user)
+ end
+
+ it 'returns an array of member object' do
+ get :members, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name, type_id: issue.id }
+
+ all = json_response.find {|member| member["username"] == 'all'}
+ the_group = json_response.find {|member| member["username"] == group.full_path}
+ the_user = json_response.find {|member| member["username"] == user.username}
+
+ expect(all.symbolize_keys).to include(username: 'all',
+ name: 'All Project and Group Members',
+ count: 1)
+
+ expect(the_group.symbolize_keys).to include(type: group.class.name,
+ name: group.full_name,
+ avatar_url: group.avatar_url,
+ count: 1)
+
+ expect(the_user.symbolize_keys).to include(type: user.class.name,
+ name: user.name,
+ avatar_url: user.avatar_url)
+ end
+ end
+end
diff --git a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
index a6017d8e5e6..e85f32d6e30 100644
--- a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
@@ -4,10 +4,11 @@ describe Projects::MergeRequests::DiffsController do
include ProjectForksHelper
let(:project) { create(:project, :repository) }
- let(:user) { project.owner }
+ let(:user) { create(:user) }
let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) }
before do
+ project.add_maintainer(user)
sign_in(user)
end
@@ -114,16 +115,6 @@ describe Projects::MergeRequests::DiffsController do
expect(paths).to include(existing_path)
end
end
-
- context 'when the path does not exist in the diff' do
- before do
- diff_for_path(old_path: 'files/ruby/nopen.rb', new_path: 'files/ruby/nopen.rb')
- end
-
- it 'returns a 404' do
- expect(response).to have_gitlab_http_status(404)
- end
- end
end
context 'when the user cannot view the merge request' do
diff --git a/spec/factories/import_state.rb b/spec/factories/import_states.rb
index d6de26dccbc..d6de26dccbc 100644
--- a/spec/factories/import_state.rb
+++ b/spec/factories/import_states.rb
diff --git a/spec/factories/personal_access_tokens.rb b/spec/factories/personal_access_tokens.rb
index 1b12f84d7b8..e7fd22a96b2 100644
--- a/spec/factories/personal_access_tokens.rb
+++ b/spec/factories/personal_access_tokens.rb
@@ -1,13 +1,14 @@
FactoryBot.define do
factory :personal_access_token do
user
- token { SecureRandom.hex(50) }
sequence(:name) { |n| "PAT #{n}" }
revoked false
expires_at { 5.days.from_now }
scopes ['api']
impersonation false
+ after(:build) { |personal_access_token| personal_access_token.ensure_token }
+
trait :impersonation do
impersonation true
end
@@ -21,7 +22,7 @@ FactoryBot.define do
end
trait :invalid do
- token nil
+ token_digest nil
end
end
end
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index a47bd7cafca..1d2b724a5e5 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -73,11 +73,16 @@ FactoryBot.define do
end
after(:create) do |user, evaluator|
- user.identities << create(
- :identity,
+ identity_attrs = {
provider: evaluator.provider,
extern_uid: evaluator.extern_uid
- )
+ }
+
+ if evaluator.respond_to?(:saml_provider)
+ identity_attrs[:saml_provider] = evaluator.saml_provider
+ end
+
+ user.identities << create(:identity, identity_attrs)
end
end
diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb
index 6c4b04ab76b..9d1c1e3acc7 100644
--- a/spec/features/dashboard/projects_spec.rb
+++ b/spec/features/dashboard/projects_spec.rb
@@ -114,7 +114,16 @@ describe 'Dashboard Projects' do
end
end
- context 'when on Starred projects tab' do
+ context 'when on Starred projects tab', :js do
+ it 'shows the empty state when there are no starred projects' do
+ visit(starred_dashboard_projects_path)
+
+ element = page.find('.row.empty-state')
+
+ expect(element).to have_content("You don't have starred projects yet.")
+ expect(element.find('.svg-content img')['src']).to have_content('illustrations/starred_empty')
+ end
+
it 'shows only starred projects' do
user.toggle_star(project2)
diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb
index 2f45ef856a5..7b6e9cd66b2 100644
--- a/spec/features/issuables/issuable_list_spec.rb
+++ b/spec/features/issuables/issuable_list_spec.rb
@@ -28,6 +28,22 @@ describe 'issuable list' do
expect(first('.fa-thumbs-down').find(:xpath, '..')).to have_content(1)
expect(first('.fa-comments').find(:xpath, '..')).to have_content(2)
end
+
+ it 'sorts labels alphabetically' do
+ label1 = create(:label, project: project, title: 'a')
+ label2 = create(:label, project: project, title: 'z')
+ label3 = create(:label, project: project, title: 'X')
+ label4 = create(:label, project: project, title: 'B')
+ issuable = create_issuable(issuable_type)
+ issuable.labels << [label1, label2, label3, label4]
+
+ visit_issuable_list(issuable_type)
+
+ expect(all('.label-link')[0].text).to have_content('B')
+ expect(all('.label-link')[1].text).to have_content('X')
+ expect(all('.label-link')[2].text).to have_content('a')
+ expect(all('.label-link')[3].text).to have_content('z')
+ end
end
it "counts merge requests closing issues icons for each issue" do
@@ -45,6 +61,14 @@ describe 'issuable list' do
end
end
+ def create_issuable(issuable_type)
+ if issuable_type == :issue
+ create(:issue, project: project)
+ else
+ create(:merge_request, source_project: project)
+ end
+ end
+
def create_issuables(issuable_type)
3.times do |n|
issuable =
diff --git a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
index d19408ee87f..c837a6752f9 100644
--- a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
@@ -222,6 +222,11 @@ describe 'Merge request > User creates image diff notes', :js do
end
def create_image_diff_note
+ expand_text = 'Click to expand it.'
+ page.all('a', text: expand_text).each do |element|
+ element.click
+ end
+
find('.js-add-image-diff-note-button', match: :first).click
find('.diff-content .note-textarea').native.send_keys('image diff test comment')
click_button 'Comment'
diff --git a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
index 90d6841af0e..9909bfb5904 100644
--- a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
+++ b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe 'User visits the profile preferences page' do
+ include Select2Helper
+
let(:user) { create(:user) }
before do
@@ -60,6 +62,28 @@ describe 'User visits the profile preferences page' do
end
end
+ describe 'User changes their language', :js do
+ it 'creates a flash message' do
+ select2('en', from: '#user_preferred_language')
+ click_button 'Save'
+
+ wait_for_requests
+
+ expect_preferences_saved_message
+ end
+
+ it 'updates their preference' do
+ wait_for_requests
+ select2('eo', from: '#user_preferred_language')
+ click_button 'Save'
+
+ wait_for_requests
+ refresh
+
+ expect(page).to have_css('html[lang="eo"]')
+ end
+ end
+
def expect_preferences_saved_message
page.within('.flash-container') do
expect(page).to have_content('Preferences saved.')
diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb
index 97757e8da92..ee71c843b80 100644
--- a/spec/features/projects/branches_spec.rb
+++ b/spec/features/projects/branches_spec.rb
@@ -229,6 +229,38 @@ describe 'Branches' do
end
end
+ describe 'comparing branches' do
+ before do
+ sign_in(user)
+ project.add_developer(user)
+ end
+
+ shared_examples 'compares branches' do
+ it 'compares branches' do
+ visit project_branches_path(project)
+
+ page.within first('.all-branches li') do
+ click_link 'Compare'
+ end
+
+ expect(page).to have_content 'Commits'
+ expect(page).to have_link 'Create merge request'
+ end
+ end
+
+ context 'on a read-only instance' do
+ before do
+ allow(Gitlab::Database).to receive(:read_only?).and_return(true)
+ end
+
+ it_behaves_like 'compares branches'
+ end
+
+ context 'on a read-write instance' do
+ it_behaves_like 'compares branches'
+ end
+ end
+
def sorted_branches(repository, count:, sort_by:, state: nil)
branches = repository.branches_sorted_by(sort_by)
branches = branches.select { |b| state == 'active' ? b.active? : b.stale? } if state
diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb
index 3b469fee867..49244c53a91 100644
--- a/spec/features/projects/wiki/markdown_preview_spec.rb
+++ b/spec/features/projects/wiki/markdown_preview_spec.rb
@@ -3,6 +3,7 @@ require 'spec_helper'
describe 'Projects > Wiki > User previews markdown changes', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
+ let(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: { title: 'home', content: '[some link](other-page)' }) }
let(:wiki_content) do
<<-HEREDOC
[regular link](regular)
@@ -18,9 +19,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
sign_in(user)
- visit project_path(project)
- find('.shortcuts-wiki').click
- click_link "Create your first page"
+ visit project_wiki_path(project, wiki_page)
end
context "while creating a new wiki page" do
diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
index 48a0d675f2d..b1a7f167977 100644
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
@@ -44,13 +44,7 @@ describe "User creates wiki page" do
end
it "shows non-escaped link in the pages list", :js do
- click_link("New page")
-
- page.within("#modal-new-wiki") do
- fill_in(:new_wiki_path, with: "one/two/three-test")
-
- click_on("Create page")
- end
+ fill_in(:wiki_title, with: "one/two/three-test")
page.within(".wiki-form") do
fill_in(:wiki_content, with: "wiki content")
diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
index f76e577b0d6..dbf8af3e5bb 100644
--- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
@@ -26,12 +26,7 @@ describe 'User updates wiki page' do
end
it 'updates a page that has a path', :js do
- click_on('New page')
-
- page.within('#modal-new-wiki') do
- fill_in(:new_wiki_path, with: 'one/two/three-test')
- click_on('Create page')
- end
+ fill_in(:wiki_title, with: 'one/two/three-test')
page.within '.wiki-form' do
fill_in(:wiki_content, with: 'wiki content')
diff --git a/spec/features/projects/wiki/user_views_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
index d4691b669c1..6e28ec0d7b2 100644
--- a/spec/features/projects/wiki/user_views_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
@@ -22,12 +22,7 @@ describe 'User views a wiki page' do
visit(project_wikis_path(project))
click_link "Create your first page"
- click_on('New page')
-
- page.within('#modal-new-wiki') do
- fill_in(:new_wiki_path, with: 'one/two/three-test')
- click_on('Create page')
- end
+ fill_in(:wiki_title, with: 'one/two/three-test')
page.within('.wiki-form') do
fill_in(:wiki_content, with: 'wiki content')
diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb
index bfe11ddf673..957c3cfc583 100644
--- a/spec/features/users/signup_spec.rb
+++ b/spec/features/users/signup_spec.rb
@@ -49,6 +49,34 @@ describe 'Signup' do
expect(page).to have_content("Please create a username with only alphanumeric characters.")
end
+
+ it 'shows an error border if the username contains emojis' do
+ simulate_input('#new_user_username', 'ehsan😀')
+
+ expect(find('.username')).to have_css '.gl-field-error-outline'
+ end
+
+ it 'shows an error message if the username contains emojis' do
+ simulate_input('#new_user_username', 'ehsan😀')
+
+ expect(page).to have_content("Invalid input, please avoid emojis")
+ end
+ end
+
+ describe 'user\'s full name validation', :js do
+ before do
+ visit root_path
+ click_link 'Register'
+ simulate_input('#new_user_name', 'Ehsan 🦋')
+ end
+
+ it 'shows an error border if the user\'s fullname contains an emoji' do
+ expect(find('.name')).to have_css '.gl-field-error-outline'
+ end
+
+ it 'shows an error message if the username contains emojis' do
+ expect(page).to have_content("Invalid input, please avoid emojis")
+ end
end
context 'with no errors' do
diff --git a/spec/finders/concerns/finder_methods_spec.rb b/spec/finders/concerns/finder_methods_spec.rb
index a4ad331f613..e074e53c2c5 100644
--- a/spec/finders/concerns/finder_methods_spec.rb
+++ b/spec/finders/concerns/finder_methods_spec.rb
@@ -12,7 +12,7 @@ describe FinderMethods do
end
def execute
- Project.all
+ Project.all.order(id: :desc)
end
end
end
@@ -38,6 +38,16 @@ describe FinderMethods do
it 'raises not found the user does not have access' do
expect { finder.find_by!(id: unauthorized_project.id) }.to raise_error(ActiveRecord::RecordNotFound)
end
+
+ it 'ignores ordering' do
+ # Memoise the finder result so we can add message expectations to it
+ relation = finder.execute
+ allow(finder).to receive(:execute).and_return(relation)
+
+ expect(relation).to receive(:reorder).with(nil).and_call_original
+
+ finder.find_by!(id: authorized_project.id)
+ end
end
describe '#find' do
@@ -66,5 +76,15 @@ describe FinderMethods do
it 'returns nil when the user does not have access' do
expect(finder.find_by(id: unauthorized_project.id)).to be_nil
end
+
+ it 'ignores ordering' do
+ # Memoise the finder result so we can add message expectations to it
+ relation = finder.execute
+ allow(finder).to receive(:execute).and_return(relation)
+
+ expect(relation).to receive(:reorder).with(nil).and_call_original
+
+ finder.find_by(id: authorized_project.id)
+ end
end
end
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 34cb09942be..fe8000e419b 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -416,6 +416,36 @@ describe IssuesFinder do
end
end
+ context 'filtering by closed_at' do
+ let!(:closed_issue1) { create(:issue, project: project1, state: :closed, closed_at: 1.week.ago) }
+ let!(:closed_issue2) { create(:issue, project: project2, state: :closed, closed_at: 1.week.from_now) }
+ let!(:closed_issue3) { create(:issue, project: project2, state: :closed, closed_at: 2.weeks.from_now) }
+
+ context 'through closed_after' do
+ let(:params) { { state: :closed, closed_after: closed_issue3.closed_at } }
+
+ it 'returns issues closed on or after the given date' do
+ expect(issues).to contain_exactly(closed_issue3)
+ end
+ end
+
+ context 'through closed_before' do
+ let(:params) { { state: :closed, closed_before: closed_issue1.closed_at } }
+
+ it 'returns issues closed on or before the given date' do
+ expect(issues).to contain_exactly(closed_issue1)
+ end
+ end
+
+ context 'through closed_after and closed_before' do
+ let(:params) { { state: :closed, closed_after: closed_issue2.closed_at, closed_before: closed_issue3.closed_at } }
+
+ it 'returns issues closed between the given dates' do
+ expect(issues).to contain_exactly(closed_issue2, closed_issue3)
+ end
+ end
+ end
+
context 'filtering by reaction name' do
context 'user searches by no reaction' do
let(:params) { { my_reaction_emoji: 'None' } }
diff --git a/spec/fixtures/api/schemas/entities/diff_viewer.json b/spec/fixtures/api/schemas/entities/diff_viewer.json
index 81325cd86c6..ae0fb32d3ac 100644
--- a/spec/fixtures/api/schemas/entities/diff_viewer.json
+++ b/spec/fixtures/api/schemas/entities/diff_viewer.json
@@ -14,6 +14,17 @@
"string",
"null"
]
+ },
+ "error_message": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "collapsed": {
+ "type": [
+ "boolean"
+ ]
}
},
"additionalProperties": false
diff --git a/spec/fixtures/security-reports/master/gl-container-scanning-report.json b/spec/fixtures/security-reports/master/gl-container-scanning-report.json
index 68c6099836b..03dfc647162 100644
--- a/spec/fixtures/security-reports/master/gl-container-scanning-report.json
+++ b/spec/fixtures/security-reports/master/gl-container-scanning-report.json
@@ -1,11 +1,14 @@
{
"image": "registry.gitlab.com/groulot/container-scanning-test/master:5f21de6956aee99ddb68ae49498662d9872f50ff",
"unapproved": [
- "CVE-2017-18018",
- "CVE-2016-2781",
- "CVE-2017-12424",
- "CVE-2007-5686",
- "CVE-2013-4235"
+ "CVE-2017-18269",
+ "CVE-2017-16997",
+ "CVE-2018-1000001",
+ "CVE-2016-10228",
+ "CVE-2018-18520",
+ "CVE-2010-4052",
+ "CVE-2018-16869",
+ "CVE-2018-18311"
],
"vulnerabilities": [
{
@@ -87,6 +90,16 @@
"link": "https://security-tracker.debian.org/tracker/CVE-2018-18311",
"severity": "Unknown",
"fixedby": "5.24.1-3+deb9u5"
+ },
+ {
+ "featurename": "foo",
+ "featureversion": "1.3",
+ "vulnerability": "CVE-2018-666",
+ "namespace": "debian:9",
+ "description": "Foo has a vulnerability nobody cares about and whitelist.",
+ "link": "https://security-tracker.debian.org/tracker/CVE-2018-666",
+ "severity": "Unknown",
+ "fixedby": "1.4"
}
]
}
diff --git a/spec/javascripts/lib/utils/ajax_cache_spec.js b/spec/frontend/lib/utils/ajax_cache_spec.js
index dc0b04173bf..e2ee70b9d69 100644
--- a/spec/javascripts/lib/utils/ajax_cache_spec.js
+++ b/spec/frontend/lib/utils/ajax_cache_spec.js
@@ -94,68 +94,54 @@ describe('AjaxCache', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
- spyOn(axios, 'get').and.callThrough();
+ jest.spyOn(axios, 'get');
});
afterEach(() => {
mock.restore();
});
- it('stores and returns data from Ajax call if cache is empty', done => {
+ it('stores and returns data from Ajax call if cache is empty', () => {
mock.onGet(dummyEndpoint).reply(200, dummyResponse);
- AjaxCache.retrieve(dummyEndpoint)
- .then(data => {
- expect(data).toEqual(dummyResponse);
- expect(AjaxCache.internalStorage[dummyEndpoint]).toEqual(dummyResponse);
- })
- .then(done)
- .catch(fail);
+ return AjaxCache.retrieve(dummyEndpoint).then(data => {
+ expect(data).toEqual(dummyResponse);
+ expect(AjaxCache.internalStorage[dummyEndpoint]).toEqual(dummyResponse);
+ });
});
- it('makes no Ajax call if request is pending', done => {
+ it('makes no Ajax call if request is pending', () => {
mock.onGet(dummyEndpoint).reply(200, dummyResponse);
- AjaxCache.retrieve(dummyEndpoint)
- .then(done)
- .catch(fail);
-
- AjaxCache.retrieve(dummyEndpoint)
- .then(done)
- .catch(fail);
-
- expect(axios.get.calls.count()).toBe(1);
+ return Promise.all([
+ AjaxCache.retrieve(dummyEndpoint),
+ AjaxCache.retrieve(dummyEndpoint),
+ ]).then(() => {
+ expect(axios.get).toHaveBeenCalledTimes(1);
+ });
});
- it('returns undefined if Ajax call fails and cache is empty', done => {
+ it('returns undefined if Ajax call fails and cache is empty', () => {
const errorMessage = 'Network Error';
mock.onGet(dummyEndpoint).networkError();
- AjaxCache.retrieve(dummyEndpoint)
- .then(data => fail(`Received unexpected data: ${JSON.stringify(data)}`))
- .catch(error => {
- expect(error.message).toBe(`${dummyEndpoint}: ${errorMessage}`);
- expect(error.textStatus).toBe(errorMessage);
- done();
- })
- .catch(fail);
+ expect.assertions(2);
+ return AjaxCache.retrieve(dummyEndpoint).catch(error => {
+ expect(error.message).toBe(`${dummyEndpoint}: ${errorMessage}`);
+ expect(error.textStatus).toBe(errorMessage);
+ });
});
- it('makes no Ajax call if matching data exists', done => {
+ it('makes no Ajax call if matching data exists', () => {
AjaxCache.internalStorage[dummyEndpoint] = dummyResponse;
- mock.onGet(dummyEndpoint).reply(() => {
- fail(new Error('expected no Ajax call!'));
- });
- AjaxCache.retrieve(dummyEndpoint)
- .then(data => {
- expect(data).toBe(dummyResponse);
- })
- .then(done)
- .catch(fail);
+ return AjaxCache.retrieve(dummyEndpoint).then(data => {
+ expect(data).toBe(dummyResponse);
+ expect(axios.get).not.toHaveBeenCalled();
+ });
});
- it('makes Ajax call even if matching data exists when forceRequest parameter is provided', done => {
+ it('makes Ajax call even if matching data exists when forceRequest parameter is provided', () => {
const oldDummyResponse = {
important: 'old dummy data',
};
@@ -164,21 +150,12 @@ describe('AjaxCache', () => {
mock.onGet(dummyEndpoint).reply(200, dummyResponse);
- // Call without forceRetrieve param
- AjaxCache.retrieve(dummyEndpoint)
- .then(data => {
- expect(data).toBe(oldDummyResponse);
- })
- .then(done)
- .catch(fail);
-
- // Call with forceRetrieve param
- AjaxCache.retrieve(dummyEndpoint, true)
- .then(data => {
- expect(data).toEqual(dummyResponse);
- })
- .then(done)
- .catch(fail);
+ return Promise.all([
+ AjaxCache.retrieve(dummyEndpoint),
+ AjaxCache.retrieve(dummyEndpoint, true),
+ ]).then(data => {
+ expect(data).toEqual([oldDummyResponse, dummyResponse]);
+ });
});
});
});
diff --git a/spec/frontend/notes/components/__snapshots__/discussion_jump_to_next_button_spec.js.snap b/spec/frontend/notes/components/__snapshots__/discussion_jump_to_next_button_spec.js.snap
new file mode 100644
index 00000000000..11d65ced180
--- /dev/null
+++ b/spec/frontend/notes/components/__snapshots__/discussion_jump_to_next_button_spec.js.snap
@@ -0,0 +1,20 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`JumpToNextDiscussionButton matches the snapshot 1`] = `
+<div
+ class="btn-group"
+ role="group"
+>
+ <button
+ class="btn btn-default discussion-next-btn"
+ data-original-title="Jump to next unresolved discussion"
+ title=""
+ >
+ <icon-stub
+ cssclasses=""
+ name="comment-next"
+ size="16"
+ />
+ </button>
+</div>
+`;
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
new file mode 100644
index 00000000000..989b0458481
--- /dev/null
+++ b/spec/frontend/notes/components/discussion_jump_to_next_button_spec.js
@@ -0,0 +1,30 @@
+import JumpToNextDiscussionButton from '~/notes/components/discussion_jump_to_next_button.vue';
+import { shallowMount } from '@vue/test-utils';
+
+describe('JumpToNextDiscussionButton', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = shallowMount(JumpToNextDiscussionButton, {
+ sync: false,
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('matches the snapshot', () => {
+ expect(wrapper.vm.$el).toMatchSnapshot();
+ });
+
+ it('emits onClick event on button click', () => {
+ const button = wrapper.find({ ref: 'button' });
+
+ button.trigger('click');
+
+ expect(wrapper.emitted()).toEqual({
+ onClick: [[]],
+ });
+ });
+});
diff --git a/spec/frontend/test_setup.js b/spec/frontend/test_setup.js
index 7ad2e97e7e6..d892889b98d 100644
--- a/spec/frontend/test_setup.js
+++ b/spec/frontend/test_setup.js
@@ -1,3 +1,7 @@
+import Vue from 'vue';
+import Translate from '~/vue_shared/translate';
+import axios from '~/lib/utils/axios_utils';
+
const testTimeoutInMs = 300;
jest.setTimeout(testTimeoutInMs);
@@ -14,3 +18,17 @@ afterEach(() => {
throw new Error(`Test took too long (${elapsedTimeInMs}ms > ${testTimeoutInMs}ms)!`);
}
});
+
+// 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.use(Translate);
diff --git a/spec/graphql/resolvers/issues_resolver_spec.rb b/spec/graphql/resolvers/issues_resolver_spec.rb
index 66de372e9fe..5f9c180cbb7 100644
--- a/spec/graphql/resolvers/issues_resolver_spec.rb
+++ b/spec/graphql/resolvers/issues_resolver_spec.rb
@@ -5,16 +5,63 @@ describe Resolvers::IssuesResolver do
let(:current_user) { create(:user) }
set(:project) { create(:project) }
- set(:issue) { create(:issue, project: project) }
- set(:issue2) { create(:issue, project: project, title: 'foo') }
+ set(:issue1) { create(:issue, project: project, state: :opened, created_at: 3.hours.ago, updated_at: 3.hours.ago) }
+ set(:issue2) { create(:issue, project: project, state: :closed, title: 'foo', created_at: 1.hour.ago, updated_at: 1.hour.ago, closed_at: 1.hour.ago) }
+ set(:label1) { create(:label, project: project) }
+ set(:label2) { create(:label, project: project) }
before do
project.add_developer(current_user)
+ create(:label_link, label: label1, target: issue1)
+ create(:label_link, label: label1, target: issue2)
+ create(:label_link, label: label2, target: issue2)
end
describe '#resolve' do
it 'finds all issues' do
- expect(resolve_issues).to contain_exactly(issue, issue2)
+ expect(resolve_issues).to contain_exactly(issue1, issue2)
+ end
+
+ it 'filters by state' do
+ expect(resolve_issues(state: 'opened')).to contain_exactly(issue1)
+ expect(resolve_issues(state: 'closed')).to contain_exactly(issue2)
+ end
+
+ it 'filters by labels' do
+ expect(resolve_issues(label_name: [label1.title])).to contain_exactly(issue1, issue2)
+ expect(resolve_issues(label_name: [label1.title, label2.title])).to contain_exactly(issue2)
+ end
+
+ describe 'filters by created_at' do
+ it 'filters by created_before' do
+ expect(resolve_issues(created_before: 2.hours.ago)).to contain_exactly(issue1)
+ end
+
+ it 'filters by created_after' do
+ expect(resolve_issues(created_after: 2.hours.ago)).to contain_exactly(issue2)
+ end
+ end
+
+ describe 'filters by updated_at' do
+ it 'filters by updated_before' do
+ expect(resolve_issues(updated_before: 2.hours.ago)).to contain_exactly(issue1)
+ end
+
+ it 'filters by updated_after' do
+ expect(resolve_issues(updated_after: 2.hours.ago)).to contain_exactly(issue2)
+ end
+ end
+
+ describe 'filters by closed_at' do
+ let!(:issue3) { create(:issue, project: project, state: :closed, closed_at: 3.hours.ago) }
+
+ it 'filters by closed_before' do
+ expect(resolve_issues(closed_before: 2.hours.ago)).to contain_exactly(issue3)
+ end
+
+ it 'filters by closed_after' do
+ expect(resolve_issues(closed_after: 2.hours.ago)).to contain_exactly(issue2)
+ end
end
it 'searches issues' do
@@ -22,7 +69,7 @@ describe Resolvers::IssuesResolver do
end
it 'sort issues' do
- expect(resolve_issues(sort: 'created_desc')).to eq [issue2, issue]
+ expect(resolve_issues(sort: 'created_desc')).to eq [issue2, issue1]
end
it 'returns issues user can see' do
@@ -30,31 +77,31 @@ describe Resolvers::IssuesResolver do
create(:issue, confidential: true)
- expect(resolve_issues).to contain_exactly(issue, issue2)
+ expect(resolve_issues).to contain_exactly(issue1, issue2)
end
it 'finds a specific issue with iid' do
- expect(resolve_issues(iid: issue.iid)).to contain_exactly(issue)
+ expect(resolve_issues(iid: issue1.iid)).to contain_exactly(issue1)
end
it 'finds a specific issue with iids' do
- expect(resolve_issues(iids: issue.iid)).to contain_exactly(issue)
+ expect(resolve_issues(iids: issue1.iid)).to contain_exactly(issue1)
end
it 'finds multiple issues with iids' do
- expect(resolve_issues(iids: [issue.iid, issue2.iid]))
- .to contain_exactly(issue, issue2)
+ expect(resolve_issues(iids: [issue1.iid, issue2.iid]))
+ .to contain_exactly(issue1, issue2)
end
it 'finds only the issues within the project we are looking at' do
another_project = create(:project)
- iids = [issue, issue2].map(&:iid)
+ iids = [issue1, issue2].map(&:iid)
iids.each do |iid|
create(:issue, project: another_project, iid: iid)
end
- expect(resolve_issues(iids: iids)).to contain_exactly(issue, issue2)
+ expect(resolve_issues(iids: iids)).to contain_exactly(issue1, issue2)
end
end
diff --git a/spec/graphql/types/issuable_state_enum_spec.rb b/spec/graphql/types/issuable_state_enum_spec.rb
new file mode 100644
index 00000000000..65a80fa4176
--- /dev/null
+++ b/spec/graphql/types/issuable_state_enum_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe GitlabSchema.types['IssuableState'] do
+ it { expect(described_class.graphql_name).to eq('IssuableState') }
+
+ it_behaves_like 'issuable state'
+end
diff --git a/spec/graphql/types/issue_state_enum_spec.rb b/spec/graphql/types/issue_state_enum_spec.rb
new file mode 100644
index 00000000000..de19e6fc505
--- /dev/null
+++ b/spec/graphql/types/issue_state_enum_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe GitlabSchema.types['IssueState'] do
+ it { expect(described_class.graphql_name).to eq('IssueState') }
+
+ it_behaves_like 'issuable state'
+end
diff --git a/spec/graphql/types/merge_request_state_enum_spec.rb b/spec/graphql/types/merge_request_state_enum_spec.rb
new file mode 100644
index 00000000000..626e33b18d3
--- /dev/null
+++ b/spec/graphql/types/merge_request_state_enum_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe GitlabSchema.types['MergeRequestState'] do
+ it { expect(described_class.graphql_name).to eq('MergeRequestState') }
+
+ it_behaves_like 'issuable state'
+
+ it 'exposes all the existing merge request states' do
+ expect(described_class.values.keys).to include('merged')
+ end
+end
diff --git a/spec/helpers/import_helper_spec.rb b/spec/helpers/import_helper_spec.rb
index af4931e3370..6e8c13db9fe 100644
--- a/spec/helpers/import_helper_spec.rb
+++ b/spec/helpers/import_helper_spec.rb
@@ -39,59 +39,12 @@ describe ImportHelper do
end
end
- describe '#provider_project_link' do
- context 'when provider is "github"' do
- let(:github_server_url) { nil }
- let(:provider) { OpenStruct.new(name: 'github', url: github_server_url) }
+ describe '#provider_project_link_url' do
+ let(:full_path) { '/repo/path' }
+ let(:host_url) { 'http://provider.com/' }
- before do
- stub_omniauth_setting(providers: [provider])
- end
-
- context 'when provider does not specify a custom URL' do
- it 'uses default GitHub URL' do
- expect(helper.provider_project_link('github', 'octocat/Hello-World'))
- .to include('href="https://github.com/octocat/Hello-World"')
- end
- end
-
- context 'when provider specify a custom URL' do
- let(:github_server_url) { 'https://github.company.com' }
-
- it 'uses custom URL' do
- expect(helper.provider_project_link('github', 'octocat/Hello-World'))
- .to include('href="https://github.company.com/octocat/Hello-World"')
- end
- end
-
- context "when custom URL contains a '/' char at the end" do
- let(:github_server_url) { 'https://github.company.com/' }
-
- it "doesn't render double slash" do
- expect(helper.provider_project_link('github', 'octocat/Hello-World'))
- .to include('href="https://github.company.com/octocat/Hello-World"')
- end
- end
-
- context 'when provider is missing' do
- it 'uses the default URL' do
- allow(Gitlab.config.omniauth).to receive(:providers).and_return([])
-
- expect(helper.provider_project_link('github', 'octocat/Hello-World'))
- .to include('href="https://github.com/octocat/Hello-World"')
- end
- end
- end
-
- context 'when provider is "gitea"' do
- before do
- assign(:gitea_host_url, 'https://try.gitea.io/')
- end
-
- it 'uses given host' do
- expect(helper.provider_project_link('gitea', 'octocat/Hello-World'))
- .to include('href="https://try.gitea.io/octocat/Hello-World"')
- end
+ it 'appends repo full path to provider host url' do
+ expect(helper.provider_project_link_url(host_url, full_path)).to match('http://provider.com/repo/path')
end
end
end
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index c04f679bcf0..012678db9c2 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -236,4 +236,17 @@ describe LabelsHelper do
expect(labels_filter_path(format: :json)).to eq(dashboard_labels_path(format: :json))
end
end
+
+ describe 'labels_sorted_by_title' do
+ it 'sorts labels alphabetically' do
+ label1 = double(:label, title: 'a')
+ label2 = double(:label, title: 'B')
+ label3 = double(:label, title: 'c')
+ label4 = double(:label, title: 'D')
+ labels = [label1, label2, label3, label4]
+
+ expect(labels_sorted_by_title(labels))
+ .to match_array([label2, label4, label1, label3])
+ end
+ end
end
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index 4c395248644..e0e8ebd0c3c 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -110,6 +110,13 @@ describe PreferencesHelper do
end
end
+ describe '#language_choices' do
+ it 'returns an array of all available languages' do
+ expect(helper.language_choices).to be_an(Array)
+ expect(helper.language_choices.map(&:second)).to eq(Gitlab::I18n.available_locales)
+ end
+ end
+
def stub_user(messages = {})
if messages.empty?
allow(helper).to receive(:current_user).and_return(nil)
diff --git a/spec/javascripts/diffs/components/app_spec.js b/spec/javascripts/diffs/components/app_spec.js
index a2cbc0f3c72..5abdfe695d0 100644
--- a/spec/javascripts/diffs/components/app_spec.js
+++ b/spec/javascripts/diffs/components/app_spec.js
@@ -68,6 +68,32 @@ describe('diffs/components/app', () => {
});
});
+ describe('resizable', () => {
+ afterEach(() => {
+ localStorage.removeItem('mr_tree_list_width');
+ });
+
+ it('sets initial width when no localStorage has been set', () => {
+ createComponent();
+
+ expect(vm.vm.treeWidth).toEqual(320);
+ });
+
+ it('sets initial width to localStorage size', () => {
+ localStorage.setItem('mr_tree_list_width', '200');
+
+ createComponent();
+
+ expect(vm.vm.treeWidth).toEqual(200);
+ });
+
+ it('sets width of tree list', () => {
+ createComponent();
+
+ expect(vm.find('.js-diff-tree-list').element.style.width).toEqual('320px');
+ });
+ });
+
describe('empty state', () => {
it('renders empty state when no diff files exist', () => {
createComponent();
diff --git a/spec/javascripts/diffs/components/diff_content_spec.js b/spec/javascripts/diffs/components/diff_content_spec.js
index 9e158327a77..a1bb51963d6 100644
--- a/spec/javascripts/diffs/components/diff_content_spec.js
+++ b/spec/javascripts/diffs/components/diff_content_spec.js
@@ -6,6 +6,7 @@ import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants';
import '~/behaviors/markdown/render_gfm';
import diffFileMockData from '../mock_data/diff_file';
import discussionsMockData from '../mock_data/diff_discussions';
+import { diffViewerModes } from '~/ide/constants';
describe('DiffContent', () => {
const Component = Vue.extend(DiffContentComponent);
@@ -52,26 +53,39 @@ describe('DiffContent', () => {
describe('empty files', () => {
beforeEach(() => {
- vm.diffFile.empty = true;
vm.diffFile.highlighted_diff_lines = [];
vm.diffFile.parallel_diff_lines = [];
});
- it('should render a message', done => {
+ it('should render a no preview message if viewer returns no preview', done => {
+ vm.diffFile.viewer.name = diffViewerModes.no_preview;
vm.$nextTick(() => {
const block = vm.$el.querySelector('.diff-viewer .nothing-here-block');
expect(block).not.toBe(null);
- expect(block.textContent.trim()).toContain('Empty file');
+ expect(block.textContent.trim()).toContain('No preview for this file type');
+
+ done();
+ });
+ });
+
+ it('should render a not diffable message if viewer returns not diffable', done => {
+ vm.diffFile.viewer.name = diffViewerModes.not_diffable;
+ vm.$nextTick(() => {
+ const block = vm.$el.querySelector('.diff-viewer .nothing-here-block');
+
+ expect(block).not.toBe(null);
+ expect(block.textContent.trim()).toContain(
+ 'This diff was suppressed by a .gitattributes entry',
+ );
done();
});
});
it('should not render multiple messages', done => {
- vm.diffFile.mode_changed = true;
vm.diffFile.b_mode = '100755';
- vm.diffFile.viewer.name = 'mode_changed';
+ vm.diffFile.viewer.name = diffViewerModes.mode_changed;
vm.$nextTick(() => {
expect(vm.$el.querySelectorAll('.nothing-here-block').length).toBe(1);
@@ -81,6 +95,7 @@ describe('DiffContent', () => {
});
it('should not render diff table', done => {
+ vm.diffFile.viewer.name = diffViewerModes.no_preview;
vm.$nextTick(() => {
expect(vm.$el.querySelector('table')).toBe(null);
@@ -157,6 +172,7 @@ describe('DiffContent', () => {
vm.diffFile.new_sha = 'DEF';
vm.diffFile.old_path = 'test.abc';
vm.diffFile.old_sha = 'ABC';
+ vm.diffFile.viewer.name = diffViewerModes.added;
vm.$nextTick(() => {
expect(el.querySelectorAll('.js-diff-inline-view').length).toEqual(0);
diff --git a/spec/javascripts/diffs/components/diff_file_header_spec.js b/spec/javascripts/diffs/components/diff_file_header_spec.js
index 787a81fd88f..005a4751ea1 100644
--- a/spec/javascripts/diffs/components/diff_file_header_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_header_spec.js
@@ -4,15 +4,15 @@ import diffsModule from '~/diffs/store/modules';
import notesModule from '~/notes/stores/modules';
import DiffFileHeader from '~/diffs/components/diff_file_header.vue';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import diffDiscussionsMockData from '../mock_data/diff_discussions';
+import { diffViewerModes } from '~/ide/constants';
Vue.use(Vuex);
-const discussionFixture = 'merge_requests/diff_discussion.json';
-
describe('diff_file_header', () => {
let vm;
let props;
- const diffDiscussionMock = getJSONFixture(discussionFixture)[0];
+ const diffDiscussionMock = diffDiscussionsMockData;
const Component = Vue.extend(DiffFileHeader);
const store = new Vuex.Store({
@@ -303,13 +303,13 @@ describe('diff_file_header', () => {
});
it('displays old and new path if the file was renamed', () => {
- props.diffFile.renamed_file = true;
+ props.diffFile.viewer.name = diffViewerModes.renamed;
vm = mountComponentWithStore(Component, { props, store });
expect(filePaths()).toHaveLength(2);
- expect(filePaths()[0]).toHaveText(props.diffFile.old_path);
- expect(filePaths()[1]).toHaveText(props.diffFile.new_path);
+ expect(filePaths()[0]).toHaveText(props.diffFile.old_path_html);
+ expect(filePaths()[1]).toHaveText(props.diffFile.new_path_html);
});
});
@@ -319,14 +319,12 @@ describe('diff_file_header', () => {
const button = vm.$el.querySelector('.btn-clipboard');
expect(button).not.toBe(null);
- expect(button.dataset.clipboardText).toBe(
- '{"text":"files/ruby/popen.rb","gfm":"`files/ruby/popen.rb`"}',
- );
+ expect(button.dataset.clipboardText).toBe('{"text":"CHANGELOG.rb","gfm":"`CHANGELOG.rb`"}');
});
describe('file mode', () => {
it('it displays old and new file mode if it changed', () => {
- props.diffFile.mode_changed = true;
+ props.diffFile.viewer.name = diffViewerModes.mode_changed;
vm = mountComponentWithStore(Component, { props, store });
@@ -338,7 +336,7 @@ describe('diff_file_header', () => {
});
it('does not display the file mode if it has not changed', () => {
- props.diffFile.mode_changed = false;
+ props.diffFile.viewer.name = diffViewerModes.text;
vm = mountComponentWithStore(Component, { props, store });
diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js
index 1af49282c36..65a1c9b8f15 100644
--- a/spec/javascripts/diffs/components/diff_file_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_spec.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import DiffFileComponent from '~/diffs/components/diff_file.vue';
+import { diffViewerModes, diffViewerErrors } from '~/ide/constants';
import store from '~/mr_notes/stores';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import diffFileMockData from '../mock_data/diff_file';
@@ -27,7 +28,6 @@ describe('DiffFile', () => {
expect(el.querySelector('.file-title-name').innerText.indexOf(file_path)).toBeGreaterThan(-1);
expect(el.querySelector('.js-syntax-highlight')).toBeDefined();
- expect(vm.file.renderIt).toEqual(false);
vm.file.renderIt = true;
vm.$nextTick(() => {
@@ -38,8 +38,8 @@ describe('DiffFile', () => {
describe('collapsed', () => {
it('should not have file content', done => {
expect(vm.$el.querySelectorAll('.diff-content').length).toEqual(1);
- expect(vm.file.collapsed).toEqual(false);
- vm.file.collapsed = true;
+ expect(vm.isCollapsed).toEqual(false);
+ vm.isCollapsed = true;
vm.file.renderIt = true;
vm.$nextTick(() => {
@@ -50,9 +50,8 @@ describe('DiffFile', () => {
});
it('should have collapsed text and link', done => {
- vm.file.renderIt = true;
- vm.file.collapsed = false;
- vm.file.highlighted_diff_lines = null;
+ vm.renderIt = true;
+ vm.isCollapsed = true;
vm.$nextTick(() => {
expect(vm.$el.innerText).toContain('This diff is collapsed');
@@ -63,8 +62,8 @@ describe('DiffFile', () => {
});
it('should have collapsed text and link even before rendered', done => {
- vm.file.renderIt = false;
- vm.file.collapsed = true;
+ vm.renderIt = false;
+ vm.isCollapsed = true;
vm.$nextTick(() => {
expect(vm.$el.innerText).toContain('This diff is collapsed');
@@ -75,10 +74,10 @@ describe('DiffFile', () => {
});
it('should be collapsed for renamed files', done => {
- vm.file.renderIt = true;
- vm.file.collapsed = false;
+ vm.renderIt = true;
+ vm.isCollapsed = false;
vm.file.highlighted_diff_lines = null;
- vm.file.renamed_file = true;
+ vm.file.viewer.name = diffViewerModes.renamed;
vm.$nextTick(() => {
expect(vm.$el.innerText).not.toContain('This diff is collapsed');
@@ -88,10 +87,10 @@ describe('DiffFile', () => {
});
it('should be collapsed for mode changed files', done => {
- vm.file.renderIt = true;
- vm.file.collapsed = false;
+ vm.renderIt = true;
+ vm.isCollapsed = false;
vm.file.highlighted_diff_lines = null;
- vm.file.mode_changed = true;
+ vm.file.viewer.name = diffViewerModes.mode_changed;
vm.$nextTick(() => {
expect(vm.$el.innerText).not.toContain('This diff is collapsed');
@@ -101,7 +100,7 @@ describe('DiffFile', () => {
});
it('should have loading icon while loading a collapsed diffs', done => {
- vm.file.collapsed = true;
+ vm.isCollapsed = true;
vm.isLoadingCollapsedDiff = true;
vm.$nextTick(() => {
@@ -116,7 +115,7 @@ describe('DiffFile', () => {
describe('too large diff', () => {
it('should have too large warning and blob link', done => {
const BLOB_LINK = '/file/view/path';
- vm.file.too_large = true;
+ vm.file.viewer.error = diffViewerErrors.too_large;
vm.file.view_path = BLOB_LINK;
vm.$nextTick(() => {
@@ -140,11 +139,11 @@ describe('DiffFile', () => {
vm.file.highlighted_diff_lines = undefined;
vm.file.parallel_diff_lines = [];
- vm.file.collapsed = true;
+ vm.isCollapsed = true;
vm.$nextTick()
.then(() => {
- vm.file.collapsed = false;
+ vm.isCollapsed = false;
return vm.$nextTick();
})
diff --git a/spec/javascripts/diffs/components/tree_list_spec.js b/spec/javascripts/diffs/components/tree_list_spec.js
index 9e556698f34..cd7bf6405e5 100644
--- a/spec/javascripts/diffs/components/tree_list_spec.js
+++ b/spec/javascripts/diffs/components/tree_list_spec.js
@@ -28,7 +28,7 @@ describe('Diffs tree list component', () => {
localStorage.removeItem('mr_diff_tree_list');
- vm = mountComponentWithStore(Component, { store });
+ vm = mountComponentWithStore(Component, { store, props: { hideFileStats: false } });
});
afterEach(() => {
@@ -77,6 +77,16 @@ describe('Diffs tree list component', () => {
expect(vm.$el.querySelectorAll('.file-row')[1].textContent).toContain('app');
});
+ it('hides file stats', done => {
+ vm.hideFileStats = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.file-row-stats')).toBe(null);
+
+ done();
+ });
+ });
+
it('calls toggleTreeOpen when clicking folder', () => {
spyOn(vm.$store, 'dispatch').and.stub();
diff --git a/spec/javascripts/diffs/mock_data/diff_discussions.js b/spec/javascripts/diffs/mock_data/diff_discussions.js
index c1e9f791925..4a091b4580b 100644
--- a/spec/javascripts/diffs/mock_data/diff_discussions.js
+++ b/spec/javascripts/diffs/mock_data/diff_discussions.js
@@ -266,7 +266,7 @@ export default {
blob_name: 'CHANGELOG',
blob_icon: '<i aria-hidden="true" data-hidden="true" class="fa fa-file-text-o fa-fw"></i>',
file_hash: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a',
- file_path: 'CHANGELOG',
+ file_path: 'CHANGELOG.rb',
new_file: false,
deleted_file: false,
renamed_file: false,
@@ -286,7 +286,7 @@ export default {
content_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13',
stored_externally: null,
external_storage: null,
- old_path_html: ['CHANGELOG', 'CHANGELOG'],
+ old_path_html: 'CHANGELOG_OLD',
new_path_html: 'CHANGELOG',
context_lines_path:
'/gitlab-org/gitlab-test/blob/c48ee0d1bf3b30453f5b32250ce03134beaa6d13/CHANGELOG/diff',
@@ -485,6 +485,10 @@ export default {
},
},
],
+ viewer: {
+ name: 'text',
+ error: null,
+ },
},
diff_discussion: true,
truncated_diff_lines: [
diff --git a/spec/javascripts/diffs/mock_data/diff_file.js b/spec/javascripts/diffs/mock_data/diff_file.js
index 031c9842f2f..32af9ea8ddd 100644
--- a/spec/javascripts/diffs/mock_data/diff_file.js
+++ b/spec/javascripts/diffs/mock_data/diff_file.js
@@ -25,6 +25,8 @@ export default {
text: true,
viewer: {
name: 'text',
+ error: null,
+ collapsed: false,
},
added_lines: 2,
removed_lines: 0,
diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/javascripts/diffs/store/actions_spec.js
index b53ae4cecfd..acff80bca62 100644
--- a/spec/javascripts/diffs/store/actions_spec.js
+++ b/spec/javascripts/diffs/store/actions_spec.js
@@ -29,6 +29,7 @@ import actions, {
renderFileForDiscussionId,
setRenderTreeList,
setShowWhitespace,
+ setRenderIt,
} from '~/diffs/store/actions';
import eventHub from '~/notes/event_hub';
import * as types from '~/diffs/store/mutation_types';
@@ -262,12 +263,16 @@ describe('DiffsStoreActions', () => {
{
id: 1,
renderIt: false,
- collapsed: false,
+ viewer: {
+ collapsed: false,
+ },
},
{
id: 2,
renderIt: false,
- collapsed: false,
+ viewer: {
+ collapsed: false,
+ },
},
],
};
@@ -766,7 +771,9 @@ describe('DiffsStoreActions', () => {
diffFiles: [
{
file_hash: 'HASH',
- collapsed,
+ viewer: {
+ collapsed,
+ },
renderIt,
},
],
@@ -849,4 +856,10 @@ describe('DiffsStoreActions', () => {
expect(window.history.pushState).toHaveBeenCalled();
});
});
+
+ describe('setRenderIt', () => {
+ it('commits RENDER_FILE', done => {
+ testAction(setRenderIt, 'file', {}, [{ type: types.RENDER_FILE, payload: 'file' }], [], done);
+ });
+ });
});
diff --git a/spec/javascripts/diffs/store/getters_spec.js b/spec/javascripts/diffs/store/getters_spec.js
index 4f69dc92ab8..0ab88e6b2aa 100644
--- a/spec/javascripts/diffs/store/getters_spec.js
+++ b/spec/javascripts/diffs/store/getters_spec.js
@@ -51,13 +51,13 @@ describe('Diffs Module Getters', () => {
describe('hasCollapsedFile', () => {
it('returns true when all files are collapsed', () => {
- localState.diffFiles = [{ collapsed: true }, { collapsed: true }];
+ localState.diffFiles = [{ viewer: { collapsed: true } }, { viewer: { collapsed: true } }];
expect(getters.hasCollapsedFile(localState)).toEqual(true);
});
it('returns true when at least one file is collapsed', () => {
- localState.diffFiles = [{ collapsed: false }, { collapsed: true }];
+ localState.diffFiles = [{ viewer: { collapsed: false } }, { viewer: { collapsed: true } }];
expect(getters.hasCollapsedFile(localState)).toEqual(true);
});
diff --git a/spec/javascripts/diffs/store/mutations_spec.js b/spec/javascripts/diffs/store/mutations_spec.js
index a6f3f9b9dc3..09ee691b602 100644
--- a/spec/javascripts/diffs/store/mutations_spec.js
+++ b/spec/javascripts/diffs/store/mutations_spec.js
@@ -121,8 +121,14 @@ describe('DiffsStoreMutations', () => {
describe('ADD_COLLAPSED_DIFFS', () => {
it('should update the state with the given data for the given file hash', () => {
const fileHash = 123;
- const state = { diffFiles: [{}, { file_hash: fileHash, existing_field: 0 }] };
- const data = { diff_files: [{ file_hash: fileHash, extra_field: 1, existing_field: 1 }] };
+ const state = {
+ diffFiles: [{}, { file_hash: fileHash, existing_field: 0 }],
+ };
+ const data = {
+ diff_files: [
+ { file_hash: fileHash, extra_field: 1, existing_field: 1, viewer: { name: 'text' } },
+ ],
+ };
mutations[types.ADD_COLLAPSED_DIFFS](state, { file: state.diffFiles[1], data });
diff --git a/spec/javascripts/diffs/store/utils_spec.js b/spec/javascripts/diffs/store/utils_spec.js
index baf6e111f9f..599ea9cd420 100644
--- a/spec/javascripts/diffs/store/utils_spec.js
+++ b/spec/javascripts/diffs/store/utils_spec.js
@@ -601,7 +601,7 @@ describe('DiffsStoreUtils', () => {
it('returns mode_changed if key has no match', () => {
expect(
utils.getDiffMode({
- mode_changed: true,
+ viewer: { name: 'mode_changed' },
}),
).toBe('mode_changed');
});
diff --git a/spec/javascripts/fixtures/blob.rb b/spec/javascripts/fixtures/blob.rb
index 1b2a3b484bb..cd66d98f92a 100644
--- a/spec/javascripts/fixtures/blob.rb
+++ b/spec/javascripts/fixtures/blob.rb
@@ -15,6 +15,7 @@ describe Projects::BlobController, '(JavaScript fixtures)', type: :controller do
before do
sign_in(admin)
+ allow(SecureRandom).to receive(:hex).and_return('securerandomhex:thereisnospoon')
end
after do
diff --git a/spec/javascripts/fixtures/commit.rb b/spec/javascripts/fixtures/commit.rb
index f0e4bb50c67..295f13b34a4 100644
--- a/spec/javascripts/fixtures/commit.rb
+++ b/spec/javascripts/fixtures/commit.rb
@@ -16,6 +16,7 @@ describe Projects::CommitController, '(JavaScript fixtures)', type: :controller
before do
project.add_maintainer(user)
sign_in(user)
+ allow(SecureRandom).to receive(:hex).and_return('securerandomhex:thereisnospoon')
end
it 'commit/show.html.raw' do |example|
diff --git a/spec/javascripts/fixtures/deploy_keys.rb b/spec/javascripts/fixtures/deploy_keys.rb
index efbda955972..a333d9c0150 100644
--- a/spec/javascripts/fixtures/deploy_keys.rb
+++ b/spec/javascripts/fixtures/deploy_keys.rb
@@ -25,7 +25,7 @@ describe Projects::DeployKeysController, '(JavaScript fixtures)', type: :control
render_views
it 'deploy_keys/keys.json' do |example|
- create(:deploy_key, public: true)
+ create(:rsa_deploy_key_2048, public: true)
project_key = create(:deploy_key, key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCdMHEHyhRjbhEZVddFn6lTWdgEy5Q6Bz4nwGB76xWZI5YT/1WJOMEW+sL5zYd31kk7sd3FJ5L9ft8zWMWrr/iWXQikC2cqZK24H1xy+ZUmrRuJD4qGAaIVoyyzBL+avL+lF8J5lg6YSw8gwJY/lX64/vnJHUlWw2n5BF8IFOWhiw== dummy@gitlab.com')
internal_key = create(:deploy_key, key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDNd/UJWhPrpb+b/G5oL109y57yKuCxE+WUGJGYaj7WQKsYRJmLYh1mgjrl+KVyfsWpq4ylOxIfFSnN9xBBFN8mlb0Fma5DC7YsSsibJr3MZ19ZNBprwNcdogET7aW9I0In7Wu5f2KqI6e5W/spJHCy4JVxzVMUvk6Myab0LnJ2iQ== dummy@gitlab.com')
create(:deploy_keys_project, project: project, deploy_key: project_key)
diff --git a/spec/javascripts/fixtures/groups.rb b/spec/javascripts/fixtures/groups.rb
index f8d55fc97c3..03136f4e661 100644
--- a/spec/javascripts/fixtures/groups.rb
+++ b/spec/javascripts/fixtures/groups.rb
@@ -4,7 +4,7 @@ describe 'Groups (JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
let(:admin) { create(:admin) }
- let(:group) { create(:group, name: 'frontend-fixtures-group' )}
+ let(:group) { create(:group, name: 'frontend-fixtures-group', runners_token: 'runnerstoken:intabulasreferre')}
render_views
diff --git a/spec/javascripts/fixtures/issues.rb b/spec/javascripts/fixtures/issues.rb
index 18fb1bebf8b..9b8e90c2a43 100644
--- a/spec/javascripts/fixtures/issues.rb
+++ b/spec/javascripts/fixtures/issues.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Projects::IssuesController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:admin) { create(:admin) }
+ let(:admin) { create(:admin, feed_token: 'feedtoken:coldfeed') }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project_empty_repo, namespace: namespace, path: 'issues-project') }
diff --git a/spec/javascripts/fixtures/merge_requests.rb b/spec/javascripts/fixtures/merge_requests.rb
index 26e81f06c0b..eb37be87e1d 100644
--- a/spec/javascripts/fixtures/merge_requests.rb
+++ b/spec/javascripts/fixtures/merge_requests.rb
@@ -35,6 +35,7 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont
before do
sign_in(admin)
+ allow(Discussion).to receive(:build_discussion_id).and_return(['discussionid:ceterumcenseo'])
end
after do
@@ -54,8 +55,10 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont
end
it 'merge_requests/merged_merge_request.html.raw' do |example|
- allow_any_instance_of(MergeRequest).to receive(:source_branch_exists?).and_return(true)
- allow_any_instance_of(MergeRequest).to receive(:can_remove_source_branch?).and_return(true)
+ expect_next_instance_of(MergeRequest) do |merge_request|
+ allow(merge_request).to receive(:source_branch_exists?).and_return(true)
+ allow(merge_request).to receive(:can_remove_source_branch?).and_return(true)
+ end
render_merge_request(example.description, merged_merge_request)
end
diff --git a/spec/javascripts/fixtures/projects.rb b/spec/javascripts/fixtures/projects.rb
index 9b48646f8f0..85f02923804 100644
--- a/spec/javascripts/fixtures/projects.rb
+++ b/spec/javascripts/fixtures/projects.rb
@@ -3,13 +3,13 @@ require 'spec_helper'
describe 'Projects (JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
+ runners_token = 'runnerstoken:intabulasreferre'
+
let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
- let(:project) { create(:project, namespace: namespace, path: 'builds-project') }
+ let(:project) { create(:project, namespace: namespace, path: 'builds-project', runners_token: runners_token) }
let(:project_with_repo) { create(:project, :repository, description: 'Code and stuff') }
- let(:project_variable_populated) { create(:project, namespace: namespace, path: 'builds-project2') }
- let!(:variable1) { create(:ci_variable, project: project_variable_populated) }
- let!(:variable2) { create(:ci_variable, project: project_variable_populated) }
+ let(:project_variable_populated) { create(:project, namespace: namespace, path: 'builds-project2', runners_token: runners_token) }
render_views
@@ -20,6 +20,7 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
before do
project.add_maintainer(admin)
sign_in(admin)
+ allow(SecureRandom).to receive(:hex).and_return('securerandomhex:thereisnospoon')
end
after do
@@ -70,6 +71,9 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
end
it 'projects/ci_cd_settings_with_variables.html.raw' do |example|
+ create(:ci_variable, project: project_variable_populated)
+ create(:ci_variable, project: project_variable_populated)
+
get :show, params: {
namespace_id: project_variable_populated.namespace.to_param,
project_id: project_variable_populated
diff --git a/spec/javascripts/fixtures/snippet.rb b/spec/javascripts/fixtures/snippet.rb
index a14837e4d4a..bcd6546f3df 100644
--- a/spec/javascripts/fixtures/snippet.rb
+++ b/spec/javascripts/fixtures/snippet.rb
@@ -7,7 +7,6 @@ describe SnippetsController, '(JavaScript fixtures)', type: :controller do
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project, :repository, namespace: namespace, path: 'branches-project') }
let(:snippet) { create(:personal_snippet, title: 'snippet.md', content: '# snippet', file_name: 'snippet.md', author: admin) }
- let!(:snippet_note) { create(:discussion_note_on_snippet, noteable: snippet, project: project, author: admin, note: '- [ ] Task List Item') }
render_views
@@ -17,6 +16,7 @@ describe SnippetsController, '(JavaScript fixtures)', type: :controller do
before do
sign_in(admin)
+ allow(Discussion).to receive(:build_discussion_id).and_return(['discussionid:ceterumcenseo'])
end
after do
@@ -24,6 +24,8 @@ describe SnippetsController, '(JavaScript fixtures)', type: :controller do
end
it 'snippets/show.html.raw' do |example|
+ create(:discussion_note_on_snippet, noteable: snippet, project: project, author: admin, note: '- [ ] Task List Item')
+
get(:show, params: { id: snippet.to_param })
expect(response).to be_success
diff --git a/spec/javascripts/fixtures/u2f.rb b/spec/javascripts/fixtures/u2f.rb
index f0aa874bf75..5cdbadef639 100644
--- a/spec/javascripts/fixtures/u2f.rb
+++ b/spec/javascripts/fixtures/u2f.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
context 'U2F' do
include JavaScriptFixturesHelpers
- let(:user) { create(:user, :two_factor_via_u2f) }
+ let(:user) { create(:user, :two_factor_via_u2f, otp_secret: 'otpsecret:coolkids') }
before(:all) do
clean_frontend_fixtures('u2f/')
@@ -33,6 +33,7 @@ context 'U2F' do
before do
sign_in(user)
+ allow_any_instance_of(Profiles::TwoFactorAuthsController).to receive(:build_qr_code).and_return('qrcode:blackandwhitesquares')
end
it 'u2f/register.html.raw' do |example|
diff --git a/spec/javascripts/import_projects/components/import_projects_table_spec.js b/spec/javascripts/import_projects/components/import_projects_table_spec.js
new file mode 100644
index 00000000000..a1ff84ce259
--- /dev/null
+++ b/spec/javascripts/import_projects/components/import_projects_table_spec.js
@@ -0,0 +1,186 @@
+import Vue from 'vue';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import store from '~/import_projects/store';
+import importProjectsTable from '~/import_projects/components/import_projects_table.vue';
+import STATUS_MAP from '~/import_projects/constants';
+import setTimeoutPromise from '../../helpers/set_timeout_promise_helper';
+
+describe('ImportProjectsTable', () => {
+ let vm;
+ let mock;
+ const reposPath = '/repos-path';
+ const jobsPath = '/jobs-path';
+ const providerTitle = 'THE PROVIDER';
+ const providerRepo = { id: 10, sanitizedName: 'sanitizedName', fullName: 'fullName' };
+ const importedProject = {
+ id: 1,
+ fullPath: 'fullPath',
+ importStatus: 'started',
+ providerLink: 'providerLink',
+ importSource: 'importSource',
+ };
+
+ function createComponent() {
+ const ImportProjectsTable = Vue.extend(importProjectsTable);
+
+ const component = new ImportProjectsTable({
+ store,
+ propsData: {
+ providerTitle,
+ },
+ }).$mount();
+
+ component.$store.dispatch('stopJobsPolling');
+
+ return component;
+ }
+
+ beforeEach(() => {
+ store.dispatch('setInitialData', { reposPath });
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ mock.restore();
+ });
+
+ it('renders a loading icon whilst repos are loading', done => {
+ mock.restore(); // Stop the mock adapter from responding to the request, keeping the spinner up
+
+ vm = createComponent();
+
+ setTimeoutPromise()
+ .then(() => {
+ expect(vm.$el.querySelector('.js-loading-button-icon')).not.toBeNull();
+ })
+ .then(() => done())
+ .catch(() => done.fail());
+ });
+
+ it('renders a table with imported projects and provider repos', done => {
+ const response = {
+ importedProjects: [importedProject],
+ providerRepos: [providerRepo],
+ namespaces: [{ path: 'path' }],
+ };
+ mock.onGet(reposPath).reply(200, response);
+
+ vm = createComponent();
+
+ setTimeoutPromise()
+ .then(() => {
+ expect(vm.$el.querySelector('.js-loading-button-icon')).toBeNull();
+ expect(vm.$el.querySelector('.table')).not.toBeNull();
+ expect(vm.$el.querySelector('.import-jobs-from-col').innerText).toMatch(
+ `From ${providerTitle}`,
+ );
+
+ expect(vm.$el.querySelector('.js-imported-project')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-provider-repo')).not.toBeNull();
+ })
+ .then(() => done())
+ .catch(() => done.fail());
+ });
+
+ it('renders an empty state if there are no imported projects or provider repos', done => {
+ const response = {
+ importedProjects: [],
+ providerRepos: [],
+ namespaces: [],
+ };
+ mock.onGet(reposPath).reply(200, response);
+
+ vm = createComponent();
+
+ setTimeoutPromise()
+ .then(() => {
+ expect(vm.$el.querySelector('.js-loading-button-icon')).toBeNull();
+ expect(vm.$el.querySelector('.table')).toBeNull();
+ expect(vm.$el.innerText).toMatch(`No ${providerTitle} repositories available to import`);
+ })
+ .then(() => done())
+ .catch(() => done.fail());
+ });
+
+ it('imports provider repos if bulk import button is clicked', done => {
+ const importPath = '/import-path';
+ const response = {
+ importedProjects: [],
+ providerRepos: [providerRepo],
+ namespaces: [{ path: 'path' }],
+ };
+
+ mock.onGet(reposPath).replyOnce(200, response);
+ mock.onPost(importPath).replyOnce(200, importedProject);
+
+ store.dispatch('setInitialData', { importPath });
+
+ vm = createComponent();
+
+ setTimeoutPromise()
+ .then(() => {
+ expect(vm.$el.querySelector('.js-imported-project')).toBeNull();
+ expect(vm.$el.querySelector('.js-provider-repo')).not.toBeNull();
+
+ vm.$el.querySelector('.js-import-all').click();
+ })
+ .then(() => setTimeoutPromise())
+ .then(() => {
+ expect(vm.$el.querySelector('.js-imported-project')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-provider-repo')).toBeNull();
+ })
+ .then(() => done())
+ .catch(() => done.fail());
+ });
+
+ it('polls to update the status of imported projects', done => {
+ const importPath = '/import-path';
+ const response = {
+ importedProjects: [importedProject],
+ providerRepos: [],
+ namespaces: [{ path: 'path' }],
+ };
+ const updatedProjects = [
+ {
+ id: importedProject.id,
+ importStatus: 'finished',
+ },
+ ];
+
+ mock.onGet(reposPath).replyOnce(200, response);
+
+ store.dispatch('setInitialData', { importPath, jobsPath });
+
+ vm = createComponent();
+
+ setTimeoutPromise()
+ .then(() => {
+ const statusObject = STATUS_MAP[importedProject.importStatus];
+
+ expect(vm.$el.querySelector('.js-imported-project')).not.toBeNull();
+ expect(vm.$el.querySelector(`.${statusObject.textClass}`).textContent).toMatch(
+ statusObject.text,
+ );
+
+ expect(vm.$el.querySelector(`.ic-status_${statusObject.icon}`)).not.toBeNull();
+
+ mock.onGet(jobsPath).replyOnce(200, updatedProjects);
+ return vm.$store.dispatch('restartJobsPolling');
+ })
+ .then(() => setTimeoutPromise())
+ .then(() => {
+ const statusObject = STATUS_MAP[updatedProjects[0].importStatus];
+
+ expect(vm.$el.querySelector('.js-imported-project')).not.toBeNull();
+ expect(vm.$el.querySelector(`.${statusObject.textClass}`).textContent).toMatch(
+ statusObject.text,
+ );
+
+ expect(vm.$el.querySelector(`.ic-status_${statusObject.icon}`)).not.toBeNull();
+ })
+ .then(() => done())
+ .catch(() => done.fail());
+ });
+});
diff --git a/spec/javascripts/import_projects/components/imported_project_table_row_spec.js b/spec/javascripts/import_projects/components/imported_project_table_row_spec.js
new file mode 100644
index 00000000000..8af3b5954a9
--- /dev/null
+++ b/spec/javascripts/import_projects/components/imported_project_table_row_spec.js
@@ -0,0 +1,50 @@
+import Vue from 'vue';
+import store from '~/import_projects/store';
+import importedProjectTableRow from '~/import_projects/components/imported_project_table_row.vue';
+import STATUS_MAP from '~/import_projects/constants';
+
+describe('ImportedProjectTableRow', () => {
+ let vm;
+ const project = {
+ id: 1,
+ fullPath: 'fullPath',
+ importStatus: 'finished',
+ providerLink: 'providerLink',
+ importSource: 'importSource',
+ };
+
+ function createComponent() {
+ const ImportedProjectTableRow = Vue.extend(importedProjectTableRow);
+
+ return new ImportedProjectTableRow({
+ store,
+ propsData: {
+ project: {
+ ...project,
+ },
+ },
+ }).$mount();
+ }
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders an imported project table row', () => {
+ vm = createComponent();
+
+ const providerLink = vm.$el.querySelector('.js-provider-link');
+ const statusObject = STATUS_MAP[project.importStatus];
+
+ expect(vm.$el.classList.contains('js-imported-project')).toBe(true);
+ expect(providerLink.href).toMatch(project.providerLink);
+ expect(providerLink.textContent).toMatch(project.importSource);
+ expect(vm.$el.querySelector('.js-full-path').textContent).toMatch(project.fullPath);
+ expect(vm.$el.querySelector(`.${statusObject.textClass}`).textContent).toMatch(
+ statusObject.text,
+ );
+
+ expect(vm.$el.querySelector(`.ic-status_${statusObject.icon}`)).not.toBeNull();
+ expect(vm.$el.querySelector('.js-go-to-project').href).toMatch(project.fullPath);
+ });
+});
diff --git a/spec/javascripts/import_projects/components/provider_repo_table_row_spec.js b/spec/javascripts/import_projects/components/provider_repo_table_row_spec.js
new file mode 100644
index 00000000000..69377f8d685
--- /dev/null
+++ b/spec/javascripts/import_projects/components/provider_repo_table_row_spec.js
@@ -0,0 +1,91 @@
+import Vue from 'vue';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import store from '~/import_projects/store';
+import providerRepoTableRow from '~/import_projects/components/provider_repo_table_row.vue';
+import STATUS_MAP, { STATUSES } from '~/import_projects/constants';
+import setTimeoutPromise from '../../helpers/set_timeout_promise_helper';
+
+describe('ProviderRepoTableRow', () => {
+ let vm;
+ const repo = {
+ id: 10,
+ sanitizedName: 'sanitizedName',
+ fullName: 'fullName',
+ providerLink: 'providerLink',
+ };
+
+ function createComponent() {
+ const ProviderRepoTableRow = Vue.extend(providerRepoTableRow);
+
+ return new ProviderRepoTableRow({
+ store,
+ propsData: {
+ repo: {
+ ...repo,
+ },
+ },
+ }).$mount();
+ }
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders a provider repo table row', () => {
+ vm = createComponent();
+
+ const providerLink = vm.$el.querySelector('.js-provider-link');
+ const statusObject = STATUS_MAP[STATUSES.NONE];
+
+ expect(vm.$el.classList.contains('js-provider-repo')).toBe(true);
+ expect(providerLink.href).toMatch(repo.providerLink);
+ expect(providerLink.textContent).toMatch(repo.fullName);
+ expect(vm.$el.querySelector(`.${statusObject.textClass}`).textContent).toMatch(
+ statusObject.text,
+ );
+
+ expect(vm.$el.querySelector(`.ic-status_${statusObject.icon}`)).not.toBeNull();
+ expect(vm.$el.querySelector('.js-import-button')).not.toBeNull();
+ });
+
+ it('renders a select2 namespace select', () => {
+ vm = createComponent();
+
+ const dropdownTrigger = vm.$el.querySelector('.js-namespace-select');
+
+ expect(dropdownTrigger).not.toBeNull();
+ expect(dropdownTrigger.classList.contains('select2-container')).toBe(true);
+
+ dropdownTrigger.click();
+
+ expect(vm.$el.querySelector('.select2-drop')).not.toBeNull();
+ });
+
+ it('imports repo when clicking import button', done => {
+ const importPath = '/import-path';
+ const defaultTargetNamespace = 'user';
+ const ciCdOnly = true;
+ const mock = new MockAdapter(axios);
+
+ store.dispatch('setInitialData', { importPath, defaultTargetNamespace, ciCdOnly });
+ mock.onPost(importPath).replyOnce(200);
+ spyOn(store, 'dispatch').and.returnValue(new Promise(() => {}));
+
+ vm = createComponent();
+
+ vm.$el.querySelector('.js-import-button').click();
+
+ setTimeoutPromise()
+ .then(() => {
+ expect(store.dispatch).toHaveBeenCalledWith('fetchImport', {
+ repo,
+ newName: repo.sanitizedName,
+ targetNamespace: defaultTargetNamespace,
+ });
+ })
+ .then(() => mock.restore())
+ .then(done)
+ .catch(done.fail);
+ });
+});
diff --git a/spec/javascripts/import_projects/store/actions_spec.js b/spec/javascripts/import_projects/store/actions_spec.js
new file mode 100644
index 00000000000..77850ee3283
--- /dev/null
+++ b/spec/javascripts/import_projects/store/actions_spec.js
@@ -0,0 +1,284 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import {
+ SET_INITIAL_DATA,
+ REQUEST_REPOS,
+ RECEIVE_REPOS_SUCCESS,
+ RECEIVE_REPOS_ERROR,
+ REQUEST_IMPORT,
+ RECEIVE_IMPORT_SUCCESS,
+ RECEIVE_IMPORT_ERROR,
+ RECEIVE_JOBS_SUCCESS,
+} from '~/import_projects/store/mutation_types';
+import {
+ setInitialData,
+ requestRepos,
+ receiveReposSuccess,
+ receiveReposError,
+ fetchRepos,
+ requestImport,
+ receiveImportSuccess,
+ receiveImportError,
+ fetchImport,
+ receiveJobsSuccess,
+ fetchJobs,
+ clearJobsEtagPoll,
+ stopJobsPolling,
+} from '~/import_projects/store/actions';
+import state from '~/import_projects/store/state';
+import testAction from 'spec/helpers/vuex_action_helper';
+import { TEST_HOST } from 'spec/test_constants';
+
+describe('import_projects store actions', () => {
+ let localState;
+ const repoId = 1;
+ const repos = [{ id: 1 }, { id: 2 }];
+ const importPayload = { newName: 'newName', targetNamespace: 'targetNamespace', repo: { id: 1 } };
+
+ beforeEach(() => {
+ localState = state();
+ });
+
+ describe('setInitialData', () => {
+ it(`commits ${SET_INITIAL_DATA} mutation`, done => {
+ const initialData = {
+ reposPath: 'reposPath',
+ provider: 'provider',
+ jobsPath: 'jobsPath',
+ importPath: 'impapp/assets/javascripts/vue_shared/components/select2_select.vueortPath',
+ defaultTargetNamespace: 'defaultTargetNamespace',
+ ciCdOnly: 'ciCdOnly',
+ canSelectNamespace: 'canSelectNamespace',
+ };
+
+ testAction(
+ setInitialData,
+ initialData,
+ localState,
+ [{ type: SET_INITIAL_DATA, payload: initialData }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('requestRepos', () => {
+ it(`requestRepos commits ${REQUEST_REPOS} mutation`, done => {
+ testAction(
+ requestRepos,
+ null,
+ localState,
+ [{ type: REQUEST_REPOS, payload: null }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveReposSuccess', () => {
+ it(`commits ${RECEIVE_REPOS_SUCCESS} mutation`, done => {
+ testAction(
+ receiveReposSuccess,
+ repos,
+ localState,
+ [{ type: RECEIVE_REPOS_SUCCESS, payload: repos }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveReposError', () => {
+ it(`commits ${RECEIVE_REPOS_ERROR} mutation`, done => {
+ testAction(receiveReposError, repos, localState, [{ type: RECEIVE_REPOS_ERROR }], [], done);
+ });
+ });
+
+ describe('fetchRepos', () => {
+ let mock;
+
+ beforeEach(() => {
+ localState.reposPath = `${TEST_HOST}/endpoint.json`;
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => mock.restore());
+
+ it('dispatches requestRepos and receiveReposSuccess actions on a successful request', done => {
+ const payload = { imported_projects: [{}], provider_repos: [{}], namespaces: [{}] };
+ mock.onGet(`${TEST_HOST}/endpoint.json`).reply(200, payload);
+
+ testAction(
+ fetchRepos,
+ null,
+ localState,
+ [],
+ [
+ { type: 'requestRepos' },
+ {
+ type: 'receiveReposSuccess',
+ payload: convertObjectPropsToCamelCase(payload, { deep: true }),
+ },
+ {
+ type: 'fetchJobs',
+ },
+ ],
+ done,
+ );
+ });
+
+ it('dispatches requestRepos and receiveReposSuccess actions on an unsuccessful request', done => {
+ mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500);
+
+ testAction(
+ fetchRepos,
+ null,
+ localState,
+ [],
+ [{ type: 'requestRepos' }, { type: 'receiveReposError' }],
+ done,
+ );
+ });
+ });
+
+ describe('requestImport', () => {
+ it(`commits ${REQUEST_IMPORT} mutation`, done => {
+ testAction(
+ requestImport,
+ repoId,
+ localState,
+ [{ type: REQUEST_IMPORT, payload: repoId }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveImportSuccess', () => {
+ it(`commits ${RECEIVE_IMPORT_SUCCESS} mutation`, done => {
+ const payload = { importedProject: { name: 'imported/project' }, repoId: 2 };
+
+ testAction(
+ receiveImportSuccess,
+ payload,
+ localState,
+ [{ type: RECEIVE_IMPORT_SUCCESS, payload }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveImportError', () => {
+ it(`commits ${RECEIVE_IMPORT_ERROR} mutation`, done => {
+ testAction(
+ receiveImportError,
+ repoId,
+ localState,
+ [{ type: RECEIVE_IMPORT_ERROR, payload: repoId }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('fetchImport', () => {
+ let mock;
+
+ beforeEach(() => {
+ localState.importPath = `${TEST_HOST}/endpoint.json`;
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => mock.restore());
+
+ it('dispatches requestImport and receiveImportSuccess actions on a successful request', done => {
+ const importedProject = { name: 'imported/project' };
+ const importRepoId = importPayload.repo.id;
+ mock.onPost(`${TEST_HOST}/endpoint.json`).reply(200, importedProject);
+
+ testAction(
+ fetchImport,
+ importPayload,
+ localState,
+ [],
+ [
+ { type: 'requestImport', payload: importRepoId },
+ {
+ type: 'receiveImportSuccess',
+ payload: {
+ importedProject: convertObjectPropsToCamelCase(importedProject, { deep: true }),
+ repoId: importRepoId,
+ },
+ },
+ ],
+ done,
+ );
+ });
+
+ it('dispatches requestImport and receiveImportSuccess actions on an unsuccessful request', done => {
+ mock.onPost(`${TEST_HOST}/endpoint.json`).reply(500);
+
+ testAction(
+ fetchImport,
+ importPayload,
+ localState,
+ [],
+ [
+ { type: 'requestImport', payload: importPayload.repo.id },
+ { type: 'receiveImportError', payload: { repoId: importPayload.repo.id } },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('receiveJobsSuccess', () => {
+ it(`commits ${RECEIVE_JOBS_SUCCESS} mutation`, done => {
+ testAction(
+ receiveJobsSuccess,
+ repos,
+ localState,
+ [{ type: RECEIVE_JOBS_SUCCESS, payload: repos }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('fetchJobs', () => {
+ let mock;
+
+ beforeEach(() => {
+ localState.jobsPath = `${TEST_HOST}/endpoint.json`;
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ stopJobsPolling();
+ clearJobsEtagPoll();
+ });
+
+ afterEach(() => mock.restore());
+
+ it('dispatches requestJobs and receiveJobsSuccess actions on a successful request', done => {
+ const updatedProjects = [{ name: 'imported/project' }, { name: 'provider/repo' }];
+ mock.onGet(`${TEST_HOST}/endpoint.json`).reply(200, updatedProjects);
+
+ testAction(
+ fetchJobs,
+ null,
+ localState,
+ [],
+ [
+ {
+ type: 'receiveJobsSuccess',
+ payload: convertObjectPropsToCamelCase(updatedProjects, { deep: true }),
+ },
+ ],
+ done,
+ );
+ });
+ });
+});
diff --git a/spec/javascripts/import_projects/store/getters_spec.js b/spec/javascripts/import_projects/store/getters_spec.js
new file mode 100644
index 00000000000..e5e4a95f473
--- /dev/null
+++ b/spec/javascripts/import_projects/store/getters_spec.js
@@ -0,0 +1,83 @@
+import {
+ namespaceSelectOptions,
+ isImportingAnyRepo,
+ hasProviderRepos,
+ hasImportedProjects,
+} from '~/import_projects/store/getters';
+import state from '~/import_projects/store/state';
+
+describe('import_projects store getters', () => {
+ let localState;
+
+ beforeEach(() => {
+ localState = state();
+ });
+
+ describe('namespaceSelectOptions', () => {
+ const namespaces = [{ fullPath: 'namespace-0' }, { fullPath: 'namespace-1' }];
+ const defaultTargetNamespace = 'current-user';
+
+ it('returns an options array with a "Users" and "Groups" optgroups', () => {
+ localState.namespaces = namespaces;
+ localState.defaultTargetNamespace = defaultTargetNamespace;
+
+ const optionsArray = namespaceSelectOptions(localState);
+ const groupsGroup = optionsArray[0];
+ const usersGroup = optionsArray[1];
+
+ expect(groupsGroup.text).toBe('Groups');
+ expect(usersGroup.text).toBe('Users');
+
+ groupsGroup.children.forEach((child, index) => {
+ expect(child.id).toBe(namespaces[index].fullPath);
+ expect(child.text).toBe(namespaces[index].fullPath);
+ });
+
+ expect(usersGroup.children.length).toBe(1);
+ expect(usersGroup.children[0].id).toBe(defaultTargetNamespace);
+ expect(usersGroup.children[0].text).toBe(defaultTargetNamespace);
+ });
+ });
+
+ describe('isImportingAnyRepo', () => {
+ it('returns true if there are any reposBeingImported', () => {
+ localState.reposBeingImported = new Array(1);
+
+ expect(isImportingAnyRepo(localState)).toBe(true);
+ });
+
+ it('returns false if there are no reposBeingImported', () => {
+ localState.reposBeingImported = [];
+
+ expect(isImportingAnyRepo(localState)).toBe(false);
+ });
+ });
+
+ describe('hasProviderRepos', () => {
+ it('returns true if there are any providerRepos', () => {
+ localState.providerRepos = new Array(1);
+
+ expect(hasProviderRepos(localState)).toBe(true);
+ });
+
+ it('returns false if there are no providerRepos', () => {
+ localState.providerRepos = [];
+
+ expect(hasProviderRepos(localState)).toBe(false);
+ });
+ });
+
+ describe('hasImportedProjects', () => {
+ it('returns true if there are any importedProjects', () => {
+ localState.importedProjects = new Array(1);
+
+ expect(hasImportedProjects(localState)).toBe(true);
+ });
+
+ it('returns false if there are no importedProjects', () => {
+ localState.importedProjects = [];
+
+ expect(hasImportedProjects(localState)).toBe(false);
+ });
+ });
+});
diff --git a/spec/javascripts/import_projects/store/mutations_spec.js b/spec/javascripts/import_projects/store/mutations_spec.js
new file mode 100644
index 00000000000..8db8e9819ba
--- /dev/null
+++ b/spec/javascripts/import_projects/store/mutations_spec.js
@@ -0,0 +1,34 @@
+import * as types from '~/import_projects/store/mutation_types';
+import mutations from '~/import_projects/store/mutations';
+
+describe('import_projects store mutations', () => {
+ describe(types.RECEIVE_IMPORT_SUCCESS, () => {
+ it('removes repoId from reposBeingImported and providerRepos, adds to importedProjects', () => {
+ const repoId = 1;
+ const state = {
+ reposBeingImported: [repoId],
+ providerRepos: [{ id: repoId }],
+ importedProjects: [],
+ };
+ const importedProject = { id: repoId };
+
+ mutations[types.RECEIVE_IMPORT_SUCCESS](state, { importedProject, repoId });
+
+ expect(state.reposBeingImported.includes(repoId)).toBe(false);
+ expect(state.providerRepos.some(repo => repo.id === repoId)).toBe(false);
+ expect(state.importedProjects.some(repo => repo.id === repoId)).toBe(true);
+ });
+ });
+
+ describe(types.RECEIVE_JOBS_SUCCESS, () => {
+ it('updates importStatus of existing importedProjects', () => {
+ const repoId = 1;
+ const state = { importedProjects: [{ id: repoId, importStatus: 'started' }] };
+ const updatedProjects = [{ id: repoId, importStatus: 'finished' }];
+
+ mutations[types.RECEIVE_JOBS_SUCCESS](state, updatedProjects);
+
+ expect(state.importedProjects[0].importStatus).toBe(updatedProjects[0].importStatus);
+ });
+ });
+});
diff --git a/spec/javascripts/notes/components/discussion_jump_to_next_button_spec.js b/spec/javascripts/notes/components/discussion_jump_to_next_button_spec.js
deleted file mode 100644
index c41b29fa788..00000000000
--- a/spec/javascripts/notes/components/discussion_jump_to_next_button_spec.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import jumpToNextDiscussionButton from '~/notes/components/discussion_jump_to_next_button.vue';
-import { shallowMount, createLocalVue } from '@vue/test-utils';
-
-const localVue = createLocalVue();
-
-describe('jumpToNextDiscussionButton', () => {
- let wrapper;
-
- beforeEach(() => {
- wrapper = shallowMount(jumpToNextDiscussionButton, {
- localVue,
- sync: false,
- });
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('emits onClick event on button click', done => {
- const button = wrapper.find({ ref: 'button' });
-
- button.trigger('click');
-
- localVue.nextTick(() => {
- expect(wrapper.emitted()).toEqual({
- onClick: [[]],
- });
-
- done();
- });
- });
-});
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 11e1664a3f4..11fb89808d9 100644
--- a/spec/javascripts/notes/components/note_actions/reply_button_spec.js
+++ b/spec/javascripts/notes/components/note_actions/reply_button_spec.js
@@ -3,27 +3,14 @@ import { createLocalVue, mount } from '@vue/test-utils';
import ReplyButton from '~/notes/components/note_actions/reply_button.vue';
describe('ReplyButton', () => {
- const noteId = 'dummy-note-id';
-
let wrapper;
- let convertToDiscussion;
beforeEach(() => {
const localVue = createLocalVue();
- convertToDiscussion = jasmine.createSpy('convertToDiscussion');
localVue.use(Vuex);
- const store = new Vuex.Store({
- actions: {
- convertToDiscussion,
- },
- });
wrapper = mount(ReplyButton, {
- propsData: {
- noteId,
- },
- store,
sync: false,
localVue,
});
@@ -33,14 +20,13 @@ describe('ReplyButton', () => {
wrapper.destroy();
});
- it('dispatches convertToDiscussion with note ID on click', () => {
+ it('emits startReplying on click', () => {
const button = wrapper.find({ ref: 'button' });
button.trigger('click');
- expect(convertToDiscussion).toHaveBeenCalledTimes(1);
- const [, payload] = convertToDiscussion.calls.argsFor(0);
-
- expect(payload).toBe(noteId);
+ expect(wrapper.emitted()).toEqual({
+ startReplying: [[]],
+ });
});
});
diff --git a/spec/javascripts/notes/components/note_app_spec.js b/spec/javascripts/notes/components/note_app_spec.js
index d5c0bf6b25d..82f58dafc78 100644
--- a/spec/javascripts/notes/components/note_app_spec.js
+++ b/spec/javascripts/notes/components/note_app_spec.js
@@ -83,6 +83,8 @@ describe('note_app', () => {
describe('render', () => {
beforeEach(() => {
+ setFixtures('<div class="js-discussions-count"></div>');
+
Vue.http.interceptors.push(mockData.individualNoteInterceptor);
wrapper = mountComponent();
});
@@ -127,6 +129,14 @@ describe('note_app', () => {
it('should render form comment button as disabled', () => {
expect(wrapper.find('.js-note-new-discussion').attributes('disabled')).toEqual('disabled');
});
+
+ it('updates discussions badge', done => {
+ setTimeout(() => {
+ expect(document.querySelector('.js-discussions-count').textContent).toEqual('2');
+
+ done();
+ });
+ });
});
describe('while fetching data', () => {
diff --git a/spec/javascripts/notes/stores/actions_spec.js b/spec/javascripts/notes/stores/actions_spec.js
index 4f70570ffcc..94ce6d8e222 100644
--- a/spec/javascripts/notes/stores/actions_spec.js
+++ b/spec/javascripts/notes/stores/actions_spec.js
@@ -737,4 +737,18 @@ describe('Actions Notes Store', () => {
);
});
});
+
+ describe('removeConvertedDiscussion', () => {
+ it('commits CONVERT_TO_DISCUSSION with noteId', done => {
+ const noteId = 'dummy-id';
+ testAction(
+ actions.removeConvertedDiscussion,
+ noteId,
+ {},
+ [{ type: 'REMOVE_CONVERTED_DISCUSSION', payload: noteId }],
+ [],
+ done,
+ );
+ });
+ });
});
diff --git a/spec/javascripts/notes/stores/mutation_spec.js b/spec/javascripts/notes/stores/mutation_spec.js
index 88c40dbc5ea..fcad1f245b6 100644
--- a/spec/javascripts/notes/stores/mutation_spec.js
+++ b/spec/javascripts/notes/stores/mutation_spec.js
@@ -536,4 +536,23 @@ describe('Notes Store mutations', () => {
expect(state.convertedDisscussionIds).toContain(discussion.id);
});
});
+
+ describe('REMOVE_CONVERTED_DISCUSSION', () => {
+ let discussion;
+ let state;
+
+ beforeEach(() => {
+ discussion = {
+ id: 42,
+ individual_note: true,
+ };
+ state = { convertedDisscussionIds: [41, 42] };
+ });
+
+ it('removes a disucssion from convertedDisscussionIds', () => {
+ mutations.REMOVE_CONVERTED_DISCUSSION(state, discussion.id);
+
+ expect(state.convertedDisscussionIds).not.toContain(discussion.id);
+ });
+ });
});
diff --git a/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js b/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js
index 6add6cdac4d..660eaddf01f 100644
--- a/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js
+++ b/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js
@@ -22,6 +22,7 @@ describe('DiffViewer', () => {
createComponent({
diffMode: 'replaced',
+ diffViewerMode: 'image',
newPath: GREEN_BOX_IMAGE_URL,
newSha: 'ABC',
oldPath: RED_BOX_IMAGE_URL,
@@ -45,6 +46,7 @@ describe('DiffViewer', () => {
it('renders fallback download diff display', done => {
createComponent({
diffMode: 'replaced',
+ diffViewerMode: 'added',
newPath: 'test.abc',
newSha: 'ABC',
oldPath: 'testold.abc',
@@ -72,6 +74,7 @@ describe('DiffViewer', () => {
it('renders renamed component', () => {
createComponent({
diffMode: 'renamed',
+ diffViewerMode: 'renamed',
newPath: 'test.abc',
newSha: 'ABC',
oldPath: 'testold.abc',
@@ -84,6 +87,7 @@ describe('DiffViewer', () => {
it('renders mode changed component', () => {
createComponent({
diffMode: 'mode_changed',
+ diffViewerMode: 'image',
newPath: 'test.abc',
newSha: 'ABC',
oldPath: 'testold.abc',
diff --git a/spec/javascripts/vue_shared/components/panel_resizer_spec.js b/spec/javascripts/vue_shared/components/panel_resizer_spec.js
index 49a580be06b..caabe3aa260 100644
--- a/spec/javascripts/vue_shared/components/panel_resizer_spec.js
+++ b/spec/javascripts/vue_shared/components/panel_resizer_spec.js
@@ -44,7 +44,10 @@ describe('Panel Resizer component', () => {
});
expect(vm.$el.tagName).toEqual('DIV');
- expect(vm.$el.getAttribute('class')).toBe('drag-handle drag-left');
+ expect(vm.$el.getAttribute('class')).toBe(
+ 'position-absolute position-top-0 position-bottom-0 drag-handle position-left-0',
+ );
+
expect(vm.$el.getAttribute('style')).toBe('cursor: ew-resize;');
});
@@ -55,7 +58,9 @@ describe('Panel Resizer component', () => {
});
expect(vm.$el.tagName).toEqual('DIV');
- expect(vm.$el.getAttribute('class')).toBe('drag-handle drag-right');
+ expect(vm.$el.getAttribute('class')).toBe(
+ 'position-absolute position-top-0 position-bottom-0 drag-handle position-right-0',
+ );
});
it('drag the resizer', () => {
diff --git a/spec/lib/bitbucket_server/client_spec.rb b/spec/lib/bitbucket_server/client_spec.rb
index b021e69800a..4f0d57ca8a6 100644
--- a/spec/lib/bitbucket_server/client_spec.rb
+++ b/spec/lib/bitbucket_server/client_spec.rb
@@ -64,7 +64,7 @@ describe BitbucketServer::Client do
let(:url) { "#{base_uri}rest/api/1.0/projects/SOME-PROJECT/repos/my-repo/branches" }
it 'requests Bitbucket to create a branch' do
- stub_request(:post, url).to_return(status: 204, headers: headers, body: '{}')
+ stub_request(:post, url).to_return(status: 204, headers: headers, body: nil)
subject.create_branch(project, repo_slug, branch, sha)
@@ -78,7 +78,7 @@ describe BitbucketServer::Client do
let(:url) { "#{base_uri}rest/branch-utils/1.0/projects/SOME-PROJECT/repos/my-repo/branches" }
it 'requests Bitbucket to create a branch' do
- stub_request(:delete, url).to_return(status: 204, headers: headers, body: '{}')
+ stub_request(:delete, url).to_return(status: 204, headers: headers, body: nil)
subject.delete_branch(project, repo_slug, branch, sha)
diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb
index 630732614b2..a7163048370 100644
--- a/spec/lib/feature_spec.rb
+++ b/spec/lib/feature_spec.rb
@@ -186,13 +186,14 @@ describe Feature do
describe Feature::Target do
describe '#targets' do
let(:project) { create(:project) }
+ let(:group) { create(:group) }
let(:user_name) { project.owner.username }
- subject { described_class.new(user: user_name, project: project.full_path) }
+ subject { described_class.new(user: user_name, project: project.full_path, group: group.full_path) }
it 'returns all found targets' do
expect(subject.targets).to be_an(Array)
- expect(subject.targets).to eq([project.owner, project])
+ expect(subject.targets).to eq([project.owner, project, group])
end
end
end
diff --git a/spec/lib/gitlab/background_migration/digest_column_spec.rb b/spec/lib/gitlab/background_migration/digest_column_spec.rb
index 3e107ac3027..a25dcb06005 100644
--- a/spec/lib/gitlab/background_migration/digest_column_spec.rb
+++ b/spec/lib/gitlab/background_migration/digest_column_spec.rb
@@ -22,7 +22,7 @@ describe Gitlab::BackgroundMigration::DigestColumn, :migration, schema: 20180913
it 'erases token' do
expect { subject.perform(PersonalAccessToken, :token, :token_digest, 1, 2) }.to(
- change { PersonalAccessToken.find(1).token }.from('token-01').to(nil))
+ change { PersonalAccessToken.find(1).read_attribute(:token) }.from('token-01').to(nil))
end
end
@@ -39,7 +39,7 @@ describe Gitlab::BackgroundMigration::DigestColumn, :migration, schema: 20180913
it 'leaves token empty' do
expect { subject.perform(PersonalAccessToken, :token, :token_digest, 1, 2) }.not_to(
- change { PersonalAccessToken.find(1).token }.from(nil))
+ change { PersonalAccessToken.find(1).read_attribute(:token) }.from(nil))
end
end
end
diff --git a/spec/lib/gitlab/chat/command_spec.rb b/spec/lib/gitlab/chat/command_spec.rb
new file mode 100644
index 00000000000..46d23ab2b62
--- /dev/null
+++ b/spec/lib/gitlab/chat/command_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Chat::Command do
+ let(:chat_name) { create(:chat_name) }
+
+ let(:command) do
+ described_class.new(
+ project: project,
+ chat_name: chat_name,
+ name: 'spinach',
+ arguments: 'foo',
+ channel: '123',
+ response_url: 'http://example.com'
+ )
+ end
+
+ describe '#try_create_pipeline' do
+ let(:project) { create(:project) }
+
+ it 'returns nil when the command is not valid' do
+ expect(command)
+ .to receive(:valid?)
+ .and_return(false)
+
+ expect(command.try_create_pipeline).to be_nil
+ end
+
+ it 'tries to create the pipeline when a command is valid' do
+ expect(command)
+ .to receive(:valid?)
+ .and_return(true)
+
+ expect(command)
+ .to receive(:create_pipeline)
+
+ command.try_create_pipeline
+ end
+ end
+
+ describe '#create_pipeline' do
+ let(:project) { create(:project, :test_repo) }
+ let(:pipeline) { command.create_pipeline }
+
+ before do
+ stub_repository_ci_yaml_file(sha: project.commit.id)
+
+ project.add_developer(chat_name.user)
+ end
+
+ it 'creates the pipeline' do
+ expect(pipeline).to be_persisted
+ end
+
+ it 'creates the chat data for the pipeline' do
+ expect(pipeline.chat_data).to be_an_instance_of(Ci::PipelineChatData)
+ end
+
+ it 'stores the chat name ID in the chat data' do
+ expect(pipeline.chat_data.chat_name_id).to eq(chat_name.id)
+ end
+
+ it 'stores the response URL in the chat data' do
+ expect(pipeline.chat_data.response_url).to eq('http://example.com')
+ end
+
+ it 'creates the environment variables for the pipeline' do
+ vars = pipeline.variables.each_with_object({}) do |row, hash|
+ hash[row.key] = row.value
+ end
+
+ expect(vars['CHAT_INPUT']).to eq('foo')
+ expect(vars['CHAT_CHANNEL']).to eq('123')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/chat/output_spec.rb b/spec/lib/gitlab/chat/output_spec.rb
new file mode 100644
index 00000000000..b179f9e9d0a
--- /dev/null
+++ b/spec/lib/gitlab/chat/output_spec.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Chat::Output do
+ let(:build) do
+ create(:ci_build, pipeline: create(:ci_pipeline, source: :chat))
+ end
+
+ let(:output) { described_class.new(build) }
+
+ describe '#to_s' do
+ it 'returns the build output as a String' do
+ trace = Gitlab::Ci::Trace.new(build)
+
+ trace.set("echo hello\nhello")
+
+ allow(build)
+ .to receive(:trace)
+ .and_return(trace)
+
+ allow(output)
+ .to receive(:read_offset_and_length)
+ .and_return([0, 13])
+
+ expect(output.to_s).to eq('he')
+ end
+ end
+
+ describe '#read_offset_and_length' do
+ context 'without the chat_reply trace section' do
+ it 'falls back to using the build_script trace section' do
+ expect(output)
+ .to receive(:find_build_trace_section)
+ .with('chat_reply')
+ .and_return(nil)
+
+ expect(output)
+ .to receive(:find_build_trace_section)
+ .with('build_script')
+ .and_return({ name: 'build_script', byte_start: 1, byte_end: 4 })
+
+ expect(output.read_offset_and_length).to eq([1, 3])
+ end
+ end
+
+ context 'without the build_script trace section' do
+ it 'raises MissingBuildSectionError' do
+ expect { output.read_offset_and_length }
+ .to raise_error(described_class::MissingBuildSectionError)
+ end
+ end
+
+ context 'with the chat_reply trace section' do
+ it 'returns the read offset and length as an Array' do
+ trace = Gitlab::Ci::Trace.new(build)
+
+ allow(build)
+ .to receive(:trace)
+ .and_return(trace)
+
+ allow(trace)
+ .to receive(:extract_sections)
+ .and_return([{ name: 'chat_reply', byte_start: 1, byte_end: 4 }])
+
+ expect(output.read_offset_and_length).to eq([1, 3])
+ end
+ end
+ end
+
+ describe '#without_executed_command_line' do
+ it 'returns the input without the first line' do
+ expect(output.without_executed_command_line("hello\nworld"))
+ .to eq('world')
+ end
+
+ it 'returns an empty String when the input is empty' do
+ expect(output.without_executed_command_line('')).to eq('')
+ end
+
+ it 'returns an empty String when the input consits of a single newline' do
+ expect(output.without_executed_command_line("\n")).to eq('')
+ end
+ end
+
+ describe '#find_build_trace_section' do
+ it 'returns nil when no section could be found' do
+ expect(output.find_build_trace_section('foo')).to be_nil
+ end
+
+ it 'returns the trace section when it could be found' do
+ section = { name: 'chat_reply', byte_start: 1, byte_end: 4 }
+
+ allow(output)
+ .to receive(:trace_sections)
+ .and_return([section])
+
+ expect(output.find_build_trace_section('chat_reply')).to eq(section)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/chat/responder/base_spec.rb b/spec/lib/gitlab/chat/responder/base_spec.rb
new file mode 100644
index 00000000000..7fa9bad9d38
--- /dev/null
+++ b/spec/lib/gitlab/chat/responder/base_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Chat::Responder::Base do
+ let(:project) { double(:project) }
+ let(:pipeline) { double(:pipeline, project: project) }
+ let(:build) { double(:build, pipeline: pipeline) }
+ let(:responder) { described_class.new(build) }
+
+ describe '#pipeline' do
+ it 'returns the pipeline' do
+ expect(responder.pipeline).to eq(pipeline)
+ end
+ end
+
+ describe '#project' do
+ it 'returns the project' do
+ expect(responder.project).to eq(project)
+ end
+ end
+
+ describe '#success' do
+ it 'raises NotImplementedError' do
+ expect { responder.success }.to raise_error(NotImplementedError)
+ end
+ end
+
+ describe '#failure' do
+ it 'raises NotImplementedError' do
+ expect { responder.failure }.to raise_error(NotImplementedError)
+ end
+ end
+
+ describe '#send_response' do
+ it 'raises NotImplementedError' do
+ expect { responder.send_response('hello') }
+ .to raise_error(NotImplementedError)
+ end
+ end
+
+ describe '#scheduled_output' do
+ it 'raises NotImplementedError' do
+ expect { responder.scheduled_output }
+ .to raise_error(NotImplementedError)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/chat/responder/slack_spec.rb b/spec/lib/gitlab/chat/responder/slack_spec.rb
new file mode 100644
index 00000000000..a1553232b32
--- /dev/null
+++ b/spec/lib/gitlab/chat/responder/slack_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Chat::Responder::Slack do
+ let(:chat_name) { create(:chat_name, chat_id: 'U123') }
+
+ let(:pipeline) do
+ pipeline = create(:ci_pipeline)
+
+ pipeline.create_chat_data!(
+ response_url: 'http://example.com',
+ chat_name_id: chat_name.id
+ )
+
+ pipeline
+ end
+
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+ let(:responder) { described_class.new(build) }
+
+ describe '#send_response' do
+ it 'sends a response back to Slack' do
+ expect(Gitlab::HTTP).to receive(:post).with(
+ 'http://example.com',
+ { headers: { Accept: 'application/json' }, body: 'hello'.to_json }
+ )
+
+ responder.send_response('hello')
+ end
+ end
+
+ describe '#success' do
+ it 'returns the output for a successful build' do
+ expect(responder)
+ .to receive(:send_response)
+ .with(hash_including(text: /<@U123>:.+hello/, response_type: :in_channel))
+
+ responder.success('hello')
+ end
+
+ it 'limits the output to a fixed size' do
+ expect(responder)
+ .to receive(:send_response)
+ .with(hash_including(text: /The output is too large/))
+
+ responder.success('a' * 4000)
+ end
+
+ it 'does not send a response if the output is empty' do
+ expect(responder).not_to receive(:send_response)
+
+ responder.success('')
+ end
+ end
+
+ describe '#failure' do
+ it 'returns the output for a failed build' do
+ expect(responder).to receive(:send_response).with(
+ hash_including(
+ text: /<@U123>:.+Sorry, the build failed!/,
+ response_type: :in_channel
+ )
+ )
+
+ responder.failure
+ end
+ end
+
+ describe '#scheduled_output' do
+ it 'returns the output for a scheduled build' do
+ output = responder.scheduled_output
+
+ expect(output).to eq({ text: '' })
+ end
+ end
+end
diff --git a/spec/lib/gitlab/chat/responder_spec.rb b/spec/lib/gitlab/chat/responder_spec.rb
new file mode 100644
index 00000000000..9893689cba9
--- /dev/null
+++ b/spec/lib/gitlab/chat/responder_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Chat::Responder do
+ describe '.responder_for' do
+ context 'using a regular build' do
+ it 'returns nil' do
+ build = create(:ci_build)
+
+ expect(described_class.responder_for(build)).to be_nil
+ end
+ end
+
+ context 'using a chat build' do
+ it 'returns the responder for the build' do
+ pipeline = create(:ci_pipeline)
+ build = create(:ci_build, pipeline: pipeline)
+ service = double(:service, chat_responder: Gitlab::Chat::Responder::Slack)
+ chat_name = double(:chat_name, service: service)
+ chat_data = double(:chat_data, chat_name: chat_name)
+
+ allow(pipeline)
+ .to receive(:chat_data)
+ .and_return(chat_data)
+
+ expect(described_class.responder_for(build))
+ .to be_an_instance_of(Gitlab::Chat::Responder::Slack)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/chat_spec.rb b/spec/lib/gitlab/chat_spec.rb
new file mode 100644
index 00000000000..d61c4b36668
--- /dev/null
+++ b/spec/lib/gitlab/chat_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe Gitlab::Chat, :use_clean_rails_memory_store_caching do
+ describe '.available?' do
+ it 'returns true when the chatops feature is available' do
+ allow(Feature)
+ .to receive(:enabled?)
+ .with(:chatops, default_enabled: true)
+ .and_return(true)
+
+ expect(described_class).to be_available
+ end
+
+ it 'returns false when the chatops feature is not available' do
+ allow(Feature)
+ .to receive(:enabled?)
+ .with(:chatops, default_enabled: true)
+ .and_return(false)
+
+ expect(described_class).not_to be_available
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb
new file mode 100644
index 00000000000..7c1c016b4bb
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Pipeline::Chain::RemoveUnwantedChatJobs do
+ let(:project) { create(:project, :repository) }
+
+ let(:pipeline) do
+ build(:ci_pipeline_with_one_job, project: project, ref: 'master')
+ end
+
+ let(:command) do
+ double(:command, project: project, chat_data: { command: 'echo' })
+ end
+
+ describe '#perform!' do
+ it 'removes unwanted jobs for chat pipelines' do
+ allow(pipeline).to receive(:chat?).and_return(true)
+
+ pipeline.config_processor.jobs[:echo] = double(:job)
+
+ described_class.new(pipeline, command).perform!
+
+ expect(pipeline.config_processor.jobs.keys).to eq([:echo])
+ end
+ end
+
+ it 'does not remove any jobs for non-chat pipelines' do
+ described_class.new(pipeline, command).perform!
+
+ expect(pipeline.config_processor.jobs.keys).to eq([:rspec])
+ end
+end
diff --git a/spec/lib/gitlab/danger/helper_spec.rb b/spec/lib/gitlab/danger/helper_spec.rb
index ac3e4b04230..00cb1e6446a 100644
--- a/spec/lib/gitlab/danger/helper_spec.rb
+++ b/spec/lib/gitlab/danger/helper_spec.rb
@@ -184,7 +184,7 @@ describe Gitlab::Danger::Helper do
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 qa/foo] }
+ expect(fake_git).to receive(:added_files) { %w[foo foo.md foo.rb foo.js db/foo qa/foo ee/changelogs/foo.yml] }
allow(fake_git).to receive(:modified_files) { [] }
allow(fake_git).to receive(:renamed_files) { [] }
@@ -193,6 +193,7 @@ describe Gitlab::Danger::Helper do
database: %w[db/foo],
docs: %w[foo.md],
frontend: %w[foo.js],
+ none: %w[ee/changelogs/foo.yml],
qa: %w[qa/foo],
unknown: %w[foo]
)
@@ -216,6 +217,7 @@ describe Gitlab::Danger::Helper do
'app/views/foo' | :frontend
'public/foo' | :frontend
'spec/javascripts/foo' | :frontend
+ 'spec/frontend/bar' | :frontend
'vendor/assets/foo' | :frontend
'jest.config.js' | :frontend
'package.json' | :frontend
@@ -224,6 +226,7 @@ describe Gitlab::Danger::Helper do
'ee/app/assets/foo' | :frontend
'ee/app/views/foo' | :frontend
'ee/spec/javascripts/foo' | :frontend
+ 'ee/spec/frontend/bar' | :frontend
'app/models/foo' | :backend
'bin/foo' | :backend
@@ -260,6 +263,9 @@ describe Gitlab::Danger::Helper do
'ee/db/foo' | :database
'ee/qa/foo' | :qa
+ 'changelogs/foo' | :none
+ 'ee/changelogs/foo' | :none
+
'FOO' | :unknown
'foo' | :unknown
@@ -283,6 +289,7 @@ describe Gitlab::Danger::Helper do
:docs | '~Documentation'
:foo | '~foo'
:frontend | '~frontend'
+ :none | ''
:qa | '~QA'
end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index c15b360b563..f2eccec4635 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -131,6 +131,7 @@ ci_pipelines:
- merge_request
- deployments
- environments
+- chat_data
pipeline_variables:
- pipeline
stages:
diff --git a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
index 27c802f34ec..95b6b3fd953 100644
--- a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
@@ -30,7 +30,7 @@ describe Gitlab::Kubernetes::Helm::Pod do
it 'should generate the appropriate specifications for the container' do
container = subject.generate.spec.containers.first
expect(container.name).to eq('helm')
- expect(container.image).to eq('registry.gitlab.com/gitlab-org/cluster-integration/helm-install-image/releases/2.12.2-kube-1.11.0')
+ expect(container.image).to eq('registry.gitlab.com/gitlab-org/cluster-integration/helm-install-image/releases/2.12.3-kube-1.11.7')
expect(container.env.count).to eq(3)
expect(container.env.map(&:name)).to match_array([:HELM_VERSION, :TILLER_NAMESPACE, :COMMAND_SCRIPT])
expect(container.command).to match_array(["/bin/sh"])
diff --git a/spec/lib/gitlab/lfs_token_spec.rb b/spec/lib/gitlab/lfs_token_spec.rb
index 1ec1ba19e39..8961ecc4be0 100644
--- a/spec/lib/gitlab/lfs_token_spec.rb
+++ b/spec/lib/gitlab/lfs_token_spec.rb
@@ -4,10 +4,8 @@ require 'spec_helper'
describe Gitlab::LfsToken, :clean_gitlab_redis_shared_state do
describe '#token' do
- shared_examples 'an LFS token generator' do
+ shared_examples 'a valid LFS token' do
it 'returns a computed token' do
- expect(Settings).to receive(:attr_encrypted_db_key_base).and_return('fbnbv6hdjweo53qka7kza8v8swxc413c05pb51qgtfte0bygh1p2e508468hfsn5ntmjcyiz7h1d92ashpet5pkdyejg7g8or3yryhuso4h8o5c73h429d9c3r6bjnet').twice
-
token = lfs_token.token
expect(token).not_to be_nil
@@ -20,11 +18,7 @@ describe Gitlab::LfsToken, :clean_gitlab_redis_shared_state do
let(:actor) { create(:user, username: 'test_user_lfs_1') }
let(:lfs_token) { described_class.new(actor) }
- before do
- allow(actor).to receive(:encrypted_password).and_return('$2a$04$ETfzVS5spE9Hexn9wh6NUenCHG1LyZ2YdciOYxieV1WLSa8DHqOFO')
- end
-
- it_behaves_like 'an LFS token generator'
+ it_behaves_like 'a valid LFS token'
it 'returns the correct username' do
expect(lfs_token.actor_name).to eq(actor.username)
@@ -40,11 +34,7 @@ describe Gitlab::LfsToken, :clean_gitlab_redis_shared_state do
let(:actor) { create(:key, user: user) }
let(:lfs_token) { described_class.new(actor) }
- before do
- allow(user).to receive(:encrypted_password).and_return('$2a$04$C1GAMKsOKouEbhKy2JQoe./3LwOfQAZc.hC8zW2u/wt8xgukvnlV.')
- end
-
- it_behaves_like 'an LFS token generator'
+ it_behaves_like 'a valid LFS token'
it 'returns the correct username' do
expect(lfs_token.actor_name).to eq(user.username)
@@ -65,7 +55,7 @@ describe Gitlab::LfsToken, :clean_gitlab_redis_shared_state do
allow(actor).to receive(:id).and_return(actor_id)
end
- it_behaves_like 'an LFS token generator'
+ it_behaves_like 'a valid LFS token'
it 'returns the correct username' do
expect(lfs_token.actor_name).to eq("lfs+deploy-key-#{actor_id}")
@@ -87,10 +77,6 @@ describe Gitlab::LfsToken, :clean_gitlab_redis_shared_state do
let(:actor) { create(:user, username: 'test_user_lfs_1') }
let(:lfs_token) { described_class.new(actor) }
- before do
- allow(actor).to receive(:encrypted_password).and_return('$2a$04$ETfzVS5spE9Hexn9wh6NUenCHG1LyZ2YdciOYxieV1WLSa8DHqOFO')
- end
-
context 'for an HMAC token' do
before do
# We're not interested in testing LegacyRedisDeviseToken here
@@ -240,4 +226,18 @@ describe Gitlab::LfsToken, :clean_gitlab_redis_shared_state do
end
end
end
+
+ describe '#authentication_payload' do
+ it 'returns a Hash designed for gitlab-shell' do
+ actor = create(:user)
+ lfs_token = described_class.new(actor)
+ repo_http_path = 'http://localhost/user/repo.git'
+ authentication_payload = lfs_token.authentication_payload(repo_http_path)
+
+ expect(authentication_payload[:username]).to eq(actor.username)
+ expect(authentication_payload[:repository_http_path]).to eq(repo_http_path)
+ expect(authentication_payload[:lfs_token]).to be_a String
+ expect(authentication_payload[:expires_in]).to eq(described_class::DEFAULT_EXPIRE_TIME)
+ end
+ end
end
diff --git a/spec/lib/gitlab/slash_commands/application_help_spec.rb b/spec/lib/gitlab/slash_commands/application_help_spec.rb
new file mode 100644
index 00000000000..b203a1ee79c
--- /dev/null
+++ b/spec/lib/gitlab/slash_commands/application_help_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SlashCommands::ApplicationHelp do
+ let(:params) { { command: '/gitlab', text: 'help' } }
+
+ describe '#execute' do
+ subject do
+ described_class.new(params).execute
+ end
+
+ it 'displays the help section' do
+ expect(subject[:response_type]).to be(:ephemeral)
+ expect(subject[:text]).to include('Available commands')
+ expect(subject[:text]).to include('/gitlab [project name or alias] issue show')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/slash_commands/presenters/error_spec.rb b/spec/lib/gitlab/slash_commands/presenters/error_spec.rb
new file mode 100644
index 00000000000..30ff81510c1
--- /dev/null
+++ b/spec/lib/gitlab/slash_commands/presenters/error_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SlashCommands::Presenters::Error do
+ subject { described_class.new('Error').message }
+
+ it { is_expected.to be_a(Hash) }
+
+ it 'shows the error message' do
+ expect(subject[:response_type]).to be(:ephemeral)
+ expect(subject[:status]).to eq(200)
+ expect(subject[:text]).to eq('Error')
+ end
+end
diff --git a/spec/lib/gitlab/slash_commands/presenters/run_spec.rb b/spec/lib/gitlab/slash_commands/presenters/run_spec.rb
new file mode 100644
index 00000000000..f3ab01ef6bb
--- /dev/null
+++ b/spec/lib/gitlab/slash_commands/presenters/run_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SlashCommands::Presenters::Run do
+ let(:presenter) { described_class.new }
+
+ describe '#present' do
+ context 'when no builds are present' do
+ it 'returns an error' do
+ builds = double(:builds, take: nil)
+ pipeline = double(:pipeline, builds: builds)
+
+ expect(presenter)
+ .to receive(:unsupported_chat_service)
+
+ presenter.present(pipeline)
+ end
+ end
+
+ context 'when a responder could be found' do
+ it 'returns the output for a scheduled pipeline' do
+ responder = double(:responder, scheduled_output: 'hello')
+ build = double(:build)
+ builds = double(:builds, take: build)
+ pipeline = double(:pipeline, builds: builds)
+
+ allow(Gitlab::Chat::Responder)
+ .to receive(:responder_for)
+ .with(build)
+ .and_return(responder)
+
+ expect(presenter)
+ .to receive(:in_channel_response)
+ .with('hello')
+
+ presenter.present(pipeline)
+ end
+ end
+
+ context 'when a responder could not be found' do
+ it 'returns an error' do
+ build = double(:build)
+ builds = double(:builds, take: build)
+ pipeline = double(:pipeline, builds: builds)
+
+ allow(Gitlab::Chat::Responder)
+ .to receive(:responder_for)
+ .with(build)
+ .and_return(nil)
+
+ expect(presenter)
+ .to receive(:unsupported_chat_service)
+
+ presenter.present(pipeline)
+ end
+ end
+ end
+
+ describe '#unsupported_chat_service' do
+ it 'returns an ephemeral response' do
+ expect(presenter)
+ .to receive(:ephemeral_response)
+ .with(text: /Sorry, this chat service is currently not supported/)
+
+ presenter.unsupported_chat_service
+ end
+ end
+
+ describe '#failed_to_schedule' do
+ it 'returns an ephemeral response' do
+ expect(presenter)
+ .to receive(:ephemeral_response)
+ .with(text: /The command could not be scheduled/)
+
+ presenter.failed_to_schedule('foo')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/slash_commands/run_spec.rb b/spec/lib/gitlab/slash_commands/run_spec.rb
new file mode 100644
index 00000000000..900fae05719
--- /dev/null
+++ b/spec/lib/gitlab/slash_commands/run_spec.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SlashCommands::Run do
+ describe '.available?' do
+ it 'returns true when builds are enabled for the project' do
+ project = double(:project, builds_enabled?: true)
+
+ allow(Gitlab::Chat)
+ .to receive(:available?)
+ .and_return(true)
+
+ expect(described_class.available?(project)).to eq(true)
+ end
+
+ it 'returns false when builds are disabled for the project' do
+ project = double(:project, builds_enabled?: false)
+
+ expect(described_class.available?(project)).to eq(false)
+ end
+
+ it 'returns false when chatops is not available' do
+ allow(Gitlab::Chat)
+ .to receive(:available?)
+ .and_return(false)
+
+ project = double(:project, builds_enabled?: true)
+
+ expect(described_class.available?(project)).to eq(false)
+ end
+ end
+
+ describe '.allowed?' do
+ it 'returns true when the user can create a pipeline' do
+ project = create(:project)
+
+ expect(described_class.allowed?(project, project.creator)).to eq(true)
+ end
+
+ it 'returns false when the user can not create a pipeline' do
+ project = create(:project)
+ user = create(:user)
+
+ expect(described_class.allowed?(project, user)).to eq(false)
+ end
+ end
+
+ describe '#execute' do
+ let(:chat_name) { create(:chat_name) }
+ let(:project) { create(:project) }
+
+ let(:command) do
+ described_class.new(project, chat_name, response_url: 'http://example.com')
+ end
+
+ context 'when a pipeline could not be scheduled' do
+ it 'returns an error' do
+ expect_any_instance_of(Gitlab::Chat::Command)
+ .to receive(:try_create_pipeline)
+ .and_return(nil)
+
+ expect_any_instance_of(Gitlab::SlashCommands::Presenters::Run)
+ .to receive(:failed_to_schedule)
+ .with('foo')
+
+ command.execute(command: 'foo', arguments: '')
+ end
+ end
+
+ context 'when a pipeline could be created but the chat service was not supported' do
+ it 'returns an error' do
+ build = double(:build)
+ pipeline = double(
+ :pipeline,
+ builds: double(:relation, take: build),
+ persisted?: true
+ )
+
+ expect_any_instance_of(Gitlab::Chat::Command)
+ .to receive(:try_create_pipeline)
+ .and_return(pipeline)
+
+ expect(Gitlab::Chat::Responder)
+ .to receive(:responder_for)
+ .with(build)
+ .and_return(nil)
+
+ expect_any_instance_of(Gitlab::SlashCommands::Presenters::Run)
+ .to receive(:unsupported_chat_service)
+
+ command.execute(command: 'foo', arguments: '')
+ end
+ end
+
+ context 'using a valid pipeline' do
+ it 'schedules the pipeline' do
+ responder = double(:responder, scheduled_output: 'hello')
+ build = double(:build)
+ pipeline = double(
+ :pipeline,
+ builds: double(:relation, take: build),
+ persisted?: true
+ )
+
+ expect_any_instance_of(Gitlab::Chat::Command)
+ .to receive(:try_create_pipeline)
+ .and_return(pipeline)
+
+ expect(Gitlab::Chat::Responder)
+ .to receive(:responder_for)
+ .with(build)
+ .and_return(responder)
+
+ expect_any_instance_of(Gitlab::SlashCommands::Presenters::Run)
+ .to receive(:in_channel_response)
+ .with(responder.scheduled_output)
+
+ command.execute(command: 'foo', arguments: '')
+ end
+ end
+ end
+end
diff --git a/spec/lib/sentry/client_spec.rb b/spec/lib/sentry/client_spec.rb
index 6fbf60a6222..88e7e2e5ebb 100644
--- a/spec/lib/sentry/client_spec.rb
+++ b/spec/lib/sentry/client_spec.rb
@@ -36,7 +36,7 @@ describe Sentry::Client do
end
it 'does not follow redirects' do
- expect { subject }.to raise_exception(Sentry::Client::Error, 'Sentry response error: 302')
+ expect { subject }.to raise_exception(Sentry::Client::Error, 'Sentry response status code: 302')
expect(redirect_req_stub).to have_been_requested
expect(redirected_req_stub).not_to have_been_requested
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 460b5c8cd31..b9567ab4d65 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -22,6 +22,7 @@ describe Ci::Pipeline, :mailer do
it { is_expected.to have_many(:builds) }
it { is_expected.to have_many(:auto_canceled_pipelines) }
it { is_expected.to have_many(:auto_canceled_jobs) }
+ it { is_expected.to have_one(:chat_data) }
it { is_expected.to validate_presence_of(:sha) }
it { is_expected.to validate_presence_of(:status) }
diff --git a/spec/models/clusters/applications/helm_spec.rb b/spec/models/clusters/applications/helm_spec.rb
index 64f6d9c8bb4..f16eff92167 100644
--- a/spec/models/clusters/applications/helm_spec.rb
+++ b/spec/models/clusters/applications/helm_spec.rb
@@ -3,16 +3,17 @@ require 'rails_helper'
describe Clusters::Applications::Helm do
include_examples 'cluster application core specs', :clusters_applications_helm
- describe '.installed' do
- subject { described_class.installed }
+ describe '.available' do
+ subject { described_class.available }
let!(:installed_cluster) { create(:clusters_applications_helm, :installed) }
+ let!(:updated_cluster) { create(:clusters_applications_helm, :updated) }
before do
create(:clusters_applications_helm, :errored)
end
- it { is_expected.to contain_exactly(installed_cluster) }
+ it { is_expected.to contain_exactly(installed_cluster, updated_cluster) }
end
describe '#issue_client_cert' do
diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb
index 03ca18c6943..d5fd42509a3 100644
--- a/spec/models/clusters/applications/ingress_spec.rb
+++ b/spec/models/clusters/applications/ingress_spec.rb
@@ -16,18 +16,6 @@ describe Clusters::Applications::Ingress do
allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async)
end
- describe '.installed' do
- subject { described_class.installed }
-
- let!(:cluster) { create(:clusters_applications_ingress, :installed) }
-
- before do
- create(:clusters_applications_ingress, :errored)
- end
-
- it { is_expected.to contain_exactly(cluster) }
- end
-
describe '#make_installed!' do
before do
application.make_installed!
diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb
index cd29e0d4f53..006b922ab27 100644
--- a/spec/models/clusters/applications/knative_spec.rb
+++ b/spec/models/clusters/applications/knative_spec.rb
@@ -24,30 +24,6 @@ describe Clusters::Applications::Knative do
it { expect(knative_no_rbac).to be_not_installable }
end
- describe '.installed' do
- subject { described_class.installed }
-
- let!(:cluster) { create(:clusters_applications_knative, :installed) }
-
- before do
- create(:clusters_applications_knative, :errored)
- end
-
- it { is_expected.to contain_exactly(cluster) }
- end
-
- describe '#make_installed' do
- subject { described_class.installed }
-
- let!(:cluster) { create(:clusters_applications_knative, :installed) }
-
- before do
- create(:clusters_applications_knative, :errored)
- end
-
- it { is_expected.to contain_exactly(cluster) }
- end
-
describe 'make_installed with external_ip' do
before do
application.make_installed!
diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb
index caf59b0fc31..81708b0c2ed 100644
--- a/spec/models/clusters/applications/prometheus_spec.rb
+++ b/spec/models/clusters/applications/prometheus_spec.rb
@@ -9,18 +9,6 @@ describe Clusters::Applications::Prometheus do
include_examples 'cluster application helm specs', :clusters_applications_prometheus
include_examples 'cluster application initial status specs'
- describe '.installed' do
- subject { described_class.installed }
-
- let!(:cluster) { create(:clusters_applications_prometheus, :installed) }
-
- before do
- create(:clusters_applications_prometheus, :errored)
- end
-
- it { is_expected.to contain_exactly(cluster) }
- end
-
describe 'transition to installed' do
let(:project) { create(:project) }
let(:cluster) { create(:cluster, :with_installed_helm, projects: [project]) }
@@ -39,65 +27,6 @@ describe Clusters::Applications::Prometheus do
end
end
- describe '#ready' do
- let(:project) { create(:project) }
- let(:cluster) { create(:cluster, projects: [project]) }
-
- it 'returns true when installed' do
- application = build(:clusters_applications_prometheus, :installed, cluster: cluster)
-
- expect(application).to be_ready
- end
-
- it 'returns false when not_installable' do
- application = build(:clusters_applications_prometheus, :not_installable, cluster: cluster)
-
- expect(application).not_to be_ready
- end
-
- it 'returns false when installable' do
- application = build(:clusters_applications_prometheus, :installable, cluster: cluster)
-
- expect(application).not_to be_ready
- end
-
- it 'returns false when scheduled' do
- application = build(:clusters_applications_prometheus, :scheduled, cluster: cluster)
-
- expect(application).not_to be_ready
- end
-
- it 'returns false when installing' do
- application = build(:clusters_applications_prometheus, :installing, cluster: cluster)
-
- expect(application).not_to be_ready
- end
-
- it 'returns false when errored' do
- application = build(:clusters_applications_prometheus, :errored, cluster: cluster)
-
- expect(application).not_to be_ready
- end
-
- it 'returns true when updating' do
- application = build(:clusters_applications_prometheus, :updating, cluster: cluster)
-
- expect(application).to be_ready
- end
-
- it 'returns true when updated' do
- application = build(:clusters_applications_prometheus, :updated, cluster: cluster)
-
- expect(application).to be_ready
- end
-
- it 'returns true when errored' do
- application = build(:clusters_applications_prometheus, :update_errored, cluster: cluster)
-
- expect(application).to be_ready
- end
- end
-
describe '#prometheus_client' do
context 'cluster is nil' do
it 'returns nil' do
@@ -192,7 +121,7 @@ describe Clusters::Applications::Prometheus do
end
context 'with knative installed' do
- let(:knative) { create(:clusters_applications_knative, :installed ) }
+ let(:knative) { create(:clusters_applications_knative, :updated ) }
let(:prometheus) { create(:clusters_applications_prometheus, cluster: knative.cluster) }
subject { prometheus.install_command }
diff --git a/spec/models/clusters/applications/runner_spec.rb b/spec/models/clusters/applications/runner_spec.rb
index 38758ff97bc..5e5b25cbf8a 100644
--- a/spec/models/clusters/applications/runner_spec.rb
+++ b/spec/models/clusters/applications/runner_spec.rb
@@ -11,18 +11,6 @@ describe Clusters::Applications::Runner do
it { is_expected.to belong_to(:runner) }
- describe '.installed' do
- subject { described_class.installed }
-
- let!(:cluster) { create(:clusters_applications_runner, :installed) }
-
- before do
- create(:clusters_applications_runner, :errored)
- end
-
- it { is_expected.to contain_exactly(cluster) }
- end
-
describe '#install_command' do
let(:kubeclient) { double('kubernetes client') }
let(:gitlab_runner) { create(:clusters_applications_runner, runner: ci_runner) }
diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb
index 92ce2b0999a..3feed4e9718 100644
--- a/spec/models/clusters/cluster_spec.rb
+++ b/spec/models/clusters/cluster_spec.rb
@@ -265,12 +265,12 @@ describe Clusters::Cluster do
it { is_expected.to be_valid }
end
- context 'when cluster has an invalid domain' do
- let(:cluster) { build(:cluster, domain: 'not-valid-domain') }
+ context 'when cluster is not a valid hostname' do
+ let(:cluster) { build(:cluster, domain: 'http://not.a.valid.hostname') }
it 'should add an error on domain' do
expect(subject).not_to be_valid
- expect(subject.errors[:domain].first).to eq('is not a fully qualified domain name')
+ expect(subject.errors[:domain].first).to eq('contains invalid characters (valid characters: [a-z0-9\\-])')
end
end
diff --git a/spec/models/commit_collection_spec.rb b/spec/models/commit_collection_spec.rb
index 12e59b35428..0f5d03ff458 100644
--- a/spec/models/commit_collection_spec.rb
+++ b/spec/models/commit_collection_spec.rb
@@ -12,26 +12,26 @@ describe CommitCollection do
end
end
- describe '.committers' do
+ describe '.authors' do
it 'returns a relation of users when users are found' do
- user = create(:user, email: commit.committer_email.upcase)
+ user = create(:user, email: commit.author_email.upcase)
collection = described_class.new(project, [commit])
- expect(collection.committers).to contain_exactly(user)
+ expect(collection.authors).to contain_exactly(user)
end
- it 'returns empty array when committers cannot be found' do
+ it 'returns empty array when authors cannot be found' do
collection = described_class.new(project, [commit])
- expect(collection.committers).to be_empty
+ expect(collection.authors).to be_empty
end
it 'excludes authors of merge commits' do
commit = project.commit("60ecb67744cb56576c30214ff52294f8ce2def98")
- create(:user, email: commit.committer_email.upcase)
+ create(:user, email: commit.author_email.upcase)
collection = described_class.new(project, [commit])
- expect(collection.committers).to be_empty
+ expect(collection.authors).to be_empty
end
end
diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb
index 97a4c212f1c..03ae45e6b17 100644
--- a/spec/models/concerns/reactive_caching_spec.rb
+++ b/spec/models/concerns/reactive_caching_spec.rb
@@ -25,7 +25,7 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
def result
with_reactive_cache do |data|
- data / 2
+ data
end
end
end
@@ -64,7 +64,7 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
stub_reactive_cache(instance, 4)
end
- it { is_expected.to eq(2) }
+ it { is_expected.to eq(4) }
it 'does not enqueue a background worker' do
expect(ReactiveCachingWorker).not_to receive(:perform_async)
@@ -94,6 +94,14 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
end
end
end
+
+ context 'when cache contains non-nil but blank value' do
+ before do
+ stub_reactive_cache(instance, false)
+ end
+
+ it { is_expected.to eq(false) }
+ end
end
describe '#clear_reactive_cache!' do
diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb
index 55d83bc3a6b..40cb4eef60a 100644
--- a/spec/models/concerns/token_authenticatable_spec.rb
+++ b/spec/models/concerns/token_authenticatable_spec.rb
@@ -97,14 +97,31 @@ describe ApplicationSetting, 'TokenAuthenticatable' do
end
describe PersonalAccessToken, 'TokenAuthenticatable' do
- let(:personal_access_token_name) { 'test-pat-01' }
+ shared_examples 'changes personal access token' do
+ it 'sets new token' do
+ subject
+
+ expect(personal_access_token.token).to eq(token_value)
+ expect(personal_access_token.token_digest).to eq(Gitlab::CryptoHelper.sha256(token_value))
+ end
+ end
+
+ shared_examples 'does not change personal access token' do
+ it 'sets new token' do
+ subject
+
+ expect(personal_access_token.token).to be(nil)
+ expect(personal_access_token.token_digest).to eq(token_digest)
+ end
+ end
+
let(:token_value) { 'token' }
+ let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) }
let(:user) { create(:user) }
let(:personal_access_token) do
- described_class.new(name: personal_access_token_name,
+ described_class.new(name: 'test-pat-01',
user_id: user.id,
scopes: [:api],
- token: token,
token_digest: token_digest)
end
@@ -115,239 +132,71 @@ describe PersonalAccessToken, 'TokenAuthenticatable' do
describe '.find_by_token' do
subject { PersonalAccessToken.find_by_token(token_value) }
- before do
+ it 'finds the token' do
personal_access_token.save
- end
- context 'token_digest already exists' do
- let(:token) { nil }
- let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) }
-
- it 'finds the token' do
- expect(subject).not_to be_nil
- expect(subject.name).to eql(personal_access_token_name)
- end
- end
-
- context 'token_digest does not exist' do
- let(:token) { token_value }
- let(:token_digest) { nil }
-
- it 'finds the token' do
- expect(subject).not_to be_nil
- expect(subject.name).to eql(personal_access_token_name)
- end
+ expect(subject).to eq(personal_access_token)
end
end
describe '#set_token' do
let(:new_token_value) { 'new-token' }
- subject { personal_access_token.set_token(new_token_value) }
-
- context 'token_digest already exists' do
- let(:token) { nil }
- let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) }
- it 'overwrites token_digest' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(new_token_value)
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(new_token_value))
- end
- end
-
- context 'token_digest does not exist but token does' do
- let(:token) { token_value }
- let(:token_digest) { nil }
-
- it 'creates new token_digest and clears token' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(new_token_value)
- expect(personal_access_token.token_digest).to eql(Gitlab::CryptoHelper.sha256(new_token_value))
- end
- end
-
- context 'token_digest does not exist, nor token' do
- let(:token) { nil }
- let(:token_digest) { nil }
+ subject { personal_access_token.set_token(new_token_value) }
- it 'creates new token_digest' do
- subject
+ it 'sets new token' do
+ subject
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(new_token_value)
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(new_token_value))
- end
+ expect(personal_access_token.token).to eq(new_token_value)
+ expect(personal_access_token.token_digest).to eq(Gitlab::CryptoHelper.sha256(new_token_value))
end
end
describe '#ensure_token' do
subject { personal_access_token.ensure_token }
- context 'token_digest already exists' do
- let(:token) { nil }
- let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) }
-
- it 'does not change token fields' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to be_nil
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
- end
- end
-
- context 'token_digest does not exist but token does' do
- let(:token) { token_value }
+ context 'token_digest does not exist' do
let(:token_digest) { nil }
- it 'does not change token fields' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to eql(token_value)
- expect(personal_access_token.token).to eql(token_value)
- expect(personal_access_token.token_digest).to be_nil
- end
+ it_behaves_like 'changes personal access token'
end
- context 'token_digest does not exist, nor token' do
- let(:token) { nil }
- let(:token_digest) { nil }
-
- it 'creates token_digest' do
- subject
+ context 'token_digest already generated' do
+ let(:token_digest) { 's3cr3t' }
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(token_value)
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
- end
+ it_behaves_like 'does not change personal access token'
end
end
describe '#ensure_token!' do
subject { personal_access_token.ensure_token! }
- context 'token_digest already exists' do
- let(:token) { nil }
- let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) }
-
- it 'does not change token fields' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to be_nil
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
- end
- end
-
- context 'token_digest does not exist but token does' do
- let(:token) { token_value }
+ context 'token_digest does not exist' do
let(:token_digest) { nil }
- it 'does not change token fields' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to eql(token_value)
- expect(personal_access_token.token).to eql(token_value)
- expect(personal_access_token.token_digest).to be_nil
- end
+ it_behaves_like 'changes personal access token'
end
- context 'token_digest does not exist, nor token' do
- let(:token) { nil }
- let(:token_digest) { nil }
+ context 'token_digest already generated' do
+ let(:token_digest) { 's3cr3t' }
- it 'creates token_digest' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(token_value)
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
- end
+ it_behaves_like 'does not change personal access token'
end
end
describe '#reset_token!' do
subject { personal_access_token.reset_token! }
- context 'token_digest already exists' do
- let(:token) { nil }
- let(:token_digest) { Gitlab::CryptoHelper.sha256('old-token') }
-
- it 'creates new token_digest' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(token_value)
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
- end
- end
-
- context 'token_digest does not exist but token does' do
- let(:token) { 'old-token' }
- let(:token_digest) { nil }
-
- it 'creates new token_digest and clears token' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(token_value)
- expect(personal_access_token.token_digest).to eql(Gitlab::CryptoHelper.sha256(token_value))
- end
- end
-
- context 'token_digest does not exist, nor token' do
- let(:token) { nil }
+ context 'token_digest does not exist' do
let(:token_digest) { nil }
- it 'creates new token_digest' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(token_value)
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
- end
- end
-
- context 'token_digest exists and newly generated token would be the same' do
- let(:token) { nil }
- let(:token_digest) { Gitlab::CryptoHelper.sha256('old-token') }
-
- before do
- personal_access_token.save
- allow(Devise).to receive(:friendly_token).and_return(
- 'old-token', token_value, 'boom!')
- end
-
- it 'regenerates a new token_digest' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(token_value)
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
- end
+ it_behaves_like 'changes personal access token'
end
- context 'token exists and newly generated token would be the same' do
- let(:token) { 'old-token' }
- let(:token_digest) { nil }
-
- before do
- personal_access_token.save
- allow(Devise).to receive(:friendly_token).and_return(
- 'old-token', token_value, 'boom!')
- end
+ context 'token_digest already generated' do
+ let(:token_digest) { 's3cr3t' }
- it 'regenerates a new token_digest' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(token_value)
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
- end
+ it_behaves_like 'changes personal access token'
end
end
end
diff --git a/spec/models/error_tracking/project_error_tracking_setting_spec.rb b/spec/models/error_tracking/project_error_tracking_setting_spec.rb
index d30228b863c..076ccc96041 100644
--- a/spec/models/error_tracking/project_error_tracking_setting_spec.rb
+++ b/spec/models/error_tracking/project_error_tracking_setting_spec.rb
@@ -145,6 +145,24 @@ describe ErrorTracking::ProjectErrorTrackingSetting do
expect(result).to be_nil
end
end
+
+ context 'when sentry client raises exception' do
+ let(:sentry_client) { spy(:sentry_client) }
+
+ before do
+ synchronous_reactive_cache(subject)
+
+ allow(subject).to receive(:sentry_client).and_return(sentry_client)
+ allow(sentry_client).to receive(:list_issues).with(opts)
+ .and_raise(Sentry::Client::Error, 'error message')
+ end
+
+ it 'returns error' do
+ expect(result).to eq(error: 'error message')
+ expect(subject).to have_received(:sentry_client)
+ expect(sentry_client).to have_received(:list_issues)
+ end
+ end
end
describe '#list_sentry_projects' do
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index afa87b8a62d..82a853a23b9 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -435,6 +435,7 @@ describe MergeRequest do
it 'does not cache issues from external trackers' do
issue = ExternalIssue.new('JIRA-123', subject.project)
commit = double('commit1', safe_message: "Fixes #{issue.to_reference}")
+
allow(subject).to receive(:commits).and_return([commit])
expect { subject.cache_merge_request_closes_issues!(subject.author) }.not_to raise_error
@@ -1023,23 +1024,23 @@ describe MergeRequest do
end
end
- describe '#committers' do
- it 'returns all the committers of every commit in the merge request' do
- users = subject.commits.map(&:committer_email).uniq.map do |email|
+ describe '#commit_authors' do
+ it 'returns all the authors of every commit in the merge request' do
+ users = subject.commits.map(&:author_email).uniq.map do |email|
create(:user, email: email)
end
- expect(subject.committers).to match_array(users)
+ expect(subject.commit_authors).to match_array(users)
end
- it 'returns an empty array if no committer is associated with a user' do
- expect(subject.committers).to be_empty
+ it 'returns an empty array if no author is associated with a user' do
+ expect(subject.commit_authors).to be_empty
end
end
describe '#authors' do
- it 'returns a list with all the committers in the merge request and author' do
- users = subject.commits.map(&:committer_email).uniq.map do |email|
+ it 'returns a list with all the commit authors in the merge request and author' do
+ users = subject.commits.map(&:author_email).uniq.map do |email|
create(:user, email: email)
end
@@ -2604,8 +2605,9 @@ describe MergeRequest do
let!(:first_pipeline) { create(:ci_pipeline_without_jobs, pipeline_arguments) }
let!(:last_pipeline) { create(:ci_pipeline_without_jobs, pipeline_arguments) }
+ let!(:last_pipeline_with_other_ref) { create(:ci_pipeline_without_jobs, pipeline_arguments.merge(ref: 'other')) }
- it 'returns latest pipeline' do
+ it 'returns latest pipeline for the target branch' do
expect(merge_request.base_pipeline).to eq(last_pipeline)
end
end
diff --git a/spec/models/project_services/slack_slash_commands_service_spec.rb b/spec/models/project_services/slack_slash_commands_service_spec.rb
index 0d95f454819..5c4bce90ace 100644
--- a/spec/models/project_services/slack_slash_commands_service_spec.rb
+++ b/spec/models/project_services/slack_slash_commands_service_spec.rb
@@ -38,4 +38,11 @@ describe SlackSlashCommandsService do
end
end
end
+
+ describe '#chat_responder' do
+ it 'returns the responder to use for Slack' do
+ expect(described_class.new.chat_responder)
+ .to eq(Gitlab::Chat::Responder::Slack)
+ end
+ end
end
diff --git a/spec/models/prometheus_metric_spec.rb b/spec/models/prometheus_metric_spec.rb
index 2b978c1c8ff..3610408c138 100644
--- a/spec/models/prometheus_metric_spec.rb
+++ b/spec/models/prometheus_metric_spec.rb
@@ -4,7 +4,6 @@ require 'spec_helper'
describe PrometheusMetric do
subject { build(:prometheus_metric) }
- let(:other_project) { build(:project) }
it_behaves_like 'having unique enum values'
@@ -16,17 +15,17 @@ describe PrometheusMetric do
describe 'common metrics' do
using RSpec::Parameterized::TableSyntax
- where(:common, :project, :result) do
- false | other_project | true
- false | nil | false
- true | other_project | false
- true | nil | true
+ where(:common, :with_project, :result) do
+ false | true | true
+ false | false | false
+ true | true | false
+ true | false | true
end
with_them do
before do
subject.common = common
- subject.project = project
+ subject.project = with_project ? build(:project) : nil
end
it { expect(subject.valid?).to eq(result) }
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 78477ab0a5a..1edd8e69b8f 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -925,6 +925,21 @@ describe User do
expect(user.manageable_groups).to contain_exactly(group, subgroup)
end
end
+
+ describe '#manageable_groups_with_routes' do
+ it 'eager loads routes from manageable groups' do
+ control_count =
+ ActiveRecord::QueryRecorder.new(skip_cached: false) do
+ user.manageable_groups_with_routes.map(&:route)
+ end.count
+
+ create(:group, parent: subgroup)
+
+ expect do
+ user.manageable_groups_with_routes.map(&:route)
+ end.not_to exceed_all_query_limit(control_count)
+ end
+ end
end
end
diff --git a/spec/requests/api/features_spec.rb b/spec/requests/api/features_spec.rb
index 22a9e36ca31..57a57e69a00 100644
--- a/spec/requests/api/features_spec.rb
+++ b/spec/requests/api/features_spec.rb
@@ -163,6 +163,40 @@ describe API::Features do
end
end
+ context 'when enabling for a group by path' do
+ context 'when the group exists' do
+ it 'sets the feature gate' do
+ group = create(:group)
+
+ post api("/features/#{feature_name}", admin), params: { value: 'true', group: group.full_path }
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response).to eq(
+ 'name' => 'my_feature',
+ 'state' => 'conditional',
+ 'gates' => [
+ { 'key' => 'boolean', 'value' => false },
+ { 'key' => 'actors', 'value' => ["Group:#{group.id}"] }
+ ])
+ end
+ end
+
+ context 'when the group does not exist' do
+ it 'sets no new values and keeps the feature disabled' do
+ post api("/features/#{feature_name}", admin), params: { value: 'true', group: 'not/a/group' }
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response).to eq(
+ "name" => "my_feature",
+ "state" => "off",
+ "gates" => [
+ { "key" => "boolean", "value" => false }
+ ]
+ )
+ end
+ end
+ end
+
it 'creates a feature with the given percentage if passed an integer' do
post api("/features/#{feature_name}", admin), params: { value: '50' }
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index 6a943b5237a..cd85151ec1b 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -167,6 +167,7 @@ describe API::Internal do
expect(response).to have_gitlab_http_status(200)
expect(json_response['username']).to eq(user.username)
expect(json_response['repository_http_path']).to eq(project.http_url_to_repo)
+ expect(json_response['expires_in']).to eq(Gitlab::LfsToken::DEFAULT_EXPIRE_TIME)
expect(Gitlab::LfsToken.new(key).token_valid?(json_response['lfs_token'])).to be_truthy
end
@@ -324,6 +325,7 @@ describe API::Internal do
expect(response).to have_gitlab_http_status(200)
expect(json_response["status"]).to be_truthy
expect(json_response["repository_path"]).to eq('/')
+ expect(json_response["gl_project_path"]).to eq(project.wiki.full_path)
expect(json_response["gl_repository"]).to eq("wiki-#{project.id}")
expect(user.reload.last_activity_on).to be_nil
end
@@ -336,6 +338,7 @@ describe API::Internal do
expect(response).to have_gitlab_http_status(200)
expect(json_response["status"]).to be_truthy
expect(json_response["repository_path"]).to eq('/')
+ expect(json_response["gl_project_path"]).to eq(project.wiki.full_path)
expect(json_response["gl_repository"]).to eq("wiki-#{project.id}")
expect(user.reload.last_activity_on).to eql(Date.today)
end
@@ -349,6 +352,7 @@ describe API::Internal do
expect(json_response["status"]).to be_truthy
expect(json_response["repository_path"]).to eq('/')
expect(json_response["gl_repository"]).to eq("project-#{project.id}")
+ expect(json_response["gl_project_path"]).to eq(project.full_path)
expect(json_response["gitaly"]).not_to be_nil
expect(json_response["gitaly"]["repository"]).not_to be_nil
expect(json_response["gitaly"]["repository"]["storage_name"]).to eq(project.repository.gitaly_repository.storage_name)
@@ -368,6 +372,7 @@ describe API::Internal do
expect(json_response["status"]).to be_truthy
expect(json_response["repository_path"]).to eq('/')
expect(json_response["gl_repository"]).to eq("project-#{project.id}")
+ expect(json_response["gl_project_path"]).to eq(project.full_path)
expect(json_response["gitaly"]).not_to be_nil
expect(json_response["gitaly"]["repository"]).not_to be_nil
expect(json_response["gitaly"]["repository"]["storage_name"]).to eq(project.repository.gitaly_repository.storage_name)
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 04908378a24..d10ee6cc320 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -359,10 +359,38 @@ describe API::Issues do
expect_paginated_array_response([])
end
- it 'sorts by created_at descending by default' do
- get api('/issues', user)
+ context 'without sort params' do
+ it 'sorts by created_at descending by default' do
+ get api('/issues', user)
- expect_paginated_array_response([issue.id, closed_issue.id])
+ expect_paginated_array_response([issue.id, closed_issue.id])
+ end
+
+ context 'with 2 issues with same created_at' do
+ let!(:closed_issue2) do
+ create :closed_issue,
+ author: user,
+ assignees: [user],
+ project: project,
+ milestone: milestone,
+ created_at: closed_issue.created_at,
+ updated_at: 1.hour.ago,
+ title: issue_title,
+ description: issue_description
+ end
+
+ it 'page breaks first page correctly' do
+ get api('/issues?per_page=2', user)
+
+ expect_paginated_array_response([issue.id, closed_issue2.id])
+ end
+
+ it 'page breaks second page correctly' do
+ get api('/issues?per_page=2&page=2', user)
+
+ expect_paginated_array_response([closed_issue.id])
+ end
+ end
end
it 'sorts ascending when requested' do
@@ -393,6 +421,24 @@ describe API::Issues do
expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('public_api/v4/issues')
end
+
+ it 'returns a related merge request count of 0 if there are no related merge requests' do
+ get api('/issues', user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/issues')
+ expect(json_response.first).to include('merge_requests_count' => 0)
+ end
+
+ it 'returns a related merge request count > 0 if there are related merge requests' do
+ create(:merge_requests_closing_issues, issue: issue)
+
+ get api('/issues', user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/issues')
+ expect(json_response.first).to include('merge_requests_count' => 1)
+ end
end
end
@@ -613,10 +659,38 @@ describe API::Issues do
expect_paginated_array_response(group_confidential_issue.id)
end
- it 'sorts by created_at descending by default' do
- get api(base_url, user)
+ context 'without sort params' do
+ it 'sorts by created_at descending by default' do
+ get api(base_url, user)
- expect_paginated_array_response([group_closed_issue.id, group_confidential_issue.id, group_issue.id])
+ expect_paginated_array_response([group_closed_issue.id, group_confidential_issue.id, group_issue.id])
+ end
+
+ context 'with 2 issues with same created_at' do
+ let!(:group_issue2) do
+ create :issue,
+ author: user,
+ assignees: [user],
+ project: group_project,
+ milestone: group_milestone,
+ updated_at: 1.hour.ago,
+ title: issue_title,
+ description: issue_description,
+ created_at: group_issue.created_at
+ end
+
+ it 'page breaks first page correctly' do
+ get api("#{base_url}?per_page=3", user)
+
+ expect_paginated_array_response([group_closed_issue.id, group_confidential_issue.id, group_issue2.id])
+ end
+
+ it 'page breaks second page correctly' do
+ get api("#{base_url}?per_page=3&page=2", user)
+
+ expect_paginated_array_response([group_issue.id])
+ end
+ end
end
it 'sorts ascending when requested' do
@@ -828,10 +902,38 @@ describe API::Issues do
expect_paginated_array_response([issue.id, closed_issue.id])
end
- it 'sorts by created_at descending by default' do
- get api("#{base_url}/issues", user)
+ context 'without sort params' do
+ it 'sorts by created_at descending by default' do
+ get api("#{base_url}/issues", user)
- expect_paginated_array_response([issue.id, confidential_issue.id, closed_issue.id])
+ expect_paginated_array_response([issue.id, confidential_issue.id, closed_issue.id])
+ end
+
+ context 'with 2 issues with same created_at' do
+ let!(:closed_issue2) do
+ create :closed_issue,
+ author: user,
+ assignees: [user],
+ project: project,
+ milestone: milestone,
+ created_at: closed_issue.created_at,
+ updated_at: 1.hour.ago,
+ title: issue_title,
+ description: issue_description
+ end
+
+ it 'page breaks first page correctly' do
+ get api("#{base_url}/issues?per_page=3", user)
+
+ expect_paginated_array_response([issue.id, confidential_issue.id, closed_issue2.id])
+ end
+
+ it 'page breaks second page correctly' do
+ get api("#{base_url}/issues?per_page=3&page=2", user)
+
+ expect_paginated_array_response([closed_issue.id])
+ end
+ end
end
it 'sorts ascending when requested' do
@@ -1792,7 +1894,7 @@ describe API::Issues do
description: "See #{issue.to_reference}"
}
create(:merge_request, attributes).tap do |merge_request|
- create(:note, :system, project: project, noteable: issue, author: user, note: merge_request.to_reference(full: true))
+ create(:note, :system, project: issue.project, noteable: issue, author: user, note: merge_request.to_reference(full: true))
end
end
@@ -1829,6 +1931,24 @@ describe API::Issues do
expect_paginated_array_response(related_mr.id)
end
+ it 'returns merge requests cross-project wide' do
+ project2 = create(:project, :public, creator_id: user.id, namespace: user.namespace)
+ merge_request = create_referencing_mr(user, project2, issue)
+
+ get_related_merge_requests(project.id, issue.iid, user)
+
+ expect_paginated_array_response([related_mr.id, merge_request.id])
+ end
+
+ it 'does not generate references to projects with no access' do
+ private_project = create(:project, :private)
+ create_referencing_mr(private_project.creator, private_project, issue)
+
+ get_related_merge_requests(project.id, issue.iid, user)
+
+ expect_paginated_array_response(related_mr.id)
+ end
+
context 'no merge request mentioned a issue' do
it 'returns empty array' do
get_related_merge_requests(project.id, closed_issue.iid, user)
diff --git a/spec/routing/import_routing_spec.rb b/spec/routing/import_routing_spec.rb
index 78ff9c6e6fd..106f92082e4 100644
--- a/spec/routing/import_routing_spec.rb
+++ b/spec/routing/import_routing_spec.rb
@@ -23,6 +23,11 @@ require 'spec_helper'
# end
shared_examples 'importer routing' do
let(:except_actions) { [] }
+ let(:is_realtime) { false }
+
+ before do
+ except_actions.push(is_realtime ? :jobs : :realtime_changes)
+ end
it 'to #create' do
expect(post("/import/#{provider}")).to route_to("import/#{provider}#create") unless except_actions.include?(:create)
@@ -43,17 +48,22 @@ shared_examples 'importer routing' do
it 'to #jobs' do
expect(get("/import/#{provider}/jobs")).to route_to("import/#{provider}#jobs") unless except_actions.include?(:jobs)
end
+
+ it 'to #realtime_changes' do
+ expect(get("/import/#{provider}/realtime_changes")).to route_to("import/#{provider}#realtime_changes") unless except_actions.include?(:realtime_changes)
+ end
end
# personal_access_token_import_github POST /import/github/personal_access_token(.:format) import/github#personal_access_token
# status_import_github GET /import/github/status(.:format) import/github#status
# callback_import_github GET /import/github/callback(.:format) import/github#callback
-# jobs_import_github GET /import/github/jobs(.:format) import/github#jobs
+# realtime_changes_import_github GET /import/github/realtime_changes(.:format) import/github#jobs
# import_github POST /import/github(.:format) import/github#create
# new_import_github GET /import/github/new(.:format) import/github#new
describe Import::GithubController, 'routing' do
it_behaves_like 'importer routing' do
let(:provider) { 'github' }
+ let(:is_realtime) { true }
end
it 'to #personal_access_token' do
@@ -63,13 +73,14 @@ end
# personal_access_token_import_gitea POST /import/gitea/personal_access_token(.:format) import/gitea#personal_access_token
# status_import_gitea GET /import/gitea/status(.:format) import/gitea#status
-# jobs_import_gitea GET /import/gitea/jobs(.:format) import/gitea#jobs
+# realtime_changes_import_gitea GET /import/gitea/realtime_changes(.:format) import/gitea#jobs
# import_gitea POST /import/gitea(.:format) import/gitea#create
# new_import_gitea GET /import/gitea/new(.:format) import/gitea#new
describe Import::GiteaController, 'routing' do
it_behaves_like 'importer routing' do
let(:except_actions) { [:callback] }
let(:provider) { 'gitea' }
+ let(:is_realtime) { true }
end
it 'to #personal_access_token' do
diff --git a/spec/serializers/diff_file_entity_spec.rb b/spec/serializers/diff_file_entity_spec.rb
index 073c13c2cbb..92b649f5b6c 100644
--- a/spec/serializers/diff_file_entity_spec.rb
+++ b/spec/serializers/diff_file_entity_spec.rb
@@ -22,7 +22,7 @@ describe DiffFileEntity do
let(:request) { EntityRequest.new(project: project, current_user: user) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:entity) { described_class.new(diff_file, request: request, merge_request: merge_request) }
- let(:exposed_urls) { %i(load_collapsed_diff_url edit_path view_path context_lines_path) }
+ let(:exposed_urls) { %i(edit_path view_path context_lines_path) }
it_behaves_like 'diff file entity'
@@ -38,6 +38,12 @@ describe DiffFileEntity do
expect(response[attribute]).to include(merge_request.target_project.to_param)
end
end
+
+ it 'exposes load_collapsed_diff_url if the file viewer is collapsed' do
+ allow(diff_file.viewer).to receive(:collapsed?) { true }
+
+ expect(subject).to include(:load_collapsed_diff_url)
+ end
end
context '#parallel_diff_lines' do
diff --git a/spec/serializers/namespace_basic_entity_spec.rb b/spec/serializers/namespace_basic_entity_spec.rb
new file mode 100644
index 00000000000..f8b71ceb9f3
--- /dev/null
+++ b/spec/serializers/namespace_basic_entity_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe NamespaceBasicEntity do
+ set(:group) { create(:group) }
+ let(:entity) do
+ described_class.represent(group)
+ end
+
+ describe '#as_json' do
+ subject { entity.as_json }
+
+ it 'includes required fields' do
+ expect(subject).to include :id, :full_path
+ end
+ end
+end
diff --git a/spec/serializers/namespace_serializer_spec.rb b/spec/serializers/namespace_serializer_spec.rb
new file mode 100644
index 00000000000..6e5bdd8c52d
--- /dev/null
+++ b/spec/serializers/namespace_serializer_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe NamespaceSerializer do
+ it 'represents NamespaceBasicEntity entities' do
+ expect(described_class.entity_class).to eq(NamespaceBasicEntity)
+ end
+end
diff --git a/spec/serializers/project_import_entity_spec.rb b/spec/serializers/project_import_entity_spec.rb
new file mode 100644
index 00000000000..e476da82729
--- /dev/null
+++ b/spec/serializers/project_import_entity_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ProjectImportEntity do
+ include ImportHelper
+
+ set(:project) { create(:project, import_status: :started, import_source: 'namespace/project') }
+ let(:provider_url) { 'https://provider.com' }
+ let(:entity) { described_class.represent(project, provider_url: provider_url) }
+
+ describe '#as_json' do
+ subject { entity.as_json }
+
+ it 'includes required fields' do
+ expect(subject[:import_source]).to eq(project.import_source)
+ expect(subject[:import_status]).to eq(project.import_status)
+ expect(subject[:human_import_status_name]).to eq(project.human_import_status_name)
+ expect(subject[:provider_link]).to eq(provider_project_link_url(provider_url, project[:import_source]))
+ end
+ end
+end
diff --git a/spec/serializers/project_serializer_spec.rb b/spec/serializers/project_serializer_spec.rb
new file mode 100644
index 00000000000..22f958fc17f
--- /dev/null
+++ b/spec/serializers/project_serializer_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ProjectSerializer do
+ set(:project) { create(:project) }
+ let(:provider_url) { 'http://provider.com' }
+
+ context 'when serializer option is :import' do
+ subject do
+ described_class.new.represent(project, serializer: :import, provider_url: provider_url)
+ end
+
+ before do
+ allow(ProjectImportEntity).to receive(:represent)
+ end
+
+ it 'represents with ProjectImportEntity' do
+ subject
+
+ expect(ProjectImportEntity)
+ .to have_received(:represent)
+ .with(project, serializer: :import, provider_url: provider_url, request: an_instance_of(EntityRequest))
+ end
+ end
+
+ context 'when serializer option is omitted' do
+ subject do
+ described_class.new.represent(project)
+ end
+
+ before do
+ allow(ProjectEntity).to receive(:represent)
+ end
+
+ it 'represents with ProjectEntity' do
+ subject
+
+ expect(ProjectEntity)
+ .to have_received(:represent)
+ .with(project, request: an_instance_of(EntityRequest))
+ end
+ end
+end
diff --git a/spec/serializers/provider_repo_entity_spec.rb b/spec/serializers/provider_repo_entity_spec.rb
new file mode 100644
index 00000000000..b67115bab10
--- /dev/null
+++ b/spec/serializers/provider_repo_entity_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ProviderRepoEntity do
+ include ImportHelper
+
+ let(:provider_repo) { { id: 1, full_name: 'full/name', name: 'name', owner: { login: 'owner' } } }
+ let(:provider) { :github }
+ let(:provider_url) { 'https://github.com' }
+ let(:entity) { described_class.represent(provider_repo, provider: provider, provider_url: provider_url) }
+
+ describe '#as_json' do
+ subject { entity.as_json }
+
+ it 'includes requried fields' do
+ expect(subject[:id]).to eq(provider_repo[:id])
+ expect(subject[:full_name]).to eq(provider_repo[:full_name])
+ expect(subject[:owner_name]).to eq(provider_repo[:owner][:login])
+ expect(subject[:sanitized_name]).to eq(sanitize_project_name(provider_repo[:name]))
+ expect(subject[:provider_link]).to eq(provider_project_link_url(provider_url, provider_repo[:full_name]))
+ end
+ end
+end
diff --git a/spec/serializers/provider_repo_serializer_spec.rb b/spec/serializers/provider_repo_serializer_spec.rb
new file mode 100644
index 00000000000..f2be30c36d9
--- /dev/null
+++ b/spec/serializers/provider_repo_serializer_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ProviderRepoSerializer do
+ it 'represents ProviderRepoEntity entities' do
+ expect(described_class.entity_class).to eq(ProviderRepoEntity)
+ end
+end
diff --git a/spec/services/error_tracking/list_issues_service_spec.rb b/spec/services/error_tracking/list_issues_service_spec.rb
index d9dab1d705c..9d4fc62f923 100644
--- a/spec/services/error_tracking/list_issues_service_spec.rb
+++ b/spec/services/error_tracking/list_issues_service_spec.rb
@@ -45,7 +45,23 @@ describe ErrorTracking::ListIssuesService do
it 'result is not ready' do
expect(result).to eq(
- status: :error, http_status: :no_content, message: 'not ready')
+ status: :error, http_status: :no_content, message: 'Not ready. Try again later')
+ end
+ end
+
+ context 'when list_sentry_issues returns error' do
+ before do
+ allow(error_tracking_setting)
+ .to receive(:list_sentry_issues)
+ .and_return(error: 'Sentry response status code: 401')
+ end
+
+ it 'returns the error' do
+ expect(result).to eq(
+ status: :error,
+ http_status: :bad_request,
+ message: 'Sentry response status code: 401'
+ )
end
end
end
@@ -58,7 +74,11 @@ describe ErrorTracking::ListIssuesService do
it 'returns error' do
result = subject.execute
- expect(result).to include(status: :error, message: 'access denied')
+ expect(result).to include(
+ status: :error,
+ message: 'Access denied',
+ http_status: :unauthorized
+ )
end
end
@@ -70,7 +90,7 @@ describe ErrorTracking::ListIssuesService do
it 'raises error' do
result = subject.execute
- expect(result).to include(status: :error, message: 'not enabled')
+ expect(result).to include(status: :error, message: 'Error Tracking is not enabled')
end
end
end
diff --git a/spec/services/error_tracking/list_projects_service_spec.rb b/spec/services/error_tracking/list_projects_service_spec.rb
index ee9c59e3f65..9f25a633deb 100644
--- a/spec/services/error_tracking/list_projects_service_spec.rb
+++ b/spec/services/error_tracking/list_projects_service_spec.rb
@@ -53,11 +53,11 @@ describe ErrorTracking::ListProjectsService do
context 'sentry client raises exception' do
before do
expect(error_tracking_setting).to receive(:list_sentry_projects)
- .and_raise(Sentry::Client::Error, 'Sentry response error: 500')
+ .and_raise(Sentry::Client::Error, 'Sentry response status code: 500')
end
it 'returns error response' do
- expect(result[:message]).to eq('Sentry response error: 500')
+ expect(result[:message]).to eq('Sentry response status code: 500')
expect(result[:http_status]).to eq(:bad_request)
end
end
diff --git a/spec/services/prometheus/adapter_service_spec.rb b/spec/services/prometheus/adapter_service_spec.rb
index 335fc5844aa..505e2935e93 100644
--- a/spec/services/prometheus/adapter_service_spec.rb
+++ b/spec/services/prometheus/adapter_service_spec.rb
@@ -22,7 +22,15 @@ describe Prometheus::AdapterService do
context "prometheus service can't execute queries" do
let(:prometheus_service) { double(:prometheus_service, can_query?: false) }
- context 'with cluster with prometheus installed' do
+ context 'with cluster with prometheus not available' do
+ let!(:prometheus) { create(:clusters_applications_prometheus, :installable, cluster: cluster) }
+
+ it 'returns nil' do
+ expect(subject.prometheus_adapter).to be_nil
+ end
+ end
+
+ context 'with cluster with prometheus available' do
let!(:prometheus) { create(:clusters_applications_prometheus, :installed, cluster: cluster) }
it 'returns application handling all environments' do
diff --git a/spec/services/web_hook_service_spec.rb b/spec/services/web_hook_service_spec.rb
index 5945a7dc0ad..747e04fb18c 100644
--- a/spec/services/web_hook_service_spec.rb
+++ b/spec/services/web_hook_service_spec.rb
@@ -102,7 +102,7 @@ describe WebHookService do
exception = exception_class.new('Exception message')
WebMock.stub_request(:post, project_hook.url).to_raise(exception)
- expect(service_instance.execute).to eq({ status: :error, message: exception.message })
+ expect(service_instance.execute).to eq({ status: :error, message: exception.to_s })
expect { service_instance.execute }.not_to raise_error
end
end
diff --git a/spec/support/controllers/githubish_import_controller_shared_examples.rb b/spec/support/controllers/githubish_import_controller_shared_examples.rb
index 697f999e4c4..5bb1269a19d 100644
--- a/spec/support/controllers/githubish_import_controller_shared_examples.rb
+++ b/spec/support/controllers/githubish_import_controller_shared_examples.rb
@@ -58,36 +58,54 @@ end
shared_examples 'a GitHub-ish import controller: GET status' do
let(:new_import_url) { public_send("new_import_#{provider}_url") }
let(:user) { create(:user) }
- let(:repo) { OpenStruct.new(login: 'vim', full_name: 'asd/vim') }
+ let(:repo) { OpenStruct.new(login: 'vim', full_name: 'asd/vim', name: 'vim', owner: { login: 'owner' }) }
let(:org) { OpenStruct.new(login: 'company') }
- let(:org_repo) { OpenStruct.new(login: 'company', full_name: 'company/repo') }
- let(:extra_assign_expectations) { {} }
+ let(:org_repo) { OpenStruct.new(login: 'company', full_name: 'company/repo', name: 'repo', owner: { login: 'owner' }) }
before do
assign_session_token(provider)
end
- it "assigns variables" do
- project = create(:project, import_type: provider, namespace: user.namespace)
+ it "returns variables for json request" do
+ project = create(:project, import_type: provider, namespace: user.namespace, import_status: :finished, import_source: 'example/repo')
+ group = create(:group)
+ group.add_owner(user)
stub_client(repos: [repo, org_repo], orgs: [org], org_repos: [org_repo])
- get :status
+ get :status, format: :json
- expect(assigns(:already_added_projects)).to eq([project])
- expect(assigns(:repos)).to eq([repo, org_repo])
- extra_assign_expectations.each do |key, value|
- expect(assigns(key)).to eq(value)
- end
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response.dig("imported_projects", 0, "id")).to eq(project.id)
+ expect(json_response.dig("provider_repos", 0, "id")).to eq(repo.id)
+ expect(json_response.dig("provider_repos", 1, "id")).to eq(org_repo.id)
+ expect(json_response.dig("namespaces", 0, "id")).to eq(group.id)
end
it "does not show already added project" do
- project = create(:project, import_type: provider, namespace: user.namespace, import_source: 'asd/vim')
+ project = create(:project, import_type: provider, namespace: user.namespace, import_status: :finished, import_source: 'asd/vim')
stub_client(repos: [repo], orgs: [])
+ get :status, format: :json
+
+ expect(json_response.dig("imported_projects", 0, "id")).to eq(project.id)
+ expect(json_response.dig("provider_repos")).to eq([])
+ end
+
+ it "touches the etag cache store" do
+ expect(stub_client(repos: [], orgs: [])).to receive(:repos)
+ expect_next_instance_of(Gitlab::EtagCaching::Store) do |store|
+ expect(store).to receive(:touch) { "realtime_changes_import_#{provider}_path" }
+ end
+
+ get :status, format: :json
+ end
+
+ it "requests provider repos list" do
+ expect(stub_client(repos: [], orgs: [])).to receive(:repos)
+
get :status
- expect(assigns(:already_added_projects)).to eq([project])
- expect(assigns(:repos)).to eq([])
+ expect(response).to have_gitlab_http_status(200)
end
it "handles an invalid access token" do
@@ -100,13 +118,32 @@ shared_examples 'a GitHub-ish import controller: GET status' do
expect(controller).to redirect_to(new_import_url)
expect(flash[:alert]).to eq("Access denied to your #{Gitlab::ImportSources.title(provider.to_s)} account.")
end
+
+ it "does not produce N+1 database queries" do
+ stub_client(repos: [repo], orgs: [])
+ group_a = create(:group)
+ group_a.add_owner(user)
+ create(:project, :import_started, import_type: provider, namespace: user.namespace)
+
+ control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do
+ get :status, format: :json
+ end.count
+
+ stub_client(repos: [repo, org_repo], orgs: [])
+ group_b = create(:group)
+ group_b.add_owner(user)
+ create(:project, :import_started, import_type: provider, namespace: user.namespace)
+
+ expect { get :status, format: :json }
+ .not_to exceed_all_query_limit(control_count)
+ end
end
shared_examples 'a GitHub-ish import controller: POST create' do
let(:user) { create(:user) }
- let(:project) { create(:project) }
let(:provider_username) { user.username }
let(:provider_user) { OpenStruct.new(login: provider_username) }
+ let(:project) { create(:project, import_type: provider, import_status: :finished, import_source: "#{provider_username}/vim") }
let(:provider_repo) do
OpenStruct.new(
name: 'vim',
@@ -145,6 +182,17 @@ shared_examples 'a GitHub-ish import controller: POST create' do
expect(json_response['errors']).to eq('Name is invalid, Path is old')
end
+ it "touches the etag cache store" do
+ allow(Gitlab::LegacyGithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider)
+ .and_return(double(execute: project))
+ expect_next_instance_of(Gitlab::EtagCaching::Store) do |store|
+ expect(store).to receive(:touch) { "realtime_changes_import_#{provider}_path" }
+ end
+
+ post :create, format: :json
+ end
+
context "when the repository owner is the provider user" do
context "when the provider user and GitLab user's usernames match" do
it "takes the current user's namespace" do
@@ -351,7 +399,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do
it 'does not create a new namespace under the user namespace' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, test_name, user.namespace, user, access_params, type: provider)
- .and_return(double(execute: build_stubbed(:project)))
+ .and_return(double(execute: project))
expect { post :create, params: { target_namespace: "#{user.namespace_path}/test_group", new_name: test_name }, format: :js }
.not_to change { Namespace.count }
@@ -365,7 +413,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do
it 'does not take the selected namespace and name' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, test_name, user.namespace, user, access_params, type: provider)
- .and_return(double(execute: build_stubbed(:project)))
+ .and_return(double(execute: project))
post :create, params: { target_namespace: 'foo/foobar/bar', new_name: test_name }, format: :js
end
@@ -373,7 +421,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do
it 'does not create the namespaces' do
allow(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
- .and_return(double(execute: build_stubbed(:project)))
+ .and_return(double(execute: project))
expect { post :create, params: { target_namespace: 'foo/foobar/bar', new_name: test_name }, format: :js }
.not_to change { Namespace.count }
@@ -390,7 +438,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, test_name, group, user, access_params, type: provider)
- .and_return(double(execute: build_stubbed(:project)))
+ .and_return(double(execute: project))
post :create, params: { target_namespace: 'foo', new_name: test_name }, format: :js
end
@@ -407,3 +455,20 @@ shared_examples 'a GitHub-ish import controller: POST create' do
end
end
end
+
+shared_examples 'a GitHub-ish import controller: GET realtime_changes' do
+ let(:user) { create(:user) }
+
+ before do
+ assign_session_token(provider)
+ end
+
+ it 'sets a Poll-Interval header' do
+ project = create(:project, import_type: provider, namespace: user.namespace, import_status: :finished, import_source: 'example/repo')
+
+ get :realtime_changes
+
+ expect(json_response).to eq([{ "id" => project.id, "import_status" => project.import_status }])
+ expect(Integer(response.headers['Poll-Interval'])).to be > -1
+ end
+end
diff --git a/spec/support/helpers/reactive_caching_helpers.rb b/spec/support/helpers/reactive_caching_helpers.rb
index a575aa99b79..b76b53db0b9 100644
--- a/spec/support/helpers/reactive_caching_helpers.rb
+++ b/spec/support/helpers/reactive_caching_helpers.rb
@@ -10,7 +10,7 @@ module ReactiveCachingHelpers
def stub_reactive_cache(subject = nil, data = nil, *qualifiers)
allow(ReactiveCachingWorker).to receive(:perform_async)
allow(ReactiveCachingWorker).to receive(:perform_in)
- write_reactive_cache(subject, data, *qualifiers) if data
+ write_reactive_cache(subject, data, *qualifiers) unless data.nil?
end
def synchronous_reactive_cache(subject)
diff --git a/spec/support/shared_contexts/services_shared_context.rb b/spec/support/shared_contexts/services_shared_context.rb
index 23f9b46ae0c..d92e8318fa0 100644
--- a/spec/support/shared_contexts/services_shared_context.rb
+++ b/spec/support/shared_contexts/services_shared_context.rb
@@ -19,7 +19,7 @@ Service.available_services_names.each do |service|
elsif service == 'irker' && k == :server_port
hash.merge!(k => 1234)
elsif service == 'jira' && k == :jira_issue_transition_id
- hash.merge!(k => 1234)
+ hash.merge!(k => '1,2,3')
else
hash.merge!(k => "someword")
end
diff --git a/spec/support/shared_examples/graphql/issuable_state_shared_examples.rb b/spec/support/shared_examples/graphql/issuable_state_shared_examples.rb
new file mode 100644
index 00000000000..713f0a879c1
--- /dev/null
+++ b/spec/support/shared_examples/graphql/issuable_state_shared_examples.rb
@@ -0,0 +1,5 @@
+RSpec.shared_examples 'issuable state' do
+ it 'exposes all the existing issuable states' do
+ expect(described_class.values.keys).to include(*%w[opened closed locked])
+ end
+end
diff --git a/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb b/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb
index c96a65cb56a..b8c19cab0c4 100644
--- a/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb
+++ b/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb
@@ -9,6 +9,19 @@ shared_examples 'cluster application status specs' do |application_name|
end
end
+ describe '.available' do
+ subject { described_class.available }
+
+ let!(:installed_cluster) { create(application_name, :installed) }
+ let!(:updated_cluster) { create(application_name, :updated) }
+
+ before do
+ create(application_name, :errored)
+ end
+
+ it { is_expected.to contain_exactly(installed_cluster, updated_cluster) }
+ end
+
describe 'status state machine' do
describe '#make_installing' do
subject { create(application_name, :scheduled) }
diff --git a/spec/support/shared_examples/requests/api/merge_requests_list.rb b/spec/support/shared_examples/requests/api/merge_requests_list.rb
index 453f42251c8..6713ec47ace 100644
--- a/spec/support/shared_examples/requests/api/merge_requests_list.rb
+++ b/spec/support/shared_examples/requests/api/merge_requests_list.rb
@@ -257,6 +257,38 @@ shared_examples 'merge requests list' do
expect(response_dates).to eq(response_dates.sort.reverse)
end
+ context '2 merge requests with equal created_at' do
+ let!(:closed_mr2) do
+ create :merge_request,
+ state: 'closed',
+ milestone: milestone1,
+ author: user,
+ assignee: user,
+ source_project: project,
+ target_project: project,
+ title: "Test",
+ created_at: @mr_earlier.created_at
+ end
+
+ it 'page breaks first page correctly' do
+ get api("#{endpoint_path}?sort=desc&per_page=4", user)
+
+ response_ids = json_response.map { |merge_request| merge_request['id'] }
+
+ expect(response_ids).to include(closed_mr2.id)
+ expect(response_ids).not_to include(@mr_earlier.id)
+ end
+
+ it 'page breaks second page correctly' do
+ get api("#{endpoint_path}?sort=desc&per_page=4&page=2", user)
+
+ response_ids = json_response.map { |merge_request| merge_request['id'] }
+
+ expect(response_ids).not_to include(closed_mr2.id)
+ expect(response_ids).to include(@mr_earlier.id)
+ end
+ end
+
it 'returns an array of merge_requests ordered by updated_at' do
path = endpoint_path + '?order_by=updated_at'
diff --git a/spec/support/shared_examples/requests/api/notes.rb b/spec/support/shared_examples/requests/api/notes.rb
index 71499e85654..57eefd5ef01 100644
--- a/spec/support/shared_examples/requests/api/notes.rb
+++ b/spec/support/shared_examples/requests/api/notes.rb
@@ -8,13 +8,45 @@ shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
create_list(:note, 3, params)
end
- it 'sorts by created_at in descending order by default' do
- get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user)
+ context 'without sort params' do
+ it 'sorts by created_at in descending order by default' do
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user)
- response_dates = json_response.map { |note| note['created_at'] }
+ response_dates = json_response.map { |note| note['created_at'] }
- expect(json_response.length).to eq(4)
- expect(response_dates).to eq(response_dates.sort.reverse)
+ expect(json_response.length).to eq(4)
+ expect(response_dates).to eq(response_dates.sort.reverse)
+ end
+
+ context '2 notes with equal created_at' do
+ before do
+ @first_note = Note.first
+
+ params = { noteable: noteable, author: user }
+ params[:project] = parent if parent.is_a?(Project)
+ params[:created_at] = @first_note.created_at
+
+ @note2 = create(:note, params)
+ end
+
+ it 'page breaks first page correctly' do
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes?per_page=4", user)
+
+ response_ids = json_response.map { |note| note['id'] }
+
+ expect(response_ids).to include(@note2.id)
+ expect(response_ids).not_to include(@first_note.id)
+ end
+
+ it 'page breaks second page correctly' do
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes?per_page=4&page=2", user)
+
+ response_ids = json_response.map { |note| note['id'] }
+
+ expect(response_ids).not_to include(@note2.id)
+ expect(response_ids).to include(@first_note.id)
+ end
+ end
end
it 'sorts by ascending order when requested' do
diff --git a/spec/support/shared_examples/serializers/diff_file_entity_examples.rb b/spec/support/shared_examples/serializers/diff_file_entity_examples.rb
index 1770308f789..96cb71be737 100644
--- a/spec/support/shared_examples/serializers/diff_file_entity_examples.rb
+++ b/spec/support/shared_examples/serializers/diff_file_entity_examples.rb
@@ -6,9 +6,9 @@ shared_examples 'diff file base entity' do
:submodule_tree_url, :old_path_html,
:new_path_html, :blob, :can_modify_blob,
:file_hash, :file_path, :old_path, :new_path,
- :collapsed, :text, :diff_refs, :stored_externally,
+ :viewer, :diff_refs, :stored_externally,
:external_storage, :renamed_file, :deleted_file,
- :mode_changed, :a_mode, :b_mode, :new_file)
+ :a_mode, :b_mode, :new_file)
end
# Converted diff files from GitHub import does not contain blob file
@@ -30,9 +30,9 @@ shared_examples 'diff file entity' do
it_behaves_like 'diff file base entity'
it 'exposes correct attributes' do
- expect(subject).to include(:too_large, :added_lines, :removed_lines,
+ expect(subject).to include(:added_lines, :removed_lines,
:context_lines_path, :highlighted_diff_lines,
- :parallel_diff_lines, :empty)
+ :parallel_diff_lines)
end
it 'includes viewer' do
diff --git a/spec/views/projects/issues/show.html.haml_spec.rb b/spec/views/projects/issues/show.html.haml_spec.rb
index ff88efd0e31..1d9c6d36ad7 100644
--- a/spec/views/projects/issues/show.html.haml_spec.rb
+++ b/spec/views/projects/issues/show.html.haml_spec.rb
@@ -21,12 +21,24 @@ describe 'projects/issues/show' do
allow(issue).to receive(:closed?).and_return(true)
end
- it 'shows "Closed (moved)" if an issue has been moved' do
- allow(issue).to receive(:moved?).and_return(true)
+ context 'when the issue was moved' do
+ let(:new_issue) { create(:issue, project: project, author: user) }
- render
+ before do
+ issue.moved_to = new_issue
+ end
+
+ it 'shows "Closed (moved)" if an issue has been moved' do
+ render
+
+ expect(rendered).to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed (moved)')
+ end
+
+ it 'links "moved" to the new issue the original issue was moved to' do
+ render
- expect(rendered).to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed (moved)')
+ expect(rendered).to have_selector("a[href=\"#{issue_path(new_issue)}\"]", text: 'moved')
+ end
end
it 'shows "Closed" if an issue has not been moved' do
diff --git a/spec/workers/build_finished_worker_spec.rb b/spec/workers/build_finished_worker_spec.rb
index acd8da11d8d..ccb26849e67 100644
--- a/spec/workers/build_finished_worker_spec.rb
+++ b/spec/workers/build_finished_worker_spec.rb
@@ -26,5 +26,24 @@ describe BuildFinishedWorker do
.not_to raise_error
end
end
+
+ it 'schedules a ChatNotification job for a chat build' do
+ build = create(:ci_build, :success, pipeline: create(:ci_pipeline, source: :chat))
+
+ expect(ChatNotificationWorker)
+ .to receive(:perform_async)
+ .with(build.id)
+
+ described_class.new.perform(build.id)
+ end
+
+ it 'does not schedule a ChatNotification job for a regular build' do
+ build = create(:ci_build, :success, pipeline: create(:ci_pipeline))
+
+ expect(ChatNotificationWorker)
+ .not_to receive(:perform_async)
+
+ described_class.new.perform(build.id)
+ end
end
end
diff --git a/spec/workers/chat_notification_worker_spec.rb b/spec/workers/chat_notification_worker_spec.rb
new file mode 100644
index 00000000000..91695674f5d
--- /dev/null
+++ b/spec/workers/chat_notification_worker_spec.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ChatNotificationWorker do
+ let(:worker) { described_class.new }
+ let(:chat_build) do
+ create(:ci_build, pipeline: create(:ci_pipeline, source: :chat))
+ end
+
+ describe '#perform' do
+ it 'does nothing when the build no longer exists' do
+ expect(worker).not_to receive(:send_response)
+
+ worker.perform(-1)
+ end
+
+ it 'sends a response for an existing build' do
+ expect(worker)
+ .to receive(:send_response)
+ .with(an_instance_of(Ci::Build))
+
+ worker.perform(chat_build.id)
+ end
+
+ it 'reschedules the job if the trace sections could not be found' do
+ expect(worker)
+ .to receive(:send_response)
+ .and_raise(Gitlab::Chat::Output::MissingBuildSectionError)
+
+ expect(described_class)
+ .to receive(:perform_in)
+ .with(described_class::RESCHEDULE_INTERVAL, chat_build.id)
+
+ worker.perform(chat_build.id)
+ end
+ end
+
+ describe '#send_response' do
+ context 'when a responder could not be found' do
+ it 'does nothing' do
+ expect(Gitlab::Chat::Responder)
+ .to receive(:responder_for)
+ .with(chat_build)
+ .and_return(nil)
+
+ expect(worker.send_response(chat_build)).to be_nil
+ end
+ end
+
+ context 'when a responder could be found' do
+ let(:responder) { double(:responder) }
+
+ before do
+ allow(Gitlab::Chat::Responder)
+ .to receive(:responder_for)
+ .with(chat_build)
+ .and_return(responder)
+ end
+
+ it 'sends the response for a succeeded build' do
+ output = double(:output, to_s: 'this is the build output')
+
+ expect(chat_build)
+ .to receive(:success?)
+ .and_return(true)
+
+ expect(responder)
+ .to receive(:success)
+ .with(an_instance_of(String))
+
+ expect(Gitlab::Chat::Output)
+ .to receive(:new)
+ .with(chat_build)
+ .and_return(output)
+
+ worker.send_response(chat_build)
+ end
+
+ it 'sends the response for a failed build' do
+ expect(chat_build)
+ .to receive(:success?)
+ .and_return(false)
+
+ expect(responder).to receive(:failure)
+
+ worker.send_response(chat_build)
+ end
+ end
+ end
+end
diff --git a/yarn.lock b/yarn.lock
index 47df016b7a6..9ecb2b82f8f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -653,15 +653,15 @@
eslint-plugin-promise "^4.0.1"
eslint-plugin-vue "^5.0.0"
-"@gitlab/svgs@^1.52.0":
- version "1.52.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.52.0.tgz#870b0112a18b10d2cde5470a48b05025193cd207"
- integrity sha512-xqDYIaSY1MJuCa7lRIDTTPoXn6x57So2qchxwmELE+SAJxlYlpYgDKCNWcGawhyTZRDZuG/qFBWp0sMeTQD//A==
+"@gitlab/svgs@^1.53.0":
+ version "1.53.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.53.0.tgz#a0c463953f24ff07a5871a68d3e9ae3cb45e5267"
+ integrity sha512-u4AqIKCVcnG727BLW6wxRkXDfDexD4KlT2JxiPtKBJLmA6mwhSiCCD3ZDMd5W+bbTfl48KdcuV03MXmiqyONNw==
-"@gitlab/ui@^2.0.2":
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-2.0.2.tgz#611571c931181fb783f57f712e1c2388059b301b"
- integrity sha512-rUWVhWmM9EkwIEruYJEjizrQKe7TzNyKArwWY/nfEL4HptDtwbe+xHfR8IJHbpql3oI87cTO3BheMxYF6b2Ebg==
+"@gitlab/ui@^2.0.4":
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-2.0.4.tgz#ba86f6e5868ef7bc7f504cef9ca504c2d2f6bffd"
+ integrity sha512-dJ+KKpeqIAPYZtYZeciXhC/whNiGPVRjp5IgjQRddh3zsreqmfwQq58nSH7HepAAIepaqTe0UFuzBgrSWvVM6w==
dependencies:
babel-standalone "^6.26.0"
bootstrap-vue "^2.0.0-rc.11"