summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWinnie Hellmann <winnie@gitlab.com>2018-10-09 19:38:35 +0200
committerWinnie Hellmann <winnie@gitlab.com>2018-10-09 19:38:35 +0200
commit23e81130fb53e7c961a225e6235f5ae8a36ed595 (patch)
tree1f482f3045137be04207142e33e0f88a915553e6
parentc12a78969315a8e25128653df2a7515861010b12 (diff)
parentca440758be33c8bec937a249db9b18b3c9734df9 (diff)
downloadgitlab-ce-23e81130fb53e7c961a225e6235f5ae8a36ed595.tar.gz
Merge remote-tracking branch 'gitlab.com/master' into scheduled-manual-jobs-environment-play-buttons
-rw-r--r--.eslintrc.yml51
-rw-r--r--.gitlab-ci.yml6
-rw-r--r--.gitlab/CODEOWNERS2
-rw-r--r--.rubocop_todo.yml79
-rw-r--r--CHANGELOG.md22
-rw-r--r--CONTRIBUTING.md84
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_PAGES_VERSION2
-rw-r--r--Gemfile1
-rw-r--r--Gemfile.lock5
-rw-r--r--Gemfile.rails5.lock5
-rw-r--r--app/assets/javascripts/api.js55
-rw-r--r--app/assets/javascripts/awards_handler.js32
-rw-r--r--app/assets/javascripts/behaviors/markdown/copy_as_gfm.js2
-rw-r--r--app/assets/javascripts/blob/file_template_mediator.js42
-rw-r--r--app/assets/javascripts/blob/template_selector.js3
-rw-r--r--app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js4
-rw-r--r--app/assets/javascripts/blob/template_selectors/dockerfile_selector.js4
-rw-r--r--app/assets/javascripts/blob/template_selectors/gitignore_selector.js4
-rw-r--r--app/assets/javascripts/blob/template_selectors/license_selector.js4
-rw-r--r--app/assets/javascripts/blob_edit/blob_bundle.js3
-rw-r--r--app/assets/javascripts/blob_edit/edit_blob.js24
-rw-r--r--app/assets/javascripts/boards/components/board.js2
-rw-r--r--app/assets/javascripts/boards/components/board_delete.js2
-rw-r--r--app/assets/javascripts/boards/components/board_sidebar.js2
-rw-r--r--app/assets/javascripts/boards/models/issue.js2
-rw-r--r--app/assets/javascripts/boards/models/list.js2
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js2
-rw-r--r--app/assets/javascripts/clusters/clusters_bundle.js10
-rw-r--r--app/assets/javascripts/clusters/clusters_index.js5
-rw-r--r--app/assets/javascripts/commit/image_file.js2
-rw-r--r--app/assets/javascripts/compare_autocomplete.js2
-rw-r--r--app/assets/javascripts/diff_notes/components/comment_resolve_btn.js2
-rw-r--r--app/assets/javascripts/diff_notes/components/jump_to_discussion.js2
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_count.js2
-rw-r--r--app/assets/javascripts/diff_notes/mixins/discussion.js2
-rw-r--r--app/assets/javascripts/diff_notes/stores/comments.js2
-rw-r--r--app/assets/javascripts/diffs/components/app.vue1
-rw-r--r--app/assets/javascripts/diffs/components/commit_item.vue10
-rw-r--r--app/assets/javascripts/diffs/components/diff_content.vue2
-rw-r--r--app/assets/javascripts/diffs/store/utils.js17
-rw-r--r--app/assets/javascripts/dismissable_callout.js27
-rw-r--r--app/assets/javascripts/environments/components/environment_item.vue7
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js40
-rw-r--r--app/assets/javascripts/gl_dropdown.js2
-rw-r--r--app/assets/javascripts/header.js55
-rw-r--r--app/assets/javascripts/ide/stores/modules/commit/actions.js6
-rw-r--r--app/assets/javascripts/ide/stores/modules/file_templates/actions.js12
-rw-r--r--app/assets/javascripts/issuable_bulk_update_actions.js2
-rw-r--r--app/assets/javascripts/issue.js2
-rw-r--r--app/assets/javascripts/issue_show/components/edit_actions.vue4
-rw-r--r--app/assets/javascripts/issue_show/components/fields/description.vue3
-rw-r--r--app/assets/javascripts/issue_show/components/fields/title.vue2
-rw-r--r--app/assets/javascripts/issue_show/components/title.vue3
-rw-r--r--app/assets/javascripts/jobs/components/commit_block.vue2
-rw-r--r--app/assets/javascripts/jobs/components/empty_state.vue4
-rw-r--r--app/assets/javascripts/jobs/components/job_app.vue15
-rw-r--r--app/assets/javascripts/jobs/store/getters.js13
-rw-r--r--app/assets/javascripts/label_manager.js2
-rw-r--r--app/assets/javascripts/labels_select.js2
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js1
-rw-r--r--app/assets/javascripts/lib/utils/notify.js2
-rw-r--r--app/assets/javascripts/lib/utils/text_markdown.js2
-rw-r--r--app/assets/javascripts/line_highlighter.js2
-rw-r--r--app/assets/javascripts/merge_conflicts/components/diff_file_editor.js2
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflict_store.js2
-rw-r--r--app/assets/javascripts/merge_request.js2
-rw-r--r--app/assets/javascripts/merge_request_tabs.js2
-rw-r--r--app/assets/javascripts/milestone_select.js2
-rw-r--r--app/assets/javascripts/monitoring/components/graph.vue9
-rw-r--r--app/assets/javascripts/monitoring/utils/multiple_time_series.js103
-rw-r--r--app/assets/javascripts/mr_notes/stores/index.js17
-rw-r--r--app/assets/javascripts/namespace_select.js2
-rw-r--r--app/assets/javascripts/network/branch_graph.js2
-rw-r--r--app/assets/javascripts/new_branch_form.js2
-rw-r--r--app/assets/javascripts/notes.js4
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue15
-rw-r--r--app/assets/javascripts/notes/components/note_actions.vue33
-rw-r--r--app/assets/javascripts/notes/components/note_body.vue2
-rw-r--r--app/assets/javascripts/notes/components/note_form.vue2
-rw-r--r--app/assets/javascripts/notes/components/note_header.vue36
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue2
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue28
-rw-r--r--app/assets/javascripts/notes/stores/actions.js19
-rw-r--r--app/assets/javascripts/notes/stores/getters.js7
-rw-r--r--app/assets/javascripts/pages/dashboard/todos/index/todos.js12
-rw-r--r--app/assets/javascripts/pages/profiles/show/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js2
-rw-r--r--app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js2
-rw-r--r--app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js2
-rw-r--r--app/assets/javascripts/pages/projects/index.js6
-rw-r--r--app/assets/javascripts/pages/projects/network/network.js2
-rw-r--r--app/assets/javascripts/pages/projects/project.js3
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue40
-rw-r--r--app/assets/javascripts/pages/root/index.js5
-rw-r--r--app/assets/javascripts/pages/sessions/new/username_validator.js2
-rw-r--r--app/assets/javascripts/pages/snippets/form.js1
-rw-r--r--app/assets/javascripts/pages/users/activity_calendar.js26
-rw-r--r--app/assets/javascripts/pages/users/user_overview_block.js42
-rw-r--r--app/assets/javascripts/pages/users/user_tabs.js84
-rw-r--r--app/assets/javascripts/persistent_user_callout.js34
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_actions.vue11
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_table.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_table_row.vue8
-rw-r--r--app/assets/javascripts/profile/gl_crop.js2
-rw-r--r--app/assets/javascripts/project_find_file.js2
-rw-r--r--app/assets/javascripts/project_select.js2
-rw-r--r--app/assets/javascripts/right_sidebar.js2
-rw-r--r--app/assets/javascripts/search_autocomplete.js2
-rw-r--r--app/assets/javascripts/set_status_modal/emoji_menu_in_modal.js21
-rw-r--r--app/assets/javascripts/set_status_modal/event_hub.js3
-rw-r--r--app/assets/javascripts/set_status_modal/set_status_modal_trigger.vue33
-rw-r--r--app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue241
-rw-r--r--app/assets/javascripts/shared/milestones/form.js1
-rw-r--r--app/assets/javascripts/templates/issuable_template_selector.js2
-rw-r--r--app/assets/javascripts/tree.js2
-rw-r--r--app/assets/javascripts/users_select.js4
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/header.vue16
-rw-r--r--app/assets/javascripts/zen_mode.js2
-rw-r--r--app/assets/stylesheets/framework/calendar.scss12
-rw-r--r--app/assets/stylesheets/framework/contextual_sidebar.scss8
-rw-r--r--app/assets/stylesheets/framework/header.scss36
-rw-r--r--app/assets/stylesheets/framework/mixins.scss66
-rw-r--r--app/assets/stylesheets/framework/variables.scss4
-rw-r--r--app/assets/stylesheets/framework/variables_overrides.scss1
-rw-r--r--app/assets/stylesheets/pages/commits.scss2
-rw-r--r--app/assets/stylesheets/pages/diff.scss1
-rw-r--r--app/assets/stylesheets/pages/notes.scss60
-rw-r--r--app/assets/stylesheets/pages/profile.scss10
-rw-r--r--app/assets/stylesheets/pages/projects.scss8
-rw-r--r--app/assets/stylesheets/performance_bar.scss3
-rw-r--r--app/controllers/admin/application_settings_controller.rb3
-rw-r--r--app/controllers/application_controller.rb1
-rw-r--r--app/controllers/concerns/labels_as_hash.rb28
-rw-r--r--app/controllers/groups/labels_controller.rb3
-rw-r--r--app/controllers/groups/settings/ci_cd_controller.rb7
-rw-r--r--app/controllers/projects/autocomplete_sources_controller.rb4
-rw-r--r--app/controllers/projects/compare_controller.rb17
-rw-r--r--app/controllers/projects/issues_controller.rb2
-rw-r--r--app/controllers/projects/jobs_controller.rb8
-rw-r--r--app/controllers/projects/labels_controller.rb1
-rw-r--r--app/controllers/projects/merge_requests/diffs_controller.rb8
-rw-r--r--app/controllers/projects/merge_requests_controller.rb10
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb7
-rw-r--r--app/controllers/projects/settings/repository_controller.rb4
-rw-r--r--app/controllers/projects_controller.rb1
-rw-r--r--app/controllers/users_controller.rb13
-rw-r--r--app/finders/group_labels_finder.rb16
-rw-r--r--app/finders/issuable_finder.rb7
-rw-r--r--app/finders/issues_finder.rb10
-rw-r--r--app/finders/labels_finder.rb17
-rw-r--r--app/finders/license_template_finder.rb40
-rw-r--r--app/finders/merge_requests_finder.rb4
-rw-r--r--app/finders/pending_todos_finder.rb64
-rw-r--r--app/finders/template_finder.rb19
-rw-r--r--app/finders/todos_finder.rb93
-rw-r--r--app/finders/user_recent_events_finder.rb2
-rw-r--r--app/finders/users_with_pending_todos_finder.rb16
-rw-r--r--app/graphql/types/permission_types/project.rb2
-rw-r--r--app/helpers/application_helper.rb3
-rw-r--r--app/helpers/blob_helper.rb27
-rw-r--r--app/helpers/boards_helper.rb4
-rw-r--r--app/helpers/clusters_helper.rb4
-rw-r--r--app/helpers/dashboard_helper.rb23
-rw-r--r--app/helpers/labels_helper.rb20
-rw-r--r--app/helpers/notes_helper.rb4
-rw-r--r--app/helpers/projects_helper.rb6
-rw-r--r--app/helpers/repository_languages_helper.rb3
-rw-r--r--app/helpers/users_helper.rb2
-rw-r--r--app/models/ci/artifact_blob.rb2
-rw-r--r--app/models/ci/build.rb9
-rw-r--r--app/models/ci/pipeline.rb32
-rw-r--r--app/models/ci/stage.rb4
-rw-r--r--app/models/commit.rb6
-rw-r--r--app/models/concerns/diff_positionable_note.rb81
-rw-r--r--app/models/concerns/issuable.rb1
-rw-r--r--app/models/concerns/subscribable.rb8
-rw-r--r--app/models/diff_note.rb68
-rw-r--r--app/models/hooks/web_hook.rb34
-rw-r--r--app/models/instance_configuration.rb4
-rw-r--r--app/models/issue.rb18
-rw-r--r--app/models/label.rb9
-rw-r--r--app/models/license_template.rb8
-rw-r--r--app/models/note.rb27
-rw-r--r--app/models/project.rb61
-rw-r--r--app/models/project_auto_devops.rb20
-rw-r--r--app/models/project_feature.rb31
-rw-r--r--app/models/project_services/hipchat_service.rb2
-rw-r--r--app/models/system_note_metadata.rb5
-rw-r--r--app/models/todo.rb43
-rw-r--r--app/models/user.rb5
-rw-r--r--app/models/user_callout.rb3
-rw-r--r--app/policies/project_policy.rb11
-rw-r--r--app/serializers/build_details_entity.rb8
-rw-r--r--app/serializers/commit_entity.rb21
-rw-r--r--app/serializers/detailed_status_entity.rb8
-rw-r--r--app/serializers/diff_file_entity.rb4
-rw-r--r--app/serializers/diff_viewer_entity.rb7
-rw-r--r--app/serializers/diffs_entity.rb4
-rw-r--r--app/serializers/discussion_entity.rb2
-rw-r--r--app/services/issues/related_branches_service.rb26
-rw-r--r--app/services/merge_requests/base_service.rb6
-rw-r--r--app/services/merge_requests/refresh_service.rb58
-rw-r--r--app/services/projects/autocomplete_service.rb33
-rw-r--r--app/services/projects/update_pages_configuration_service.rb8
-rw-r--r--app/services/projects/update_service.rb10
-rw-r--r--app/services/quick_actions/interpret_service.rb19
-rw-r--r--app/services/todo_service.rb26
-rw-r--r--app/views/admin/projects/show.html.haml4
-rw-r--r--app/views/admin/runners/index.html.haml183
-rw-r--r--app/views/ci/runner/_how_to_setup_runner.html.haml4
-rw-r--r--app/views/ci/runner/_how_to_setup_shared_runner.html.haml3
-rw-r--r--app/views/ci/runner/_how_to_setup_specific_runner.html.haml26
-rw-r--r--app/views/dashboard/activity.html.haml3
-rw-r--r--app/views/dashboard/groups/index.html.haml3
-rw-r--r--app/views/dashboard/issues.html.haml3
-rw-r--r--app/views/dashboard/merge_requests.html.haml3
-rw-r--r--app/views/dashboard/projects/index.html.haml3
-rw-r--r--app/views/dashboard/projects/starred.html.haml3
-rw-r--r--app/views/dashboard/todos/index.html.haml3
-rw-r--r--app/views/explore/groups/index.html.haml3
-rw-r--r--app/views/explore/projects/index.html.haml3
-rw-r--r--app/views/explore/projects/starred.html.haml3
-rw-r--r--app/views/explore/projects/trending.html.haml3
-rw-r--r--app/views/groups/labels/index.html.haml21
-rw-r--r--app/views/groups/runners/_group_runners.html.haml4
-rw-r--r--app/views/groups/settings/_advanced.html.haml4
-rw-r--r--app/views/layouts/header/_current_user_dropdown.html.haml7
-rw-r--r--app/views/layouts/header/_default.html.haml3
-rw-r--r--app/views/layouts/nav/_breadcrumbs.html.haml1
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml2
-rw-r--r--app/views/profiles/two_factor_auths/_codes.html.haml2
-rw-r--r--app/views/profiles/two_factor_auths/show.html.haml6
-rw-r--r--app/views/projects/_md_preview.html.haml1
-rw-r--r--app/views/projects/_new_project_fields.html.haml4
-rw-r--r--app/views/projects/ci/builds/_build.html.haml2
-rw-r--r--app/views/projects/clusters/_banner.html.haml4
-rw-r--r--app/views/projects/clusters/_gcp_signup_offer_banner.html.haml4
-rw-r--r--app/views/projects/clusters/gcp/_form.html.haml17
-rw-r--r--app/views/projects/clusters/gcp/_show.html.haml15
-rw-r--r--app/views/projects/clusters/user/_form.html.haml17
-rw-r--r--app/views/projects/clusters/user/_show.html.haml15
-rw-r--r--app/views/projects/environments/_external_url.html.haml2
-rw-r--r--app/views/projects/environments/empty.html.haml18
-rw-r--r--app/views/projects/environments/show.html.haml9
-rw-r--r--app/views/projects/issues/_discussion.html.haml2
-rw-r--r--app/views/projects/jobs/_empty_state.html.haml18
-rw-r--r--app/views/projects/jobs/_empty_states.html.haml9
-rw-r--r--app/views/projects/jobs/show.html.haml2
-rw-r--r--app/views/projects/labels/index.html.haml23
-rw-r--r--app/views/projects/merge_requests/show.html.haml2
-rw-r--r--app/views/projects/runners/_specific_runners.html.haml30
-rw-r--r--app/views/projects/settings/ci_cd/_autodevops_form.html.haml9
-rw-r--r--app/views/projects/settings/ci_cd/_form.html.haml10
-rw-r--r--app/views/projects/settings/ci_cd/show.html.haml2
-rw-r--r--app/views/shared/boards/_show.html.haml2
-rw-r--r--app/views/shared/boards/components/sidebar/_labels.html.haml2
-rw-r--r--app/views/shared/issuable/_label_dropdown.html.haml2
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml2
-rw-r--r--app/views/shared/labels/_nav.html.haml20
-rw-r--r--app/views/shared/labels/_sort_dropdown.html.haml2
-rw-r--r--app/views/users/_overview.html.haml32
-rw-r--r--app/views/users/show.html.haml38
-rw-r--r--app/workers/prune_old_events_worker.rb7
-rw-r--r--changelogs/unreleased/-51457-Show-percentage-of-language-detection-on-the-language-bar.yml5
-rw-r--r--changelogs/unreleased/22104-fix-environment-name-overlap.yml4
-rw-r--r--changelogs/unreleased/22188-drop-down-filter-for-project-snippets.yml5
-rw-r--r--changelogs/unreleased/40140-2FA-mobile-options-should-be-rephrased.yml5
-rw-r--r--changelogs/unreleased/40636-instance-configuration-shows-incorrect-ssh-fingerprints.yml5
-rw-r--r--changelogs/unreleased/41922-simplify-runner-registration-token-resetting.yml5
-rw-r--r--changelogs/unreleased/46050_add_new_ci_predefined_variables_for_gitlab_version.yml5
-rw-r--r--changelogs/unreleased/47496-more-n-1s-in-calculating-notification-recipients.yml5
-rw-r--r--changelogs/unreleased/48222-fix-todos-status-button.yml6
-rw-r--r--changelogs/unreleased/48494-fix-merge-request-buttons-spacing.yml5
-rw-r--r--changelogs/unreleased/49075-add-status-message-from-within-user-menu.yml5
-rw-r--r--changelogs/unreleased/49801-add-new-overview-tab-on-user-profile-page.yml5
-rw-r--r--changelogs/unreleased/50246-can-t-sort-group-issues-by-popularity-when-searching.yml6
-rw-r--r--changelogs/unreleased/50359-activerecord-statementinvalid-pg-querycanceled-error-canceling-statement-due-to-statement-timeout.yml5
-rw-r--r--changelogs/unreleased/50552-unable-to-close-performance-bar.yml5
-rw-r--r--changelogs/unreleased/51009-remove-rbac-clusters-feature-flag.yml5
-rw-r--r--changelogs/unreleased/51748-filter-any-milestone-via-api.yml5
-rw-r--r--changelogs/unreleased/51803-include-commits-stats-in-projects-api.yml5
-rw-r--r--changelogs/unreleased/51958-fix-mr-discussion-loading.yml5
-rw-r--r--changelogs/unreleased/52178-markdown-table-borders.yml5
-rw-r--r--changelogs/unreleased/52193-Pipeline-graph-is-not-vertically-aligned-in-commit-page.yml5
-rw-r--r--changelogs/unreleased/52194-trim-extra-whitespace-invite-member.yml5
-rw-r--r--changelogs/unreleased/52242-ui-ux-bug-in-change-group-path.yml5
-rw-r--r--changelogs/unreleased/52353-keyboard-navigation-project-slug-is-not-focused-on-new-project-page.yml5
-rw-r--r--changelogs/unreleased/52367-cleanup-web-hooks-columns.yml5
-rw-r--r--changelogs/unreleased/52408-pip-cache-dir-to-cache-python-dependencies.yml5
-rw-r--r--changelogs/unreleased/5987-project-templates-api.yml5
-rw-r--r--changelogs/unreleased/Fix-pipeline-redirect.yml5
-rw-r--r--changelogs/unreleased/add-button-to-insert-table-in-markdown.yml5
-rw-r--r--changelogs/unreleased/add-installation-type-backup-information.yml5
-rw-r--r--changelogs/unreleased/add_reliable_fetcher.yml5
-rw-r--r--changelogs/unreleased/auth.yml5
-rw-r--r--changelogs/unreleased/autodevops-timed-incremental-rollout.yml5
-rw-r--r--changelogs/unreleased/clone-nurtch-demo-repo.yml5
-rw-r--r--changelogs/unreleased/copy-changes-for-abuse-clarity.yml5
-rw-r--r--changelogs/unreleased/dz-labels-subscribe-filter.yml5
-rw-r--r--changelogs/unreleased/feature-gb-pipeline-only-except-with-modified-paths.yml5
-rw-r--r--changelogs/unreleased/features-unauth-access-ssh-keys.yml5
-rw-r--r--changelogs/unreleased/gt-remove-duplicate-button-from-the-md-header-toolbar.yml5
-rw-r--r--changelogs/unreleased/load_project_features.yml5
-rw-r--r--changelogs/unreleased/mao-48221-issues_show_sql_count.yml5
-rw-r--r--changelogs/unreleased/more-frozen-string-enable-lib.yml5
-rw-r--r--changelogs/unreleased/osw-fix-lfs-images-not-rendering.yml5
-rw-r--r--changelogs/unreleased/osw-remove-dead-code-on-mr-show.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-artifacts-controller-spec.yml6
-rw-r--r--changelogs/unreleased/rails5-mysql-schedule-build.yml5
-rw-r--r--changelogs/unreleased/rails5-user-status-spec.yml5
-rw-r--r--changelogs/unreleased/security-bw-confidential-titles-through-markdown-api.yml5
-rw-r--r--changelogs/unreleased/security-fix-leaking-private-project-namespace.yml5
-rw-r--r--changelogs/unreleased/security-osw-user-info-leak-discussions.yml5
-rw-r--r--changelogs/unreleased/sh-handle-invalid-comparison.yml5
-rw-r--r--changelogs/unreleased/update-operations-metrics-empty-state.yml5
-rw-r--r--changelogs/unreleased/zj-render-log-artifacts.yml5
-rw-r--r--config/gitlab.yml.example1
-rw-r--r--config/initializers/1_settings.rb1
-rw-r--r--config/initializers/sidekiq.rb10
-rw-r--r--config/routes/admin.rb2
-rw-r--r--config/routes/group.rb4
-rw-r--r--config/routes/project.rb4
-rw-r--r--config/routes/user.rb1
-rw-r--r--db/migrate/20170506185517_add_foreign_key_pipeline_schedules_and_pipelines.rb2
-rw-r--r--db/migrate/20180423204600_add_pages_access_level_to_project_feature.rb16
-rw-r--r--db/migrate/20180924201039_add_partial_index_to_scheduled_at.rb2
-rw-r--r--db/migrate/20181002172433_remove_restricted_todos_with_cte.rb32
-rw-r--r--db/post_migrate/20161221153951_rename_reserved_project_names.rb2
-rw-r--r--db/post_migrate/20170313133418_rename_more_reserved_project_names.rb2
-rw-r--r--db/post_migrate/20181008145341_steal_encrypt_columns.rb15
-rw-r--r--db/post_migrate/20181008145359_remove_web_hooks_token_and_url.rb10
-rw-r--r--db/schema.rb7
-rw-r--r--doc/administration/custom_hooks.md3
-rw-r--r--doc/administration/operations/moving_repositories.md26
-rw-r--r--doc/administration/pages/index.md75
-rw-r--r--doc/administration/raketasks/github_import.md32
-rw-r--r--doc/api/README.md4
-rw-r--r--doc/api/commits.md1
-rw-r--r--doc/api/events.md2
-rw-r--r--doc/api/issues.md2
-rw-r--r--doc/api/merge_requests.md682
-rw-r--r--doc/api/project_templates.md135
-rw-r--r--doc/api/repository_files.md15
-rw-r--r--doc/api/templates/dockerfiles.md113
-rw-r--r--doc/api/templates/gitignores.md520
-rw-r--r--doc/api/templates/gitlab_ci_ymls.md75
-rw-r--r--doc/api/users.md2
-rw-r--r--doc/ci/caching/index.md4
-rw-r--r--doc/ci/docker/using_docker_images.md32
-rw-r--r--doc/ci/examples/deployment/README.md2
-rw-r--r--doc/ci/interactive_web_terminal/index.md6
-rw-r--r--doc/ci/variables/README.md14
-rw-r--r--doc/ci/variables/img/secret_variables.pngbin32886 -> 0 bytes
-rw-r--r--doc/ci/variables/img/variables.pngbin0 -> 116263 bytes
-rw-r--r--doc/ci/variables/where_variables_can_be_used.md20
-rw-r--r--doc/ci/yaml/README.md65
-rw-r--r--doc/development/contributing/issue_workflow.md2
-rw-r--r--doc/development/database_merge_request_checklist.md10
-rw-r--r--doc/development/documentation/structure.md43
-rw-r--r--doc/development/rolling_out_changes_using_feature_flags.md24
-rw-r--r--doc/install/installation.md6
-rw-r--r--doc/topics/authentication/index.md4
-rw-r--r--doc/topics/autodevops/index.md66
-rw-r--r--doc/topics/autodevops/quick_start_guide.md8
-rw-r--r--doc/update/11.2-to-11.3.md (renamed from doc/update/11.2-to-11-3.md)0
-rw-r--r--doc/update/11.3-to-11.4.md378
-rw-r--r--doc/user/admin_area/settings/terms.md8
-rw-r--r--doc/user/img/color_inline_colorchip_render_gfm.pngbin0 -> 11534 bytes
-rw-r--r--doc/user/img/math_inline_sup_render_gfm.pngbin0 -> 1359 bytes
-rw-r--r--doc/user/img/mermaid_diagram_render_gfm.pngbin0 -> 4587 bytes
-rw-r--r--doc/user/img/task_list_ordered_render_gfm.pngbin0 -> 6247 bytes
-rw-r--r--doc/user/img/unordered_check_list_render_gfm.pngbin0 -> 6207 bytes
-rw-r--r--doc/user/markdown.md74
-rw-r--r--doc/user/permissions.md11
-rw-r--r--doc/user/profile/account/two_factor_authentication.md16
-rw-r--r--doc/user/profile/index.md7
-rw-r--r--doc/user/project/clusters/index.md31
-rw-r--r--doc/user/project/import/svn.md7
-rw-r--r--doc/user/project/issues/create_new_issue.md18
-rw-r--r--doc/user/project/quick_actions.md12
-rw-r--r--doc/user/project/repository/gpg_signed_commits/index.md2
-rw-r--r--lib/after_commit_queue.rb2
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/commits.rb3
-rw-r--r--lib/api/entities.rb1
-rw-r--r--lib/api/markdown.rb7
-rw-r--r--lib/api/project_templates.rb59
-rw-r--r--lib/api/projects.rb6
-rw-r--r--lib/api/runners.rb2
-rw-r--r--lib/api/templates.rb9
-rw-r--r--lib/api/users.rb6
-rw-r--r--lib/backup.rb2
-rw-r--r--lib/backup/manager.rb1
-rw-r--r--lib/banzai.rb9
-rw-r--r--lib/banzai/color_parser.rb2
-rw-r--r--lib/banzai/commit_renderer.rb2
-rw-r--r--lib/banzai/cross_project_reference.rb3
-rw-r--r--lib/banzai/filter.rb2
-rw-r--r--lib/banzai/filter/epic_reference_filter.rb6
-rw-r--r--lib/banzai/filter/label_reference_filter.rb2
-rw-r--r--lib/banzai/filter/markdown_engines/common_mark.rb2
-rw-r--r--lib/banzai/filter/markdown_engines/redcarpet.rb2
-rw-r--r--lib/banzai/filter/wiki_link_filter/rewriter.rb2
-rw-r--r--lib/banzai/filter_array.rb2
-rw-r--r--lib/banzai/issuable_extractor.rb2
-rw-r--r--lib/banzai/object_renderer.rb3
-rw-r--r--lib/banzai/pipeline.rb2
-rw-r--r--lib/banzai/pipeline/ascii_doc_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/atom_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/base_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/broadcast_message_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/combined_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/commit_description_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/description_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/email_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/full_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/markup_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/note_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/plain_markdown_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/post_process_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/pre_process_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/relative_link_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/single_line_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/wiki_pipeline.rb2
-rw-r--r--lib/banzai/querying.rb2
-rw-r--r--lib/banzai/redactor.rb10
-rw-r--r--lib/banzai/reference_extractor.rb2
-rw-r--r--lib/banzai/reference_parser.rb2
-rw-r--r--lib/banzai/reference_parser/base_parser.rb4
-rw-r--r--lib/banzai/reference_parser/commit_parser.rb2
-rw-r--r--lib/banzai/reference_parser/commit_range_parser.rb2
-rw-r--r--lib/banzai/reference_parser/directly_addressed_user_parser.rb2
-rw-r--r--lib/banzai/reference_parser/epic_parser.rb2
-rw-r--r--lib/banzai/reference_parser/external_issue_parser.rb2
-rw-r--r--lib/banzai/reference_parser/issuable_parser.rb2
-rw-r--r--lib/banzai/reference_parser/issue_parser.rb2
-rw-r--r--lib/banzai/reference_parser/label_parser.rb2
-rw-r--r--lib/banzai/reference_parser/merge_request_parser.rb2
-rw-r--r--lib/banzai/reference_parser/milestone_parser.rb2
-rw-r--r--lib/banzai/reference_parser/snippet_parser.rb2
-rw-r--r--lib/banzai/reference_parser/user_parser.rb2
-rw-r--r--lib/banzai/renderer.rb2
-rw-r--r--lib/banzai/renderer/common_mark/html.rb2
-rw-r--r--lib/banzai/renderer/redcarpet/html.rb2
-rw-r--r--lib/banzai/request_store_reference_cache.rb2
-rw-r--r--lib/bitbucket/client.rb2
-rw-r--r--lib/bitbucket/collection.rb2
-rw-r--r--lib/bitbucket/connection.rb2
-rw-r--r--lib/bitbucket/error/unauthorized.rb2
-rw-r--r--lib/bitbucket/page.rb2
-rw-r--r--lib/bitbucket/paginator.rb2
-rw-r--r--lib/bitbucket/representation/base.rb2
-rw-r--r--lib/bitbucket/representation/comment.rb2
-rw-r--r--lib/bitbucket/representation/issue.rb2
-rw-r--r--lib/bitbucket/representation/pull_request.rb2
-rw-r--r--lib/bitbucket/representation/pull_request_comment.rb2
-rw-r--r--lib/bitbucket/representation/repo.rb2
-rw-r--r--lib/bitbucket/representation/user.rb2
-rw-r--r--lib/carrier_wave_string_file.rb2
-rw-r--r--lib/constraints/feature_constrainer.rb2
-rw-r--r--lib/constraints/group_url_constrainer.rb2
-rw-r--r--lib/constraints/project_url_constrainer.rb2
-rw-r--r--lib/constraints/user_url_constrainer.rb2
-rw-r--r--lib/container_registry/blob.rb2
-rw-r--r--lib/container_registry/client.rb2
-rw-r--r--lib/container_registry/config.rb2
-rw-r--r--lib/container_registry/path.rb2
-rw-r--r--lib/container_registry/registry.rb2
-rw-r--r--lib/container_registry/tag.rb2
-rw-r--r--lib/declarative_policy.rb4
-rw-r--r--lib/declarative_policy/base.rb2
-rw-r--r--lib/declarative_policy/cache.rb2
-rw-r--r--lib/declarative_policy/condition.rb2
-rw-r--r--lib/declarative_policy/delegate_dsl.rb2
-rw-r--r--lib/declarative_policy/policy_dsl.rb2
-rw-r--r--lib/declarative_policy/preferred_scope.rb5
-rw-r--r--lib/declarative_policy/rule.rb2
-rw-r--r--lib/declarative_policy/rule_dsl.rb2
-rw-r--r--lib/declarative_policy/runner.rb2
-rw-r--r--lib/declarative_policy/step.rb2
-rw-r--r--lib/expand_variables.rb2
-rw-r--r--lib/extracts_path.rb12
-rw-r--r--lib/feature.rb8
-rw-r--r--lib/file_size_validator.rb2
-rw-r--r--lib/forever.rb2
-rw-r--r--lib/gitlab.rb2
-rw-r--r--lib/gitlab/background_migration/remove_restricted_todos.rb84
-rw-r--r--lib/gitlab/ci/build/policy/changes.rb25
-rw-r--r--lib/gitlab/ci/config/entry/job.rb2
-rw-r--r--lib/gitlab/ci/config/entry/legacy_validation_helpers.rb9
-rw-r--r--lib/gitlab/ci/config/entry/policy.rb6
-rw-r--r--lib/gitlab/ci/config/entry/validators.rb6
-rw-r--r--lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml95
-rw-r--r--lib/gitlab/ci/templates/Laravel.gitlab-ci.yml9
-rw-r--r--lib/gitlab/ci/templates/Python.gitlab-ci.yml2
-rw-r--r--lib/gitlab/diff/position.rb4
-rw-r--r--lib/gitlab/git/diff_stats_collection.rb4
-rw-r--r--lib/gitlab/git/push.rb56
-rw-r--r--lib/gitlab/git_access.rb4
-rw-r--r--lib/gitlab/gitaly_client.rb2
-rw-r--r--lib/gitlab/template/base_template.rb11
-rw-r--r--lib/gitlab/url_sanitizer.rb2
-rw-r--r--lib/gt_one_coercion.rb2
-rw-r--r--lib/milestone_array.rb2
-rw-r--r--lib/mysql_zero_date.rb2
-rw-r--r--lib/static_model.rb2
-rw-r--r--lib/system_check.rb2
-rw-r--r--lib/unfold_form.rb2
-rw-r--r--lib/uploaded_file.rb2
-rw-r--r--lib/version_check.rb2
-rw-r--r--locale/ar_SA/gitlab.po2
-rw-r--r--locale/bg/gitlab.po2
-rw-r--r--locale/ca_ES/gitlab.po2
-rw-r--r--locale/cs_CZ/gitlab.po2
-rw-r--r--locale/da_DK/gitlab.po2
-rw-r--r--locale/de/gitlab.po2
-rw-r--r--locale/eo/gitlab.po2
-rw-r--r--locale/es/gitlab.po2
-rw-r--r--locale/et_EE/gitlab.po2
-rw-r--r--locale/fil_PH/gitlab.po2
-rw-r--r--locale/fr/gitlab.po2
-rw-r--r--locale/gitlab.pot108
-rw-r--r--locale/gl_ES/gitlab.po2
-rw-r--r--locale/he_IL/gitlab.po2
-rw-r--r--locale/id_ID/gitlab.po2
-rw-r--r--locale/it/gitlab.po2
-rw-r--r--locale/ja/gitlab.po2
-rw-r--r--locale/ko/gitlab.po2
-rw-r--r--locale/nl_NL/gitlab.po2
-rw-r--r--locale/pl_PL/gitlab.po2
-rw-r--r--locale/pt_BR/gitlab.po2
-rw-r--r--locale/ro_RO/gitlab.po2
-rw-r--r--locale/ru/gitlab.po2
-rw-r--r--locale/sq_AL/gitlab.po2
-rw-r--r--locale/tr_TR/gitlab.po2
-rw-r--r--locale/uk/gitlab.po2
-rw-r--r--locale/zh_CN/gitlab.po2
-rw-r--r--locale/zh_HK/gitlab.po2
-rw-r--r--locale/zh_TW/gitlab.po2
-rw-r--r--package.json3
-rw-r--r--qa/qa.rb20
-rw-r--r--qa/qa/factory/resource/branch.rb2
-rw-r--r--qa/qa/factory/resource/deploy_key.rb2
-rw-r--r--qa/qa/factory/resource/fork.rb2
-rw-r--r--qa/qa/factory/resource/kubernetes_cluster.rb3
-rw-r--r--qa/qa/factory/resource/personal_access_token.rb4
-rw-r--r--qa/qa/factory/resource/project_milestone.rb2
-rw-r--r--qa/qa/factory/resource/runner.rb2
-rw-r--r--qa/qa/factory/resource/sandbox.rb2
-rw-r--r--qa/qa/factory/resource/secret_variable.rb2
-rw-r--r--qa/qa/factory/resource/ssh_key.rb4
-rw-r--r--qa/qa/factory/resource/user.rb4
-rw-r--r--qa/qa/factory/resource/wiki.rb2
-rw-r--r--qa/qa/factory/settings/hashed_storage.rb6
-rw-r--r--qa/qa/page/README.md8
-rw-r--r--qa/qa/page/admin/menu.rb (renamed from qa/qa/page/menu/admin.rb)6
-rw-r--r--qa/qa/page/component/issuable/common.rb35
-rw-r--r--qa/qa/page/main/login.rb8
-rw-r--r--qa/qa/page/main/menu.rb (renamed from qa/qa/page/menu/main.rb)6
-rw-r--r--qa/qa/page/main/sign_up.rb2
-rw-r--r--qa/qa/page/profile/menu.rb (renamed from qa/qa/page/menu/profile.rb)6
-rw-r--r--qa/qa/page/project/issue/show.rb2
-rw-r--r--qa/qa/page/project/menu.rb (renamed from qa/qa/page/menu/side.rb)15
-rw-r--r--qa/qa/page/project/operations/environments/index.rb23
-rw-r--r--qa/qa/page/project/operations/environments/show.rb23
-rw-r--r--qa/qa/page/project/operations/kubernetes/add_existing.rb5
-rw-r--r--qa/qa/runtime/env.rb14
-rw-r--r--qa/qa/service/kubernetes_cluster.rb72
-rw-r--r--qa/qa/service/shellout.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb8
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/use_ssh_key_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb104
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/mattermost/create_group_with_mattermost_team_spec.rb2
-rw-r--r--qa/qa/specs/runner.rb2
-rw-r--r--qa/spec/runtime/env_spec.rb40
-rw-r--r--qa/spec/specs/runner_spec.rb14
-rwxr-xr-xscripts/trigger-build73
-rw-r--r--spec/controllers/admin/application_settings_controller_spec.rb18
-rw-r--r--spec/controllers/admin/projects_controller_spec.rb11
-rw-r--r--spec/controllers/groups/settings/ci_cd_controller_spec.rb14
-rw-r--r--spec/controllers/groups_controller_spec.rb29
-rw-r--r--spec/controllers/projects/artifacts_controller_spec.rb14
-rw-r--r--spec/controllers/projects/compare_controller_spec.rb24
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb12
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb66
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb50
-rw-r--r--spec/controllers/projects/settings/ci_cd_controller_spec.rb13
-rw-r--r--spec/factories/broadcast_messages.rb12
-rw-r--r--spec/factories/ci/builds.rb6
-rw-r--r--spec/factories/ci/runners.rb2
-rw-r--r--spec/factories/clusters/applications/helm.rb2
-rw-r--r--spec/factories/clusters/platforms/kubernetes.rb3
-rw-r--r--spec/factories/emails.rb2
-rw-r--r--spec/factories/gpg_keys.rb4
-rw-r--r--spec/factories/group_members.rb2
-rw-r--r--spec/factories/merge_requests.rb2
-rw-r--r--spec/factories/notes.rb2
-rw-r--r--spec/factories/oauth_access_grants.rb2
-rw-r--r--spec/factories/project_auto_devops.rb12
-rw-r--r--spec/factories/project_members.rb2
-rw-r--r--spec/factories/projects.rb18
-rw-r--r--spec/factories/todos.rb2
-rw-r--r--spec/factories/uploads.rb10
-rw-r--r--spec/features/calendar_spec.rb21
-rw-r--r--spec/features/dashboard/datetime_on_tooltips_spec.rb2
-rw-r--r--spec/features/groups/group_settings_spec.rb16
-rw-r--r--spec/features/groups/labels/subscription_spec.rb50
-rw-r--r--spec/features/groups/settings/ci_cd_spec.rb39
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb11
-rw-r--r--spec/features/profiles/user_edit_profile_spec.rb256
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb4
-rw-r--r--spec/features/projects/clusters/user_spec.rb4
-rw-r--r--spec/features/projects/jobs_spec.rb17
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb18
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb37
-rw-r--r--spec/features/projects/settings/pipelines_settings_spec.rb24
-rw-r--r--spec/features/search/user_uses_search_filters_spec.rb4
-rw-r--r--spec/features/u2f_spec.rb4
-rw-r--r--spec/features/users/overview_spec.rb123
-rw-r--r--spec/features/users/show_spec.rb2
-rw-r--r--spec/finders/group_labels_finder_spec.rb19
-rw-r--r--spec/finders/issues_finder_spec.rb8
-rw-r--r--spec/finders/labels_finder_spec.rb10
-rw-r--r--spec/finders/license_template_finder_spec.rb4
-rw-r--r--spec/finders/merge_requests_finder_spec.rb32
-rw-r--r--spec/finders/pending_todos_finder_spec.rb63
-rw-r--r--spec/finders/template_finder_spec.rb11
-rw-r--r--spec/finders/todos_finder_spec.rb17
-rw-r--r--spec/finders/users_with_pending_todos_finder_spec.rb19
-rw-r--r--spec/fixtures/api/schemas/entities/diff_viewer.json8
-rw-r--r--spec/fixtures/api/schemas/entities/note_user_entity.json21
-rw-r--r--spec/fixtures/api/schemas/job/deployment_status.json2
-rw-r--r--spec/fixtures/api/schemas/job/runners.json3
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/license.json30
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/template.json12
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/template_list.json15
-rw-r--r--spec/fixtures/ssh_host_example_key.pub2
-rw-r--r--spec/graphql/types/permission_types/project_spec.rb2
-rw-r--r--spec/helpers/application_helper_spec.rb4
-rw-r--r--spec/javascripts/api_spec.js64
-rw-r--r--spec/javascripts/awards_handler_spec.js2
-rw-r--r--spec/javascripts/boards/boards_store_spec.js2
-rw-r--r--spec/javascripts/boards/issue_spec.js1
-rw-r--r--spec/javascripts/boards/list_spec.js1
-rw-r--r--spec/javascripts/diff_comments_store_spec.js2
-rw-r--r--spec/javascripts/diffs/components/commit_item_spec.js35
-rw-r--r--spec/javascripts/diffs/components/diff_content_spec.js5
-rw-r--r--spec/javascripts/diffs/components/diff_file_spec.js5
-rw-r--r--spec/javascripts/diffs/mock_data/diff_discussions.js16
-rw-r--r--spec/javascripts/diffs/mock_data/diff_file.js3
-rw-r--r--spec/javascripts/diffs/store/actions_spec.js9
-rw-r--r--spec/javascripts/diffs/store/mutations_spec.js16
-rw-r--r--spec/javascripts/diffs/store/utils_spec.js16
-rw-r--r--spec/javascripts/droplab/plugins/input_setter_spec.js2
-rw-r--r--spec/javascripts/gfm_auto_complete_spec.js2
-rw-r--r--spec/javascripts/gl_dropdown_spec.js2
-rw-r--r--spec/javascripts/graphs/stat_graph_contributors_graph_spec.js3
-rw-r--r--spec/javascripts/graphs/stat_graph_contributors_util_spec.js2
-rw-r--r--spec/javascripts/issue_spec.js2
-rw-r--r--spec/javascripts/jobs/components/commit_block_spec.js2
-rw-r--r--spec/javascripts/jobs/components/empty_state_spec.js2
-rw-r--r--spec/javascripts/jobs/components/job_app_spec.js139
-rw-r--r--spec/javascripts/jobs/store/getters_spec.js92
-rw-r--r--spec/javascripts/line_highlighter_spec.js2
-rw-r--r--spec/javascripts/monitoring/dashboard_spec.js38
-rw-r--r--spec/javascripts/monitoring/graph/legend_spec.js2
-rw-r--r--spec/javascripts/monitoring/graph/track_info_spec.js2
-rw-r--r--spec/javascripts/monitoring/graph/track_line_spec.js2
-rw-r--r--spec/javascripts/monitoring/graph_path_spec.js2
-rw-r--r--spec/javascripts/monitoring/mock_data.js1
-rw-r--r--spec/javascripts/monitoring/utils/multiple_time_series_spec.js2
-rw-r--r--spec/javascripts/new_branch_spec.js2
-rw-r--r--spec/javascripts/notes/mock_data.js24
-rw-r--r--spec/javascripts/pipelines/pipelines_actions_spec.js104
-rw-r--r--spec/javascripts/pipelines/pipelines_table_row_spec.js7
-rw-r--r--spec/javascripts/right_sidebar_spec.js2
-rw-r--r--spec/javascripts/search_autocomplete_spec.js2
-rw-r--r--spec/javascripts/syntax_highlight_spec.js2
-rw-r--r--spec/javascripts/u2f/mock_u2f_device.js2
-rw-r--r--spec/javascripts/vue_shared/components/markdown/header_spec.js13
-rw-r--r--spec/lib/banzai/cross_project_reference_spec.rb19
-rw-r--r--spec/lib/banzai/filter/commit_range_reference_filter_spec.rb1
-rw-r--r--spec/lib/banzai/reference_parser/commit_parser_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/build/policy/changes_spec.rb107
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb15
-rw-r--r--spec/lib/gitlab/ci/config/entry/policy_spec.rb20
-rw-r--r--spec/lib/gitlab/ci/status/build/scheduled_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb10
-rw-r--r--spec/lib/gitlab/diff/position_spec.rb88
-rw-r--r--spec/lib/gitlab/git/diff_stats_collection_spec.rb8
-rw-r--r--spec/lib/gitlab/git/push_spec.rb166
-rw-r--r--spec/lib/gitlab/import_export/relation_factory_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml1
-rw-r--r--spec/lib/gitlab/version_info_spec.rb3
-rw-r--r--spec/migrations/add_pages_access_level_to_project_feature_spec.rb30
-rw-r--r--spec/models/ci/build_spec.rb6
-rw-r--r--spec/models/ci/pipeline_spec.rb81
-rw-r--r--spec/models/ci/stage_spec.rb4
-rw-r--r--spec/models/concerns/cache_markdown_field_spec.rb5
-rw-r--r--spec/models/instance_configuration_spec.rb6
-rw-r--r--spec/models/internal_id_spec.rb3
-rw-r--r--spec/models/issue_spec.rb39
-rw-r--r--spec/models/label_spec.rb36
-rw-r--r--spec/models/license_template_spec.rb2
-rw-r--r--spec/models/note_spec.rb67
-rw-r--r--spec/models/project_auto_devops_spec.rb27
-rw-r--r--spec/models/project_feature_spec.rb31
-rw-r--r--spec/models/project_spec.rb57
-rw-r--r--spec/models/todo_spec.rb125
-rw-r--r--spec/presenters/ci/build_presenter_spec.rb2
-rw-r--r--spec/requests/api/commits_spec.rb14
-rw-r--r--spec/requests/api/issues_spec.rb10
-rw-r--r--spec/requests/api/markdown_spec.rb46
-rw-r--r--spec/requests/api/pages/internal_access_spec.rb88
-rw-r--r--spec/requests/api/pages/private_access_spec.rb88
-rw-r--r--spec/requests/api/pages/public_access_spec.rb88
-rw-r--r--spec/requests/api/project_templates_spec.rb145
-rw-r--r--spec/requests/api/users_spec.rb38
-rw-r--r--spec/routing/project_routing_spec.rb11
-rw-r--r--spec/serializers/commit_entity_spec.rb28
-rw-r--r--spec/serializers/diff_file_entity_spec.rb5
-rw-r--r--spec/serializers/diff_viewer_entity_spec.rb19
-rw-r--r--spec/serializers/discussion_entity_spec.rb7
-rw-r--r--spec/services/issues/related_branches_service_spec.rb39
-rw-r--r--spec/services/notification_recipient_service_spec.rb47
-rw-r--r--spec/services/projects/autocomplete_service_spec.rb4
-rw-r--r--spec/services/projects/update_service_spec.rb21
-rw-r--r--spec/services/system_note_service_spec.rb2
-rw-r--r--spec/spec_helper.rb5
-rw-r--r--spec/support/helpers/reference_parser_helpers.rb6
-rw-r--r--spec/support/services/clusters/create_service_shared.rb4
-rw-r--r--spec/workers/prune_old_events_worker_spec.rb20
-rw-r--r--vendor/gitignore/Global/JetBrains.gitignore3
-rw-r--r--vendor/gitignore/Node.gitignore2
-rw-r--r--vendor/gitignore/TeX.gitignore6
-rw-r--r--vendor/gitignore/VisualStudio.gitignore6
-rw-r--r--vendor/jupyter/values.yaml4
-rw-r--r--vendor/licenses.csv407
-rw-r--r--yarn.lock109
753 files changed, 8795 insertions, 3382 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml
index a954bb4ff37..d04a10a9127 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -1,9 +1,9 @@
----
env:
browser: true
es6: true
extends:
- airbnb-base
+ - prettier
- plugin:vue/recommended
globals:
__webpack_public_path__: true
@@ -19,34 +19,31 @@ plugins:
- promise
settings:
html/html-extensions:
- - ".html"
- - ".html.raw"
+ - '.html'
+ - '.html.raw'
import/resolver:
webpack:
- config: "./config/webpack.config.js"
+ config: './config/webpack.config.js'
rules:
filenames/match-regex:
- error
- - "^[a-z0-9_]+$"
+ - '^[a-z0-9_]+$'
import/no-commonjs: error
- no-multiple-empty-lines:
- - error
- - max: 1
promise/catch-or-return: error
no-param-reassign:
- error
- props: true
ignorePropertyModificationsFor:
- - "acc" # for reduce accumulators
- - "accumulator" # for reduce accumulators
- - "el" # for DOM elements
- - "element" # for DOM elements
- - "state" # for Vuex mutations
+ - 'acc' # for reduce accumulators
+ - 'accumulator' # for reduce accumulators
+ - 'el' # for DOM elements
+ - 'element' # for DOM elements
+ - 'state' # for Vuex mutations
no-underscore-dangle:
- error
- allow:
- - __
- - _links
+ - __
+ - _links
no-mixed-operators: off
vue/html-self-closing:
- error
@@ -60,31 +57,7 @@ rules:
- error
- properties: never
ignoreDestructuring: true
- ## Conflicting rules with prettier:
- space-before-function-paren: off
- curly: off
- arrow-parens: off
- function-paren-newline: off
- object-curly-newline: off
- padded-blocks: off
- # Disabled for now, to make the eslint 3 -> eslint 5 update smoother
- ## Indent rule. We are using the old for now: https://eslint.org/docs/user-guide/migrating-to-4.0.0#indent-rewrite
- indent: off
- indent-legacy:
- - error
- - 2
- - SwitchCase: 1
- VariableDeclarator: 1
- outerIIFEBody: 1
- FunctionDeclaration:
- parameters: 1
- body: 1
- FunctionExpression:
- parameters: 1
- body: 1
# Disabled for now, to make the airbnb-base 12.1.0 -> 13.1.0 update smoother
- operator-linebreak: off
- implicit-arrow-linebreak: off
no-else-return:
- error
- allowElseIf: true
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2a299ea79ef..c652b6c75e2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -606,7 +606,7 @@ static-analysis:
docs lint:
<<: *dedicated-runner
<<: *except-qa
- image: "registry.gitlab.com/gitlab-org/gitlab-build-images:nanoc-bootstrap-ruby-2.4-alpine"
+ image: "registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-docs-lint"
stage: test
cache: {}
dependencies: []
@@ -614,8 +614,8 @@ docs lint:
script:
- scripts/lint-doc.sh
- scripts/lint-changelog-yaml
- - mv doc/ /nanoc/content/
- - cd /nanoc
+ - mv doc/ /tmp/gitlab-docs/content/
+ - cd /tmp/gitlab-docs
# Build HTML from Markdown
- bundle exec nanoc
# Check the internal links
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index 96a157648fb..a4b773b15a9 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -16,3 +16,5 @@ db/ @abrandl @NikolayS
/ee/lib/gitlab/code_owners/ @reprazent
/ee/lib/ee/gitlab/auth/ldap/ @dblessing @mkozono
/lib/gitlab/auth/ldap/ @dblessing @mkozono
+/lib/gitlab/ci/templates/ @nolith @zj
+/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @DylanGriffith @mayra-cabrera @tkuah
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index d4f7615c80e..571df7534cb 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -10,24 +10,6 @@
Capybara/CurrentPathExpectation:
Enabled: false
-# Offense count: 23
-FactoryBot/DynamicAttributeDefinedStatically:
- Exclude:
- - 'spec/factories/broadcast_messages.rb'
- - 'spec/factories/ci/builds.rb'
- - 'spec/factories/ci/runners.rb'
- - 'spec/factories/clusters/applications/helm.rb'
- - 'spec/factories/clusters/platforms/kubernetes.rb'
- - 'spec/factories/emails.rb'
- - 'spec/factories/gpg_keys.rb'
- - 'spec/factories/group_members.rb'
- - 'spec/factories/merge_requests.rb'
- - 'spec/factories/notes.rb'
- - 'spec/factories/oauth_access_grants.rb'
- - 'spec/factories/project_members.rb'
- - 'spec/factories/todos.rb'
- - 'spec/factories/uploads.rb'
-
# Offense count: 167
# Cop supports --auto-correct.
Layout/EmptyLinesAroundArguments:
@@ -53,20 +35,6 @@ Layout/IndentArray:
Layout/IndentHash:
Enabled: false
-# Offense count: 11
-# Cop supports --auto-correct.
-# Configuration parameters: AllowForAlignment.
-Layout/SpaceBeforeFirstArg:
- Exclude:
- - 'config/routes/project.rb'
- - 'db/migrate/20170506185517_add_foreign_key_pipeline_schedules_and_pipelines.rb'
- - 'features/steps/project/source/browse_files.rb'
- - 'features/steps/project/source/markdown_render.rb'
- - 'lib/api/runners.rb'
- - 'spec/features/search/user_uses_search_filters_spec.rb'
- - 'spec/routing/project_routing_spec.rb'
- - 'spec/services/system_note_service_spec.rb'
-
# Offense count: 93
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
@@ -74,15 +42,6 @@ Layout/SpaceBeforeFirstArg:
Layout/SpaceInLambdaLiteral:
Enabled: false
-# Offense count: 1
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBrackets.
-# SupportedStyles: space, no_space, compact
-# SupportedStylesForEmptyBrackets: space, no_space
-Layout/SpaceInsideArrayLiteralBrackets:
- Exclude:
- - 'spec/lib/gitlab/import_export/relation_factory_spec.rb'
-
# Offense count: 327
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
@@ -96,14 +55,6 @@ Layout/SpaceInsideBlockBraces:
Layout/SpaceInsideParens:
Enabled: false
-# Offense count: 14
-# Cop supports --auto-correct.
-Layout/SpaceInsidePercentLiteralDelimiters:
- Exclude:
- - 'lib/gitlab/git_access.rb'
- - 'lib/gitlab/health_checks/fs_shards_check.rb'
- - 'spec/lib/gitlab/health_checks/fs_shards_check_spec.rb'
-
# Offense count: 26
Lint/DuplicateMethods:
Exclude:
@@ -135,31 +86,11 @@ Lint/InterpolationCheck:
Lint/MissingCopEnableDirective:
Enabled: false
-# Offense count: 2
-Lint/NestedPercentLiteral:
- Exclude:
- - 'lib/gitlab/git/repository.rb'
- - 'spec/support/shared_examples/email_format_shared_examples.rb'
-
# Offense count: 1
Lint/ReturnInVoidContext:
Exclude:
- 'app/models/project.rb'
-# Offense count: 1
-# Configuration parameters: IgnoreImplicitReferences.
-Lint/ShadowedArgument:
- Exclude:
- - 'lib/gitlab/database/sha_attribute.rb'
-
-# Offense count: 3
-# Cop supports --auto-correct.
-Lint/UnneededRequireStatement:
- Exclude:
- - 'db/post_migrate/20161221153951_rename_reserved_project_names.rb'
- - 'db/post_migrate/20170313133418_rename_more_reserved_project_names.rb'
- - 'lib/declarative_policy.rb'
-
# Offense count: 9
Lint/UriEscapeUnescape:
Exclude:
@@ -199,16 +130,6 @@ Naming/HeredocDelimiterCase:
Naming/HeredocDelimiterNaming:
Enabled: false
-# Offense count: 1
-Performance/UnfreezeString:
- Exclude:
- - 'features/steps/project/commits/commits.rb'
-
-# Offense count: 1
-# Cop supports --auto-correct.
-Performance/UriDefaultParser:
- Exclude:
- - 'lib/gitlab/url_sanitizer.rb'
# Offense count: 3821
# Configuration parameters: Prefixes.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index be204a76645..dcc2c01931d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 11.3.3 (2018-10-04)
+
+- No changes.
+
## 11.3.2 (2018-10-03)
### Fixed (4 changes)
@@ -275,6 +279,15 @@ entry.
- Creates Vue component for artifacts block on job page.
+## 11.2.5 (2018-10-05)
+
+### Security (3 changes)
+
+- Filter user sensitive data from discussions JSON. !2538
+- Properly filter private references from system notes.
+- Markdown API no longer displays confidential title references unless authorized.
+
+
## 11.2.4 (2018-09-26)
### Security (6 changes)
@@ -554,6 +567,15 @@ entry.
- Moves help_popover component to a common location.
+## 11.1.8 (2018-10-05)
+
+### Security (3 changes)
+
+- Filter user sensitive data from discussions JSON. !2539
+- Properly filter private references from system notes.
+- Markdown API no longer displays confidential title references unless authorized.
+
+
## 11.1.7 (2018-09-26)
### Security (6 changes)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 81fc46c2b6f..d2d385dff8f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -92,35 +92,79 @@ Please report suspected security vulnerabilities in private to
Please do **NOT** create publicly viewable issues for suspected security
vulnerabilities.
-## Code of conduct
+## Code of Conduct
-As contributors and maintainers of this project, we pledge to respect all
-people who contribute through reporting issues, posting feature requests,
-updating documentation, submitting pull requests or patches, and other
-activities.
+### Our Pledge
-We are committed to making participation in this project a harassment-free
-experience for everyone, regardless of level of experience, gender, gender
-identity and expression, sexual orientation, disability, personal appearance,
-body size, race, ethnicity, age, or religion.
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
-Examples of unacceptable behavior by participants include the use of sexual
-language or imagery, derogatory comments or personal attacks, trolling, public
-or private harassment, insults, or other unprofessional conduct.
+### Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+### Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
-that are not aligned to this Code of Conduct. Project maintainers who do not
-follow the Code of Conduct may be removed from the project team.
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+### Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+### Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at conduct@gitlab.com. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
-This code of conduct applies both within project spaces and in public spaces
-when an individual is representing the project or its community.
+### Attribution
-Instances of abusive, harassing, or otherwise unacceptable behavior can be
-reported by emailing `contact@gitlab.com`.
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
-This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0,
-available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/).
+[homepage]: https://www.contributor-covenant.org
## Closing policy for issues and merge requests
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 3ba7bd5ba83..b1fa68e5df9 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.123.0
+0.125.0
diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION
index 9084fa2f716..26aaba0e866 100644
--- a/GITLAB_PAGES_VERSION
+++ b/GITLAB_PAGES_VERSION
@@ -1 +1 @@
-1.1.0
+1.2.0
diff --git a/Gemfile b/Gemfile
index 52de588deb3..ecbfba0827d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -295,6 +295,7 @@ gem 'peek-mysql2', '~> 1.1.0', group: :mysql
gem 'peek-pg', '~> 1.3.0', group: :postgres
gem 'peek-rblineprof', '~> 0.2.0'
gem 'peek-redis', '~> 1.2.0'
+gem 'gitlab-sidekiq-fetcher', require: 'sidekiq-reliable-fetch'
# Metrics
group :metrics do
diff --git a/Gemfile.lock b/Gemfile.lock
index 301f4132476..f995d92abf0 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -301,6 +301,8 @@ GEM
mime-types (>= 1.16)
posix-spawn (~> 0.3)
gitlab-markup (1.6.4)
+ gitlab-sidekiq-fetcher (0.3.0)
+ sidekiq (~> 5)
gitlab-styles (2.4.1)
rubocop (~> 0.54.0)
rubocop-gitlab-security (~> 0.1.0)
@@ -795,7 +797,7 @@ GEM
rubyzip (1.2.2)
rufus-scheduler (3.4.0)
et-orbi (~> 1.0)
- rugged (0.27.4)
+ rugged (0.27.5)
safe_yaml (1.0.4)
sanitize (4.6.6)
crass (~> 1.0.2)
@@ -1031,6 +1033,7 @@ DEPENDENCIES
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)
gitlab-markup (~> 1.6.4)
+ gitlab-sidekiq-fetcher
gitlab-styles (~> 2.4)
gitlab_omniauth-ldap (~> 2.0.4)
gon (~> 6.2)
diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock
index 1e129bc2b54..52f9b0ccd55 100644
--- a/Gemfile.rails5.lock
+++ b/Gemfile.rails5.lock
@@ -304,6 +304,8 @@ GEM
mime-types (>= 1.16)
posix-spawn (~> 0.3)
gitlab-markup (1.6.4)
+ gitlab-sidekiq-fetcher (0.3.0)
+ sidekiq (~> 5)
gitlab-styles (2.4.1)
rubocop (~> 0.54.0)
rubocop-gitlab-security (~> 0.1.0)
@@ -803,7 +805,7 @@ GEM
rubyzip (1.2.2)
rufus-scheduler (3.4.0)
et-orbi (~> 1.0)
- rugged (0.27.4)
+ rugged (0.27.5)
safe_yaml (1.0.4)
sanitize (4.6.6)
crass (~> 1.0.2)
@@ -1040,6 +1042,7 @@ DEPENDENCIES
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)
gitlab-markup (~> 1.6.4)
+ gitlab-sidekiq-fetcher
gitlab-styles (~> 2.4)
gitlab_omniauth-ldap (~> 2.0.4)
gon (~> 6.2)
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index cd800d75f7a..3f7a1ef1bfc 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -15,13 +15,11 @@ const Api = {
mergeRequestChangesPath: '/api/:version/projects/:id/merge_requests/:mrid/changes',
mergeRequestVersionsPath: '/api/:version/projects/:id/merge_requests/:mrid/versions',
groupLabelsPath: '/groups/:namespace_path/-/labels',
- templatesPath: '/api/:version/templates/:key',
- licensePath: '/api/:version/templates/licenses/:key',
- gitignorePath: '/api/:version/templates/gitignores/:key',
- gitlabCiYmlPath: '/api/:version/templates/gitlab_ci_ymls/:key',
- dockerfilePath: '/api/:version/templates/dockerfiles/:key',
issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key',
+ projectTemplatePath: '/api/:version/projects/:id/templates/:type/:key',
+ projectTemplatesPath: '/api/:version/projects/:id/templates/:type',
usersPath: '/api/:version/users.json',
+ userStatusPath: '/api/:version/user/status',
commitPath: '/api/:version/projects/:id/repository/commits',
commitPipelinesPath: '/:project_id/commit/:sha/pipelines',
branchSinglePath: '/api/:version/projects/:id/repository/branches/:branch',
@@ -195,29 +193,29 @@ const Api = {
return axios.get(url);
},
- // Return text for a specific license
- licenseText(key, data, callback) {
- const url = Api.buildUrl(Api.licensePath).replace(':key', key);
- return axios
- .get(url, {
- params: data,
- })
- .then(res => callback(res.data));
- },
+ projectTemplate(id, type, key, options, callback) {
+ const url = Api.buildUrl(this.projectTemplatePath)
+ .replace(':id', encodeURIComponent(id))
+ .replace(':type', type)
+ .replace(':key', encodeURIComponent(key));
- gitignoreText(key, callback) {
- const url = Api.buildUrl(Api.gitignorePath).replace(':key', key);
- return axios.get(url).then(({ data }) => callback(data));
- },
+ return axios.get(url, { params: options }).then(res => {
+ if (callback) callback(res.data);
- gitlabCiYml(key, callback) {
- const url = Api.buildUrl(Api.gitlabCiYmlPath).replace(':key', key);
- return axios.get(url).then(({ data }) => callback(data));
+ return res;
+ });
},
- dockerfileYml(key, callback) {
- const url = Api.buildUrl(Api.dockerfilePath).replace(':key', key);
- return axios.get(url).then(({ data }) => callback(data));
+ projectTemplates(id, type, params = {}, callback) {
+ const url = Api.buildUrl(this.projectTemplatesPath)
+ .replace(':id', encodeURIComponent(id))
+ .replace(':type', type);
+
+ return axios.get(url, { params }).then(res => {
+ if (callback) callback(res.data);
+
+ return res;
+ });
},
issueTemplate(namespacePath, projectPath, key, type, callback) {
@@ -266,10 +264,13 @@ const Api = {
});
},
- templates(key, params = {}) {
- const url = Api.buildUrl(this.templatesPath).replace(':key', key);
+ postUserStatus({ emoji, message }) {
+ const url = Api.buildUrl(this.userStatusPath);
- return axios.get(url, { params });
+ return axios.put(url, {
+ emoji,
+ message,
+ });
},
buildUrl(url) {
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 5b0c4285339..cace8bb9dba 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -42,10 +42,11 @@ export class AwardsHandler {
}
bindEvents() {
+ const $parentEl = this.targetContainerEl ? $(this.targetContainerEl) : $(document);
// If the user shows intent let's pre-build the menu
this.registerEventListener(
'one',
- $(document),
+ $parentEl,
'mouseenter focus',
this.toggleButtonSelector,
'mouseenter focus',
@@ -58,7 +59,7 @@ export class AwardsHandler {
}
},
);
- this.registerEventListener('on', $(document), 'click', this.toggleButtonSelector, e => {
+ this.registerEventListener('on', $parentEl, 'click', this.toggleButtonSelector, e => {
e.stopPropagation();
e.preventDefault();
this.showEmojiMenu($(e.currentTarget));
@@ -76,7 +77,7 @@ export class AwardsHandler {
});
const emojiButtonSelector = `.js-awards-block .js-emoji-btn, .${this.menuClass} .js-emoji-btn`;
- this.registerEventListener('on', $(document), 'click', emojiButtonSelector, e => {
+ this.registerEventListener('on', $parentEl, 'click', emojiButtonSelector, e => {
e.preventDefault();
const $target = $(e.currentTarget);
const $glEmojiElement = $target.find('gl-emoji');
@@ -168,7 +169,8 @@ export class AwardsHandler {
</div>
`;
- document.body.insertAdjacentHTML('beforeend', emojiMenuMarkup);
+ const targetEl = this.targetContainerEl ? this.targetContainerEl : document.body;
+ targetEl.insertAdjacentHTML('beforeend', emojiMenuMarkup);
this.addRemainingEmojiMenuCategories();
this.setupSearch();
@@ -250,6 +252,12 @@ export class AwardsHandler {
}
positionMenu($menu, $addBtn) {
+ if (this.targetContainerEl) {
+ return $menu.css({
+ top: `${$addBtn.outerHeight()}px`,
+ });
+ }
+
const position = $addBtn.data('position');
// The menu could potentially be off-screen or in a hidden overflow element
// So we position the element absolute in the body
@@ -424,9 +432,7 @@ export class AwardsHandler {
users = origTitle.trim().split(FROM_SENTENCE_REGEX);
}
users.unshift('You');
- return awardBlock
- .attr('title', this.toSentence(users))
- .tooltip('_fixTitle');
+ return awardBlock.attr('title', this.toSentence(users)).tooltip('_fixTitle');
}
createAwardButtonForVotesBlock(votesBlock, emojiName) {
@@ -609,13 +615,11 @@ export class AwardsHandler {
let awardsHandlerPromise = null;
export default function loadAwardsHandler(reload = false) {
if (!awardsHandlerPromise || reload) {
- awardsHandlerPromise = import(/* webpackChunkName: 'emoji' */ './emoji').then(
- Emoji => {
- const awardsHandler = new AwardsHandler(Emoji);
- awardsHandler.bindEvents();
- return awardsHandler;
- },
- );
+ awardsHandlerPromise = import(/* webpackChunkName: 'emoji' */ './emoji').then(Emoji => {
+ const awardsHandler = new AwardsHandler(Emoji);
+ awardsHandler.bindEvents();
+ return awardsHandler;
+ });
}
return awardsHandlerPromise;
}
diff --git a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
index 5d7a3bed301..0d7e8a5a3cb 100644
--- a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
+++ b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
@@ -1,4 +1,4 @@
-/* eslint-disable object-shorthand, no-unused-vars, no-use-before-define, max-len, no-restricted-syntax, guard-for-in, no-continue */
+/* eslint-disable object-shorthand, no-unused-vars, no-use-before-define, no-restricted-syntax, guard-for-in, no-continue */
import $ from 'jquery';
import _ from 'underscore';
diff --git a/app/assets/javascripts/blob/file_template_mediator.js b/app/assets/javascripts/blob/file_template_mediator.js
index ff1cbcad145..addacf29f1e 100644
--- a/app/assets/javascripts/blob/file_template_mediator.js
+++ b/app/assets/javascripts/blob/file_template_mediator.js
@@ -1,4 +1,4 @@
-/* eslint-disable class-methods-use-this */
+import Api from '~/api';
import $ from 'jquery';
import Flash from '../flash';
@@ -9,9 +9,10 @@ import GitignoreSelector from './template_selectors/gitignore_selector';
import LicenseSelector from './template_selectors/license_selector';
export default class FileTemplateMediator {
- constructor({ editor, currentAction }) {
+ constructor({ editor, currentAction, projectId }) {
this.editor = editor;
this.currentAction = currentAction;
+ this.projectId = projectId;
this.initTemplateSelectors();
this.initTemplateTypeSelector();
@@ -33,15 +34,14 @@ export default class FileTemplateMediator {
initTemplateTypeSelector() {
this.typeSelector = new FileTemplateTypeSelector({
mediator: this,
- dropdownData: this.templateSelectors
- .map((templateSelector) => {
- const cfg = templateSelector.config;
-
- return {
- name: cfg.name,
- key: cfg.key,
- };
- }),
+ dropdownData: this.templateSelectors.map(templateSelector => {
+ const cfg = templateSelector.config;
+
+ return {
+ name: cfg.name,
+ key: cfg.key,
+ };
+ }),
});
}
@@ -89,7 +89,7 @@ export default class FileTemplateMediator {
}
listenForPreviewMode() {
- this.$navLinks.on('click', 'a', (e) => {
+ this.$navLinks.on('click', 'a', e => {
const urlPieces = e.target.href.split('#');
const hash = urlPieces[1];
if (hash === 'preview') {
@@ -105,7 +105,7 @@ export default class FileTemplateMediator {
e.preventDefault();
}
- this.templateSelectors.forEach((selector) => {
+ this.templateSelectors.forEach(selector => {
if (selector.config.key === item.key) {
selector.show();
} else {
@@ -126,8 +126,8 @@ export default class FileTemplateMediator {
selector.renderLoading();
// in case undo menu is already already there
this.destroyUndoMenu();
- this.fetchFileTemplate(selector.config.endpoint, query, data)
- .then((file) => {
+ this.fetchFileTemplate(selector.config.type, query, data)
+ .then(file => {
this.showUndoMenu();
this.setEditorContent(file);
this.setFilename(selector.config.name);
@@ -138,7 +138,7 @@ export default class FileTemplateMediator {
displayMatchedTemplateSelector() {
const currentInput = this.getFilename();
- this.templateSelectors.forEach((selector) => {
+ this.templateSelectors.forEach(selector => {
const match = selector.config.pattern.test(currentInput);
if (match) {
@@ -149,15 +149,11 @@ export default class FileTemplateMediator {
});
}
- fetchFileTemplate(apiCall, query, data) {
- return new Promise((resolve) => {
+ fetchFileTemplate(type, query, data = {}) {
+ return new Promise(resolve => {
const resolveFile = file => resolve(file);
- if (!data) {
- apiCall(query, resolveFile);
- } else {
- apiCall(query, data, resolveFile);
- }
+ Api.projectTemplate(this.projectId, type, query, data, resolveFile);
});
}
diff --git a/app/assets/javascripts/blob/template_selector.js b/app/assets/javascripts/blob/template_selector.js
index 9dfdb06007d..9db1fa70ffb 100644
--- a/app/assets/javascripts/blob/template_selector.js
+++ b/app/assets/javascripts/blob/template_selector.js
@@ -66,9 +66,6 @@ export default class TemplateSelector {
// be added by all subclasses.
}
- // To be implemented on the extending class
- // e.g. Api.gitlabCiYml(query.name, file => this.setEditorContent(file));
-
setEditorContent(file, { skipFocus } = {}) {
if (!file) return;
diff --git a/app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js b/app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js
index 9c41e429c8d..43f7aead8b9 100644
--- a/app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js
+++ b/app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js
@@ -1,5 +1,3 @@
-import Api from '../../api';
-
import FileTemplateSelector from '../file_template_selector';
export default class BlobCiYamlSelector extends FileTemplateSelector {
@@ -9,7 +7,7 @@ export default class BlobCiYamlSelector extends FileTemplateSelector {
key: 'gitlab-ci-yaml',
name: '.gitlab-ci.yml',
pattern: /(.gitlab-ci.yml)/,
- endpoint: Api.gitlabCiYml,
+ type: 'gitlab_ci_ymls',
dropdown: '.js-gitlab-ci-yml-selector',
wrapper: '.js-gitlab-ci-yml-selector-wrap',
};
diff --git a/app/assets/javascripts/blob/template_selectors/dockerfile_selector.js b/app/assets/javascripts/blob/template_selectors/dockerfile_selector.js
index 45fb614fe00..4718b642617 100644
--- a/app/assets/javascripts/blob/template_selectors/dockerfile_selector.js
+++ b/app/assets/javascripts/blob/template_selectors/dockerfile_selector.js
@@ -1,5 +1,3 @@
-import Api from '../../api';
-
import FileTemplateSelector from '../file_template_selector';
export default class DockerfileSelector extends FileTemplateSelector {
@@ -9,7 +7,7 @@ export default class DockerfileSelector extends FileTemplateSelector {
key: 'dockerfile',
name: 'Dockerfile',
pattern: /(Dockerfile)/,
- endpoint: Api.dockerfileYml,
+ type: 'dockerfiles',
dropdown: '.js-dockerfile-selector',
wrapper: '.js-dockerfile-selector-wrap',
};
diff --git a/app/assets/javascripts/blob/template_selectors/gitignore_selector.js b/app/assets/javascripts/blob/template_selectors/gitignore_selector.js
index a894953cc86..a8067ec5c84 100644
--- a/app/assets/javascripts/blob/template_selectors/gitignore_selector.js
+++ b/app/assets/javascripts/blob/template_selectors/gitignore_selector.js
@@ -1,5 +1,3 @@
-import Api from '../../api';
-
import FileTemplateSelector from '../file_template_selector';
export default class BlobGitignoreSelector extends FileTemplateSelector {
@@ -9,7 +7,7 @@ export default class BlobGitignoreSelector extends FileTemplateSelector {
key: 'gitignore',
name: '.gitignore',
pattern: /(.gitignore)/,
- endpoint: Api.gitignoreText,
+ type: 'gitignores',
dropdown: '.js-gitignore-selector',
wrapper: '.js-gitignore-selector-wrap',
};
diff --git a/app/assets/javascripts/blob/template_selectors/license_selector.js b/app/assets/javascripts/blob/template_selectors/license_selector.js
index b7c4da0f62e..ac1fe95eee5 100644
--- a/app/assets/javascripts/blob/template_selectors/license_selector.js
+++ b/app/assets/javascripts/blob/template_selectors/license_selector.js
@@ -1,5 +1,3 @@
-import Api from '../../api';
-
import FileTemplateSelector from '../file_template_selector';
export default class BlobLicenseSelector extends FileTemplateSelector {
@@ -9,7 +7,7 @@ export default class BlobLicenseSelector extends FileTemplateSelector {
key: 'license',
name: 'LICENSE',
pattern: /^(.+\/)?(licen[sc]e|copying)($|\.)/i,
- endpoint: Api.licenseText,
+ type: 'licenses',
dropdown: '.js-license-selector',
wrapper: '.js-license-selector-wrap',
};
diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js
index a603d89b84a..4e4598870fa 100644
--- a/app/assets/javascripts/blob_edit/blob_bundle.js
+++ b/app/assets/javascripts/blob_edit/blob_bundle.js
@@ -15,8 +15,9 @@ export default () => {
const assetsPath = editBlobForm.data('assetsPrefix');
const blobLanguage = editBlobForm.data('blobLanguage');
const currentAction = $('.js-file-title').data('currentAction');
+ const projectId = editBlobForm.data('project-id');
- new EditBlob(`${urlRoot}${assetsPath}`, blobLanguage, currentAction);
+ new EditBlob(`${urlRoot}${assetsPath}`, blobLanguage, currentAction, projectId);
new NewCommitForm(editBlobForm);
}
diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js
index 82a3d494b67..ec2b130ab7d 100644
--- a/app/assets/javascripts/blob_edit/edit_blob.js
+++ b/app/assets/javascripts/blob_edit/edit_blob.js
@@ -7,11 +7,11 @@ import { __ } from '~/locale';
import TemplateSelectorMediator from '../blob/file_template_mediator';
export default class EditBlob {
- constructor(assetsPath, aceMode, currentAction) {
+ constructor(assetsPath, aceMode, currentAction, projectId) {
this.configureAceEditor(aceMode, assetsPath);
this.initModePanesAndLinks();
this.initSoftWrap();
- this.initFileSelectors(currentAction);
+ this.initFileSelectors(currentAction, projectId);
}
configureAceEditor(aceMode, assetsPath) {
@@ -30,10 +30,11 @@ export default class EditBlob {
}
}
- initFileSelectors(currentAction) {
+ initFileSelectors(currentAction, projectId) {
this.fileTemplateMediator = new TemplateSelectorMediator({
currentAction,
editor: this.editor,
+ projectId,
});
}
@@ -60,14 +61,15 @@ export default class EditBlob {
if (paneId === '#preview') {
this.$toggleButton.hide();
- axios.post(currentLink.data('previewUrl'), {
- content: this.editor.getValue(),
- })
- .then(({ data }) => {
- currentPane.empty().append(data);
- currentPane.renderGFM();
- })
- .catch(() => createFlash(__('An error occurred previewing the blob')));
+ axios
+ .post(currentLink.data('previewUrl'), {
+ content: this.editor.getValue(),
+ })
+ .then(({ data }) => {
+ currentPane.empty().append(data);
+ currentPane.renderGFM();
+ })
+ .catch(() => createFlash(__('An error occurred previewing the blob')));
}
this.$toggleButton.show();
diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js
index 9ad451fa375..1fc7a29f785 100644
--- a/app/assets/javascripts/boards/components/board.js
+++ b/app/assets/javascripts/boards/components/board.js
@@ -1,5 +1,3 @@
-/* eslint-disable comma-dangle */
-
import Sortable from 'sortablejs';
import Vue from 'vue';
import { n__ } from '~/locale';
diff --git a/app/assets/javascripts/boards/components/board_delete.js b/app/assets/javascripts/boards/components/board_delete.js
index c5945e8098d..240d0911a31 100644
--- a/app/assets/javascripts/boards/components/board_delete.js
+++ b/app/assets/javascripts/boards/components/board_delete.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, no-alert */
+/* eslint-disable no-alert */
import $ from 'jquery';
import Vue from 'vue';
diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js
index 109e60cbde2..df7efd3fa5c 100644
--- a/app/assets/javascripts/boards/components/board_sidebar.js
+++ b/app/assets/javascripts/boards/components/board_sidebar.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, no-new */
+/* eslint-disable no-new */
import $ from 'jquery';
import Vue from 'vue';
diff --git a/app/assets/javascripts/boards/models/issue.js b/app/assets/javascripts/boards/models/issue.js
index c7cfb72067c..609659bdf93 100644
--- a/app/assets/javascripts/boards/models/issue.js
+++ b/app/assets/javascripts/boards/models/issue.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-unused-vars, comma-dangle */
+/* eslint-disable no-unused-vars */
/* global ListLabel */
/* global ListMilestone */
/* global ListAssignee */
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index d416b76f0f4..58e423fbd44 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-underscore-dangle, class-methods-use-this, consistent-return, no-shadow, no-param-reassign, max-len */
+/* eslint-disable no-underscore-dangle, class-methods-use-this, consistent-return, no-shadow, no-param-reassign */
/* global ListIssue */
import { __ } from '~/locale';
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index 957114cf420..bd181807e1f 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, no-shadow */
+/* eslint-disable no-shadow */
/* global List */
import $ from 'jquery';
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js
index ebf76af5966..65e7cee7039 100644
--- a/app/assets/javascripts/clusters/clusters_bundle.js
+++ b/app/assets/javascripts/clusters/clusters_bundle.js
@@ -1,6 +1,6 @@
import Visibility from 'visibilityjs';
import Vue from 'vue';
-import initDismissableCallout from '~/dismissable_callout';
+import PersistentUserCallout from '../persistent_user_callout';
import { s__, sprintf } from '../locale';
import Flash from '../flash';
import Poll from '../lib/utils/poll';
@@ -62,7 +62,7 @@ export default class Clusters {
this.showTokenButton = document.querySelector('.js-show-cluster-token');
this.tokenField = document.querySelector('.js-cluster-token');
- initDismissableCallout('.js-cluster-security-warning');
+ Clusters.initDismissableCallout();
initSettingsPanels();
setupToggleButtons(document.querySelector('.js-cluster-enable-toggle-area'));
this.initApplications();
@@ -105,6 +105,12 @@ export default class Clusters {
});
}
+ static initDismissableCallout() {
+ const callout = document.querySelector('.js-cluster-security-warning');
+
+ if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
+ }
+
addListeners() {
if (this.showTokenButton) this.showTokenButton.addEventListener('click', this.showToken);
eventHub.$on('installApplication', this.installApplication);
diff --git a/app/assets/javascripts/clusters/clusters_index.js b/app/assets/javascripts/clusters/clusters_index.js
index 789c8360124..e32d507d1f7 100644
--- a/app/assets/javascripts/clusters/clusters_index.js
+++ b/app/assets/javascripts/clusters/clusters_index.js
@@ -1,14 +1,15 @@
import createFlash from '~/flash';
import { __ } from '~/locale';
import setupToggleButtons from '~/toggle_buttons';
-import initDismissableCallout from '~/dismissable_callout';
+import PersistentUserCallout from '../persistent_user_callout';
import ClustersService from './services/clusters_service';
export default () => {
const clusterList = document.querySelector('.js-clusters-list');
- initDismissableCallout('.gcp-signup-offer');
+ const callout = document.querySelector('.gcp-signup-offer');
+ if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
// The empty state won't have a clusterList
if (clusterList) {
diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js
index 410580b4c25..30d9b656fec 100644
--- a/app/assets/javascripts/commit/image_file.js
+++ b/app/assets/javascripts/commit/image_file.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, wrap-iife, no-var, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, quotes, one-var, one-var-declaration-per-line, no-unused-vars, no-return-assign, comma-dangle, quote-props, no-unused-expressions, no-sequences, max-len */
+/* eslint-disable func-names, no-var, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, one-var, no-unused-vars, no-return-assign, no-unused-expressions, no-sequences */
import $ from 'jquery';
diff --git a/app/assets/javascripts/compare_autocomplete.js b/app/assets/javascripts/compare_autocomplete.js
index a252036d657..852d71f4e84 100644
--- a/app/assets/javascripts/compare_autocomplete.js
+++ b/app/assets/javascripts/compare_autocomplete.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, one-var, no-var, one-var-declaration-per-line, object-shorthand, no-else-return, max-len */
+/* eslint-disable func-names, one-var, no-var, object-shorthand, no-else-return */
import $ from 'jquery';
import { __ } from './locale';
diff --git a/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js b/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js
index ed24d1775f4..87621761500 100644
--- a/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js
+++ b/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, quotes, no-lonely-if, max-len */
+/* eslint-disable object-shorthand, func-names, no-else-return, no-lonely-if */
/* global CommentsStore */
import $ from 'jquery';
diff --git a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js
index 2b893e35b6d..2b78bb58735 100644
--- a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js
+++ b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, guard-for-in, no-restricted-syntax, no-lonely-if, no-continue, brace-style, max-len, quotes */
+/* eslint-disable object-shorthand, func-names, no-else-return, guard-for-in, no-restricted-syntax, no-lonely-if, no-continue */
/* global CommentsStore */
import $ from 'jquery';
diff --git a/app/assets/javascripts/diff_notes/components/resolve_count.js b/app/assets/javascripts/diff_notes/components/resolve_count.js
index e2683e09f40..eb539c6b348 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_count.js
+++ b/app/assets/javascripts/diff_notes/components/resolve_count.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, object-shorthand, func-names */
+/* eslint-disable object-shorthand, func-names */
/* global CommentsStore */
import Vue from 'vue';
diff --git a/app/assets/javascripts/diff_notes/mixins/discussion.js b/app/assets/javascripts/diff_notes/mixins/discussion.js
index ef35b589e58..7589f9dd6e0 100644
--- a/app/assets/javascripts/diff_notes/mixins/discussion.js
+++ b/app/assets/javascripts/diff_notes/mixins/discussion.js
@@ -1,4 +1,4 @@
-/* eslint-disable object-shorthand, func-names, guard-for-in, no-restricted-syntax, comma-dangle, */
+/* eslint-disable object-shorthand, func-names, guard-for-in, no-restricted-syntax, */
const DiscussionMixins = {
computed: {
diff --git a/app/assets/javascripts/diff_notes/stores/comments.js b/app/assets/javascripts/diff_notes/stores/comments.js
index d7da7d974f3..d012cd02d10 100644
--- a/app/assets/javascripts/diff_notes/stores/comments.js
+++ b/app/assets/javascripts/diff_notes/stores/comments.js
@@ -1,4 +1,4 @@
-/* eslint-disable object-shorthand, func-names, camelcase, no-restricted-syntax, guard-for-in, comma-dangle, max-len */
+/* eslint-disable object-shorthand, func-names, camelcase, no-restricted-syntax, guard-for-in */
/* global DiscussionModel */
import Vue from 'vue';
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index e60c53338fe..edca45f22f9 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -127,7 +127,6 @@ export default {
'startRenderDiffsQueue',
'assignDiscussionsToDiff',
]),
-
fetchData() {
this.fetchDiffFiles()
.then(() => {
diff --git a/app/assets/javascripts/diffs/components/commit_item.vue b/app/assets/javascripts/diffs/components/commit_item.vue
index 5758588e82e..993206b2e73 100644
--- a/app/assets/javascripts/diffs/components/commit_item.vue
+++ b/app/assets/javascripts/diffs/components/commit_item.vue
@@ -5,6 +5,7 @@ import Icon from '~/vue_shared/components/icon.vue';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import CIIcon from '~/vue_shared/components/ci_icon.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import CommitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
/**
* CommitItem
@@ -29,6 +30,7 @@ export default {
ClipboardButton,
CIIcon,
TimeAgoTooltip,
+ CommitPipelineStatus,
},
props: {
commit: {
@@ -102,6 +104,14 @@ export default {
></pre>
</div>
<div class="commit-actions flex-row d-none d-sm-flex">
+ <div
+ v-if="commit.signatureHtml"
+ v-html="commit.signatureHtml"
+ ></div>
+ <commit-pipeline-status
+ v-if="commit.pipelineStatusPath"
+ :endpoint="commit.pipelineStatusPath"
+ />
<div class="commit-sha-group">
<div
class="label label-monospace"
diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue
index 02d5be1821b..fb5556e3cd7 100644
--- a/app/assets/javascripts/diffs/components/diff_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_content.vue
@@ -28,7 +28,7 @@ export default {
return diffModes[diffModeKey] || diffModes.replaced;
},
isTextFile() {
- return this.diffFile.text;
+ return this.diffFile.viewer.name === 'text';
},
},
};
diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js
index 4ae588042e4..a482a2b82c0 100644
--- a/app/assets/javascripts/diffs/store/utils.js
+++ b/app/assets/javascripts/diffs/store/utils.js
@@ -25,7 +25,7 @@ export const getReversePosition = linePosition => {
return LINE_POSITION_RIGHT;
};
-export function getNoteFormData(params) {
+export function getFormData(params) {
const {
note,
noteableType,
@@ -70,9 +70,15 @@ export function getNoteFormData(params) {
},
};
+ return postData;
+}
+
+export function getNoteFormData(params) {
+ const data = getFormData(params);
+
return {
- endpoint: noteableData.create_note_path,
- data: postData,
+ endpoint: params.noteableData.create_note_path,
+ data,
};
}
@@ -244,6 +250,7 @@ export function getDiffPositionByLineCode(diffFiles) {
oldLine,
newLine,
lineCode,
+ positionType: 'text',
};
}
});
@@ -259,8 +266,8 @@ export function isDiscussionApplicableToLine({ discussion, diffPosition, latestD
const { lineCode, ...diffPositionCopy } = diffPosition;
if (discussion.original_position && discussion.position) {
- const originalRefs = convertObjectPropsToCamelCase(discussion.original_position.formatter);
- const refs = convertObjectPropsToCamelCase(discussion.position.formatter);
+ const originalRefs = convertObjectPropsToCamelCase(discussion.original_position);
+ const refs = convertObjectPropsToCamelCase(discussion.position);
return _.isEqual(refs, diffPositionCopy) || _.isEqual(originalRefs, diffPositionCopy);
}
diff --git a/app/assets/javascripts/dismissable_callout.js b/app/assets/javascripts/dismissable_callout.js
deleted file mode 100644
index 5185b019376..00000000000
--- a/app/assets/javascripts/dismissable_callout.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import $ from 'jquery';
-import axios from '~/lib/utils/axios_utils';
-import { __ } from '~/locale';
-import Flash from '~/flash';
-
-export default function initDismissableCallout(alertSelector) {
- const alertEl = document.querySelector(alertSelector);
- if (!alertEl) {
- return;
- }
-
- const closeButtonEl = alertEl.getElementsByClassName('close')[0];
- const { dismissEndpoint, featureId } = closeButtonEl.dataset;
-
- closeButtonEl.addEventListener('click', () => {
- axios
- .post(dismissEndpoint, {
- feature_name: featureId,
- })
- .then(() => {
- $(alertEl).alert('close');
- })
- .catch(() => {
- Flash(__('An error occurred while dismissing the alert. Refresh the page and try again.'));
- });
- });
-}
diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue
index ff50e75dbda..c16931521a7 100644
--- a/app/assets/javascripts/environments/components/environment_item.vue
+++ b/app/assets/javascripts/environments/components/environment_item.vue
@@ -453,7 +453,9 @@ export default {
class="gl-responsive-table-row"
role="row">
<div
- class="table-section section-wrap section-15"
+ v-tooltip
+ :title="model.name"
+ class="table-section section-wrap section-15 text-truncate"
role="gridcell"
>
<div
@@ -467,9 +469,8 @@ export default {
v-if="!model.isFolder"
class="environment-name table-mobile-content">
<a
- v-tooltip
+ class="qa-environment-link"
:href="environmentPath"
- :title="model.name"
>
{{ model.name }}
</a>
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 73b2cd0b2c7..95636a9ccdd 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -15,6 +15,7 @@ export const defaultAutocompleteConfig = {
epics: true,
milestones: true,
labels: true,
+ snippets: true,
};
class GfmAutoComplete {
@@ -50,6 +51,7 @@ class GfmAutoComplete {
if (this.enableMap.milestones) this.setupMilestones($input);
if (this.enableMap.mergeRequests) this.setupMergeRequests($input);
if (this.enableMap.labels) this.setupLabels($input);
+ if (this.enableMap.snippets) this.setupSnippets($input);
// We don't instantiate the quick actions autocomplete for note and issue/MR edit forms
$input.filter('[data-supports-quick-actions="true"]').atwho({
@@ -360,6 +362,39 @@ class GfmAutoComplete {
});
}
+ setupSnippets($input) {
+ $input.atwho({
+ at: '$',
+ alias: 'snippets',
+ searchKey: 'search',
+ displayTpl(value) {
+ let tmpl = GfmAutoComplete.Loading.template;
+ if (value.title != null) {
+ tmpl = GfmAutoComplete.Issues.template;
+ }
+ return tmpl;
+ },
+ data: GfmAutoComplete.defaultLoadingData,
+ // eslint-disable-next-line no-template-curly-in-string
+ insertTpl: '${atwho-at}${id}',
+ callbacks: {
+ ...this.getDefaultCallbacks(),
+ beforeSave(snippets) {
+ return $.map(snippets, (m) => {
+ if (m.title == null) {
+ return m;
+ }
+ return {
+ id: m.id,
+ title: sanitize(m.title),
+ search: `${m.id} ${m.title}`,
+ };
+ });
+ },
+ },
+ });
+ }
+
getDefaultCallbacks() {
const fetchData = this.fetchData.bind(this);
@@ -470,7 +505,7 @@ class GfmAutoComplete {
// The below is taken from At.js source
// Tweaked to commands to start without a space only if char before is a non-word character
// https://github.com/ichord/At.js
- const atSymbolsWithBar = Object.keys(controllers).join('|');
+ const atSymbolsWithBar = Object.keys(controllers).join('|').replace(/[$]/, '\\$&');
const atSymbolsWithoutBar = Object.keys(controllers).join('');
const targetSubtext = subtext.split(GfmAutoComplete.regexSubtext).pop();
const resultantFlag = flag.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
@@ -497,6 +532,7 @@ GfmAutoComplete.atTypeMap = {
'~': 'labels',
'%': 'milestones',
'/': 'commands',
+ $: 'snippets',
};
// Emoji
@@ -519,7 +555,7 @@ GfmAutoComplete.Labels = {
// eslint-disable-next-line no-template-curly-in-string
template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>',
};
-// Issues and MergeRequests
+// Issues, MergeRequests and Snippets
GfmAutoComplete.Issues = {
// eslint-disable-next-line no-template-curly-in-string
template: '<li><small>${id}</small> ${title}</li>',
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index c3959ef3e9e..a8ac2f510a4 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-underscore-dangle, no-var, one-var, one-var-declaration-per-line, max-len, vars-on-top, wrap-iife, no-unused-vars, no-shadow, no-cond-assign, prefer-arrow-callback, no-return-assign, no-else-return, camelcase, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, prefer-template, no-param-reassign, no-loop-func */
+/* eslint-disable func-names, no-underscore-dangle, no-var, one-var, vars-on-top, no-unused-vars, no-shadow, no-cond-assign, prefer-arrow-callback, no-return-assign, no-else-return, camelcase, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, prefer-template, no-param-reassign, no-loop-func */
/* global fuzzaldrinPlus */
import $ from 'jquery';
diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js
index 4ae3a714bee..175d0b8498b 100644
--- a/app/assets/javascripts/header.js
+++ b/app/assets/javascripts/header.js
@@ -1,5 +1,9 @@
import $ from 'jquery';
+import Vue from 'vue';
+import Translate from '~/vue_shared/translate';
import { highCountTrim } from '~/lib/utils/text_utility';
+import SetStatusModalTrigger from './set_status_modal/set_status_modal_trigger.vue';
+import SetStatusModalWrapper from './set_status_modal/set_status_modal_wrapper.vue';
/**
* Updates todo counter when todos are toggled.
@@ -17,3 +21,54 @@ export default function initTodoToggle() {
$todoPendingCount.toggleClass('hidden', parsedCount === 0);
});
}
+
+document.addEventListener('DOMContentLoaded', () => {
+ const setStatusModalTriggerEl = document.querySelector('.js-set-status-modal-trigger');
+ const setStatusModalWrapperEl = document.querySelector('.js-set-status-modal-wrapper');
+
+ if (setStatusModalTriggerEl || setStatusModalWrapperEl) {
+ Vue.use(Translate);
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: setStatusModalTriggerEl,
+ data() {
+ const { hasStatus } = this.$options.el.dataset;
+
+ return {
+ hasStatus: hasStatus === 'true',
+ };
+ },
+ render(createElement) {
+ return createElement(SetStatusModalTrigger, {
+ props: {
+ hasStatus: this.hasStatus,
+ },
+ });
+ },
+ });
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: setStatusModalWrapperEl,
+ data() {
+ const { currentEmoji, currentMessage } = this.$options.el.dataset;
+
+ return {
+ currentEmoji,
+ currentMessage,
+ };
+ },
+ render(createElement) {
+ const { currentEmoji, currentMessage } = this;
+
+ return createElement(SetStatusModalWrapper, {
+ props: {
+ currentEmoji,
+ currentMessage,
+ },
+ });
+ },
+ });
+ }
+});
diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js
index 462ca45db9b..d97a950a8b2 100644
--- a/app/assets/javascripts/ide/stores/modules/commit/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js
@@ -30,9 +30,9 @@ export const setLastCommitMessage = ({ rootState, commit }, data) => {
const currentProject = rootState.projects[rootState.currentProjectId];
const commitStats = data.stats
? sprintf(__('with %{additions} additions, %{deletions} deletions.'), {
- additions: data.stats.additions, // eslint-disable-line indent-legacy
- deletions: data.stats.deletions, // eslint-disable-line indent-legacy
- }) // eslint-disable-line indent-legacy
+ additions: data.stats.additions,
+ deletions: data.stats.deletions,
+ })
: '';
const commitMsg = sprintf(
__('Your changes have been committed. Commit %{commitId} %{commitStats}'),
diff --git a/app/assets/javascripts/ide/stores/modules/file_templates/actions.js b/app/assets/javascripts/ide/stores/modules/file_templates/actions.js
index cc9f6c8638c..b7090e09daf 100644
--- a/app/assets/javascripts/ide/stores/modules/file_templates/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/file_templates/actions.js
@@ -23,12 +23,12 @@ export const receiveTemplateTypesError = ({ commit, dispatch }) => {
export const receiveTemplateTypesSuccess = ({ commit }, templates) =>
commit(types.RECEIVE_TEMPLATE_TYPES_SUCCESS, templates);
-export const fetchTemplateTypes = ({ dispatch, state }, page = 1) => {
+export const fetchTemplateTypes = ({ dispatch, state, rootState }, page = 1) => {
if (!Object.keys(state.selectedTemplateType).length) return Promise.reject();
dispatch('requestTemplateTypes');
- return Api.templates(state.selectedTemplateType.key, { page })
+ return Api.projectTemplates(rootState.currentProjectId, state.selectedTemplateType.key, { page })
.then(({ data, headers }) => {
const nextPage = parseInt(normalizeHeaders(headers)['X-NEXT-PAGE'], 10);
@@ -74,12 +74,16 @@ export const receiveTemplateError = ({ dispatch }, template) => {
);
};
-export const fetchTemplate = ({ dispatch, state }, template) => {
+export const fetchTemplate = ({ dispatch, state, rootState }, template) => {
if (template.content) {
return dispatch('setFileTemplate', template);
}
- return Api.templates(`${state.selectedTemplateType.key}/${template.key || template.name}`)
+ return Api.projectTemplate(
+ rootState.currentProjectId,
+ state.selectedTemplateType.key,
+ template.key || template.name,
+ )
.then(({ data }) => {
dispatch('setFileTemplate', data);
})
diff --git a/app/assets/javascripts/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable_bulk_update_actions.js
index 9e848699163..9848bcc2e64 100644
--- a/app/assets/javascripts/issuable_bulk_update_actions.js
+++ b/app/assets/javascripts/issuable_bulk_update_actions.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, quotes, consistent-return, func-names, array-callback-return, prefer-arrow-callback, max-len, no-unused-vars */
+/* eslint-disable consistent-return, func-names, array-callback-return, prefer-arrow-callback, no-unused-vars */
import $ from 'jquery';
import _ from 'underscore';
diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js
index 8c225cd7d91..4b4e9aa48ab 100644
--- a/app/assets/javascripts/issue.js
+++ b/app/assets/javascripts/issue.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-var, one-var, one-var-declaration-per-line, no-unused-vars, consistent-return, quotes, max-len */
+/* eslint-disable no-var, one-var, no-unused-vars, consistent-return */
import $ from 'jquery';
import axios from './lib/utils/axios_utils';
diff --git a/app/assets/javascripts/issue_show/components/edit_actions.vue b/app/assets/javascripts/issue_show/components/edit_actions.vue
index bcf8686afcc..c3d39082714 100644
--- a/app/assets/javascripts/issue_show/components/edit_actions.vue
+++ b/app/assets/javascripts/issue_show/components/edit_actions.vue
@@ -66,7 +66,7 @@
<button
:class="{ disabled: formState.updateLoading || !isSubmitEnabled }"
:disabled="formState.updateLoading || !isSubmitEnabled"
- class="btn btn-success float-left"
+ class="btn btn-success float-left qa-save-button"
type="submit"
@click.prevent="updateIssuable">
Save changes
@@ -86,7 +86,7 @@
v-if="shouldShowDeleteButton"
:class="{ disabled: deleteLoading }"
:disabled="deleteLoading"
- class="btn btn-danger float-right append-right-default"
+ class="btn btn-danger float-right append-right-default qa-delete-button"
type="button"
@click="deleteIssuable">
Delete
diff --git a/app/assets/javascripts/issue_show/components/fields/description.vue b/app/assets/javascripts/issue_show/components/fields/description.vue
index 97acc5ba385..1a78c59d715 100644
--- a/app/assets/javascripts/issue_show/components/fields/description.vue
+++ b/app/assets/javascripts/issue_show/components/fields/description.vue
@@ -61,7 +61,8 @@
ref="textarea"
slot="textarea"
v-model="formState.description"
- class="note-textarea js-gfm-input js-autosize markdown-area"
+ class="note-textarea js-gfm-input js-autosize markdown-area
+ qa-description-textarea"
data-supports-quick-actions="false"
aria-label="Description"
placeholder="Write a comment or drag your files here…"
diff --git a/app/assets/javascripts/issue_show/components/fields/title.vue b/app/assets/javascripts/issue_show/components/fields/title.vue
index 7d1526a64b4..b7f2b1a6050 100644
--- a/app/assets/javascripts/issue_show/components/fields/title.vue
+++ b/app/assets/javascripts/issue_show/components/fields/title.vue
@@ -22,7 +22,7 @@
<input
id="issuable-title"
v-model="formState.title"
- class="form-control"
+ class="form-control qa-title-input"
type="text"
placeholder="Title"
aria-label="Title"
diff --git a/app/assets/javascripts/issue_show/components/title.vue b/app/assets/javascripts/issue_show/components/title.vue
index cf99e9a9cd8..ed26e53ac0e 100644
--- a/app/assets/javascripts/issue_show/components/title.vue
+++ b/app/assets/javascripts/issue_show/components/title.vue
@@ -79,7 +79,8 @@ export default {
v-if="showInlineEditButton && canUpdate"
v-tooltip
type="button"
- class="btn btn-default btn-edit btn-svg js-issuable-edit"
+ class="btn btn-default btn-edit btn-svg js-issuable-edit
+ qa-edit-button"
title="Edit title and description"
data-placement="bottom"
data-container="body"
diff --git a/app/assets/javascripts/jobs/components/commit_block.vue b/app/assets/javascripts/jobs/components/commit_block.vue
index 39a4ff159e2..4b1788a1c16 100644
--- a/app/assets/javascripts/jobs/components/commit_block.vue
+++ b/app/assets/javascripts/jobs/components/commit_block.vue
@@ -46,7 +46,7 @@
v-if="mergeRequest"
:href="mergeRequest.path"
class="js-link-commit link-commit"
- >{{ mergeRequest.iid }}</a>
+ >!{{ mergeRequest.iid }}</a>
</p>
<p class="build-light-text append-bottom-0">
diff --git a/app/assets/javascripts/jobs/components/empty_state.vue b/app/assets/javascripts/jobs/components/empty_state.vue
index ff45a5b05f8..ff500eddddb 100644
--- a/app/assets/javascripts/jobs/components/empty_state.vue
+++ b/app/assets/javascripts/jobs/components/empty_state.vue
@@ -27,7 +27,7 @@
value === null ||
(Object.prototype.hasOwnProperty.call(value, 'path') &&
Object.prototype.hasOwnProperty.call(value, 'method') &&
- Object.prototype.hasOwnProperty.call(value, 'title'))
+ Object.prototype.hasOwnProperty.call(value, 'button_title'))
);
},
},
@@ -67,7 +67,7 @@
:data-method="action.method"
class="js-job-empty-state-action btn btn-primary"
>
- {{ action.title }}
+ {{ action.button_title }}
</a>
</div>
</div>
diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job_app.vue
index bac8bd71d64..047e55866ce 100644
--- a/app/assets/javascripts/jobs/components/job_app.vue
+++ b/app/assets/javascripts/jobs/components/job_app.vue
@@ -2,6 +2,7 @@
import { mapGetters, mapState } from 'vuex';
import CiHeader from '~/vue_shared/components/header_ci_component.vue';
import Callout from '~/vue_shared/components/callout.vue';
+ import EmptyState from './empty_state.vue';
import EnvironmentsBlock from './environments_block.vue';
import ErasedBlock from './erased_block.vue';
import StuckBlock from './stuck_block.vue';
@@ -11,6 +12,7 @@
components: {
CiHeader,
Callout,
+ EmptyState,
EnvironmentsBlock,
ErasedBlock,
StuckBlock,
@@ -31,6 +33,8 @@
'jobHasStarted',
'hasEnvironment',
'isJobStuck',
+ 'hasTrace',
+ 'emptyStateIllustration',
]),
},
};
@@ -77,12 +81,14 @@
<environments-block
v-if="hasEnvironment"
+ class="js-job-environment"
:deployment-status="job.deployment_status"
:icon-status="job.status"
/>
<erased-block
v-if="job.erased"
+ class="js-job-erased"
:user="job.erased_by"
:erased-at="job.erased_at"
/>
@@ -91,6 +97,15 @@
<!-- EO job log -->
<!--empty state -->
+ <empty-state
+ v-if="!hasTrace"
+ class="js-job-empty-state"
+ :illustration-path="emptyStateIllustration.image"
+ :illustration-size-class="emptyStateIllustration.size"
+ :title="emptyStateIllustration.title"
+ :content="emptyStateIllustration.content"
+ :action="job.status.action"
+ />
<!-- EO empty state -->
<!-- EO Body Section -->
diff --git a/app/assets/javascripts/jobs/store/getters.js b/app/assets/javascripts/jobs/store/getters.js
index 62d154ff584..afe5f88b292 100644
--- a/app/assets/javascripts/jobs/store/getters.js
+++ b/app/assets/javascripts/jobs/store/getters.js
@@ -30,13 +30,24 @@ export const jobHasStarted = state => !(state.job.started === false);
export const hasEnvironment = state => !_.isEmpty(state.job.deployment_status);
/**
+ * Checks if it the job has trace.
+ * Used to check if it should render the job log or the empty state
+ * @returns {Boolean}
+ */
+export const hasTrace = state => state.job.has_trace || state.job.status.group === 'running';
+
+export const emptyStateIllustration = state =>
+ (state.job && state.job.status && state.job.status.illustration) || {};
+
+/**
* When the job is pending and there are no available runners
* we need to render the stuck block;
*
* @returns {Boolean}
*/
export const isJobStuck = state =>
- state.job.status.group === 'pending' && state.job.runners && state.job.runners.available === false;
+ state.job.status.group === 'pending' &&
+ (!_.isEmpty(state.job.runners) && state.job.runners.available === false);
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/label_manager.js b/app/assets/javascripts/label_manager.js
index c10b1a2b233..2c995c5902f 100644
--- a/app/assets/javascripts/label_manager.js
+++ b/app/assets/javascripts/label_manager.js
@@ -1,4 +1,4 @@
-/* eslint-disable class-methods-use-this, no-underscore-dangle, no-param-reassign, no-unused-vars, func-names, max-len */
+/* eslint-disable class-methods-use-this, no-underscore-dangle, no-param-reassign, no-unused-vars, func-names */
import $ from 'jquery';
import Sortable from 'sortablejs';
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index 1c7bca78df3..68ae1ca6842 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-useless-return, func-names, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, dot-notation, no-empty */
+/* eslint-disable no-useless-return, func-names, no-var, no-underscore-dangle, prefer-arrow-callback, one-var, no-unused-vars, prefer-template, no-new, consistent-return, object-shorthand, no-shadow, no-param-reassign, vars-on-top, no-lonely-if, no-else-return, dot-notation, no-empty */
/* global Issuable */
/* global ListLabel */
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index abfcf1eaf3f..833dbefd3dc 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -373,6 +373,7 @@ window.gl.utils = {
/**
* Formats milliseconds as timestamp (e.g. 01:02:03).
+ * This takes durations longer than a day into account (e.g. two days would be 48:00:00).
*
* @param milliseconds
* @returns {string}
diff --git a/app/assets/javascripts/lib/utils/notify.js b/app/assets/javascripts/lib/utils/notify.js
index 305ad3e5e26..f4eb652a41a 100644
--- a/app/assets/javascripts/lib/utils/notify.js
+++ b/app/assets/javascripts/lib/utils/notify.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, consistent-return, prefer-arrow-callback, no-return-assign, object-shorthand, comma-dangle, max-len */
+/* eslint-disable func-names, no-var, consistent-return, prefer-arrow-callback, no-return-assign, object-shorthand */
function notificationGranted(message, opts, onclick) {
var notification;
diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js
index f7429601afa..df20785b178 100644
--- a/app/assets/javascripts/lib/utils/text_markdown.js
+++ b/app/assets/javascripts/lib/utils/text_markdown.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, no-param-reassign, quotes, one-var, one-var-declaration-per-line, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, max-len, consistent-return, no-unused-vars, max-len */
+/* eslint-disable func-names, no-var, no-param-reassign, one-var, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, consistent-return, no-unused-vars */
import $ from 'jquery';
import { insertText } from '~/lib/utils/common_utils';
diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js
index 291655235d5..d58fd63bb33 100644
--- a/app/assets/javascripts/line_highlighter.js
+++ b/app/assets/javascripts/line_highlighter.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, no-underscore-dangle, no-param-reassign, prefer-template, quotes, comma-dangle, consistent-return, one-var, one-var-declaration-per-line, no-else-return, max-len */
+/* eslint-disable func-names, no-var, no-underscore-dangle, no-param-reassign, prefer-template, consistent-return, one-var, no-else-return */
import $ from 'jquery';
diff --git a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js
index 81950515ab4..c1832d034ef 100644
--- a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js
+++ b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, quote-props, no-useless-computed-key, object-shorthand, no-param-reassign, max-len */
+/* eslint-disable no-useless-computed-key, object-shorthand, no-param-reassign */
/* global ace */
import Vue from 'vue';
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js
index 1501296ac4f..2cd70247bc6 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js
+++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, object-shorthand, no-param-reassign, camelcase, no-nested-ternary, no-continue, max-len */
+/* eslint-disable object-shorthand, no-param-reassign, camelcase, no-nested-ternary, no-continue */
import $ from 'jquery';
import Vue from 'vue';
diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js
index 7bf2c56dd5d..9b6d7d1772f 100644
--- a/app/assets/javascripts/merge_request.js
+++ b/app/assets/javascripts/merge_request.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, wrap-iife, quotes, no-underscore-dangle, one-var, one-var-declaration-per-line, consistent-return, comma-dangle, max-len, prefer-arrow-callback */
+/* eslint-disable func-names, no-var, no-underscore-dangle, one-var, consistent-return, prefer-arrow-callback */
import $ from 'jquery';
import { __ } from '~/locale';
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 78f56ab57ff..03f3bb42193 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -18,7 +18,6 @@ import syntaxHighlight from './syntax_highlight';
import Notes from './notes';
import { polyfillSticky } from './lib/utils/sticky';
-/* eslint-disable max-len */
// MergeRequestTabs
//
// Handles persisting and restoring the current tab selection and lazily-loading
@@ -62,7 +61,6 @@ import { polyfillSticky } from './lib/utils/sticky';
// </div>
// </div>
//
-/* eslint-enable max-len */
// Store the `location` object, allowing for easier stubbing in tests
let { location } = window;
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js
index 640a4c8260f..67c2d7909a2 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -1,4 +1,4 @@
-/* eslint-disable max-len, one-var, one-var-declaration-per-line, no-unused-vars, object-shorthand, no-else-return, no-self-compare, consistent-return, no-param-reassign, no-shadow */
+/* eslint-disable one-var, no-unused-vars, object-shorthand, no-else-return, no-self-compare, consistent-return, no-param-reassign, no-shadow */
/* global Issuable */
/* global ListMilestone */
diff --git a/app/assets/javascripts/monitoring/components/graph.vue b/app/assets/javascripts/monitoring/components/graph.vue
index ff44f51b8f8..3cccaf72ed7 100644
--- a/app/assets/javascripts/monitoring/components/graph.vue
+++ b/app/assets/javascripts/monitoring/components/graph.vue
@@ -82,6 +82,7 @@ export default {
showFlag: false,
showFlagContent: false,
timeSeries: [],
+ graphDrawData: {},
realPixelRatio: 1,
seriesUnderMouse: [],
};
@@ -180,12 +181,12 @@ export default {
});
},
renderAxesPaths() {
- this.timeSeries = createTimeSeries(
+ ({ timeSeries: this.timeSeries, graphDrawData: this.graphDrawData } = createTimeSeries(
this.graphData.queries,
this.graphWidth,
this.graphHeight,
this.graphHeightOffset,
- );
+ ));
if (_.findWhere(this.timeSeries, { renderCanary: true })) {
this.timeSeries = this.timeSeries.map(series => ({ ...series, renderCanary: true }));
@@ -288,6 +289,10 @@ export default {
:viewBox="innerViewBox"
class="graph-data"
>
+ <slot
+ name="additionalSvgContent"
+ :graphDrawData="graphDrawData"
+ />
<graph-path
v-for="(path, index) in timeSeries"
:key="index"
diff --git a/app/assets/javascripts/monitoring/utils/multiple_time_series.js b/app/assets/javascripts/monitoring/utils/multiple_time_series.js
index eff0d7325cd..d5971730e31 100644
--- a/app/assets/javascripts/monitoring/utils/multiple_time_series.js
+++ b/app/assets/javascripts/monitoring/utils/multiple_time_series.js
@@ -30,7 +30,7 @@ const defaultColorOrder = ['blue', 'orange', 'red', 'green', 'purple'];
const defaultStyleOrder = ['solid', 'dashed', 'dotted'];
-function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom, yDom, lineStyle) {
+function queryTimeSeries(query, graphDrawData, lineStyle) {
let usedColors = [];
let renderCanary = false;
const timeSeriesParsed = [];
@@ -64,7 +64,7 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
// but we need a regularly-spaced set of time/value pairs
// this gives us a complete range of one minute intervals
// offset the same amount as the original data
- const [minX, maxX] = xDom;
+ const [minX, maxX] = graphDrawData.xDom;
const offset = d3.timeMinute(minX) - Number(minX);
const datesWithoutGaps = d3.timeSecond.every(60)
.range(d3.timeMinute.offset(minX, -1), maxX)
@@ -84,31 +84,6 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
renderCanary = true;
}
- const timeSeriesScaleX = d3.scaleTime().range([0, graphWidth - 70]);
-
- const timeSeriesScaleY = d3.scaleLinear().range([graphHeight - graphHeightOffset, 0]);
-
- timeSeriesScaleX.domain(xDom);
- timeSeriesScaleX.ticks(d3.timeMinute, 60);
- timeSeriesScaleY.domain(yDom);
-
- const defined = d => !Number.isNaN(d.value) && d.value != null;
-
- const lineFunction = d3
- .line()
- .defined(defined)
- .curve(d3.curveLinear) // d3 v4 uses curbe instead of interpolate
- .x(d => timeSeriesScaleX(d.time))
- .y(d => timeSeriesScaleY(d.value));
-
- const areaFunction = d3
- .area()
- .defined(defined)
- .curve(d3.curveLinear)
- .x(d => timeSeriesScaleX(d.time))
- .y0(graphHeight - graphHeightOffset)
- .y1(d => timeSeriesScaleY(d.value));
-
const timeSeriesMetricLabel = timeSeries.metric[Object.keys(timeSeries.metric)[0]];
const seriesCustomizationData =
query.series != null && _.findWhere(query.series[0].when, { value: timeSeriesMetricLabel });
@@ -144,10 +119,10 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
}));
timeSeriesParsed.push({
- linePath: lineFunction(values),
- areaPath: areaFunction(values),
- timeSeriesScaleX,
- timeSeriesScaleY,
+ linePath: graphDrawData.lineFunction(values),
+ areaPath: graphDrawData.areaBelowLine(values),
+ timeSeriesScaleX: graphDrawData.timeSeriesScaleX,
+ timeSeriesScaleY: graphDrawData.timeSeriesScaleY,
values: timeSeries.values,
max: maximumValue,
average: accum / timeSeries.values.length,
@@ -164,7 +139,7 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
return timeSeriesParsed;
}
-export default function createTimeSeries(queries, graphWidth, graphHeight, graphHeightOffset) {
+function xyDomain(queries) {
const allValues = queries.reduce(
(allQueryResults, query) =>
allQueryResults.concat(
@@ -176,10 +151,70 @@ export default function createTimeSeries(queries, graphWidth, graphHeight, graph
const xDom = d3.extent(allValues, d => d.time);
const yDom = [0, d3.max(allValues.map(d => d.value))];
- return queries.reduce((series, query, index) => {
+ return {
+ xDom,
+ yDom,
+ };
+}
+
+export function generateGraphDrawData(queries, graphWidth, graphHeight, graphHeightOffset) {
+ const { xDom, yDom } = xyDomain(queries);
+
+ const timeSeriesScaleX = d3.scaleTime().range([0, graphWidth - 70]);
+ const timeSeriesScaleY = d3.scaleLinear().range([graphHeight - graphHeightOffset, 0]);
+
+ timeSeriesScaleX.domain(xDom);
+ timeSeriesScaleX.ticks(d3.timeMinute, 60);
+ timeSeriesScaleY.domain(yDom);
+
+ const defined = d => !Number.isNaN(d.value) && d.value != null;
+
+ const lineFunction = d3
+ .line()
+ .defined(defined)
+ .curve(d3.curveLinear) // d3 v4 uses curbe instead of interpolate
+ .x(d => timeSeriesScaleX(d.time))
+ .y(d => timeSeriesScaleY(d.value));
+
+ const areaBelowLine = d3
+ .area()
+ .defined(defined)
+ .curve(d3.curveLinear)
+ .x(d => timeSeriesScaleX(d.time))
+ .y0(graphHeight - graphHeightOffset)
+ .y1(d => timeSeriesScaleY(d.value));
+
+ const areaAboveLine = d3
+ .area()
+ .defined(defined)
+ .curve(d3.curveLinear)
+ .x(d => timeSeriesScaleX(d.time))
+ .y0(0)
+ .y1(d => timeSeriesScaleY(d.value));
+
+ return {
+ lineFunction,
+ areaBelowLine,
+ areaAboveLine,
+ xDom,
+ yDom,
+ timeSeriesScaleX,
+ timeSeriesScaleY,
+ };
+}
+
+export default function createTimeSeries(queries, graphWidth, graphHeight, graphHeightOffset) {
+ const graphDrawData = generateGraphDrawData(queries, graphWidth, graphHeight, graphHeightOffset);
+
+ const timeSeries = queries.reduce((series, query, index) => {
const lineStyle = defaultStyleOrder[index % defaultStyleOrder.length];
return series.concat(
- queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom, yDom, lineStyle),
+ queryTimeSeries(query, graphDrawData, lineStyle),
);
}, []);
+
+ return {
+ timeSeries,
+ graphDrawData,
+ };
}
diff --git a/app/assets/javascripts/mr_notes/stores/index.js b/app/assets/javascripts/mr_notes/stores/index.js
index 446eb477efc..c4225c8ec08 100644
--- a/app/assets/javascripts/mr_notes/stores/index.js
+++ b/app/assets/javascripts/mr_notes/stores/index.js
@@ -6,10 +6,13 @@ import mrPageModule from './modules';
Vue.use(Vuex);
-export default new Vuex.Store({
- modules: {
- page: mrPageModule,
- notes: notesModule(),
- diffs: diffsModule(),
- },
-});
+export const createStore = () =>
+ new Vuex.Store({
+ modules: {
+ page: mrPageModule,
+ notes: notesModule(),
+ diffs: diffsModule(),
+ },
+ });
+
+export default createStore();
diff --git a/app/assets/javascripts/namespace_select.js b/app/assets/javascripts/namespace_select.js
index 17370edeb0c..ec4c0910e92 100644
--- a/app/assets/javascripts/namespace_select.js
+++ b/app/assets/javascripts/namespace_select.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, comma-dangle, object-shorthand, no-else-return, prefer-template, quotes, prefer-arrow-callback, max-len */
+/* eslint-disable func-names, object-shorthand, no-else-return, prefer-template, prefer-arrow-callback */
import $ from 'jquery';
import Api from './api';
diff --git a/app/assets/javascripts/network/branch_graph.js b/app/assets/javascripts/network/branch_graph.js
index 94da1be4066..ad7136adb8c 100644
--- a/app/assets/javascripts/network/branch_graph.js
+++ b/app/assets/javascripts/network/branch_graph.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, wrap-iife, quotes, comma-dangle, one-var, one-var-declaration-per-line, no-loop-func, no-floating-decimal, consistent-return, no-unused-vars, prefer-template, prefer-arrow-callback, camelcase, max-len */
+/* eslint-disable func-names, no-var, one-var, no-loop-func, consistent-return, no-unused-vars, prefer-template, prefer-arrow-callback, camelcase */
import $ from 'jquery';
import { __ } from '../locale';
diff --git a/app/assets/javascripts/new_branch_form.js b/app/assets/javascripts/new_branch_form.js
index 205d9766656..8f6ea9e61c1 100644
--- a/app/assets/javascripts/new_branch_form.js
+++ b/app/assets/javascripts/new_branch_form.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, one-var, max-len, wrap-iife, consistent-return, comma-dangle, one-var-declaration-per-line, quotes, no-return-assign, prefer-arrow-callback, prefer-template, no-shadow, no-else-return, max-len */
+/* eslint-disable func-names, no-var, one-var, consistent-return, no-return-assign, prefer-arrow-callback, prefer-template, no-shadow, no-else-return */
import $ from 'jquery';
import RefSelectDropdown from './ref_select_dropdown';
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index f301f093ef4..1369b5820d5 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -1,5 +1,5 @@
-/* eslint-disable no-restricted-properties, func-names, no-var, wrap-iife, camelcase,
-no-unused-expressions, max-len, one-var, one-var-declaration-per-line, default-case,
+/* eslint-disable no-restricted-properties, func-names, no-var, camelcase,
+no-unused-expressions, one-var, default-case,
prefer-template, consistent-return, no-alert, no-return-assign,
no-param-reassign, prefer-arrow-callback, no-else-return, vars-on-top,
no-unused-vars, no-shadow, no-useless-escape, class-methods-use-this */
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
index 7735133c470..b980e43b898 100644
--- a/app/assets/javascripts/notes/components/comment_form.vue
+++ b/app/assets/javascripts/notes/components/comment_form.vue
@@ -7,7 +7,11 @@ import { __, sprintf } from '~/locale';
import Flash from '../../flash';
import Autosave from '../../autosave';
import TaskList from '../../task_list';
-import { capitalizeFirstCharacter, convertToCamelCase, splitCamelCase } from '../../lib/utils/text_utility';
+import {
+ capitalizeFirstCharacter,
+ convertToCamelCase,
+ splitCamelCase,
+} from '../../lib/utils/text_utility';
import * as constants from '../constants';
import eventHub from '../event_hub';
import issueWarning from '../../vue_shared/components/issue/issue_warning.vue';
@@ -122,7 +126,9 @@ export default {
return this.getNoteableData.create_note_path;
},
issuableTypeTitle() {
- return this.noteableType === constants.MERGE_REQUEST_NOTEABLE_TYPE ? 'merge request' : 'issue';
+ return this.noteableType === constants.MERGE_REQUEST_NOTEABLE_TYPE
+ ? 'merge request'
+ : 'issue';
},
},
watch: {
@@ -359,7 +365,7 @@ Please check your network connection and try again.`;
:disabled="isSubmitting"
name="note[note]"
class="note-textarea js-vue-comment-form js-note-text
-js-gfm-input js-autosize markdown-area js-vue-textarea"
+js-gfm-input js-autosize markdown-area js-vue-textarea qa-comment-input"
data-supports-quick-actions="true"
aria-label="Description"
placeholder="Write a comment or drag your files here…"
@@ -374,7 +380,8 @@ js-gfm-input js-autosize markdown-area js-vue-textarea"
append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown">
<button
:disabled="isSubmitButtonDisabled"
- class="btn btn-success comment-btn js-comment-button js-comment-submit-button"
+ class="btn btn-create comment-btn js-comment-button js-comment-submit-button
+ qa-comment-button"
type="submit"
@click.prevent="handleSave()">
{{ __(commentButtonTitle) }}
diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue
index beb53da0e6d..e075f94b82b 100644
--- a/app/assets/javascripts/notes/components/note_actions.vue
+++ b/app/assets/javascripts/notes/components/note_actions.vue
@@ -7,10 +7,14 @@ import editSvg from 'icons/_icon_pencil.svg';
import resolveDiscussionSvg from 'icons/_icon_resolve_discussion.svg';
import resolvedDiscussionSvg from 'icons/_icon_status_success_solid.svg';
import ellipsisSvg from 'icons/_ellipsis_v.svg';
+import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
export default {
name: 'NoteActions',
+ components: {
+ Icon,
+ },
directives: {
tooltip,
},
@@ -20,7 +24,7 @@ export default {
required: true,
},
noteId: {
- type: String,
+ type: [String, Number],
required: true,
},
noteUrl: {
@@ -35,7 +39,8 @@ export default {
},
reportAbusePath: {
type: String,
- required: true,
+ required: false,
+ default: null,
},
canEdit: {
type: Boolean,
@@ -84,6 +89,9 @@ export default {
shouldShowActionsDropdown() {
return this.currentUserId && (this.canEdit || this.canReportAsAbuse);
},
+ showDeleteAction() {
+ return this.canDelete && !this.canReportAsAbuse && !this.noteUrl;
+ },
isAuthoredByCurrentUser() {
return this.authorId === this.currentUserId;
},
@@ -201,7 +209,26 @@ export default {
</button>
</div>
<div
- v-if="shouldShowActionsDropdown"
+ v-if="showDeleteAction"
+ class="note-actions-item"
+ >
+ <button
+ v-tooltip
+ type="button"
+ title="Delete comment"
+ class="note-action-button js-note-delete btn btn-transparent"
+ data-container="body"
+ data-placement="bottom"
+ @click="onDelete"
+ >
+ <icon
+ name="remove"
+ class="link-highlight"
+ />
+ </button>
+ </div>
+ <div
+ v-else-if="shouldShowActionsDropdown"
class="dropdown more-actions note-actions-item">
<button
v-tooltip
diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue
index 6f4a0709825..cf4c35de42c 100644
--- a/app/assets/javascripts/notes/components/note_body.vue
+++ b/app/assets/javascripts/notes/components/note_body.vue
@@ -109,7 +109,7 @@ export default {
class="note_edited_ago"
/>
<note-awards-list
- v-if="note.award_emoji.length"
+ v-if="note.award_emoji && note.award_emoji.length"
:note-id="note.id"
:note-author-id="note.author.id"
:awards="note.award_emoji"
diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue
index 2d47d55f33c..33998394a69 100644
--- a/app/assets/javascripts/notes/components/note_form.vue
+++ b/app/assets/javascripts/notes/components/note_form.vue
@@ -20,7 +20,7 @@ export default {
default: '',
},
noteId: {
- type: String,
+ type: [String, Number],
required: false,
default: '',
},
diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue
index d669d12a39b..7b6e7b72caf 100644
--- a/app/assets/javascripts/notes/components/note_header.vue
+++ b/app/assets/javascripts/notes/components/note_header.vue
@@ -14,7 +14,8 @@ export default {
},
createdAt: {
type: String,
- required: true,
+ required: false,
+ default: null,
},
actionText: {
type: String,
@@ -22,8 +23,9 @@ export default {
default: '',
},
noteId: {
- type: String,
- required: true,
+ type: [String, Number],
+ required: false,
+ default: null,
},
includeToggle: {
type: Boolean,
@@ -96,18 +98,22 @@ export default {
<span class="system-note-message">
<slot></slot>
</span>
- <span class="system-note-separator">
- &middot;
- </span>
- <a
- :href="noteTimestampLink"
- class="note-timestamp system-note-separator"
- @click="updateTargetNoteHash">
- <time-ago-tooltip
- :time="createdAt"
- tooltip-placement="bottom"
- />
- </a>
+ <template
+ v-if="createdAt"
+ >
+ <span class="system-note-separator">
+ &middot;
+ </span>
+ <a
+ :href="noteTimestampLink"
+ class="note-timestamp system-note-separator"
+ @click="updateTargetNoteHash">
+ <time-ago-tooltip
+ :time="createdAt"
+ tooltip-placement="bottom"
+ />
+ </a>
+ </template>
<i
class="fa fa-spinner fa-spin editing-spinner"
aria-label="Comment is being updated"
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index e9218723149..c5fdfa1d47c 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -379,7 +379,7 @@ Please check your network connection and try again.`;
role="group">
<button
type="button"
- class="btn btn-default mr-2"
+ class="btn btn-default"
@click="resolveHandler()"
>
<i
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index 7579fc852c6..f391ed848a4 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -52,7 +52,7 @@ export default {
return this.note.resolvable && !!this.getUserData.id;
},
canReportAsAbuse() {
- return this.note.report_abuse_path && this.author.id !== this.getUserData.id;
+ return !!this.note.report_abuse_path && this.author.id !== this.getUserData.id;
},
noteAnchorId() {
return `note_${this.note.id}`;
@@ -81,13 +81,17 @@ export default {
...mapActions(['deleteNote', 'updateNote', 'toggleResolveNote', 'scrollToNoteIfNeeded']),
editHandler() {
this.isEditing = true;
+ this.$emit('handleEdit');
},
deleteHandler() {
+ const typeOfComment = this.note.isDraft ? 'pending comment' : 'comment';
// eslint-disable-next-line no-alert
- if (window.confirm('Are you sure you want to delete this comment?')) {
+ if (window.confirm(`Are you sure you want to delete this ${typeOfComment}?`)) {
this.isDeleting = true;
this.$emit('handleDeleteNote', this.note);
+ if (this.note.isDraft) return;
+
this.deleteNote(this.note)
.then(() => {
this.isDeleting = false;
@@ -98,7 +102,20 @@ export default {
});
}
},
+ updateSuccess() {
+ this.isEditing = false;
+ this.isRequesting = false;
+ this.oldContent = null;
+ $(this.$refs.noteBody.$el).renderGFM();
+ this.$refs.noteBody.resetAutoSave();
+ this.$emit('updateSuccess');
+ },
formUpdateHandler(noteText, parentElement, callback) {
+ this.$emit('handleUpdateNote', {
+ note: this.note,
+ noteText,
+ callback: () => this.updateSuccess(),
+ });
const data = {
endpoint: this.note.path,
note: {
@@ -113,11 +130,7 @@ export default {
this.updateNote(data)
.then(() => {
- this.isEditing = false;
- this.isRequesting = false;
- this.oldContent = null;
- $(this.$refs.noteBody.$el).renderGFM();
- this.$refs.noteBody.resetAutoSave();
+ this.updateSuccess();
callback();
})
.catch(() => {
@@ -142,6 +155,7 @@ export default {
this.oldContent = null;
}
this.isEditing = false;
+ this.$emit('cancelForm');
},
recoverNoteContent(noteText) {
// we need to do this to prevent noteForm inconsistent content warning
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index 320dfa47d5a..7ab7e5a9abb 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -150,11 +150,24 @@ export const toggleIssueLocalState = ({ commit }, newState) => {
export const saveNote = ({ commit, dispatch }, noteData) => {
// For MR discussuions we need to post as `note[note]` and issue we use `note.note`.
- const note = noteData.data['note[note]'] || noteData.data.note.note;
+ // For batch comments, we use draft_note
+ const note = noteData.data.draft_note || noteData.data['note[note]'] || noteData.data.note.note;
let placeholderText = note;
const hasQuickActions = utils.hasQuickActions(placeholderText);
const replyId = noteData.data.in_reply_to_discussion_id;
- const methodToDispatch = replyId ? 'replyToDiscussion' : 'createNewNote';
+ let methodToDispatch;
+ const postData = Object.assign({}, noteData);
+ if (postData.isDraft === true) {
+ methodToDispatch = replyId
+ ? 'batchComments/addDraftToDiscussion'
+ : 'batchComments/createNewDraft';
+ if (!postData.draft_note && noteData.note) {
+ postData.draft_note = postData.note;
+ delete postData.note;
+ }
+ } else {
+ methodToDispatch = replyId ? 'replyToDiscussion' : 'createNewNote';
+ }
$('.notes-form .flash-container').hide(); // hide previous flash notification
commit(types.REMOVE_PLACEHOLDER_NOTES); // remove previous placeholders
@@ -180,7 +193,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
}
}
- return dispatch(methodToDispatch, noteData).then(res => {
+ return dispatch(methodToDispatch, postData, { root: true }).then(res => {
const { errors } = res;
const commandsChanges = res.commands_changes;
diff --git a/app/assets/javascripts/notes/stores/getters.js b/app/assets/javascripts/notes/stores/getters.js
index d4babf1fab2..a829149a17e 100644
--- a/app/assets/javascripts/notes/stores/getters.js
+++ b/app/assets/javascripts/notes/stores/getters.js
@@ -74,6 +74,9 @@ export const allDiscussions = (state, getters) => {
return Object.values(resolved).concat(unresolved);
};
+export const isDiscussionResolved = (state, getters) => discussionId =>
+ getters.resolvedDiscussionsById[discussionId] !== undefined;
+
export const allResolvableDiscussions = (state, getters) =>
getters.allDiscussions.filter(d => !d.individual_note && d.resolvable);
@@ -126,8 +129,8 @@ export const unresolvedDiscussionsIdsByDiff = (state, getters) =>
const filenameComparison = a.diff_file.file_path.localeCompare(b.diff_file.file_path);
// Get the line numbers, to compare within the same file
- const aLines = [a.position.formatter.new_line, a.position.formatter.old_line];
- const bLines = [b.position.formatter.new_line, b.position.formatter.old_line];
+ const aLines = [a.position.new_line, a.position.old_line];
+ const bLines = [b.position.new_line, b.position.old_line];
return filenameComparison < 0 ||
(filenameComparison === 0 &&
diff --git a/app/assets/javascripts/pages/dashboard/todos/index/todos.js b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
index 9aa83ce6269..72f3f70b98f 100644
--- a/app/assets/javascripts/pages/dashboard/todos/index/todos.js
+++ b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
@@ -79,10 +79,13 @@ export default class Todos {
.then(({ data }) => {
this.updateRowState(target);
this.updateBadges(data);
- }).catch(() => flash(__('Error updating todo status.')));
+ }).catch(() => {
+ this.updateRowState(target, true);
+ return flash(__('Error updating todo status.'));
+ });
}
- updateRowState(target) {
+ updateRowState(target, isInactive = false) {
const row = target.closest('li');
const restoreBtn = row.querySelector('.js-undo-todo');
const doneBtn = row.querySelector('.js-done-todo');
@@ -91,7 +94,10 @@ export default class Todos {
target.removeAttribute('disabled');
target.classList.remove('disabled');
- if (target === doneBtn) {
+ if (isInactive === true) {
+ restoreBtn.classList.add('hidden');
+ doneBtn.classList.remove('hidden');
+ } else if (target === doneBtn) {
row.classList.add('done-reversible');
restoreBtn.classList.remove('hidden');
} else if (target === restoreBtn) {
diff --git a/app/assets/javascripts/pages/profiles/show/index.js b/app/assets/javascripts/pages/profiles/show/index.js
index aea7b649c20..c7ce4675573 100644
--- a/app/assets/javascripts/pages/profiles/show/index.js
+++ b/app/assets/javascripts/pages/profiles/show/index.js
@@ -11,7 +11,7 @@ document.addEventListener('DOMContentLoaded', () => {
const statusEmojiField = document.getElementById('js-status-emoji-field');
const statusMessageField = document.getElementById('js-status-message-field');
- const toggleNoEmojiPlaceholder = (isVisible) => {
+ const toggleNoEmojiPlaceholder = isVisible => {
const placeholderElement = document.getElementById('js-no-emoji-placeholder');
placeholderElement.classList.toggle('hidden', !isVisible);
};
@@ -69,5 +69,5 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
})
- .catch(() => createFlash('Failed to load emoji list!'));
+ .catch(() => createFlash('Failed to load emoji list.'));
});
diff --git a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js
index 6c1788dc160..58bb8c5b0c8 100644
--- a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js
+++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign */
+/* eslint-disable func-names, no-var, one-var, camelcase, no-param-reassign, prefer-template, no-return-assign */
import $ from 'jquery';
import _ from 'underscore';
diff --git a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js
index a02ec9e5f00..5f91686347a 100644
--- a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js
+++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, max-len, no-restricted-syntax, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, comma-dangle, no-return-assign, prefer-arrow-callback, quotes, prefer-template, newline-per-chained-call, no-else-return, no-shadow */
+/* eslint-disable func-names, no-restricted-syntax, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, no-return-assign, prefer-arrow-callback, prefer-template, no-else-return, no-shadow */
import $ from 'jquery';
import _ from 'underscore';
diff --git a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js
index d12249bf612..cd0e2bc023c 100644
--- a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js
+++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, object-shorthand, no-var, one-var, camelcase, one-var-declaration-per-line, comma-dangle, no-param-reassign, no-return-assign, quotes, prefer-arrow-callback, wrap-iife, consistent-return, no-unused-vars, max-len, no-cond-assign, no-else-return, max-len */
+/* eslint-disable func-names, object-shorthand, no-var, one-var, camelcase, no-param-reassign, no-return-assign, prefer-arrow-callback, consistent-return, no-unused-vars, no-cond-assign, no-else-return */
import _ from 'underscore';
export default {
diff --git a/app/assets/javascripts/pages/projects/index.js b/app/assets/javascripts/pages/projects/index.js
index 5659e13981a..b0345b4e50d 100644
--- a/app/assets/javascripts/pages/projects/index.js
+++ b/app/assets/javascripts/pages/projects/index.js
@@ -1,5 +1,5 @@
-import initDismissableCallout from '~/dismissable_callout';
import initGkeDropdowns from '~/projects/gke_cluster_dropdowns';
+import PersistentUserCallout from '../../persistent_user_callout';
import Project from './project';
import ShortcutsNavigation from '../../behaviors/shortcuts/shortcuts_navigation';
@@ -12,7 +12,9 @@ document.addEventListener('DOMContentLoaded', () => {
];
if (newClusterViews.indexOf(page) > -1) {
- initDismissableCallout('.gcp-signup-offer');
+ const callout = document.querySelector('.gcp-signup-offer');
+ if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
+
initGkeDropdowns();
}
diff --git a/app/assets/javascripts/pages/projects/network/network.js b/app/assets/javascripts/pages/projects/network/network.js
index 77368c47451..70fbb3f301c 100644
--- a/app/assets/javascripts/pages/projects/network/network.js
+++ b/app/assets/javascripts/pages/projects/network/network.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, wrap-iife, no-var, quotes, quote-props, prefer-template, comma-dangle, max-len */
+/* eslint-disable func-names, no-var, prefer-template */
import $ from 'jquery';
import BranchGraph from '../../../network/branch_graph';
diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js
index 34a13eb3251..52d66beefc9 100644
--- a/app/assets/javascripts/pages/projects/project.js
+++ b/app/assets/javascripts/pages/projects/project.js
@@ -1,5 +1,4 @@
-/* eslint-disable func-names, no-var, no-return-assign, one-var,
- one-var-declaration-per-line, object-shorthand, vars-on-top */
+/* eslint-disable func-names, no-var, no-return-assign, one-var, object-shorthand, vars-on-top */
import $ from 'jquery';
import Cookies from 'js-cookie';
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
index 875f6928bed..a16f7e6b77c 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
@@ -52,6 +52,21 @@
required: false,
default: '',
},
+ pagesAvailable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ pagesAccessControlEnabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ pagesHelpPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
data() {
@@ -64,6 +79,7 @@
buildsAccessLevel: 20,
wikiAccessLevel: 20,
snippetsAccessLevel: 20,
+ pagesAccessLevel: 20,
containerRegistryEnabled: true,
lfsEnabled: true,
requestAccessEnabled: true,
@@ -90,6 +106,13 @@
);
},
+ pagesFeatureAccessLevelOptions() {
+ if (this.visibilityLevel !== visibilityOptions.PUBLIC) {
+ return this.featureAccessLevelOptions.concat([[30, 'Everyone']]);
+ }
+ return this.featureAccessLevelOptions;
+ },
+
repositoryEnabled() {
return this.repositoryAccessLevel > 0;
},
@@ -109,6 +132,10 @@
this.buildsAccessLevel = Math.min(10, this.buildsAccessLevel);
this.wikiAccessLevel = Math.min(10, this.wikiAccessLevel);
this.snippetsAccessLevel = Math.min(10, this.snippetsAccessLevel);
+ if (this.pagesAccessLevel === 20) {
+ // When from Internal->Private narrow access for only members
+ this.pagesAccessLevel = 10;
+ }
this.highlightChanges();
} else if (oldValue === visibilityOptions.PRIVATE) {
// if changing away from private, make enabled features more permissive
@@ -118,6 +145,7 @@
if (this.buildsAccessLevel > 0) this.buildsAccessLevel = 20;
if (this.wikiAccessLevel > 0) this.wikiAccessLevel = 20;
if (this.snippetsAccessLevel > 0) this.snippetsAccessLevel = 20;
+ if (this.pagesAccessLevel === 10) this.pagesAccessLevel = 20;
this.highlightChanges();
}
},
@@ -323,6 +351,18 @@
name="project[project_feature_attributes][snippets_access_level]"
/>
</project-setting-row>
+ <project-setting-row
+ v-if="pagesAvailable && pagesAccessControlEnabled"
+ :help-path="pagesHelpPath"
+ label="Pages"
+ help-text="Static website for the project."
+ >
+ <project-feature-setting
+ v-model="pagesAccessLevel"
+ :options="pagesFeatureAccessLevelOptions"
+ name="project[project_feature_attributes][pages_access_level]"
+ />
+ </project-setting-row>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/pages/root/index.js b/app/assets/javascripts/pages/root/index.js
new file mode 100644
index 00000000000..09f8185d3b5
--- /dev/null
+++ b/app/assets/javascripts/pages/root/index.js
@@ -0,0 +1,5 @@
+// if the "projects dashboard" is a user's default dashboard, when they visit the
+// instance root index, the dashboard will be served by the root controller instead
+// of a dashboard controller. The root index redirects for all other default dashboards.
+
+import '../dashboard/projects/index';
diff --git a/app/assets/javascripts/pages/sessions/new/username_validator.js b/app/assets/javascripts/pages/sessions/new/username_validator.js
index 97cf1aeaadc..d621f988d86 100644
--- a/app/assets/javascripts/pages/sessions/new/username_validator.js
+++ b/app/assets/javascripts/pages/sessions/new/username_validator.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, consistent-return, class-methods-use-this */
+/* eslint-disable consistent-return, class-methods-use-this */
import $ from 'jquery';
import _ from 'underscore';
diff --git a/app/assets/javascripts/pages/snippets/form.js b/app/assets/javascripts/pages/snippets/form.js
index f369c7ef9a6..8859557e62d 100644
--- a/app/assets/javascripts/pages/snippets/form.js
+++ b/app/assets/javascripts/pages/snippets/form.js
@@ -11,6 +11,7 @@ export default () => {
epics: false,
milestones: false,
labels: false,
+ snippets: false,
});
new ZenMode(); // eslint-disable-line no-new
};
diff --git a/app/assets/javascripts/pages/users/activity_calendar.js b/app/assets/javascripts/pages/users/activity_calendar.js
index 9892a039941..bf592ba7a3c 100644
--- a/app/assets/javascripts/pages/users/activity_calendar.js
+++ b/app/assets/javascripts/pages/users/activity_calendar.js
@@ -43,7 +43,15 @@ const initColorKey = () =>
.domain([0, 3]);
export default class ActivityCalendar {
- constructor(container, timestamps, calendarActivitiesPath, utcOffset = 0, firstDayOfWeek = 0) {
+ constructor(
+ container,
+ activitiesContainer,
+ timestamps,
+ calendarActivitiesPath,
+ utcOffset = 0,
+ firstDayOfWeek = 0,
+ monthsAgo = 12,
+ ) {
this.calendarActivitiesPath = calendarActivitiesPath;
this.clickDay = this.clickDay.bind(this);
this.currentSelectedDate = '';
@@ -66,6 +74,8 @@ export default class ActivityCalendar {
];
this.months = [];
this.firstDayOfWeek = firstDayOfWeek;
+ this.activitiesContainer = activitiesContainer;
+ this.container = container;
// Loop through the timestamps to create a group of objects
// The group of objects will be grouped based on the day of the week they are
@@ -75,13 +85,13 @@ export default class ActivityCalendar {
const today = getSystemDate(utcOffset);
today.setHours(0, 0, 0, 0, 0);
- const oneYearAgo = new Date(today);
- oneYearAgo.setFullYear(today.getFullYear() - 1);
+ const timeAgo = new Date(today);
+ timeAgo.setMonth(today.getMonth() - monthsAgo);
- const days = getDayDifference(oneYearAgo, today);
+ const days = getDayDifference(timeAgo, today);
for (let i = 0; i <= days; i += 1) {
- const date = new Date(oneYearAgo);
+ const date = new Date(timeAgo);
date.setDate(date.getDate() + i);
const day = date.getDay();
@@ -280,7 +290,7 @@ export default class ActivityCalendar {
this.currentSelectedDate.getDate(),
].join('-');
- $('.user-calendar-activities').html(LOADING_HTML);
+ $(this.activitiesContainer).html(LOADING_HTML);
axios
.get(this.calendarActivitiesPath, {
@@ -289,11 +299,11 @@ export default class ActivityCalendar {
},
responseType: 'text',
})
- .then(({ data }) => $('.user-calendar-activities').html(data))
+ .then(({ data }) => $(this.activitiesContainer).html(data))
.catch(() => flash(__('An error occurred while retrieving calendar activity')));
} else {
this.currentSelectedDate = '';
- $('.user-calendar-activities').html('');
+ $(this.activitiesContainer).html('');
}
}
}
diff --git a/app/assets/javascripts/pages/users/user_overview_block.js b/app/assets/javascripts/pages/users/user_overview_block.js
new file mode 100644
index 00000000000..0009419cd0c
--- /dev/null
+++ b/app/assets/javascripts/pages/users/user_overview_block.js
@@ -0,0 +1,42 @@
+import axios from '~/lib/utils/axios_utils';
+
+export default class UserOverviewBlock {
+ constructor(options = {}) {
+ this.container = options.container;
+ this.url = options.url;
+ this.limit = options.limit || 20;
+ this.loadData();
+ }
+
+ loadData() {
+ const loadingEl = document.querySelector(`${this.container} .loading`);
+
+ loadingEl.classList.remove('hide');
+
+ axios
+ .get(this.url, {
+ params: {
+ limit: this.limit,
+ },
+ })
+ .then(({ data }) => this.render(data))
+ .catch(() => loadingEl.classList.add('hide'));
+ }
+
+ render(data) {
+ const { html, count } = data;
+ const contentList = document.querySelector(`${this.container} .overview-content-list`);
+
+ contentList.innerHTML += html;
+
+ const loadingEl = document.querySelector(`${this.container} .loading`);
+
+ if (count && count > 0) {
+ document.querySelector(`${this.container} .js-view-all`).classList.remove('hide');
+ } else {
+ document.querySelector(`${this.container} .nothing-here-block`).classList.add('text-left', 'p-0');
+ }
+
+ loadingEl.classList.add('hide');
+ }
+}
diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js
index a2ca03536f2..23b0348a99f 100644
--- a/app/assets/javascripts/pages/users/user_tabs.js
+++ b/app/assets/javascripts/pages/users/user_tabs.js
@@ -2,9 +2,10 @@ import $ from 'jquery';
import axios from '~/lib/utils/axios_utils';
import Activities from '~/activities';
import { localTimeAgo } from '~/lib/utils/datetime_utility';
-import { __ } from '~/locale';
+import { __, sprintf } from '~/locale';
import flash from '~/flash';
import ActivityCalendar from './activity_calendar';
+import UserOverviewBlock from './user_overview_block';
/**
* UserTabs
@@ -61,19 +62,28 @@ import ActivityCalendar from './activity_calendar';
* </div>
*/
-const CALENDAR_TEMPLATE = `
- <div class="clearfix calendar">
- <div class="js-contrib-calendar"></div>
- <div class="calendar-hint">
- Summary of issues, merge requests, push events, and comments
+const CALENDAR_TEMPLATES = {
+ activity: `
+ <div class="clearfix calendar">
+ <div class="js-contrib-calendar"></div>
+ <div class="calendar-hint bottom-right"></div>
</div>
- </div>
-`;
+ `,
+ overview: `
+ <div class="clearfix calendar">
+ <div class="calendar-hint"></div>
+ <div class="js-contrib-calendar prepend-top-20"></div>
+ </div>
+ `,
+};
+
+const CALENDAR_PERIOD_6_MONTHS = 6;
+const CALENDAR_PERIOD_12_MONTHS = 12;
export default class UserTabs {
constructor({ defaultAction, action, parentEl }) {
this.loaded = {};
- this.defaultAction = defaultAction || 'activity';
+ this.defaultAction = defaultAction || 'overview';
this.action = action || this.defaultAction;
this.$parentEl = $(parentEl) || $(document);
this.windowLocation = window.location;
@@ -124,6 +134,8 @@ export default class UserTabs {
}
if (action === 'activity') {
this.loadActivities();
+ } else if (action === 'overview') {
+ this.loadOverviewTab();
}
const loadableActions = ['groups', 'contributed', 'projects', 'snippets'];
@@ -154,7 +166,40 @@ export default class UserTabs {
if (this.loaded.activity) {
return;
}
- const $calendarWrap = this.$parentEl.find('.user-calendar');
+
+ this.loadActivityCalendar('activity');
+
+ // eslint-disable-next-line no-new
+ new Activities();
+
+ this.loaded.activity = true;
+ }
+
+ loadOverviewTab() {
+ if (this.loaded.overview) {
+ return;
+ }
+
+ this.loadActivityCalendar('overview');
+
+ UserTabs.renderMostRecentBlocks('#js-overview .activities-block', 5);
+ UserTabs.renderMostRecentBlocks('#js-overview .projects-block', 10);
+
+ this.loaded.overview = true;
+ }
+
+ static renderMostRecentBlocks(container, limit) {
+ // eslint-disable-next-line no-new
+ new UserOverviewBlock({
+ container,
+ url: $(`${container} .overview-content-list`).data('href'),
+ limit,
+ });
+ }
+
+ loadActivityCalendar(action) {
+ const monthsAgo = action === 'overview' ? CALENDAR_PERIOD_6_MONTHS : CALENDAR_PERIOD_12_MONTHS;
+ const $calendarWrap = this.$parentEl.find('.tab-pane.active .user-calendar');
const calendarPath = $calendarWrap.data('calendarPath');
const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath');
const utcOffset = $calendarWrap.data('utcOffset');
@@ -166,17 +211,22 @@ export default class UserTabs {
axios
.get(calendarPath)
.then(({ data }) => {
- $calendarWrap.html(CALENDAR_TEMPLATE);
- $calendarWrap.find('.calendar-hint').append(`(Timezone: ${utcFormatted})`);
+ $calendarWrap.html(CALENDAR_TEMPLATES[action]);
+
+ let calendarHint = '';
+
+ if (action === 'activity') {
+ calendarHint = sprintf(__('Summary of issues, merge requests, push events, and comments (Timezone: %{utcFormatted})'), { utcFormatted });
+ } else if (action === 'overview') {
+ calendarHint = __('Issues, merge requests, pushes and comments.');
+ }
+
+ $calendarWrap.find('.calendar-hint').text(calendarHint);
// eslint-disable-next-line no-new
- new ActivityCalendar('.js-contrib-calendar', data, calendarActivitiesPath, utcOffset);
+ new ActivityCalendar('.tab-pane.active .js-contrib-calendar', '.tab-pane.active .user-calendar-activities', data, calendarActivitiesPath, utcOffset, 0, monthsAgo);
})
.catch(() => flash(__('There was an error loading users activity calendar.')));
-
- // eslint-disable-next-line no-new
- new Activities();
- this.loaded.activity = true;
}
toggleLoading(status) {
diff --git a/app/assets/javascripts/persistent_user_callout.js b/app/assets/javascripts/persistent_user_callout.js
new file mode 100644
index 00000000000..1e34e74a152
--- /dev/null
+++ b/app/assets/javascripts/persistent_user_callout.js
@@ -0,0 +1,34 @@
+import axios from './lib/utils/axios_utils';
+import { __ } from './locale';
+import Flash from './flash';
+
+export default class PersistentUserCallout {
+ constructor(container) {
+ const { dismissEndpoint, featureId } = container.dataset;
+ this.container = container;
+ this.dismissEndpoint = dismissEndpoint;
+ this.featureId = featureId;
+
+ this.init();
+ }
+
+ init() {
+ const closeButton = this.container.querySelector('.js-close');
+ closeButton.addEventListener('click', event => this.dismiss(event));
+ }
+
+ dismiss(event) {
+ event.preventDefault();
+
+ axios
+ .post(this.dismissEndpoint, {
+ feature_name: this.featureId,
+ })
+ .then(() => {
+ this.container.remove();
+ })
+ .catch(() => {
+ Flash(__('An error occurred while dismissing the alert. Refresh the page and try again.'));
+ });
+ }
+}
diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
index f0e845d0773..16e69759091 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_actions.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
@@ -26,8 +26,13 @@ export default {
methods: {
onClickAction(action) {
if (action.scheduled_at) {
- const confirmationMessage = sprintf(s__("DelayedJobs|Are you sure you want to run %{jobName} immediately? This job will run automatically after it's timer finishes."), { jobName: action.name });
- // https://gitlab.com/gitlab-org/gitlab-ce/issues/52099
+ const confirmationMessage = sprintf(
+ s__(
+ "DelayedJobs|Are you sure you want to run %{jobName} immediately? This job will run automatically after it's timer finishes.",
+ ),
+ { jobName: action.name },
+ );
+ // https://gitlab.com/gitlab-org/gitlab-ce/issues/52156
// eslint-disable-next-line no-alert
if (!window.confirm(confirmationMessage)) {
return;
@@ -49,7 +54,7 @@ export default {
remainingTime(action) {
const remainingMilliseconds = new Date(action.scheduled_at).getTime() - Date.now();
- return formatTime(remainingMilliseconds);
+ return formatTime(Math.max(0, remainingMilliseconds));
},
},
};
diff --git a/app/assets/javascripts/pipelines/components/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_table.vue
index 0d7324f3fb5..cb14d4400d1 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_table.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table.vue
@@ -35,7 +35,7 @@ export default {
},
data() {
return {
- pipelineId: '',
+ pipelineId: 0,
endpoint: '',
cancelingPipeline: null,
};
diff --git a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
index 88957554d12..b03438ddba1 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
@@ -47,7 +47,7 @@ export default {
required: true,
},
cancelingPipeline: {
- type: String,
+ type: Number,
required: false,
default: null,
},
@@ -63,10 +63,10 @@ export default {
if (!this.pipeline || !this.pipeline.details) {
return [];
}
- const { details: pipelineDetails } = this.pipeline;
+ const { details } = this.pipeline;
return [
- ...(pipelineDetails.manual_actions || []),
- ...(pipelineDetails.scheduled_actions || []),
+ ...(details.manual_actions || []),
+ ...(details.scheduled_actions || []),
];
},
/**
diff --git a/app/assets/javascripts/profile/gl_crop.js b/app/assets/javascripts/profile/gl_crop.js
index f641b23e519..af134881f31 100644
--- a/app/assets/javascripts/profile/gl_crop.js
+++ b/app/assets/javascripts/profile/gl_crop.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-useless-escape, max-len, no-var, no-underscore-dangle, func-names, no-unused-vars, no-return-assign, object-shorthand, one-var, one-var-declaration-per-line, comma-dangle, consistent-return, class-methods-use-this, new-parens */
+/* eslint-disable no-useless-escape, no-var, no-underscore-dangle, func-names, no-unused-vars, no-return-assign, object-shorthand, one-var, consistent-return, class-methods-use-this */
import $ from 'jquery';
import 'cropper';
diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js
index 05485e352dc..12cfa7de316 100644
--- a/app/assets/javascripts/project_find_file.js
+++ b/app/assets/javascripts/project_find_file.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, wrap-iife, quotes, consistent-return, one-var, one-var-declaration-per-line, no-cond-assign, max-len, prefer-template, no-unused-vars, no-return-assign */
+/* eslint-disable func-names, no-var, consistent-return, one-var, no-cond-assign, prefer-template, no-unused-vars, no-return-assign */
import $ from 'jquery';
import fuzzaldrinPlus from 'fuzzaldrin-plus';
diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js
index 6f3b32f8eea..eaaeda8b339 100644
--- a/app/assets/javascripts/project_select.js
+++ b/app/assets/javascripts/project_select.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, wrap-iife, no-var, comma-dangle, object-shorthand, one-var, one-var-declaration-per-line, no-else-return, quotes, max-len */
+/* eslint-disable func-names, no-var, object-shorthand, one-var, no-else-return */
import $ from 'jquery';
import Api from './api';
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index b27d635c6ac..64f3dde5be7 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, no-unused-vars, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, no-else-return, no-param-reassign, max-len */
+/* eslint-disable func-names, no-var, no-unused-vars, consistent-return, one-var, prefer-template, no-else-return, no-param-reassign */
import $ from 'jquery';
import _ from 'underscore';
diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js
index 50dd3c12382..7bde4860973 100644
--- a/app/assets/javascripts/search_autocomplete.js
+++ b/app/assets/javascripts/search_autocomplete.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-return-assign, one-var, no-var, one-var-declaration-per-line, no-unused-vars, consistent-return, object-shorthand, prefer-template, class-methods-use-this, no-lonely-if, vars-on-top, max-len */
+/* eslint-disable no-return-assign, one-var, no-var, no-unused-vars, consistent-return, object-shorthand, prefer-template, class-methods-use-this, no-lonely-if, vars-on-top */
import $ from 'jquery';
import { escape, throttle } from 'underscore';
diff --git a/app/assets/javascripts/set_status_modal/emoji_menu_in_modal.js b/app/assets/javascripts/set_status_modal/emoji_menu_in_modal.js
new file mode 100644
index 00000000000..14a89ef9293
--- /dev/null
+++ b/app/assets/javascripts/set_status_modal/emoji_menu_in_modal.js
@@ -0,0 +1,21 @@
+import { AwardsHandler } from '~/awards_handler';
+
+class EmojiMenuInModal extends AwardsHandler {
+ constructor(emoji, toggleButtonSelector, menuClass, selectEmojiCallback, targetContainerEl) {
+ super(emoji);
+
+ this.selectEmojiCallback = selectEmojiCallback;
+ this.toggleButtonSelector = toggleButtonSelector;
+ this.menuClass = menuClass;
+ this.targetContainerEl = targetContainerEl;
+
+ this.bindEvents();
+ }
+
+ postEmoji($emojiButton, awardUrl, selectedEmoji, callback) {
+ this.selectEmojiCallback(selectedEmoji, this.emoji.glEmojiTag(selectedEmoji));
+ callback();
+ }
+}
+
+export default EmojiMenuInModal;
diff --git a/app/assets/javascripts/set_status_modal/event_hub.js b/app/assets/javascripts/set_status_modal/event_hub.js
new file mode 100644
index 00000000000..0948c2e5352
--- /dev/null
+++ b/app/assets/javascripts/set_status_modal/event_hub.js
@@ -0,0 +1,3 @@
+import Vue from 'vue';
+
+export default new Vue();
diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_trigger.vue b/app/assets/javascripts/set_status_modal/set_status_modal_trigger.vue
new file mode 100644
index 00000000000..48e5ede80f2
--- /dev/null
+++ b/app/assets/javascripts/set_status_modal/set_status_modal_trigger.vue
@@ -0,0 +1,33 @@
+<script>
+import { s__ } from '~/locale';
+import eventHub from './event_hub';
+
+export default {
+ props: {
+ hasStatus: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ computed: {
+ buttonText() {
+ return this.hasStatus ? s__('SetStatusModal|Edit status') : s__('SetStatusModal|Set status');
+ },
+ },
+ methods: {
+ openModal() {
+ eventHub.$emit('openModal');
+ },
+ },
+};
+</script>
+
+<template>
+ <button
+ type="button"
+ class="btn menu-item"
+ @click="openModal"
+ >
+ {{ buttonText }}
+ </button>
+</template>
diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
new file mode 100644
index 00000000000..43f0b6651b9
--- /dev/null
+++ b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
@@ -0,0 +1,241 @@
+<script>
+import $ from 'jquery';
+import createFlash from '~/flash';
+import Icon from '~/vue_shared/components/icon.vue';
+import GfmAutoComplete from '~/gfm_auto_complete';
+import { __, s__ } from '~/locale';
+import Api from '~/api';
+import eventHub from './event_hub';
+import EmojiMenuInModal from './emoji_menu_in_modal';
+
+const emojiMenuClass = 'js-modal-status-emoji-menu';
+
+export default {
+ components: {
+ Icon,
+ },
+ props: {
+ currentEmoji: {
+ type: String,
+ required: true,
+ },
+ currentMessage: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ defaultEmojiTag: '',
+ emoji: this.currentEmoji,
+ emojiMenu: null,
+ emojiTag: '',
+ isEmojiMenuVisible: false,
+ message: this.currentMessage,
+ modalId: 'set-user-status-modal',
+ noEmoji: true,
+ };
+ },
+ computed: {
+ isDirty() {
+ return this.message.length || this.emoji.length;
+ },
+ },
+ mounted() {
+ eventHub.$on('openModal', this.openModal);
+ },
+ beforeDestroy() {
+ this.emojiMenu.destroy();
+ },
+ methods: {
+ openModal() {
+ this.$root.$emit('bv::show::modal', this.modalId);
+ },
+ closeModal() {
+ this.$root.$emit('bv::hide::modal', this.modalId);
+ },
+ setupEmojiListAndAutocomplete() {
+ const toggleEmojiMenuButtonSelector = '#set-user-status-modal .js-toggle-emoji-menu';
+ const emojiAutocomplete = new GfmAutoComplete();
+ emojiAutocomplete.setup($(this.$refs.statusMessageField), { emojis: true });
+
+ import(/* webpackChunkName: 'emoji' */ '~/emoji')
+ .then(Emoji => {
+ if (this.emoji) {
+ this.emojiTag = Emoji.glEmojiTag(this.emoji);
+ }
+ this.noEmoji = this.emoji === '';
+ this.defaultEmojiTag = Emoji.glEmojiTag('speech_balloon');
+
+ this.emojiMenu = new EmojiMenuInModal(
+ Emoji,
+ toggleEmojiMenuButtonSelector,
+ emojiMenuClass,
+ this.setEmoji,
+ this.$refs.userStatusForm,
+ );
+ })
+ .catch(() => createFlash(__('Failed to load emoji list.')));
+ },
+ showEmojiMenu() {
+ this.isEmojiMenuVisible = true;
+ this.emojiMenu.showEmojiMenu($(this.$refs.toggleEmojiMenuButton));
+ },
+ hideEmojiMenu() {
+ if (!this.isEmojiMenuVisible) {
+ return;
+ }
+
+ this.isEmojiMenuVisible = false;
+ this.emojiMenu.hideMenuElement($(`.${emojiMenuClass}`));
+ },
+ setDefaultEmoji() {
+ const { emojiTag } = this;
+ const hasStatusMessage = this.message;
+ if (hasStatusMessage && emojiTag) {
+ return;
+ }
+
+ if (hasStatusMessage) {
+ this.noEmoji = false;
+ this.emojiTag = this.defaultEmojiTag;
+ } else if (emojiTag === this.defaultEmojiTag) {
+ this.noEmoji = true;
+ this.clearEmoji();
+ }
+ },
+ setEmoji(emoji, emojiTag) {
+ this.emoji = emoji;
+ this.noEmoji = false;
+ this.clearEmoji();
+ this.emojiTag = emojiTag;
+ },
+ clearEmoji() {
+ if (this.emojiTag) {
+ this.emojiTag = '';
+ }
+ },
+ clearStatusInputs() {
+ this.emoji = '';
+ this.message = '';
+ this.noEmoji = true;
+ this.clearEmoji();
+ this.hideEmojiMenu();
+ },
+ removeStatus() {
+ this.clearStatusInputs();
+ this.setStatus();
+ },
+ setStatus() {
+ const { emoji, message } = this;
+
+ Api.postUserStatus({
+ emoji,
+ message,
+ })
+ .then(this.onUpdateSuccess)
+ .catch(this.onUpdateFail);
+ },
+ onUpdateSuccess() {
+ this.closeModal();
+ window.location.reload();
+ },
+ onUpdateFail() {
+ createFlash(
+ s__("SetStatusModal|Sorry, we weren't able to set your status. Please try again later."),
+ );
+
+ this.closeModal();
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-ui-modal
+ :title="s__('SetStatusModal|Set a status')"
+ :modal-id="modalId"
+ :ok-title="s__('SetStatusModal|Set status')"
+ :cancel-title="s__('SetStatusModal|Remove status')"
+ ok-variant="success"
+ class="set-user-status-modal"
+ @shown="setupEmojiListAndAutocomplete"
+ @hide="hideEmojiMenu"
+ @ok="setStatus"
+ @cancel="removeStatus"
+ >
+ <div>
+ <input
+ v-model="emoji"
+ class="js-status-emoji-field"
+ type="hidden"
+ name="user[status][emoji]"
+ />
+ <div
+ ref="userStatusForm"
+ class="form-group position-relative m-0"
+ >
+ <div class="input-group">
+ <span class="input-group-btn">
+ <button
+ ref="toggleEmojiMenuButton"
+ v-gl-tooltip.bottom
+ :title="s__('SetStatusModal|Add status emoji')"
+ :aria-label="s__('SetStatusModal|Add status emoji')"
+ name="button"
+ type="button"
+ class="js-toggle-emoji-menu emoji-menu-toggle-button btn"
+ @click="showEmojiMenu"
+ >
+ <span v-html="emojiTag"></span>
+ <span
+ v-show="noEmoji"
+ class="js-no-emoji-placeholder no-emoji-placeholder position-relative"
+ >
+ <icon
+ name="emoji_slightly_smiling_face"
+ css-classes="award-control-icon-neutral"
+ />
+ <icon
+ name="emoji_smiley"
+ css-classes="award-control-icon-positive"
+ />
+ <icon
+ name="emoji_smile"
+ css-classes="award-control-icon-super-positive"
+ />
+ </span>
+ </button>
+ </span>
+ <input
+ ref="statusMessageField"
+ v-model="message"
+ :placeholder="s__('SetStatusModal|What\'s your status?')"
+ type="text"
+ class="form-control form-control input-lg js-status-message-field"
+ name="user[status][message]"
+ @keyup="setDefaultEmoji"
+ @keyup.enter.prevent
+ @click="hideEmojiMenu"
+ />
+ <span
+ v-show="isDirty"
+ class="input-group-btn"
+ >
+ <button
+ v-gl-tooltip.bottom
+ :title="s__('SetStatusModal|Clear status')"
+ :aria-label="s__('SetStatusModal|Clear status')"
+ name="button"
+ type="button"
+ class="js-clear-user-status-button clear-user-status btn"
+ @click="clearStatusInputs()"
+ >
+ <icon name="close" />
+ </button>
+ </span>
+ </div>
+ </div>
+ </div>
+ </gl-ui-modal>
+</template>
diff --git a/app/assets/javascripts/shared/milestones/form.js b/app/assets/javascripts/shared/milestones/form.js
index 8681a1776c6..0ff84dc4667 100644
--- a/app/assets/javascripts/shared/milestones/form.js
+++ b/app/assets/javascripts/shared/milestones/form.js
@@ -15,5 +15,6 @@ export default (initGFM = true) => {
epics: initGFM,
milestones: initGFM,
labels: initGFM,
+ snippets: initGFM,
});
};
diff --git a/app/assets/javascripts/templates/issuable_template_selector.js b/app/assets/javascripts/templates/issuable_template_selector.js
index 6fea03af46a..74166313940 100644
--- a/app/assets/javascripts/templates/issuable_template_selector.js
+++ b/app/assets/javascripts/templates/issuable_template_selector.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-useless-return, max-len */
+/* eslint-disable no-useless-return */
import $ from 'jquery';
import Api from '../api';
diff --git a/app/assets/javascripts/tree.js b/app/assets/javascripts/tree.js
index 85123a63a45..066fd6278a7 100644
--- a/app/assets/javascripts/tree.js
+++ b/app/assets/javascripts/tree.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, max-len, quotes, consistent-return, no-var, one-var, one-var-declaration-per-line, no-else-return, prefer-arrow-callback, class-methods-use-this */
+/* eslint-disable func-names, consistent-return, no-var, one-var, no-else-return, prefer-arrow-callback, class-methods-use-this */
import $ from 'jquery';
import { visitUrl } from './lib/utils/url_utility';
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index e19bbbacf4d..e2259a8d07b 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, one-var, no-var, prefer-rest-params, quotes, max-len, one-var-declaration-per-line, vars-on-top, prefer-arrow-callback, consistent-return, comma-dangle, object-shorthand, no-shadow, no-unused-vars, no-else-return, no-self-compare, prefer-template, no-unused-expressions, yoda, prefer-spread, no-void, camelcase, no-param-reassign */
+/* eslint-disable func-names, one-var, no-var, prefer-rest-params, vars-on-top, prefer-arrow-callback, consistent-return, object-shorthand, no-shadow, no-unused-vars, no-else-return, no-self-compare, prefer-template, no-unused-expressions, yoda, prefer-spread, no-void, camelcase, no-param-reassign */
/* global Issuable */
/* global emitSidebarEvent */
@@ -592,7 +592,7 @@ function UsersSelect(currentUser, els, options = {}) {
if (showEmailUser && data.results.length === 0 && query.term.match(/^[^@]+@[^@]+$/)) {
var trimmed = query.term.trim();
emailUser = {
- name: "Invite \"" + query.term + "\" by email",
+ name: "Invite \"" + trimmed + "\" by email",
username: trimmed,
id: trimmed,
invite: true
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index d62537021ca..10e8ddad9cd 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -76,6 +76,7 @@
epics: this.enableAutocomplete,
milestones: this.enableAutocomplete,
labels: this.enableAutocomplete,
+ snippets: this.enableAutocomplete,
});
},
beforeDestroy() {
diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue
index afc4196c729..ccd53158820 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/header.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue
@@ -18,6 +18,16 @@
required: true,
},
},
+ computed: {
+ mdTable() {
+ return [
+ '| header | header |',
+ '| ------ | ------ |',
+ '| cell | cell |',
+ '| cell | cell |',
+ ].join('\n');
+ },
+ },
mounted() {
$(document).on('markdown-preview:show.vue', this.previewMarkdownTab);
$(document).on('markdown-preview:hide.vue', this.writeMarkdownTab);
@@ -129,6 +139,12 @@
button-title="Add a task list"
icon="task-done"
/>
+ <toolbar-button
+ :tag="mdTable"
+ :prepend="true"
+ :button-title="__('Add a table')"
+ icon="table"
+ />
<button
v-tooltip
aria-label="Go full screen"
diff --git a/app/assets/javascripts/zen_mode.js b/app/assets/javascripts/zen_mode.js
index 0138c9be803..bdb2351c344 100644
--- a/app/assets/javascripts/zen_mode.js
+++ b/app/assets/javascripts/zen_mode.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, wrap-iife, prefer-arrow-callback, no-unused-vars, consistent-return, camelcase, comma-dangle, max-len, class-methods-use-this */
+/* eslint-disable func-names, prefer-arrow-callback, no-unused-vars, consistent-return, camelcase, class-methods-use-this */
// Zen Mode (full screen) textarea
//
diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss
index 0b9dff64b0b..9638fee6078 100644
--- a/app/assets/stylesheets/framework/calendar.scss
+++ b/app/assets/stylesheets/framework/calendar.scss
@@ -1,8 +1,7 @@
-.calender-block {
+.calendar-block {
padding-left: 0;
padding-right: 0;
border-top: 0;
- direction: rtl;
@media (min-width: map-get($grid-breakpoints, sm)) and (max-width: map-get($grid-breakpoints, sm)) {
overflow-x: auto;
@@ -42,10 +41,13 @@
}
.calendar-hint {
- margin-top: -23px;
- float: right;
font-size: 12px;
- direction: ltr;
+
+ &.bottom-right {
+ direction: ltr;
+ margin-top: -23px;
+ float: right;
+ }
}
.pika-single.gitlab-theme {
diff --git a/app/assets/stylesheets/framework/contextual_sidebar.scss b/app/assets/stylesheets/framework/contextual_sidebar.scss
index 2193e8e8de3..2e7f25d975e 100644
--- a/app/assets/stylesheets/framework/contextual_sidebar.scss
+++ b/app/assets/stylesheets/framework/contextual_sidebar.scss
@@ -9,8 +9,7 @@
padding-left: $contextual-sidebar-width;
}
- .issues-bulk-update.right-sidebar.right-sidebar-expanded
- .issuable-sidebar-header {
+ .issues-bulk-update.right-sidebar.right-sidebar-expanded .issuable-sidebar-header {
padding: 10px 0 15px;
}
}
@@ -75,7 +74,7 @@
.nav-sidebar {
transition: width $sidebar-transition-duration, left $sidebar-transition-duration;
position: fixed;
- z-index: 400;
+ z-index: 600;
width: $contextual-sidebar-width;
top: $header-height;
bottom: 0;
@@ -86,8 +85,7 @@
&:not(.sidebar-collapsed-desktop) {
@media (min-width: map-get($grid-breakpoints, sm)) and (max-width: map-get($grid-breakpoints, sm)) {
- box-shadow: inset -1px 0 0 $border-color,
- 2px 1px 3px $dropdown-shadow-color;
+ box-shadow: inset -1px 0 0 $border-color, 2px 1px 3px $dropdown-shadow-color;
}
}
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 11a30d83f03..c430009bfe0 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -529,9 +529,10 @@
}
.header-user {
- .dropdown-menu {
+ &.show .dropdown-menu {
width: auto;
min-width: unset;
+ max-height: 323px;
margin-top: 4px;
color: $gl-text-color;
left: auto;
@@ -542,6 +543,18 @@
.user-name {
display: block;
}
+
+ .user-status-emoji {
+ margin-right: 0;
+ display: block;
+ vertical-align: text-top;
+ max-width: 148px;
+ font-size: 12px;
+
+ gl-emoji {
+ font-size: $gl-font-size;
+ }
+ }
}
svg {
@@ -573,3 +586,24 @@
}
}
}
+
+.set-user-status-modal {
+ .modal-body {
+ min-height: unset;
+ }
+
+ .input-lg {
+ max-width: unset;
+ }
+
+ .no-emoji-placeholder,
+ .clear-user-status {
+ svg {
+ fill: $gl-text-color-secondary;
+ }
+ }
+
+ .emoji-menu-toggle-button {
+ @include emoji-menu-toggle-button;
+ }
+}
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index 7f37dd3de91..be41dbfc61f 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -20,20 +20,24 @@
display: inline-block;
overflow-x: auto;
border: 0;
- border-color: $gray-100;
+ border-color: $gl-gray-100;
@supports (width: fit-content) {
display: block;
width: fit-content;
}
+ tbody {
+ background-color: $white-light;
+ }
+
tr {
th {
- border-bottom: solid 2px $gray-100;
+ border-bottom: solid 2px $gl-gray-100;
}
td {
- border-color: $gray-100;
+ border-color: $gl-gray-100;
}
}
}
@@ -266,3 +270,59 @@
border-radius: 50%;
}
}
+
+@mixin emoji-menu-toggle-button {
+ line-height: 1;
+ padding: 0;
+ min-width: 16px;
+ color: $gray-darkest;
+ fill: $gray-darkest;
+
+ .fa {
+ position: relative;
+ font-size: 16px;
+ }
+
+ svg {
+ @include btn-svg;
+ margin: 0;
+ }
+
+ .award-control-icon-positive,
+ .award-control-icon-super-positive {
+ position: absolute;
+ top: 0;
+ left: 0;
+ opacity: 0;
+ }
+
+ &:hover,
+ &.is-active {
+ .danger-highlight {
+ color: $red-500;
+ }
+
+ .link-highlight {
+ color: $blue-600;
+ fill: $blue-600;
+ }
+
+ .award-control-icon-neutral {
+ opacity: 0;
+ }
+
+ .award-control-icon-positive {
+ opacity: 1;
+ }
+ }
+
+ &.is-active {
+ .award-control-icon-positive {
+ opacity: 0;
+ }
+
+ .award-control-icon-super-positive {
+ opacity: 1;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 837016db67b..b7a95f604b8 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -314,7 +314,8 @@ $diff-jagged-border-gradient-color: darken($white-normal, 8%);
$monospace-font: 'Menlo', 'DejaVu Sans Mono', 'Liberation Mono', 'Consolas', 'Ubuntu Mono',
'Courier New', 'andale mono', 'lucida console', monospace;
$regular-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell,
- 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
+ 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
+ 'Noto Color Emoji';
/*
* Dropdowns
@@ -634,5 +635,4 @@ Modals
*/
$modal-body-height: 134px;
-
$priority-label-empty-state-width: 114px;
diff --git a/app/assets/stylesheets/framework/variables_overrides.scss b/app/assets/stylesheets/framework/variables_overrides.scss
index 7d90452e1f4..759b4f333ca 100644
--- a/app/assets/stylesheets/framework/variables_overrides.scss
+++ b/app/assets/stylesheets/framework/variables_overrides.scss
@@ -18,3 +18,4 @@ $success: $green-500;
$info: $blue-500;
$warning: $orange-500;
$danger: $red-500;
+$zindex-modal-backdrop: 1040;
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 628a4ca38da..11966931a6c 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -59,7 +59,7 @@
vertical-align: middle;
.stage-cell .stage-container {
- margin: 3px 3px 3px 0;
+ margin: 0 3px 3px 0;
}
.stage-container:last-child {
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 5035714b95f..17b02c6e31e 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -1000,6 +1000,7 @@
}
.tree-list-holder {
+ position: -webkit-sticky;
position: sticky;
top: 100px;
max-height: calc(100vh - 100px);
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index c9e0899425f..bfba1bf1b2b 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -343,6 +343,10 @@ ul.notes {
&.parallel {
border-width: 1px;
+
+ &.new {
+ border-right-width: 0;
+ }
}
.discussion-notes {
@@ -519,59 +523,7 @@ ul.notes {
}
.note-action-button {
- line-height: 1;
- padding: 0;
- min-width: 16px;
- color: $gray-darkest;
- fill: $gray-darkest;
-
- .fa {
- position: relative;
- font-size: 16px;
- }
-
- svg {
- @include btn-svg;
- margin: 0;
- }
-
- .award-control-icon-positive,
- .award-control-icon-super-positive {
- position: absolute;
- top: 0;
- left: 0;
- opacity: 0;
- }
-
- &:hover,
- &.is-active {
- .danger-highlight {
- color: $red-500;
- }
-
- .link-highlight {
- color: $blue-600;
- fill: $blue-600;
- }
-
- .award-control-icon-neutral {
- opacity: 0;
- }
-
- .award-control-icon-positive {
- opacity: 1;
- }
- }
-
- &.is-active {
- .award-control-icon-positive {
- opacity: 0;
- }
-
- .award-control-icon-super-positive {
- opacity: 1;
- }
- }
+ @include emoji-menu-toggle-button;
}
.discussion-toggle-button {
@@ -790,7 +742,7 @@ ul.notes {
padding-top: 0;
.discussion-wrapper {
- border-color: transparent;
+ border: 0;
}
}
}
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index caa839c32a5..f084adaf5d3 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -81,14 +81,14 @@
// Middle dot divider between each element in a list of items.
.middle-dot-divider {
&::after {
- content: "\00B7"; // Middle Dot
+ content: '\00B7'; // Middle Dot
padding: 0 6px;
font-weight: $gl-font-weight-bold;
}
&:last-child {
&::after {
- content: "";
+ content: '';
padding: 0;
}
}
@@ -191,7 +191,6 @@
@include media-breakpoint-down(xs) {
width: auto;
}
-
}
.profile-crop-image-container {
@@ -215,7 +214,6 @@
}
}
-
.user-profile {
.cover-controls a {
margin-left: 5px;
@@ -418,7 +416,7 @@ table.u2f-registrations {
}
&.unverified {
- @include status-color($gray-dark, color("gray"), $common-gray-dark);
+ @include status-color($gray-dark, color('gray'), $common-gray-dark);
}
}
}
@@ -431,7 +429,7 @@ table.u2f-registrations {
}
.emoji-menu-toggle-button {
- @extend .note-action-button;
+ @include emoji-menu-toggle-button;
.no-emoji-placeholder {
position: relative;
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 7c42dcad959..da3d8aa53ad 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -830,6 +830,14 @@
}
}
+.repository-language-bar-tooltip-language {
+ font-weight: $gl-font-weight-bold;
+}
+
+.repository-language-bar-tooltip-share {
+ color: $theme-gray-400;
+}
+
pre.light-well {
border-color: $well-light-border;
}
diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss
index 2e2ab8532d2..59fdbf31fe9 100644
--- a/app/assets/stylesheets/performance_bar.scss
+++ b/app/assets/stylesheets/performance_bar.scss
@@ -1,4 +1,5 @@
@import 'framework/variables';
+@import 'framework/variables_overrides';
@import 'peek/views/rblineprof';
#js-peek {
@@ -6,7 +7,7 @@
left: 0;
top: 0;
width: 100%;
- z-index: 1039;
+ z-index: #{$zindex-modal-backdrop + 1};
height: $performance-bar-height;
background: $black;
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index b7c758a42ed..8040a14ef56 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -67,8 +67,9 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
end
end
- def reset_runners_token
+ def reset_registration_token
@application_setting.reset_runners_registration_token!
+
flash[:notice] = 'New runners registration token has been generated!'
redirect_to admin_runners_path
end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index d7dbc712743..ec45e2813c5 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -67,7 +67,6 @@ class ApplicationController < ActionController::Base
end
rescue_from Gitlab::Git::Storage::Inaccessible, GRPC::Unavailable, Gitlab::Git::CommandError do |exception|
- Raven.capture_exception(exception) if sentry_enabled?
log_exception(exception)
headers['Retry-After'] = exception.retry_after if exception.respond_to?(:retry_after)
diff --git a/app/controllers/concerns/labels_as_hash.rb b/app/controllers/concerns/labels_as_hash.rb
new file mode 100644
index 00000000000..1171aa9cf44
--- /dev/null
+++ b/app/controllers/concerns/labels_as_hash.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module LabelsAsHash
+ extend ActiveSupport::Concern
+
+ def labels_as_hash(target = nil, params = {})
+ available_labels = LabelsFinder.new(
+ current_user,
+ params
+ ).execute
+
+ label_hashes = available_labels.as_json(only: [:title, :color])
+
+ if target&.respond_to?(:labels)
+ already_set_labels = available_labels & target.labels
+ if already_set_labels.present?
+ titles = already_set_labels.map(&:title)
+ label_hashes.each do |hash|
+ if titles.include?(hash['title'])
+ hash[:set] = true
+ end
+ end
+ end
+ end
+
+ label_hashes
+ end
+end
diff --git a/app/controllers/groups/labels_controller.rb b/app/controllers/groups/labels_controller.rb
index cb9ab35de85..26768c628ca 100644
--- a/app/controllers/groups/labels_controller.rb
+++ b/app/controllers/groups/labels_controller.rb
@@ -12,7 +12,8 @@ class Groups::LabelsController < Groups::ApplicationController
def index
respond_to do |format|
format.html do
- @labels = GroupLabelsFinder.new(@group, params.merge(sort: sort)).execute
+ @labels = GroupLabelsFinder
+ .new(current_user, @group, params.merge(sort: sort)).execute
end
format.json do
render json: LabelSerializer.new.represent_appearance(available_labels)
diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb
index 6d9a225b771..93f3eb2be6d 100644
--- a/app/controllers/groups/settings/ci_cd_controller.rb
+++ b/app/controllers/groups/settings/ci_cd_controller.rb
@@ -10,6 +10,13 @@ module Groups
define_secret_variables
end
+ def reset_registration_token
+ @group.reset_runners_token!
+
+ flash[:notice] = 'New runners registration token has been generated!'
+ redirect_to group_settings_ci_cd_path
+ end
+
private
def define_secret_variables
diff --git a/app/controllers/projects/autocomplete_sources_controller.rb b/app/controllers/projects/autocomplete_sources_controller.rb
index 7c93cf36862..d386fb63d9f 100644
--- a/app/controllers/projects/autocomplete_sources_controller.rb
+++ b/app/controllers/projects/autocomplete_sources_controller.rb
@@ -27,6 +27,10 @@ class Projects::AutocompleteSourcesController < Projects::ApplicationController
render json: @autocomplete_service.commands(target, params[:type])
end
+ def snippets
+ render json: @autocomplete_service.snippets
+ end
+
private
def load_autocomplete_service
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index c2df7b34f90..2917925947f 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -16,6 +16,8 @@ class Projects::CompareController < Projects::ApplicationController
before_action :define_diff_notes_disabled, only: [:show, :diff_for_path]
before_action :define_commits, only: [:show, :diff_for_path, :signatures]
before_action :merge_request, only: [:index, :show]
+ # Validation
+ before_action :validate_refs!
def index
end
@@ -63,6 +65,21 @@ class Projects::CompareController < Projects::ApplicationController
private
+ def valid_ref?(ref_name)
+ return true unless ref_name.present?
+
+ Gitlab::GitRefValidator.validate(ref_name)
+ end
+
+ def validate_refs!
+ valid = [head_ref, start_ref].map { |ref| valid_ref?(ref) }
+
+ return if valid.all?
+
+ flash[:alert] = "Invalid branch name"
+ redirect_to project_compare_index_path(@project)
+ end
+
def compare
return @compare if defined?(@compare)
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 4e859de6fde..b06a6f3bb0d 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -127,7 +127,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
def related_branches
- @related_branches = @issue.related_branches(current_user)
+ @related_branches = Issues::RelatedBranchesService.new(project, current_user).execute(issue)
respond_to do |format|
format.json do
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index 9c9bbe04947..3ecf94c008e 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -2,6 +2,7 @@
class Projects::JobsController < Projects::ApplicationController
include SendFileUpload
+ include ContinueParams
before_action :build, except: [:index, :cancel_all]
before_action :authorize_read_build!
@@ -107,7 +108,12 @@ class Projects::JobsController < Projects::ApplicationController
return respond_422 unless @build.cancelable?
@build.cancel
- redirect_to build_path(@build)
+
+ if continue_params
+ redirect_to continue_params[:to]
+ else
+ redirect_to builds_project_pipeline_path(@project, @build.pipeline.id)
+ end
end
def unschedule
diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb
index a0ce3b08d9f..640038818f2 100644
--- a/app/controllers/projects/labels_controller.rb
+++ b/app/controllers/projects/labels_controller.rb
@@ -163,6 +163,7 @@ class Projects::LabelsController < Projects::ApplicationController
project_id: @project.id,
include_ancestor_groups: params[:include_ancestor_groups],
search: params[:search],
+ subscribed: params[:subscribed],
sort: sort).execute
end
diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb
index 25d2c11b7db..5307cd0720a 100644
--- a/app/controllers/projects/merge_requests/diffs_controller.rb
+++ b/app/controllers/projects/merge_requests/diffs_controller.rb
@@ -25,7 +25,13 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
@diffs.write_cache
- render json: DiffsSerializer.new(current_user: current_user, project: @merge_request.project).represent(@diffs, additional_attributes)
+ request = {
+ current_user: current_user,
+ project: @merge_request.project,
+ render: ->(partial, locals) { view_to_html_string(partial, locals) }
+ }
+
+ render json: DiffsSerializer.new(request).represent(@diffs, additional_attributes)
end
def define_diff_vars
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index d691744d72a..8bc3a81d771 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -44,12 +44,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
@noteable = @merge_request
@commits_count = @merge_request.commits_count
- # TODO cleanup- Fatih Simon Create an issue to remove these after the refactoring
- # we no longer render notes here. I see it will require a small frontend refactoring,
- # since we gather some data from this collection.
- @discussions = @merge_request.discussions
- @notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes), @noteable)
-
labels
set_pipeline_variables
@@ -207,7 +201,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
environments =
begin
@merge_request.environments_for(current_user).map do |environment|
- project = environment.project
+ project = environment.project
deployment = environment.first_deployment_for(@merge_request.diff_head_sha)
stop_url =
@@ -217,7 +211,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
metrics_url =
if can?(current_user, :read_environment, environment) && environment.has_metrics?
- metrics_project_environment_deployment_path(environment.project, environment, deployment)
+ metrics_project_environment_deployment_path(project, environment, deployment)
end
metrics_monitoring_url =
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index a2d1b7866c2..3a1344651df 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -36,6 +36,13 @@ module Projects
end
end
+ def reset_registration_token
+ @project.reset_runners_token!
+
+ flash[:notice] = 'New runners registration token has been generated!'
+ redirect_to namespace_project_settings_ci_cd_path
+ end
+
private
def update_params
diff --git a/app/controllers/projects/settings/repository_controller.rb b/app/controllers/projects/settings/repository_controller.rb
index 6d83d24cdb8..1d76c90d4eb 100644
--- a/app/controllers/projects/settings/repository_controller.rb
+++ b/app/controllers/projects/settings/repository_controller.rb
@@ -14,10 +14,10 @@ module Projects
@new_deploy_token = DeployTokens::CreateService.new(@project, current_user, deploy_token_params).execute
if @new_deploy_token.persisted?
- flash[:notice] = s_('DeployTokens|Your new project deploy token has been created.')
+ flash.now[:notice] = s_('DeployTokens|Your new project deploy token has been created.')
end
- redirect_to action: :show
+ render_show
end
private
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index a9417369ca2..ee438e160f2 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -366,6 +366,7 @@ class ProjectsController < Projects::ApplicationController
repository_access_level
snippets_access_level
wiki_access_level
+ pages_access_level
]
]
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index e509098d778..d16240af404 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -29,11 +29,17 @@ class UsersController < ApplicationController
format.json do
load_events
- pager_json("events/_events", @events.count)
+ pager_json("events/_events", @events.count, events: @events)
end
end
end
+ def activity
+ respond_to do |format|
+ format.html { render 'show' }
+ end
+ end
+
def groups
load_groups
@@ -53,9 +59,7 @@ class UsersController < ApplicationController
respond_to do |format|
format.html { render 'show' }
format.json do
- render json: {
- html: view_to_html_string("shared/projects/_list", projects: @projects)
- }
+ pager_json("shared/projects/_list", @projects.count, projects: @projects)
end
end
end
@@ -125,6 +129,7 @@ class UsersController < ApplicationController
@projects =
PersonalProjectsFinder.new(user).execute(current_user)
.page(params[:page])
+ .per(params[:limit])
prepare_projects_for_rendering(@projects)
end
diff --git a/app/finders/group_labels_finder.rb b/app/finders/group_labels_finder.rb
index 903023033ed..a668a0f0fae 100644
--- a/app/finders/group_labels_finder.rb
+++ b/app/finders/group_labels_finder.rb
@@ -1,17 +1,29 @@
# frozen_string_literal: true
class GroupLabelsFinder
- attr_reader :group, :params
+ attr_reader :current_user, :group, :params
- def initialize(group, params = {})
+ def initialize(current_user, group, params = {})
+ @current_user = current_user
@group = group
@params = params
end
def execute
group.labels
+ .optionally_subscribed_by(subscriber_id)
.optionally_search(params[:search])
.order_by(params[:sort])
.page(params[:page])
end
+
+ private
+
+ def subscriber_id
+ current_user&.id if subscribed?
+ end
+
+ def subscribed?
+ params[:subscribed] == 'true'
+ end
end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 9e24154e4b6..1f98ecf95ca 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -363,6 +363,7 @@ class IssuableFinder
def use_cte_for_search?
return false unless search
return false unless Gitlab::Database.postgresql?
+ return false unless Feature.enabled?(:use_cte_for_group_issues_search, default_enabled: true)
params[:use_cte_for_search]
end
@@ -428,6 +429,10 @@ class IssuableFinder
params[:milestone_title] == Milestone::Upcoming.name
end
+ def filter_by_any_milestone?
+ params[:milestone_title] == Milestone::Any.title
+ end
+
def filter_by_started_milestone?
params[:milestone_title] == Milestone::Started.name
end
@@ -437,6 +442,8 @@ class IssuableFinder
if milestones?
if filter_by_no_milestone?
items = items.left_joins_milestones.where(milestone_id: [-1, nil])
+ elsif filter_by_any_milestone?
+ items = items.any_milestone
elsif filter_by_upcoming_milestone?
upcoming_ids = Milestone.upcoming_ids_by_projects(projects(items))
items = items.left_joins_milestones.where(milestone_id: upcoming_ids)
diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb
index 770e0bfe1a3..abdc47b9866 100644
--- a/app/finders/issues_finder.rb
+++ b/app/finders/issues_finder.rb
@@ -120,9 +120,13 @@ class IssuesFinder < IssuableFinder
return @user_can_see_all_confidential_issues = true if current_user.full_private_access?
@user_can_see_all_confidential_issues =
- project? &&
- project &&
- project.team.max_member_access(current_user.id) >= CONFIDENTIAL_ACCESS_LEVEL
+ if project? && project
+ project.team.max_member_access(current_user.id) >= CONFIDENTIAL_ACCESS_LEVEL
+ elsif group
+ group.max_member_access_for_user(current_user) >= CONFIDENTIAL_ACCESS_LEVEL
+ else
+ false
+ end
end
def user_cannot_see_confidential_issues?
diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb
index 08fc2968e77..d000af21be3 100644
--- a/app/finders/labels_finder.rb
+++ b/app/finders/labels_finder.rb
@@ -17,6 +17,7 @@ class LabelsFinder < UnionFinder
@skip_authorization = skip_authorization
items = find_union(label_ids, Label) || Label.none
items = with_title(items)
+ items = by_subscription(items)
items = by_search(items)
sort(items)
end
@@ -84,6 +85,18 @@ class LabelsFinder < UnionFinder
labels.search(params[:search])
end
+ def by_subscription(labels)
+ labels.optionally_subscribed_by(subscriber_id)
+ end
+
+ def subscriber_id
+ current_user&.id if subscribed?
+ end
+
+ def subscribed?
+ params[:subscribed] == 'true'
+ end
+
# Gets redacted array of group ids
# which can include the ancestors and descendants of the requested group.
def group_ids_for(group)
@@ -116,7 +129,7 @@ class LabelsFinder < UnionFinder
end
def project?
- params[:project_id].present?
+ params[:project].present? || params[:project_id].present?
end
def projects?
@@ -139,7 +152,7 @@ class LabelsFinder < UnionFinder
return @project if defined?(@project)
if project?
- @project = Project.find(params[:project_id])
+ @project = params[:project] || Project.find(params[:project_id])
@project = nil unless authorized_to_read_labels?(@project)
else
@project = nil
diff --git a/app/finders/license_template_finder.rb b/app/finders/license_template_finder.rb
index 196922709f7..d735a4c1d69 100644
--- a/app/finders/license_template_finder.rb
+++ b/app/finders/license_template_finder.rb
@@ -5,33 +5,47 @@
# Used to find license templates, which may come from a variety of external
# sources
#
-# Arguments:
+# Params can be any of the following:
# popular: boolean. When set to true, only "popular" licenses are shown. When
# false, all licenses except popular ones are shown. When nil (the
# default), *all* licenses will be shown.
+# name: string. If set, return a single license matching that name (or nil)
class LicenseTemplateFinder
- attr_reader :params
+ include Gitlab::Utils::StrongMemoize
- def initialize(params = {})
+ attr_reader :project, :params
+
+ def initialize(project, params = {})
+ @project = project
@params = params
end
def execute
- Licensee::License.all(featured: popular_only?).map do |license|
- LicenseTemplate.new(
- id: license.key,
- name: license.name,
- nickname: license.nickname,
- category: (license.featured? ? :Popular : :Other),
- content: license.content,
- url: license.url,
- meta: license.meta
- )
+ if params[:name]
+ vendored_licenses.find { |template| template.key == params[:name] }
+ else
+ vendored_licenses
end
end
private
+ def vendored_licenses
+ strong_memoize(:vendored_licenses) do
+ Licensee::License.all(featured: popular_only?).map do |license|
+ LicenseTemplate.new(
+ key: license.key,
+ name: license.name,
+ nickname: license.nickname,
+ category: (license.featured? ? :Popular : :Other),
+ content: license.content,
+ url: license.url,
+ meta: license.meta
+ )
+ end
+ end
+ end
+
def popular_only?
params.fetch(:popular, nil)
end
diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb
index 50c051c3aa1..e190d5d90c9 100644
--- a/app/finders/merge_requests_finder.rb
+++ b/app/finders/merge_requests_finder.rb
@@ -66,10 +66,6 @@ class MergeRequestsFinder < IssuableFinder
items.where(target_branch: target_branch)
end
- def item_project_ids(items)
- items&.reorder(nil)&.select(:target_project_id)
- end
-
def by_wip(items)
if params[:wip] == 'yes'
items.where(wip_match(items.arel_table))
diff --git a/app/finders/pending_todos_finder.rb b/app/finders/pending_todos_finder.rb
new file mode 100644
index 00000000000..c21d90c9182
--- /dev/null
+++ b/app/finders/pending_todos_finder.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+# Finder for retrieving the pending todos of a user, optionally filtered using
+# various fields.
+#
+# While this finder is a bit more verbose compared to use
+# `where(params.slice(...))`, it allows us to decouple the input parameters from
+# the actual column names. For example, if we ever decide to use separate
+# columns for target types (e.g. `issue_id`, `merge_request_id`, etc), we no
+# longer need to change _everything_ that uses this finder. Instead, we just
+# change the various `by_*` methods in this finder, without having to touch
+# everything that uses it.
+class PendingTodosFinder
+ attr_reader :current_user, :params
+
+ # current_user - The user to retrieve the todos for.
+ # params - A Hash containing columns and values to use for filtering todos.
+ def initialize(current_user, params = {})
+ @current_user = current_user
+ @params = params
+ end
+
+ def execute
+ todos = current_user.todos.pending
+ todos = by_project(todos)
+ todos = by_target_id(todos)
+ todos = by_target_type(todos)
+ todos = by_commit_id(todos)
+
+ todos
+ end
+
+ def by_project(todos)
+ if (id = params[:project_id])
+ todos.for_project(id)
+ else
+ todos
+ end
+ end
+
+ def by_target_id(todos)
+ if (id = params[:target_id])
+ todos.for_target(id)
+ else
+ todos
+ end
+ end
+
+ def by_target_type(todos)
+ if (type = params[:target_type])
+ todos.for_type(type)
+ else
+ todos
+ end
+ end
+
+ def by_commit_id(todos)
+ if (id = params[:commit_id])
+ todos.for_commit(id)
+ else
+ todos
+ end
+ end
+end
diff --git a/app/finders/template_finder.rb b/app/finders/template_finder.rb
index c92ee9ca9ac..3e483716064 100644
--- a/app/finders/template_finder.rb
+++ b/app/finders/template_finder.rb
@@ -1,29 +1,32 @@
# frozen_string_literal: true
class TemplateFinder
- VENDORED_TEMPLATES = {
+ include Gitlab::Utils::StrongMemoize
+
+ VENDORED_TEMPLATES = HashWithIndifferentAccess.new(
dockerfiles: ::Gitlab::Template::DockerfileTemplate,
gitignores: ::Gitlab::Template::GitignoreTemplate,
gitlab_ci_ymls: ::Gitlab::Template::GitlabCiYmlTemplate
- }.freeze
+ ).freeze
class << self
- def build(type, params = {})
- if type == :licenses
- LicenseTemplateFinder.new(params) # rubocop: disable CodeReuse/Finder
+ def build(type, project, params = {})
+ if type.to_s == 'licenses'
+ LicenseTemplateFinder.new(project, params) # rubocop: disable CodeReuse/Finder
else
- new(type, params)
+ new(type, project, params)
end
end
end
- attr_reader :type, :params
+ attr_reader :type, :project, :params
attr_reader :vendored_templates
private :vendored_templates
- def initialize(type, params = {})
+ def initialize(type, project, params = {})
@type = type
+ @project = project
@params = params
@vendored_templates = VENDORED_TEMPLATES.fetch(type)
diff --git a/app/finders/todos_finder.rb b/app/finders/todos_finder.rb
index 74baf79e4f2..d001e18fea9 100644
--- a/app/finders/todos_finder.rb
+++ b/app/finders/todos_finder.rb
@@ -23,6 +23,8 @@ class TodosFinder
NONE = '0'.freeze
+ TODO_TYPES = Set.new(%w(Issue MergeRequest Epic)).freeze
+
attr_accessor :current_user, :params
def initialize(current_user, params = {})
@@ -45,6 +47,13 @@ class TodosFinder
sort(items)
end
+ # Returns `true` if the current user has any todos for the given target.
+ #
+ # target - The value of the `target_type` column, such as `Issue`.
+ def any_for_target?(target)
+ current_user.todos.any_for_target?(target)
+ end
+
private
def action_id?
@@ -72,14 +81,11 @@ class TodosFinder
end
def author
- return @author if defined?(@author)
-
- @author =
+ strong_memoize(:author) do
if author? && params[:author_id] != NONE
User.find(params[:author_id])
- else
- nil
end
+ end
end
def project?
@@ -91,17 +97,9 @@ class TodosFinder
end
def project
- return @project if defined?(@project)
-
- if project?
- @project = Project.find(params[:project_id])
-
- @project = nil if @project.pending_delete?
- else
- @project = nil
+ strong_memoize(:project) do
+ Project.find_without_deleted(params[:project_id]) if project?
end
-
- @project
end
def group
@@ -111,7 +109,7 @@ class TodosFinder
end
def type?
- type.present? && %w(Issue MergeRequest Epic).include?(type)
+ type.present? && TODO_TYPES.include?(type)
end
def type
@@ -119,77 +117,66 @@ class TodosFinder
end
def sort(items)
- params[:sort] ? items.sort_by_attribute(params[:sort]) : items.order_id_desc
+ if params[:sort]
+ items.sort_by_attribute(params[:sort])
+ else
+ items.order_id_desc
+ end
end
- # rubocop: disable CodeReuse/ActiveRecord
def by_action(items)
if action?
- items = items.where(action: to_action_id)
+ items.for_action(to_action_id)
+ else
+ items
end
-
- items
end
- # rubocop: enable CodeReuse/ActiveRecord
- # rubocop: disable CodeReuse/ActiveRecord
def by_action_id(items)
if action_id?
- items = items.where(action: action_id)
+ items.for_action(action_id)
+ else
+ items
end
-
- items
end
- # rubocop: enable CodeReuse/ActiveRecord
- # rubocop: disable CodeReuse/ActiveRecord
def by_author(items)
if author?
- items = items.where(author_id: author.try(:id))
+ items.for_author(author)
+ else
+ items
end
-
- items
end
- # rubocop: enable CodeReuse/ActiveRecord
- # rubocop: disable CodeReuse/ActiveRecord
def by_project(items)
if project?
- items = items.where(project: project)
+ items.for_project(project)
+ else
+ items
end
-
- items
end
- # rubocop: enable CodeReuse/ActiveRecord
- # rubocop: disable CodeReuse/ActiveRecord
def by_group(items)
- return items unless group?
-
- groups = group.self_and_descendants
- project_todos = items.where(project_id: Project.where(group: groups).select(:id))
- group_todos = items.where(group_id: groups.select(:id))
-
- Todo.from_union([project_todos, group_todos])
+ if group?
+ items.for_group_and_descendants(group)
+ else
+ items
+ end
end
- # rubocop: enable CodeReuse/ActiveRecord
def by_state(items)
- case params[:state].to_s
- when 'done'
+ if params[:state].to_s == 'done'
items.done
else
items.pending
end
end
- # rubocop: disable CodeReuse/ActiveRecord
def by_type(items)
if type?
- items = items.where(target_type: type)
+ items.for_type(type)
+ else
+ items
end
-
- items
end
- # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/finders/user_recent_events_finder.rb b/app/finders/user_recent_events_finder.rb
index eeca5026da1..3f2e813d381 100644
--- a/app/finders/user_recent_events_finder.rb
+++ b/app/finders/user_recent_events_finder.rb
@@ -31,7 +31,7 @@ class UserRecentEventsFinder
recent_events(params[:offset] || 0)
.joins(:project)
.with_associations
- .limit_recent(LIMIT, params[:offset])
+ .limit_recent(params[:limit].presence || LIMIT, params[:offset])
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/finders/users_with_pending_todos_finder.rb b/app/finders/users_with_pending_todos_finder.rb
new file mode 100644
index 00000000000..461bd92a366
--- /dev/null
+++ b/app/finders/users_with_pending_todos_finder.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+# Finder that given a target (e.g. an issue) finds all the users that have
+# pending todos for said target.
+class UsersWithPendingTodosFinder
+ attr_reader :target
+
+ # target - The target, such as an Issue or MergeRequest.
+ def initialize(target)
+ @target = target
+ end
+
+ def execute
+ User.for_todos(target.todos.pending)
+ end
+end
diff --git a/app/graphql/types/permission_types/project.rb b/app/graphql/types/permission_types/project.rb
index 066ce64a254..ab37c282fe5 100644
--- a/app/graphql/types/permission_types/project.rb
+++ b/app/graphql/types/permission_types/project.rb
@@ -16,7 +16,7 @@ module Types
:create_deployment, :push_to_delete_protected_branch,
:admin_wiki, :admin_project, :update_pages,
:admin_remote_mirror, :create_label, :update_wiki, :destroy_wiki,
- :create_pages, :destroy_pages
+ :create_pages, :destroy_pages, :read_pages_content
end
end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 32fc8e5e9ce..4f91e3e4117 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -292,7 +292,8 @@ module ApplicationHelper
mergeRequests: merge_requests_project_autocomplete_sources_path(object),
labels: labels_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]),
milestones: milestones_project_autocomplete_sources_path(object),
- commands: commands_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id])
+ commands: commands_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]),
+ snippets: snippets_project_autocomplete_sources_path(object)
}
end
end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 9cbd5b5f785..883e5ddff57 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -159,10 +159,6 @@ module BlobHelper
end
end
- def licenses_for_select
- @licenses_for_select ||= template_dropdown_names(TemplateFinder.build(:licenses).execute)
- end
-
def ref_project
@ref_project ||= @target_project || @project
end
@@ -173,29 +169,34 @@ module BlobHelper
categories.each_with_object({}) do |category, hash|
hash[category] = grouped[category].map do |item|
- { name: item.name, id: item.id }
+ { name: item.name, id: item.key }
end
end
end
private :template_dropdown_names
- def gitignore_names
- @gitignore_names ||= template_dropdown_names(TemplateFinder.build(:gitignores).execute)
+ def licenses_for_select(project = @project)
+ @licenses_for_select ||= template_dropdown_names(TemplateFinder.build(:licenses, project).execute)
+ end
+
+ def gitignore_names(project = @project)
+ @gitignore_names ||= template_dropdown_names(TemplateFinder.build(:gitignores, project).execute)
end
- def gitlab_ci_ymls
- @gitlab_ci_ymls ||= template_dropdown_names(TemplateFinder.build(:gitlab_ci_ymls).execute)
+ def gitlab_ci_ymls(project = @project)
+ @gitlab_ci_ymls ||= template_dropdown_names(TemplateFinder.build(:gitlab_ci_ymls, project).execute)
end
- def dockerfile_names
- @dockerfile_names ||= template_dropdown_names(TemplateFinder.build(:dockerfiles).execute)
+ def dockerfile_names(project = @project)
+ @dockerfile_names ||= template_dropdown_names(TemplateFinder.build(:dockerfiles, project).execute)
end
- def blob_editor_paths
+ def blob_editor_paths(project = @project)
{
'relative-url-root' => Rails.application.config.relative_url_root,
'assets-prefix' => Gitlab::Application.config.assets.prefix,
- 'blob-language' => @blob && @blob.language.try(:ace_mode)
+ 'blob-language' => @blob && @blob.language.try(:ace_mode),
+ 'project-id' => project.id
}
end
diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb
index e3b74f443f7..be1e7016a1e 100644
--- a/app/helpers/boards_helper.rb
+++ b/app/helpers/boards_helper.rb
@@ -59,8 +59,8 @@ module BoardsHelper
{
toggle: "dropdown",
- list_labels_path: labels_filter_path(true, include_ancestor_groups: true),
- labels: labels_filter_path(true, include_descendant_groups: include_descendant_groups),
+ list_labels_path: labels_filter_path_with_defaults(only_group_labels: true, include_ancestor_groups: true),
+ labels: labels_filter_path_with_defaults(only_group_labels: true, include_descendant_groups: include_descendant_groups),
labels_endpoint: @labels_endpoint,
namespace_path: @namespace_path,
project_path: @project&.path,
diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb
index a67c91b21d7..19eb763e1de 100644
--- a/app/helpers/clusters_helper.rb
+++ b/app/helpers/clusters_helper.rb
@@ -13,8 +13,4 @@ module ClustersHelper
render 'projects/clusters/gcp_signup_offer_banner'
end
end
-
- def rbac_clusters_feature_enabled?
- Feature.enabled?(:rbac_clusters)
- end
end
diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb
index 463f4145bdd..33c53021c11 100644
--- a/app/helpers/dashboard_helper.rb
+++ b/app/helpers/dashboard_helper.rb
@@ -21,6 +21,29 @@ module DashboardHelper
links.any? { |link| dashboard_nav_link?(link) }
end
+ def controller_action_to_child_dashboards(controller = controller_name, action = action_name)
+ case "#{controller}##{action}"
+ when 'projects#index', 'root#index', 'projects#starred', 'projects#trending'
+ %w(projects stars)
+ when 'dashboard#activity'
+ %w(starred_project_activity project_activity)
+ when 'groups#index'
+ %w(groups)
+ when 'todos#index'
+ %w(todos)
+ when 'dashboard#issues'
+ %w(issues)
+ when 'dashboard#merge_requests'
+ %w(merge_requests)
+ else
+ []
+ end
+ end
+
+ def user_default_dashboard?(user = current_user)
+ controller_action_to_child_dashboards.any? {|dashboard| dashboard == user.dashboard }
+ end
+
private
def get_dashboard_nav_links
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index 6c51739ba1a..76ed8efe2c6 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -131,20 +131,26 @@ module LabelsHelper
end
end
- def labels_filter_path(only_group_labels = false, include_ancestor_groups: true, include_descendant_groups: false)
- project = @target_project || @project
-
+ def labels_filter_path_with_defaults(only_group_labels: false, include_ancestor_groups: true, include_descendant_groups: false)
options = {}
options[:include_ancestor_groups] = include_ancestor_groups if include_ancestor_groups
options[:include_descendant_groups] = include_descendant_groups if include_descendant_groups
+ options[:only_group_labels] = only_group_labels if only_group_labels && @group
+ options[:format] = :json
+
+ labels_filter_path(options)
+ end
+
+ def labels_filter_path(options = {})
+ project = @target_project || @project
+ format = options.delete(:format) || :html
if project
- project_labels_path(project, :json, options)
+ project_labels_path(project, format, options)
elsif @group
- options[:only_group_labels] = only_group_labels if only_group_labels
- group_labels_path(@group, :json, options)
+ group_labels_path(@group, format, options)
else
- dashboard_labels_path(:json)
+ dashboard_labels_path(format, options)
end
end
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index a80c8f273a8..033686823a2 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -142,7 +142,7 @@ module NotesHelper
def initial_notes_data(autocomplete)
{
notesUrl: notes_url,
- notesIds: @notes.map(&:id),
+ notesIds: @noteable.notes.pluck(:id), # rubocop: disable CodeReuse/ActiveRecord
now: Time.now.to_i,
diffView: diff_view,
enableGFM: {
@@ -178,7 +178,7 @@ module NotesHelper
notesPath: notes_url,
totalNotes: issuable.discussions.length,
lastFetchedAt: Time.now.to_i
- }.to_json
+ }
end
def discussion_resolved_intro(discussion)
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 8b17e6ef75d..0016f89db5c 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -454,6 +454,7 @@ module ProjectsHelper
buildsAccessLevel: feature.builds_access_level,
wikiAccessLevel: feature.wiki_access_level,
snippetsAccessLevel: feature.snippets_access_level,
+ pagesAccessLevel: feature.pages_access_level,
containerRegistryEnabled: !!project.container_registry_enabled,
lfsEnabled: !!project.lfs_enabled
}
@@ -468,7 +469,10 @@ module ProjectsHelper
registryAvailable: Gitlab.config.registry.enabled,
registryHelpPath: help_page_path('user/project/container_registry'),
lfsAvailable: Gitlab.config.lfs.enabled,
- lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
+ lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs'),
+ pagesAvailable: Gitlab.config.pages.enabled,
+ pagesAccessControlEnabled: Gitlab.config.pages.access_control,
+ pagesHelpPath: help_page_path('user/project/pages/index.md')
}
end
diff --git a/app/helpers/repository_languages_helper.rb b/app/helpers/repository_languages_helper.rb
index c1505b52808..cf7eee7fff3 100644
--- a/app/helpers/repository_languages_helper.rb
+++ b/app/helpers/repository_languages_helper.rb
@@ -13,6 +13,7 @@ module RepositoryLanguagesHelper
content_tag :div, nil,
class: "progress-bar has-tooltip",
style: "width: #{lang.share}%; background-color:#{lang.color}",
- title: lang.name
+ data: { html: true },
+ title: "<span class=\"repository-language-bar-tooltip-language\">#{escape_javascript(lang.name)}</span>&nbsp;<span class=\"repository-language-bar-tooltip-share\">#{lang.share.round(1)}%</span>"
end
end
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index bcd91f619c8..42b533ad772 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -76,7 +76,7 @@ module UsersHelper
tabs = []
if can?(current_user, :read_user_profile, @user)
- tabs += [:activity, :groups, :contributed, :projects, :snippets]
+ tabs += [:overview, :activity, :groups, :contributed, :projects, :snippets]
end
tabs
diff --git a/app/models/ci/artifact_blob.rb b/app/models/ci/artifact_blob.rb
index cd0b31482d2..d87d6a5cb2f 100644
--- a/app/models/ci/artifact_blob.rb
+++ b/app/models/ci/artifact_blob.rb
@@ -4,7 +4,7 @@ module Ci
class ArtifactBlob
include BlobLike
- EXTENSIONS_SERVED_BY_PAGES = %w[.html .htm .txt .json].freeze
+ EXTENSIONS_SERVED_BY_PAGES = %w[.html .htm .txt .json .log].freeze
attr_reader :entry
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 9b0bb2e05fc..f244ffc1c3a 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -263,7 +263,7 @@ module Ci
end
def schedulable?
- Feature.enabled?('ci_enable_scheduled_build') &&
+ Feature.enabled?('ci_enable_scheduled_build', default_enabled: true) &&
self.when == 'delayed' && options[:start_in].present?
end
@@ -792,6 +792,9 @@ module Ci
variables.append(key: 'GITLAB_FEATURES', value: project.licensed_features.join(','))
variables.append(key: 'CI_SERVER_NAME', value: 'GitLab')
variables.append(key: 'CI_SERVER_VERSION', value: Gitlab::VERSION)
+ variables.append(key: 'CI_SERVER_VERSION_MAJOR', value: gitlab_version_info.major.to_s)
+ variables.append(key: 'CI_SERVER_VERSION_MINOR', value: gitlab_version_info.minor.to_s)
+ variables.append(key: 'CI_SERVER_VERSION_PATCH', value: gitlab_version_info.patch.to_s)
variables.append(key: 'CI_SERVER_REVISION', value: Gitlab.revision)
variables.append(key: 'CI_JOB_NAME', value: name)
variables.append(key: 'CI_JOB_STAGE', value: stage)
@@ -806,6 +809,10 @@ module Ci
end
end
+ def gitlab_version_info
+ @gitlab_version_info ||= Gitlab::VersionInfo.parse(Gitlab::VERSION)
+ end
+
def legacy_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables.append(key: 'CI_BUILD_REF', value: sha)
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 765943104a0..17024e8a0af 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -633,6 +633,22 @@ module Ci
end
end
+ def branch_updated?
+ strong_memoize(:branch_updated) do
+ push_details.branch_updated?
+ end
+ end
+
+ def modified_paths
+ strong_memoize(:modified_paths) do
+ push_details.modified_paths
+ end
+ end
+
+ def default_branch?
+ ref == project.default_branch
+ end
+
private
def ci_yaml_from_repo
@@ -656,6 +672,22 @@ module Ci
Gitlab::DataBuilder::Pipeline.build(self)
end
+ def push_details
+ strong_memoize(:push_details) do
+ Gitlab::Git::Push.new(project, before_sha, sha, push_ref)
+ end
+ end
+
+ def push_ref
+ if branch?
+ Gitlab::Git::BRANCH_REF_PREFIX + ref.to_s
+ elsif tag?
+ Gitlab::Git::TAG_REF_PREFIX + ref.to_s
+ else
+ raise ArgumentError, 'Invalid pipeline type!'
+ end
+ end
+
def latest_builds_status
return 'failed' unless yaml_errors.blank?
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index 811261a252e..58f3fe2460a 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -66,7 +66,7 @@ module Ci
transition any - [:manual] => :manual
end
- event :schedule do
+ event :delay do
transition any - [:scheduled] => :scheduled
end
end
@@ -81,7 +81,7 @@ module Ci
when 'failed' then drop
when 'canceled' then cancel
when 'manual' then block
- when 'scheduled' then schedule
+ when 'scheduled' then delay
when 'skipped', nil then skip
else
raise HasStatus::UnknownStatusError,
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 49c36ad9d3f..a61ed03cf35 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -319,7 +319,11 @@ class Commit
def status(ref = nil)
return @statuses[ref] if @statuses.key?(ref)
- @statuses[ref] = project.pipelines.latest_status_per_commit(id, ref)[id]
+ @statuses[ref] = status_for_project(ref, project)
+ end
+
+ def status_for_project(ref, pipeline_project)
+ pipeline_project.pipelines.latest_status_per_commit(id, ref)[id]
end
def set_status_for_ref(ref, status)
diff --git a/app/models/concerns/diff_positionable_note.rb b/app/models/concerns/diff_positionable_note.rb
new file mode 100644
index 00000000000..b61bf29e6ad
--- /dev/null
+++ b/app/models/concerns/diff_positionable_note.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+module DiffPositionableNote
+ extend ActiveSupport::Concern
+
+ included do
+ before_validation :set_original_position, on: :create
+ before_validation :update_position, on: :create, if: :on_text?
+
+ serialize :original_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
+ serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
+ serialize :change_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
+ end
+
+ %i(original_position position change_position).each do |meth|
+ define_method "#{meth}=" do |new_position|
+ if new_position.is_a?(String)
+ new_position = JSON.parse(new_position) rescue nil
+ end
+
+ if new_position.is_a?(Hash)
+ new_position = new_position.with_indifferent_access
+ new_position = Gitlab::Diff::Position.new(new_position)
+ end
+
+ return if new_position == read_attribute(meth)
+
+ super(new_position)
+ end
+ end
+
+ def on_text?
+ position&.position_type == "text"
+ end
+
+ def on_image?
+ position&.position_type == "image"
+ end
+
+ def supported?
+ for_commit? || self.noteable.has_complete_diff_refs?
+ end
+
+ def active?(diff_refs = nil)
+ return false unless supported?
+ return true if for_commit?
+
+ diff_refs ||= noteable.diff_refs
+
+ self.position.diff_refs == diff_refs
+ end
+
+ def set_original_position
+ return unless position
+
+ self.original_position = self.position.dup unless self.original_position&.complete?
+ end
+
+ def update_position
+ return unless supported?
+ return if for_commit?
+
+ return if active?
+ return unless position
+
+ tracer = Gitlab::Diff::PositionTracer.new(
+ project: self.project,
+ old_diff_refs: self.position.diff_refs,
+ new_diff_refs: self.noteable.diff_refs,
+ paths: self.position.paths
+ )
+
+ result = tracer.trace(self.position)
+ return unless result
+
+ if result[:outdated]
+ self.change_position = result[:position]
+ else
+ self.position = result[:position]
+ end
+ end
+end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 5f65fceb7af..2aa52bbaeea 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -76,6 +76,7 @@ module Issuable
scope :recent, -> { reorder(id: :desc) }
scope :of_projects, ->(ids) { where(project_id: ids) }
scope :of_milestones, ->(ids) { where(milestone_id: ids) }
+ scope :any_milestone, -> { where('milestone_id IS NOT NULL') }
scope :with_milestone, ->(title) { left_joins_milestones.where(milestones: { title: title }) }
scope :opened, -> { with_state(:opened) }
scope :only_opened, -> { with_state(:opened) }
diff --git a/app/models/concerns/subscribable.rb b/app/models/concerns/subscribable.rb
index 1d0a61364b0..92a5c1112af 100644
--- a/app/models/concerns/subscribable.rb
+++ b/app/models/concerns/subscribable.rb
@@ -31,9 +31,11 @@ module Subscribable
end
def subscribers(project)
- subscriptions_available(project)
- .where(subscribed: true)
- .map(&:user)
+ relation = subscriptions_available(project)
+ .where(subscribed: true)
+ .select(:user_id)
+
+ User.where(id: relation)
end
def toggle_subscription(user, project = nil)
diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb
index 047d353b4b5..95694377fe3 100644
--- a/app/models/diff_note.rb
+++ b/app/models/diff_note.rb
@@ -5,14 +5,11 @@
# A note of this type can be resolvable.
class DiffNote < Note
include NoteOnDiff
+ include DiffPositionableNote
include Gitlab::Utils::StrongMemoize
NOTEABLE_TYPES = %w(MergeRequest Commit).freeze
- serialize :original_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
- serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
- serialize :change_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
-
validates :original_position, presence: true
validates :position, presence: true
validates :line_code, presence: true, line_code: true, if: :on_text?
@@ -21,8 +18,6 @@ class DiffNote < Note
validate :verify_supported
validate :diff_refs_match_commit, if: :for_commit?
- before_validation :set_original_position, on: :create
- before_validation :update_position, on: :create, if: :on_text?
before_validation :set_line_code, if: :on_text?
after_save :keep_around_commits
after_commit :create_diff_file, on: :create
@@ -31,31 +26,6 @@ class DiffNote < Note
DiffDiscussion
end
- %i(original_position position change_position).each do |meth|
- define_method "#{meth}=" do |new_position|
- if new_position.is_a?(String)
- new_position = JSON.parse(new_position) rescue nil
- end
-
- if new_position.is_a?(Hash)
- new_position = new_position.with_indifferent_access
- new_position = Gitlab::Diff::Position.new(new_position)
- end
-
- return if new_position == read_attribute(meth)
-
- super(new_position)
- end
- end
-
- def on_text?
- position.position_type == "text"
- end
-
- def on_image?
- position.position_type == "image"
- end
-
def create_diff_file
return unless should_create_diff_file?
@@ -87,15 +57,6 @@ class DiffNote < Note
self.diff_file.line_code(self.diff_line)
end
- def active?(diff_refs = nil)
- return false unless supported?
- return true if for_commit?
-
- diff_refs ||= noteable.diff_refs
-
- self.position.diff_refs == diff_refs
- end
-
def created_at_diff?(diff_refs)
return false unless supported?
return true if for_commit?
@@ -141,37 +102,10 @@ class DiffNote < Note
for_commit? || self.noteable.has_complete_diff_refs?
end
- def set_original_position
- self.original_position = self.position.dup unless self.original_position&.complete?
- end
-
def set_line_code
self.line_code = self.position.line_code(self.project.repository)
end
- def update_position
- return unless supported?
- return if for_commit?
-
- return if active?
-
- tracer = Gitlab::Diff::PositionTracer.new(
- project: self.project,
- old_diff_refs: self.position.diff_refs,
- new_diff_refs: self.noteable.diff_refs,
- paths: self.position.paths
- )
-
- result = tracer.trace(self.position)
- return unless result
-
- if result[:outdated]
- self.change_position = result[:position]
- else
- self.position = result[:position]
- end
- end
-
def verify_supported
return if supported?
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index 68ba4b213b2..b2fb79bc7ed 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -37,38 +37,4 @@ class WebHook < ActiveRecord::Base
def allow_local_requests?
false
end
-
- # In 11.4, the web_hooks table has both `token` and `encrypted_token` fields.
- # Ensure that the encrypted version always takes precedence if present.
- alias_method :attr_encrypted_token, :token
- def token
- attr_encrypted_token.presence || read_attribute(:token)
- end
-
- # In 11.4, the web_hooks table has both `token` and `encrypted_token` fields.
- # Pending a background migration to encrypt all fields, we should just clear
- # the unencrypted value whenever the new value is set.
- alias_method :'attr_encrypted_token=', :'token='
- def token=(value)
- self.attr_encrypted_token = value
-
- write_attribute(:token, nil)
- end
-
- # In 11.4, the web_hooks table has both `url` and `encrypted_url` fields.
- # Ensure that the encrypted version always takes precedence if present.
- alias_method :attr_encrypted_url, :url
- def url
- attr_encrypted_url.presence || read_attribute(:url)
- end
-
- # In 11.4, the web_hooks table has both `url` and `encrypted_url` fields.
- # Pending a background migration to encrypt all fields, we should just clear
- # the unencrypted value whenever the new value is set.
- alias_method :'attr_encrypted_url=', :'url='
- def url=(value)
- self.attr_encrypted_url = value
-
- write_attribute(:url, nil)
- end
end
diff --git a/app/models/instance_configuration.rb b/app/models/instance_configuration.rb
index 7d8ce0bbd05..11289887e00 100644
--- a/app/models/instance_configuration.rb
+++ b/app/models/instance_configuration.rb
@@ -64,10 +64,10 @@ class InstanceConfiguration
end
def ssh_algorithm_md5(ssh_file_content)
- OpenSSL::Digest::MD5.hexdigest(ssh_file_content).scan(/../).join(':')
+ Gitlab::SSHPublicKey.new(ssh_file_content).fingerprint
end
def ssh_algorithm_sha256(ssh_file_content)
- OpenSSL::Digest::SHA256.hexdigest(ssh_file_content)
+ Gitlab::SSHPublicKey.new(ssh_file_content).fingerprint('SHA256')
end
end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index d13fbcf002c..4ace5d3ab97 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -170,24 +170,6 @@ class Issue < ActiveRecord::Base
"#{project.to_reference(from, full: full)}#{reference}"
end
- # All branches containing the current issue's ID, except for
- # those with a merge request open referencing the current issue.
- # rubocop: disable CodeReuse/ServiceClass
- def related_branches(current_user)
- branches_with_iid = project.repository.branch_names.select do |branch|
- branch =~ /\A#{iid}-(?!\d+-stable)/i
- end
-
- branches_with_merge_request =
- Issues::ReferencedMergeRequestsService
- .new(project, current_user)
- .referenced_merge_requests(self)
- .map(&:source_branch)
-
- branches_with_iid - branches_with_merge_request
- end
- # rubocop: enable CodeReuse/ServiceClass
-
def suggested_branch_name
return to_branch_name unless project.repository.branch_exists?(to_branch_name)
diff --git a/app/models/label.rb b/app/models/label.rb
index 9ef57a05b3e..43b49445765 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -45,6 +45,7 @@ class Label < ActiveRecord::Base
scope :on_project_boards, ->(project_id) { with_lists_and_board.where(boards: { project_id: project_id }) }
scope :order_name_asc, -> { reorder(title: :asc) }
scope :order_name_desc, -> { reorder(title: :desc) }
+ scope :subscribed_by, ->(user_id) { joins(:subscriptions).where(subscriptions: { user_id: user_id, subscribed: true }) }
def self.prioritized(project)
joins(:priorities)
@@ -74,6 +75,14 @@ class Label < ActiveRecord::Base
joins(label_priorities)
end
+ def self.optionally_subscribed_by(user_id)
+ if user_id
+ subscribed_by(user_id)
+ else
+ all
+ end
+ end
+
alias_attribute :name, :title
def self.reference_prefix
diff --git a/app/models/license_template.rb b/app/models/license_template.rb
index 693a6a89fd2..73e403f98b4 100644
--- a/app/models/license_template.rb
+++ b/app/models/license_template.rb
@@ -12,12 +12,10 @@ class LicenseTemplate
(fullname|name\sof\s(author|copyright\sowner))
[\>\}\]]}xi.freeze
- attr_reader :id, :name, :category, :nickname, :url, :meta
+ attr_reader :key, :name, :category, :nickname, :url, :meta
- alias_method :key, :id
-
- def initialize(id:, name:, category:, content:, nickname: nil, url: nil, meta: {})
- @id = id
+ def initialize(key:, name:, category:, content:, nickname: nil, url: nil, meta: {})
+ @key = key
@name = name
@category = category
@content = content
diff --git a/app/models/note.rb b/app/models/note.rb
index bea02d69b65..95e1d3afa00 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -42,8 +42,10 @@ class Note < ActiveRecord::Base
# Banzai::ObjectRenderer.
attr_accessor :redacted_note_html
- # An Array containing the number of visible references as generated by
- # Banzai::ObjectRenderer
+ # Total of all references as generated by Banzai::ObjectRenderer
+ attr_accessor :total_reference_count
+
+ # Number of user visible references as generated by Banzai::ObjectRenderer
attr_accessor :user_visible_reference_count
# Attribute used to store the attributes that have been changed by quick actions.
@@ -288,15 +290,7 @@ class Note < ActiveRecord::Base
end
def cross_reference_not_visible_for?(user)
- cross_reference? && !has_referenced_mentionables?(user)
- end
-
- def has_referenced_mentionables?(user)
- if user_visible_reference_count.present?
- user_visible_reference_count > 0
- else
- referenced_mentionables(user).any?
- end
+ cross_reference? && !all_referenced_mentionables_allowed?(user)
end
def award_emoji?
@@ -466,9 +460,18 @@ class Note < ActiveRecord::Base
self.discussion_id ||= discussion_class.discussion_id(self)
end
+ def all_referenced_mentionables_allowed?(user)
+ if user_visible_reference_count.present? && total_reference_count.present?
+ # if they are not equal, then there are private/confidential references as well
+ user_visible_reference_count > 0 && user_visible_reference_count == total_reference_count
+ else
+ referenced_mentionables(user).any?
+ end
+ end
+
def force_cross_reference_regex_check?
return unless system?
- SystemNoteMetadata::TYPES_WITH_CROSS_REFERENCES.include?(system_note_metadata&.action)
+ system_note_metadata&.cross_reference_types&.include?(system_note_metadata&.action)
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 59f088156c7..05e14c578b5 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -55,8 +55,8 @@ class Project < ActiveRecord::Base
cache_markdown_field :description, pipeline: :description
delegate :feature_available?, :builds_enabled?, :wiki_enabled?,
- :merge_requests_enabled?, :issues_enabled?, to: :project_feature,
- allow_nil: true
+ :merge_requests_enabled?, :issues_enabled?, :pages_enabled?, :public_pages?,
+ to: :project_feature, allow_nil: true
delegate :base_dir, :disk_path, :ensure_storage_path_exists, to: :storage
@@ -356,7 +356,7 @@ class Project < ActiveRecord::Base
# "enabled" here means "not disabled". It includes private features!
scope :with_feature_enabled, ->(feature) {
access_level_attribute = ProjectFeature.access_level_attribute(feature)
- with_project_feature.where(project_features: { access_level_attribute => [nil, ProjectFeature::PRIVATE, ProjectFeature::ENABLED] })
+ with_project_feature.where(project_features: { access_level_attribute => [nil, ProjectFeature::PRIVATE, ProjectFeature::ENABLED, ProjectFeature::PUBLIC] })
}
# Picks a feature where the level is exactly that given.
@@ -386,6 +386,13 @@ class Project < ActiveRecord::Base
only_integer: true,
message: 'needs to be beetween 10 minutes and 1 month' }
+ # Returns a project, if it is not about to be removed.
+ #
+ # id - The ID of the project to retrieve.
+ def self.find_without_deleted(id)
+ without_deleted.find_by_id(id)
+ end
+
# Paginates a collection using a `WHERE id < ?` condition.
#
# before - A project ID to use for filtering out projects with an equal or
@@ -418,15 +425,15 @@ class Project < ActiveRecord::Base
end
end
- # project features may be "disabled", "internal" or "enabled". If "internal",
+ # project features may be "disabled", "internal", "enabled" or "public". If "internal",
# they are only available to team members. This scope returns projects where
- # the feature is either enabled, or internal with permission for the user.
+ # the feature is either public, enabled, or internal with permission for the user.
#
# This method uses an optimised version of `with_feature_access_level` for
# logged in users to more efficiently get private projects with the given
# feature.
def self.with_feature_available_for_user(feature, user)
- visible = [nil, ProjectFeature::ENABLED]
+ visible = [nil, ProjectFeature::ENABLED, ProjectFeature::PUBLIC]
if user&.admin?
with_feature_enabled(feature)
@@ -450,6 +457,7 @@ class Project < ActiveRecord::Base
scope :joins_import_state, -> { joins("LEFT JOIN project_mirror_data import_state ON import_state.project_id = projects.id") }
scope :import_started, -> { joins_import_state.where("import_state.status = 'started' OR projects.import_status = 'started'") }
+ scope :for_group, -> (group) { where(group: group) }
class << self
# Searches for a list of projects based on the query given in `query`.
@@ -1082,31 +1090,13 @@ class Project < ActiveRecord::Base
end
def find_or_initialize_services(exceptions: [])
- services_templates = Service.where(template: true)
-
available_services_names = Service.available_services_names - exceptions
available_services = available_services_names.map do |service_name|
- service = find_service(services, service_name)
-
- if service
- service
- else
- # We should check if template for the service exists
- template = find_service(services_templates, service_name)
-
- if template.nil?
- # If no template, we should create an instance. Ex `build_gitlab_ci_service`
- public_send("build_#{service_name}_service") # rubocop:disable GitlabSecurity/PublicSend
- else
- Service.build_from_template(id, template)
- end
- end
+ find_or_initialize_service(service_name)
end
- available_services.reject do |service|
- disabled_services.include?(service.to_param)
- end
+ available_services.compact
end
def disabled_services
@@ -1114,7 +1104,20 @@ class Project < ActiveRecord::Base
end
def find_or_initialize_service(name)
- find_or_initialize_services.find { |service| service.to_param == name }
+ return if disabled_services.include?(name)
+
+ service = find_service(services, name)
+ return service if service
+
+ # We should check if template for the service exists
+ template = find_service(services_templates, name)
+
+ if template
+ Service.build_from_template(id, template)
+ else
+ # If no template, we should create an instance. Ex `build_gitlab_ci_service`
+ public_send("build_#{name}_service") # rubocop:disable GitlabSecurity/PublicSend
+ end
end
# rubocop: disable CodeReuse/ServiceClass
@@ -2277,4 +2280,8 @@ class Project < ActiveRecord::Base
check_access.call
end
end
+
+ def services_templates
+ @services_templates ||= Service.where(template: true)
+ end
end
diff --git a/app/models/project_auto_devops.rb b/app/models/project_auto_devops.rb
index dc6736dd9cd..2253ad7b543 100644
--- a/app/models/project_auto_devops.rb
+++ b/app/models/project_auto_devops.rb
@@ -5,7 +5,8 @@ class ProjectAutoDevops < ActiveRecord::Base
enum deploy_strategy: {
continuous: 0,
- manual: 1
+ manual: 1,
+ timed_incremental: 2
}
scope :enabled, -> { where(enabled: true) }
@@ -30,10 +31,7 @@ class ProjectAutoDevops < ActiveRecord::Base
value: domain.presence || instance_domain)
end
- if manual?
- variables.append(key: 'STAGING_ENABLED', value: '1')
- variables.append(key: 'INCREMENTAL_ROLLOUT_ENABLED', value: '1')
- end
+ variables.concat(deployment_strategy_default_variables)
end
end
@@ -51,4 +49,16 @@ class ProjectAutoDevops < ActiveRecord::Base
!project.public? &&
!project.deploy_tokens.find_by(name: DeployToken::GITLAB_DEPLOY_TOKEN_NAME).present?
end
+
+ def deployment_strategy_default_variables
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ if manual?
+ variables.append(key: 'STAGING_ENABLED', value: '1')
+ variables.append(key: 'INCREMENTAL_ROLLOUT_ENABLED', value: '1') # deprecated
+ variables.append(key: 'INCREMENTAL_ROLLOUT_MODE', value: 'manual')
+ elsif timed_incremental?
+ variables.append(key: 'INCREMENTAL_ROLLOUT_MODE', value: 'timed')
+ end
+ end
+ end
end
diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb
index 4a0324e8b5c..39f2b8fe0de 100644
--- a/app/models/project_feature.rb
+++ b/app/models/project_feature.rb
@@ -13,14 +13,16 @@ class ProjectFeature < ActiveRecord::Base
# Disabled: not enabled for anyone
# Private: enabled only for team members
# Enabled: enabled for everyone able to access the project
+ # Public: enabled for everyone (only allowed for pages)
#
# Permission levels
DISABLED = 0
PRIVATE = 10
ENABLED = 20
+ PUBLIC = 30
- FEATURES = %i(issues merge_requests wiki snippets builds repository).freeze
+ FEATURES = %i(issues merge_requests wiki snippets builds repository pages).freeze
class << self
def access_level_attribute(feature)
@@ -46,6 +48,7 @@ class ProjectFeature < ActiveRecord::Base
validates :project, presence: true
validate :repository_children_level
+ validate :allowed_access_levels
default_value_for :builds_access_level, value: ENABLED, allows_nil: false
default_value_for :issues_access_level, value: ENABLED, allows_nil: false
@@ -55,6 +58,9 @@ class ProjectFeature < ActiveRecord::Base
default_value_for :repository_access_level, value: ENABLED, allows_nil: false
def feature_available?(feature, user)
+ # This feature might not be behind a feature flag at all, so default to true
+ return false unless ::Feature.enabled?(feature, user, default_enabled: true)
+
get_permission(user, access_level(feature))
end
@@ -78,6 +84,16 @@ class ProjectFeature < ActiveRecord::Base
issues_access_level > DISABLED
end
+ def pages_enabled?
+ pages_access_level > DISABLED
+ end
+
+ def public_pages?
+ return true unless Gitlab.config.pages.access_control
+
+ pages_access_level == PUBLIC || pages_access_level == ENABLED && project.public?
+ end
+
private
# Validates builds and merge requests access level
@@ -92,6 +108,17 @@ class ProjectFeature < ActiveRecord::Base
%i(merge_requests_access_level builds_access_level).each(&validator)
end
+ # Validates access level for other than pages cannot be PUBLIC
+ def allowed_access_levels
+ validator = lambda do |field|
+ level = public_send(field) || ProjectFeature::ENABLED # rubocop:disable GitlabSecurity/PublicSend
+ not_allowed = level > ProjectFeature::ENABLED
+ self.errors.add(field, "cannot have public visibility level") if not_allowed
+ end
+
+ (FEATURES - %i(pages)).each {|f| validator.call("#{f}_access_level")}
+ end
+
def get_permission(user, level)
case level
when DISABLED
@@ -100,6 +127,8 @@ class ProjectFeature < ActiveRecord::Base
user && (project.team.member?(user) || user.full_private_access?)
when ENABLED
true
+ when PUBLIC
+ true
else
true
end
diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
index 66012f0da99..a69b7b4c4b6 100644
--- a/app/models/project_services/hipchat_service.rb
+++ b/app/models/project_services/hipchat_service.rb
@@ -149,7 +149,7 @@ class HipchatService < Service
context.merge!(options)
- html = Banzai.post_process(Banzai.render(text, context), context)
+ html = Banzai.render_and_post_process(text, context)
sanitized_html = sanitize(html, tags: HIPCHAT_ALLOWED_TAGS, attributes: %w[href title alt])
sanitized_html.truncate(200, separator: ' ', omission: '...')
diff --git a/app/models/system_note_metadata.rb b/app/models/system_note_metadata.rb
index 6fadbcefa53..d555ebe5322 100644
--- a/app/models/system_note_metadata.rb
+++ b/app/models/system_note_metadata.rb
@@ -9,6 +9,7 @@ class SystemNoteMetadata < ActiveRecord::Base
TYPES_WITH_CROSS_REFERENCES = %w[
commit cross_reference
close duplicate
+ moved
].freeze
ICON_TYPES = %w[
@@ -26,4 +27,8 @@ class SystemNoteMetadata < ActiveRecord::Base
def icon_types
ICON_TYPES
end
+
+ def cross_reference_types
+ TYPES_WITH_CROSS_REFERENCES
+ end
end
diff --git a/app/models/todo.rb b/app/models/todo.rb
index 265fb932f7c..7b64615f699 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -40,6 +40,13 @@ class Todo < ActiveRecord::Base
scope :pending, -> { with_state(:pending) }
scope :done, -> { with_state(:done) }
+ scope :for_action, -> (action) { where(action: action) }
+ scope :for_author, -> (author) { where(author: author) }
+ scope :for_project, -> (project) { where(project: project) }
+ scope :for_group, -> (group) { where(group: group) }
+ scope :for_type, -> (type) { where(target_type: type) }
+ scope :for_target, -> (id) { where(target_id: id) }
+ scope :for_commit, -> (id) { where(commit_id: id) }
state_machine :state, initial: :pending do
event :done do
@@ -53,6 +60,42 @@ class Todo < ActiveRecord::Base
after_save :keep_around_commit, if: :commit_id
class << self
+ # Returns all todos for the given group and its descendants.
+ #
+ # group - A `Group` to retrieve todos for.
+ #
+ # Returns an `ActiveRecord::Relation`.
+ def for_group_and_descendants(group)
+ groups = group.self_and_descendants
+
+ from_union([
+ for_project(Project.for_group(groups)),
+ for_group(groups)
+ ])
+ end
+
+ # Returns `true` if the current user has any todos for the given target.
+ #
+ # target - The value of the `target_type` column, such as `Issue`.
+ def any_for_target?(target)
+ exists?(target: target)
+ end
+
+ # Updates the state of a relation of todos to the new state.
+ #
+ # new_state - The new state of the todos.
+ #
+ # Returns an `Array` containing the IDs of the updated todos.
+ def update_state(new_state)
+ # Only update those that are not really on that state
+ base = where.not(state: new_state).except(:order)
+ ids = base.pluck(:id)
+
+ base.update_all(state: new_state)
+
+ ids
+ end
+
# Priority sorting isn't displayed in the dropdown, because we don't show
# milestones, but still show something if the user has a URL with that
# selected.
diff --git a/app/models/user.rb b/app/models/user.rb
index cd3b1c95b7e..8a7acfb73b1 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -265,6 +265,7 @@ class User < ActiveRecord::Base
scope :order_oldest_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('current_sign_in_at', 'ASC')) }
scope :confirmed, -> { where.not(confirmed_at: nil) }
scope :by_username, -> (usernames) { iwhere(username: usernames) }
+ scope :for_todos, -> (todos) { where(id: todos.select(:user_id)) }
# Limits the users to those that have TODOs, optionally in the given state.
#
@@ -1366,6 +1367,10 @@ class User < ActiveRecord::Base
!consented_usage_stats? && 7.days.ago > self.created_at && !has_current_license? && User.single_user?
end
+ def todos_limited_to(ids)
+ todos.where(id: ids)
+ end
+
# @deprecated
alias_method :owned_or_masters_groups, :owned_or_maintainers_groups
diff --git a/app/models/user_callout.rb b/app/models/user_callout.rb
index 1cd05cf3aac..2c0e8659fc1 100644
--- a/app/models/user_callout.rb
+++ b/app/models/user_callout.rb
@@ -6,7 +6,8 @@ class UserCallout < ActiveRecord::Base
enum feature_name: {
gke_cluster_integration: 1,
gcp_signup_offer: 2,
- cluster_security_warning: 3
+ cluster_security_warning: 3,
+ gold_trial: 4
}
validates :user, presence: true
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index d0e84b1aa38..a76a083bceb 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -110,6 +110,7 @@ class ProjectPolicy < BasePolicy
snippets
wiki
builds
+ pages
]
features.each do |f|
@@ -167,6 +168,7 @@ class ProjectPolicy < BasePolicy
enable :upload_file
enable :read_cycle_analytics
enable :award_emoji
+ enable :read_pages_content
end
# These abilities are not allowed to admins that are not members of the project,
@@ -286,6 +288,8 @@ class ProjectPolicy < BasePolicy
prevent(*create_read_update_admin_destroy(:merge_request))
end
+ rule { pages_disabled }.prevent :read_pages_content
+
rule { issues_disabled & merge_requests_disabled }.policy do
prevent(*create_read_update_admin_destroy(:label))
prevent(*create_read_update_admin_destroy(:milestone))
@@ -345,6 +349,7 @@ class ProjectPolicy < BasePolicy
enable :download_code
enable :download_wiki_code
enable :read_cycle_analytics
+ enable :read_pages_content
# NOTE: may be overridden by IssuePolicy
enable :read_issue
@@ -390,7 +395,11 @@ class ProjectPolicy < BasePolicy
greedy_load_subject ||= !@user.persisted?
if greedy_load_subject
- project.team.members.include?(user)
+ # We want to load all the members with one query. Calling #include? on
+ # project.team.members will perform a separate query for each user, unless
+ # project.team.members was loaded before somewhere else. Calling #to_a
+ # ensures it's always loaded before checking for membership.
+ project.team.members.to_a.include?(user)
else
# otherwise we just make a specific query for
# this particular user.
diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb
index c85b1790e73..3d508a9a407 100644
--- a/app/serializers/build_details_entity.rb
+++ b/app/serializers/build_details_entity.rb
@@ -1,10 +1,6 @@
# frozen_string_literal: true
class BuildDetailsEntity < JobEntity
- include EnvironmentHelper
- include RequestAwareEntity
- include CiStatusHelper
-
expose :coverage, :erased_at, :duration
expose :tag_list, as: :tags
expose :has_trace?, as: :has_trace
@@ -15,10 +11,6 @@ class BuildDetailsEntity < JobEntity
expose :deployment_status, if: -> (*) { build.has_environment? } do
expose :deployment_status, as: :status
- expose :icon do |build|
- ci_label_for_status(build.status)
- end
-
expose :persisted_environment, as: :environment, with: EnvironmentEntity
end
diff --git a/app/serializers/commit_entity.rb b/app/serializers/commit_entity.rb
index 396e95a03c8..a94e32478ce 100644
--- a/app/serializers/commit_entity.rb
+++ b/app/serializers/commit_entity.rb
@@ -25,4 +25,25 @@ class CommitEntity < API::Entities::Commit
expose :title_html, if: { type: :full } do |commit|
markdown_field(commit, :title)
end
+
+ expose :signature_html, if: { type: :full } do |commit|
+ render('projects/commit/_signature', signature: commit.signature) if commit.has_signature?
+ end
+
+ expose :pipeline_status_path, if: { type: :full } do |commit, options|
+ pipeline_ref = options[:pipeline_ref]
+ pipeline_project = options[:pipeline_project] || commit.project
+ next unless pipeline_ref && pipeline_project
+
+ status = commit.status_for_project(pipeline_ref, pipeline_project)
+ next unless status
+
+ pipelines_project_commit_path(pipeline_project, commit.id, ref: pipeline_ref)
+ end
+
+ def render(*args)
+ return unless request.respond_to?(:render) && request.render.respond_to?(:call)
+
+ request.render.call(*args)
+ end
end
diff --git a/app/serializers/detailed_status_entity.rb b/app/serializers/detailed_status_entity.rb
index c772c807f76..da994d78286 100644
--- a/app/serializers/detailed_status_entity.rb
+++ b/app/serializers/detailed_status_entity.rb
@@ -10,7 +10,12 @@ class DetailedStatusEntity < Grape::Entity
expose :illustration do |status|
begin
- status.illustration
+ illustration = {
+ image: ActionController::Base.helpers.image_path(status.illustration[:image])
+ }
+ illustration = status.illustration.merge(illustration)
+
+ illustration
rescue NotImplementedError
# ignored
end
@@ -25,5 +30,6 @@ class DetailedStatusEntity < Grape::Entity
expose :action_title, as: :title
expose :action_path, as: :path
expose :action_method, as: :method
+ expose :action_button_title, as: :button_title
end
end
diff --git a/app/serializers/diff_file_entity.rb b/app/serializers/diff_file_entity.rb
index c193ed10fef..63ea8e8f95f 100644
--- a/app/serializers/diff_file_entity.rb
+++ b/app/serializers/diff_file_entity.rb
@@ -116,6 +116,10 @@ class DiffFileEntity < Grape::Entity
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
new file mode 100644
index 00000000000..27fba03cb3f
--- /dev/null
+++ b/app/serializers/diff_viewer_entity.rb
@@ -0,0 +1,7 @@
+# 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
+end
diff --git a/app/serializers/diffs_entity.rb b/app/serializers/diffs_entity.rb
index 00dc55fc004..b51e4a7e6d0 100644
--- a/app/serializers/diffs_entity.rb
+++ b/app/serializers/diffs_entity.rb
@@ -18,7 +18,9 @@ class DiffsEntity < Grape::Entity
expose :commit do |diffs, options|
CommitEntity.represent options[:commit], options.merge(
type: :full,
- commit_url_params: { merge_request_iid: merge_request&.iid }
+ commit_url_params: { merge_request_iid: merge_request&.iid },
+ pipeline_ref: merge_request&.source_branch,
+ pipeline_project: merge_request&.source_project
)
end
diff --git a/app/serializers/discussion_entity.rb b/app/serializers/discussion_entity.rb
index ebe76c9fcda..b6786a0d597 100644
--- a/app/serializers/discussion_entity.rb
+++ b/app/serializers/discussion_entity.rb
@@ -27,7 +27,7 @@ class DiscussionEntity < Grape::Entity
expose :resolved?, as: :resolved
expose :resolved_by_push?, as: :resolved_by_push
- expose :resolved_by
+ expose :resolved_by, using: NoteUserEntity
expose :resolved_at
expose :resolve_path, if: -> (d, _) { d.resolvable? } do |discussion|
resolve_project_merge_request_discussion_path(discussion.project, discussion.noteable, discussion.id)
diff --git a/app/services/issues/related_branches_service.rb b/app/services/issues/related_branches_service.rb
new file mode 100644
index 00000000000..76af482b7ac
--- /dev/null
+++ b/app/services/issues/related_branches_service.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+# This service fetches all branches containing the current issue's ID, except for
+# those with a merge request open referencing the current issue.
+module Issues
+ class RelatedBranchesService < Issues::BaseService
+ def execute(issue)
+ branches_with_iid_of(issue) - branches_with_merge_request_for(issue)
+ end
+
+ private
+
+ def branches_with_merge_request_for(issue)
+ Issues::ReferencedMergeRequestsService
+ .new(project, current_user)
+ .referenced_merge_requests(issue)
+ .map(&:source_branch)
+ end
+
+ def branches_with_iid_of(issue)
+ project.repository.branch_names.select do |branch|
+ branch =~ /\A#{issue.iid}-(?!\d+-stable)/i
+ end
+ end
+ end
+end
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index aa5d8406d0f..28c3219b37b 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -57,10 +57,10 @@ module MergeRequests
# Returns all origin and fork merge requests from `@project` satisfying passed arguments.
# rubocop: disable CodeReuse/ActiveRecord
def merge_requests_for(source_branch, mr_states: [:opened])
- MergeRequest
+ @project.source_of_merge_requests
.with_state(mr_states)
- .where(source_branch: source_branch, source_project_id: @project.id)
- .preload(:source_project) # we don't need a #includes since we're just preloading for the #select
+ .where(source_branch: source_branch)
+ .preload(:source_project) # we don't need #includes since we're just preloading for the #select
.select(&:source_project)
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index bcdd752ddc4..b03d14fa3cc 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -3,16 +3,16 @@
module MergeRequests
class RefreshService < MergeRequests::BaseService
def execute(oldrev, newrev, ref)
- return true unless Gitlab::Git.branch_ref?(ref)
+ push = Gitlab::Git::Push.new(@project, oldrev, newrev, ref)
+ return true unless push.branch_push?
- do_execute(oldrev, newrev, ref)
+ refresh_merge_requests!(push)
end
private
- def do_execute(oldrev, newrev, ref)
- @oldrev, @newrev = oldrev, newrev
- @branch_name = Gitlab::Git.ref_name(ref)
+ def refresh_merge_requests!(push)
+ @push = push
Gitlab::GitalyClient.allow_n_plus_1_calls(&method(:find_new_commits))
# Be sure to close outstanding MRs before reloading them to avoid generating an
@@ -25,7 +25,7 @@ module MergeRequests
cache_merge_requests_closing_issues
# Leave a system note if a branch was deleted/added
- if branch_added? || branch_removed?
+ if @push.branch_added? || @push.branch_removed?
comment_mr_branch_presence_changed
end
@@ -54,8 +54,10 @@ module MergeRequests
# rubocop: disable CodeReuse/ActiveRecord
def post_merge_manually_merged
commit_ids = @commits.map(&:id)
- merge_requests = @project.merge_requests.preload(:latest_merge_request_diff).opened.where(target_branch: @branch_name).to_a
- merge_requests = merge_requests.select(&:diff_head_commit)
+ merge_requests = @project.merge_requests.opened
+ .preload(:latest_merge_request_diff)
+ .where(target_branch: @push.branch_name).to_a
+ .select(&:diff_head_commit)
merge_requests = merge_requests.select do |merge_request|
commit_ids.include?(merge_request.diff_head_sha) &&
@@ -70,24 +72,20 @@ module MergeRequests
end
# rubocop: enable CodeReuse/ActiveRecord
- def force_push?
- Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev)
- end
-
# Refresh merge request diff if we push to source or target branch of merge request
# Note: we should update merge requests from forks too
# rubocop: disable CodeReuse/ActiveRecord
def reload_merge_requests
merge_requests = @project.merge_requests.opened
- .by_source_or_target_branch(@branch_name).to_a
+ .by_source_or_target_branch(@push.branch_name).to_a
# Fork merge requests
merge_requests += MergeRequest.opened
- .where(source_branch: @branch_name, source_project: @project)
+ .where(source_branch: @push.branch_name, source_project: @project)
.where.not(target_project: @project).to_a
filter_merge_requests(merge_requests).each do |merge_request|
- if merge_request.source_branch == @branch_name || force_push?
+ if merge_request.source_branch == @push.branch_name || @push.force_push?
merge_request.reload_diff(current_user)
else
mr_commit_ids = merge_request.commit_shas
@@ -117,7 +115,7 @@ module MergeRequests
end
def find_new_commits
- if branch_added?
+ if @push.branch_added?
@commits = []
merge_request = merge_requests_for_source_branch.first
@@ -126,28 +124,28 @@ module MergeRequests
begin
# Since any number of commits could have been made to the restored branch,
# find the common root to see what has been added.
- common_ref = @project.repository.merge_base(merge_request.diff_head_sha, @newrev)
+ common_ref = @project.repository.merge_base(merge_request.diff_head_sha, @push.newrev)
# If the a commit no longer exists in this repo, gitlab_git throws
# a Rugged::OdbError. This is fixed in https://gitlab.com/gitlab-org/gitlab_git/merge_requests/52
- @commits = @project.repository.commits_between(common_ref, @newrev) if common_ref
+ @commits = @project.repository.commits_between(common_ref, @push.newrev) if common_ref
rescue
end
- elsif branch_removed?
+ elsif @push.branch_removed?
# No commits for a deleted branch.
@commits = []
else
- @commits = @project.repository.commits_between(@oldrev, @newrev)
+ @commits = @project.repository.commits_between(@push.oldrev, @push.newrev)
end
end
# Add comment about branches being deleted or added to merge requests
def comment_mr_branch_presence_changed
- presence = branch_added? ? :add : :delete
+ presence = @push.branch_added? ? :add : :delete
merge_requests_for_source_branch.each do |merge_request|
SystemNoteService.change_branch_presence(
merge_request, merge_request.project, @current_user,
- :source, @branch_name, presence)
+ :source, @push.branch_name, presence)
end
end
@@ -164,7 +162,7 @@ module MergeRequests
SystemNoteService.add_commits(merge_request, merge_request.project,
@current_user, new_commits,
- existing_commits, @oldrev)
+ existing_commits, @push.oldrev)
notification_service.push_to_merge_request(merge_request, @current_user, new_commits: new_commits, existing_commits: existing_commits)
end
@@ -195,7 +193,7 @@ module MergeRequests
# Call merge request webhook with update branches
def execute_mr_web_hooks
merge_requests_for_source_branch.each do |merge_request|
- execute_hooks(merge_request, 'update', old_rev: @oldrev)
+ execute_hooks(merge_request, 'update', old_rev: @push.oldrev)
end
end
@@ -203,7 +201,7 @@ module MergeRequests
# `MergeRequestsClosingIssues` model (as a performance optimization).
# rubocop: disable CodeReuse/ActiveRecord
def cache_merge_requests_closing_issues
- @project.merge_requests.where(source_branch: @branch_name).each do |merge_request|
+ @project.merge_requests.where(source_branch: @push.branch_name).each do |merge_request|
merge_request.cache_merge_request_closes_issues!(@current_user)
end
end
@@ -215,15 +213,7 @@ module MergeRequests
def merge_requests_for_source_branch(reload: false)
@source_merge_requests = nil if reload
- @source_merge_requests ||= merge_requests_for(@branch_name)
- end
-
- def branch_added?
- Gitlab::Git.blank_ref?(@oldrev)
- end
-
- def branch_removed?
- Gitlab::Git.blank_ref?(@newrev)
+ @source_merge_requests ||= merge_requests_for(@push.branch_name)
end
end
end
diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb
index 5286b92ab6b..61f6402a810 100644
--- a/app/services/projects/autocomplete_service.rb
+++ b/app/services/projects/autocomplete_service.rb
@@ -2,6 +2,7 @@
module Projects
class AutocompleteService < BaseService
+ include LabelsAsHash
def issues
IssuesFinder.new(current_user, project_id: project.id, state: 'opened').execute.select([:iid, :title])
end
@@ -22,34 +23,18 @@ module Projects
MergeRequestsFinder.new(current_user, project_id: project.id, state: 'opened').execute.select([:iid, :title])
end
- def labels_as_hash(target = nil)
- available_labels = LabelsFinder.new(
- current_user,
- project_id: project.id,
- include_ancestor_groups: true
- ).execute
-
- label_hashes = available_labels.as_json(only: [:title, :color])
-
- if target&.respond_to?(:labels)
- already_set_labels = available_labels & target.labels
- if already_set_labels.present?
- titles = already_set_labels.map(&:title)
- label_hashes.each do |hash|
- if titles.include?(hash['title'])
- hash[:set] = true
- end
- end
- end
- end
-
- label_hashes
- end
-
def commands(noteable, type)
return [] unless noteable
QuickActions::InterpretService.new(project, current_user).available_commands(noteable)
end
+
+ def snippets
+ SnippetsFinder.new(current_user, project: project).execute.select([:id, :title])
+ end
+
+ def labels_as_hash(target)
+ super(target, project_id: project.id, include_ancestor_groups: true)
+ end
end
end
diff --git a/app/services/projects/update_pages_configuration_service.rb b/app/services/projects/update_pages_configuration_service.rb
index efbd4c7b323..abf40b3ad7a 100644
--- a/app/services/projects/update_pages_configuration_service.rb
+++ b/app/services/projects/update_pages_configuration_service.rb
@@ -21,7 +21,9 @@ module Projects
def pages_config
{
domains: pages_domains_config,
- https_only: project.pages_https_only?
+ https_only: project.pages_https_only?,
+ id: project.project_id,
+ access_control: !project.public_pages?
}
end
@@ -31,7 +33,9 @@ module Projects
domain: domain.domain,
certificate: domain.certificate,
key: domain.key,
- https_only: project.pages_https_only? && domain.https?
+ https_only: project.pages_https_only? && domain.https?,
+ id: project.project_id,
+ access_control: !project.public_pages?
}
end
end
diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb
index d6d9bacf232..f25a4e30938 100644
--- a/app/services/projects/update_service.rb
+++ b/app/services/projects/update_service.rb
@@ -72,7 +72,11 @@ module Projects
system_hook_service.execute_hooks_for(project, :update)
end
- update_pages_config if changing_pages_https_only?
+ update_pages_config if changing_pages_related_config?
+ end
+
+ def changing_pages_related_config?
+ changing_pages_https_only? || changing_pages_access_level?
end
def update_failed!
@@ -102,6 +106,10 @@ module Projects
params.dig(:project_feature_attributes, :wiki_access_level).to_i > ProjectFeature::DISABLED
end
+ def changing_pages_access_level?
+ params.dig(:project_feature_attributes, :pages_access_level)
+ end
+
def ensure_wiki_exists
ProjectWiki.new(project, project.owner).wiki
rescue ProjectWiki::CouldNotCreateWikiError
diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
index 8933bef29ee..defa579f9a8 100644
--- a/app/services/quick_actions/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -210,9 +210,14 @@ module QuickActions
end
params '~label1 ~"label 2"'
condition do
- available_labels = LabelsFinder.new(current_user, project_id: project.id, include_ancestor_groups: true).execute
+ if project
+ available_labels = LabelsFinder
+ .new(current_user, project_id: project.id, include_ancestor_groups: true)
+ .execute
+ end
- current_user.can?(:"admin_#{issuable.to_ability_name}", project) &&
+ project &&
+ current_user.can?(:"admin_#{issuable.to_ability_name}", project) &&
available_labels.any?
end
command :label do |labels_param|
@@ -286,7 +291,8 @@ module QuickActions
end
params '#issue | !merge_request'
condition do
- current_user.can?(:"update_#{issuable.to_ability_name}", issuable)
+ [MergeRequest, Issue].include?(issuable.class) &&
+ current_user.can?(:"update_#{issuable.to_ability_name}", issuable)
end
parse_params do |issuable_param|
extract_references(issuable_param, :issue).first ||
@@ -443,7 +449,8 @@ module QuickActions
end
params '<time(1h30m | -1h30m)> <date(YYYY-MM-DD)>'
condition do
- current_user.can?(:"admin_#{issuable.to_ability_name}", issuable)
+ issuable.is_a?(TimeTrackable) &&
+ current_user.can?(:"admin_#{issuable.to_ability_name}", issuable)
end
parse_params do |raw_time_date|
Gitlab::QuickActions::SpendTimeAndDateSeparator.new(raw_time_date).execute
@@ -493,7 +500,7 @@ module QuickActions
desc "Lock the discussion"
explanation "Locks the discussion"
condition do
- issuable.is_a?(Issuable) &&
+ [MergeRequest, Issue].include?(issuable.class) &&
issuable.persisted? &&
!issuable.discussion_locked? &&
current_user.can?(:"admin_#{issuable.to_ability_name}", issuable)
@@ -505,7 +512,7 @@ module QuickActions
desc "Unlock the discussion"
explanation "Unlocks the discussion"
condition do
- issuable.is_a?(Issuable) &&
+ [MergeRequest, Issue].include?(issuable.class) &&
issuable.persisted? &&
issuable.discussion_locked? &&
current_user.can?(:"admin_#{issuable.to_ability_name}", issuable)
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index 4fe6c1ec986..f357dc37fe7 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -41,15 +41,13 @@ class TodoService
# collects the todo users before the todos themselves are deleted, then
# updates the todo counts for those users.
#
- # rubocop: disable CodeReuse/ActiveRecord
def destroy_target(target)
- todo_users = User.where(id: target.todos.pending.select(:user_id)).to_a
+ todo_users = UsersWithPendingTodosFinder.new(target).execute.to_a
yield target
todo_users.each(&:update_todos_count_cache)
end
- # rubocop: enable CodeReuse/ActiveRecord
# When we reassign an issue we should:
#
@@ -200,30 +198,23 @@ class TodoService
create_todos(current_user, attributes)
end
- # rubocop: disable CodeReuse/ActiveRecord
def todo_exist?(issuable, current_user)
- TodosFinder.new(current_user).execute.exists?(target: issuable)
+ TodosFinder.new(current_user).any_for_target?(issuable)
end
- # rubocop: enable CodeReuse/ActiveRecord
private
- # rubocop: disable CodeReuse/ActiveRecord
def todos_by_ids(ids, current_user)
- current_user.todos.where(id: Array(ids))
+ current_user.todos_limited_to(Array(ids))
end
- # rubocop: enable CodeReuse/ActiveRecord
- # rubocop: disable CodeReuse/ActiveRecord
def update_todos_state(todos, current_user, state)
- # Only update those that are not really on that state
- todos = todos.where.not(state: state)
- todos_ids = todos.pluck(:id)
- todos.unscope(:order).update_all(state: state)
+ todos_ids = todos.update_state(state)
+
current_user.update_todos_count_cache
+
todos_ids
end
- # rubocop: enable CodeReuse/ActiveRecord
def create_todos(users, attributes)
Array(users).map do |user|
@@ -348,10 +339,7 @@ class TodoService
end
end
- # rubocop: disable CodeReuse/ActiveRecord
def pending_todos(user, criteria = {})
- valid_keys = [:project_id, :target_id, :target_type, :commit_id]
- user.todos.pending.where(criteria.slice(*valid_keys))
+ PendingTodosFinder.new(user, criteria).execute
end
- # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index ccba1c461fc..fefb4c7455d 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -1,6 +1,8 @@
- add_to_breadcrumbs "Projects", admin_projects_path
- breadcrumb_title @project.full_name
- page_title @project.full_name, "Projects"
+- @content_class = "admin-projects"
+
%h3.page-title
Project: #{@project.full_name}
= link_to edit_project_path(@project), class: "btn btn-nr float-right" do
@@ -110,6 +112,8 @@
= visibility_level_icon(@project.visibility_level)
= visibility_level_label(@project.visibility_level)
+ = render_if_exists 'admin/projects/geo_status_widget', locals: { project: @project }
+
.card
.card-header
Transfer project
diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml
index a5326f4b909..e9e4e0847d3 100644
--- a/app/views/admin/runners/index.html.haml
+++ b/app/views/admin/runners/index.html.haml
@@ -2,106 +2,103 @@
- @no_container = true
%div{ class: container_class }
- .bs-callout
- %p
- = (_"A 'Runner' is a process which runs a job. You can set up as many Runners as you need.")
- %br
- = _('Runners can be placed on separate users, servers, even on your local machine.')
- %br
+ .row
+ .col-sm-6
+ .bs-callout
+ %p
+ = (_"A 'Runner' is a process which runs a job. You can set up as many Runners as you need.")
+ %br
+ = _('Runners can be placed on separate users, servers, even on your local machine.')
+ %br
- %div
- %span= _('Each Runner can be in one of the following states:')
- %ul
- %li
- %span.badge.badge-success shared
- \-
- = _('Runner runs jobs from all unassigned projects')
- %li
- %span.badge.badge-success group
- \-
- = _('Runner runs jobs from all unassigned projects in its group')
- %li
- %span.badge.badge-info specific
- \-
- = _('Runner runs jobs from assigned projects')
- %li
- %span.badge.badge-warning locked
- \-
- = _('Runner cannot be assigned to other projects')
- %li
- %span.badge.badge-danger paused
- \-
- = _('Runner will not receive any new jobs')
+ %div
+ %span= _('Each Runner can be in one of the following states:')
+ %ul
+ %li
+ %span.badge.badge-success shared
+ \-
+ = _('Runner runs jobs from all unassigned projects')
+ %li
+ %span.badge.badge-success group
+ \-
+ = _('Runner runs jobs from all unassigned projects in its group')
+ %li
+ %span.badge.badge-info specific
+ \-
+ = _('Runner runs jobs from assigned projects')
+ %li
+ %span.badge.badge-warning locked
+ \-
+ = _('Runner cannot be assigned to other projects')
+ %li
+ %span.badge.badge-danger paused
+ \-
+ = _('Runner will not receive any new jobs')
- .bs-callout.clearfix
- .float-left
- %p
- = _('You can reset runners registration token by pressing a button below.')
- .prepend-top-10
- = button_to _('Reset runners registration token'), reset_runners_token_admin_application_settings_path,
- method: :put, class: 'btn btn-default',
- data: { confirm: _('Are you sure you want to reset registration token?') }
+ .col-sm-6
+ .bs-callout
+ = render partial: 'ci/runner/how_to_setup_runner',
+ locals: { registration_token: Gitlab::CurrentSettings.runners_registration_token,
+ type: 'shared',
+ reset_token_url: reset_registration_token_admin_application_settings_path }
- = render partial: 'ci/runner/how_to_setup_shared_runner',
- locals: { registration_token: Gitlab::CurrentSettings.runners_registration_token }
+ .row
+ .col-sm-9
+ = form_tag admin_runners_path, id: 'runners-search', method: :get, class: 'filter-form js-filter-form' do
+ .filtered-search-wrapper
+ .filtered-search-box
+ = dropdown_tag(custom_icon('icon_history'),
+ options: { wrapper_class: 'filtered-search-history-dropdown-wrapper',
+ toggle_class: 'filtered-search-history-dropdown-toggle-button',
+ dropdown_class: 'filtered-search-history-dropdown',
+ content_class: 'filtered-search-history-dropdown-content',
+ title: _('Recent searches') }) do
+ .js-filtered-search-history-dropdown{ data: { full_path: admin_runners_path } }
+ .filtered-search-box-input-container.droplab-dropdown
+ .scroll-container
+ %ul.tokens-container.list-unstyled
+ %li.input-token
+ %input.form-control.filtered-search{ { id: 'filtered-search-runners', placeholder: _('Search or filter results...') } }
+ #js-dropdown-hint.filtered-search-input-dropdown-menu.dropdown-menu.hint-dropdown
+ %ul{ data: { dropdown: true } }
+ %li.filter-dropdown-item{ data: { action: 'submit' } }
+ = button_tag class: %w[btn btn-link] do
+ = sprite_icon('search')
+ %span
+ = _('Press Enter or click to search')
+ %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
+ %li.filter-dropdown-item
+ = button_tag class: %w[btn btn-link] do
+ -# Encapsulate static class name `{{icon}}` inside #{} to bypass
+ -# haml lint's ClassAttributeWithStaticValue
+ %svg
+ %use{ 'xlink:href': "#{'{{icon}}'}" }
+ %span.js-filter-hint
+ {{hint}}
+ %span.js-filter-tag.dropdown-light-content
+ {{tag}}
- .bs-callout
- %p
- = _('Runners currently online: %{active_runners_count}') % { active_runners_count: @active_runners_count }
+ #js-dropdown-admin-runner-status.filtered-search-input-dropdown-menu.dropdown-menu
+ %ul{ data: { dropdown: true } }
+ - Ci::Runner::AVAILABLE_STATUSES.each do |status|
+ %li.filter-dropdown-item{ data: { value: status } }
+ = button_tag class: %w[btn btn-link] do
+ = status.titleize
- .row-content-block.second-block
- = form_tag admin_runners_path, id: 'runners-search', method: :get, class: 'filter-form js-filter-form' do
- .filtered-search-wrapper
- .filtered-search-box
- = dropdown_tag(custom_icon('icon_history'),
- options: { wrapper_class: 'filtered-search-history-dropdown-wrapper',
- toggle_class: 'filtered-search-history-dropdown-toggle-button',
- dropdown_class: 'filtered-search-history-dropdown',
- content_class: 'filtered-search-history-dropdown-content',
- title: _('Recent searches') }) do
- .js-filtered-search-history-dropdown{ data: { full_path: admin_runners_path } }
- .filtered-search-box-input-container.droplab-dropdown
- .scroll-container
- %ul.tokens-container.list-unstyled
- %li.input-token
- %input.form-control.filtered-search{ { id: 'filtered-search-runners', placeholder: _('Search or filter results...') } }
- #js-dropdown-hint.filtered-search-input-dropdown-menu.dropdown-menu.hint-dropdown
- %ul{ data: { dropdown: true } }
- %li.filter-dropdown-item{ data: { action: 'submit' } }
- = button_tag class: %w[btn btn-link] do
- = sprite_icon('search')
- %span
- = _('Press Enter or click to search')
- %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
- %li.filter-dropdown-item
- = button_tag class: %w[btn btn-link] do
- -# Encapsulate static class name `{{icon}}` inside #{} to bypass
- -# haml lint's ClassAttributeWithStaticValue
- %svg
- %use{ 'xlink:href': "#{'{{icon}}'}" }
- %span.js-filter-hint
- {{hint}}
- %span.js-filter-tag.dropdown-light-content
- {{tag}}
+ #js-dropdown-admin-runner-type.filtered-search-input-dropdown-menu.dropdown-menu
+ %ul{ data: { dropdown: true } }
+ - Ci::Runner::AVAILABLE_TYPES.each do |runner_type|
+ %li.filter-dropdown-item{ data: { value: runner_type } }
+ = button_tag class: %w[btn btn-link] do
+ = runner_type.titleize
- #js-dropdown-admin-runner-status.filtered-search-input-dropdown-menu.dropdown-menu
- %ul{ data: { dropdown: true } }
- - Ci::Runner::AVAILABLE_STATUSES.each do |status|
- %li.filter-dropdown-item{ data: { value: status } }
- = button_tag class: %w[btn btn-link] do
- = status.titleize
+ = button_tag class: %w[clear-search hidden] do
+ = icon('times')
+ .filter-dropdown-container
+ = render 'sort_dropdown'
- #js-dropdown-admin-runner-type.filtered-search-input-dropdown-menu.dropdown-menu
- %ul{ data: { dropdown: true } }
- - Ci::Runner::AVAILABLE_TYPES.each do |runner_type|
- %li.filter-dropdown-item{ data: { value: runner_type } }
- = button_tag class: %w[btn btn-link] do
- = runner_type.titleize
-
- = button_tag class: %w[clear-search hidden] do
- = icon('times')
- .filter-dropdown-container
- = render 'sort_dropdown'
+ .col-sm-3.text-right-lg
+ = _('Runners currently online: %{active_runners_count}') % { active_runners_count: @active_runners_count }
- if @runners.any?
.runners-content.content-list
diff --git a/app/views/ci/runner/_how_to_setup_runner.html.haml b/app/views/ci/runner/_how_to_setup_runner.html.haml
index b1b142460b0..4307060d748 100644
--- a/app/views/ci/runner/_how_to_setup_runner.html.haml
+++ b/app/views/ci/runner/_how_to_setup_runner.html.haml
@@ -13,5 +13,9 @@
= _("Use the following registration token during setup:")
%code#registration_token= registration_token
= clipboard_button(target: '#registration_token', title: _("Copy token to clipboard"), class: "btn-transparent btn-clipboard")
+ .prepend-top-10.append-bottom-10
+ = button_to _("Reset runners registration token"), reset_token_url,
+ method: :put, class: 'btn btn-default',
+ data: { confirm: _("Are you sure you want to reset registration token?") }
%li
= _("Start the Runner!")
diff --git a/app/views/ci/runner/_how_to_setup_shared_runner.html.haml b/app/views/ci/runner/_how_to_setup_shared_runner.html.haml
deleted file mode 100644
index 2a190cb9250..00000000000
--- a/app/views/ci/runner/_how_to_setup_shared_runner.html.haml
+++ /dev/null
@@ -1,3 +0,0 @@
-.bs-callout.help-callout
- = render partial: 'ci/runner/how_to_setup_runner',
- locals: { registration_token: registration_token, type: 'shared' }
diff --git a/app/views/ci/runner/_how_to_setup_specific_runner.html.haml b/app/views/ci/runner/_how_to_setup_specific_runner.html.haml
deleted file mode 100644
index afe57bdfa01..00000000000
--- a/app/views/ci/runner/_how_to_setup_specific_runner.html.haml
+++ /dev/null
@@ -1,26 +0,0 @@
-.bs-callout.help-callout
- .append-bottom-10
- %h4= _('Set up a specific Runner automatically')
-
- %p
- - link_to_help_page = link_to(_('Learn more about Kubernetes'),
- help_page_path('user/project/clusters/index'),
- target: '_blank',
- rel: 'noopener noreferrer')
-
- = _('You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page }
-
- %ol
- %li
- = _('Click the button below to begin the install process by navigating to the Kubernetes page')
- %li
- = _('Select an existing Kubernetes cluster or create a new one')
- %li
- = _('From the Kubernetes cluster details view, install Runner from the applications list')
-
- = link_to _('Install Runner on Kubernetes'),
- project_clusters_path(@project),
- class: 'btn btn-info'
- %hr
- = render partial: 'ci/runner/how_to_setup_runner',
- locals: { registration_token: registration_token, type: 'specific' }
diff --git a/app/views/dashboard/activity.html.haml b/app/views/dashboard/activity.html.haml
index 31d4b3da4f1..3cee5841bbc 100644
--- a/app/views/dashboard/activity.html.haml
+++ b/app/views/dashboard/activity.html.haml
@@ -4,6 +4,9 @@
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
+= content_for :above_breadcrumbs_content do
+ = render_if_exists "shared/gold_trial_callout"
+
- page_title "Activity"
- header_title "Activity", activity_dashboard_path
diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml
index 50f39f93283..985928305a2 100644
--- a/app/views/dashboard/groups/index.html.haml
+++ b/app/views/dashboard/groups/index.html.haml
@@ -3,6 +3,9 @@
- header_title "Groups", dashboard_groups_path
= render 'dashboard/groups_head'
+= content_for :above_breadcrumbs_content do
+ = render_if_exists "shared/gold_trial_callout"
+
- if params[:filter].blank? && @groups.empty?
= render 'shared/groups/empty_state'
- else
diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml
index 86a21e24ac9..91f58ddcfcc 100644
--- a/app/views/dashboard/issues.html.haml
+++ b/app/views/dashboard/issues.html.haml
@@ -4,6 +4,9 @@
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{current_user.name} issues")
+= content_for :above_breadcrumbs_content do
+ = render_if_exists "shared/gold_trial_callout"
+
.top-area
= render 'shared/issuable/nav', type: :issues, display_count: !@no_filters_set
.nav-controls
diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml
index 61aae31be60..27f53a8d1c6 100644
--- a/app/views/dashboard/merge_requests.html.haml
+++ b/app/views/dashboard/merge_requests.html.haml
@@ -2,6 +2,9 @@
- page_title _("Merge Requests")
- @breadcrumb_link = merge_requests_dashboard_path(assignee_id: current_user.id)
+= content_for :above_breadcrumbs_content do
+ = render_if_exists "shared/gold_trial_callout"
+
.top-area
= render 'shared/issuable/nav', type: :merge_requests, display_count: !@no_filters_set
.nav-controls
diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml
index deed774a4a5..f0d16936a51 100644
--- a/app/views/dashboard/projects/index.html.haml
+++ b/app/views/dashboard/projects/index.html.haml
@@ -4,6 +4,9 @@
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
+= content_for :above_breadcrumbs_content do
+ = render_if_exists "shared/gold_trial_callout"
+
- page_title "Projects"
- header_title "Projects", dashboard_projects_path
diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml
index 8933d9e31ff..42638b8528d 100644
--- a/app/views/dashboard/projects/starred.html.haml
+++ b/app/views/dashboard/projects/starred.html.haml
@@ -4,6 +4,9 @@
- page_title "Starred Projects"
- header_title "Projects", dashboard_projects_path
+= content_for :above_breadcrumbs_content do
+ = render_if_exists "shared/gold_trial_callout"
+
%div{ class: container_class }
= render "projects/last_push"
= render 'dashboard/projects_head'
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index 8b3974d97f8..bbfa4cc7413 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -2,6 +2,9 @@
- page_title "Todos"
- header_title "Todos", dashboard_todos_path
+= content_for :above_breadcrumbs_content do
+ = render_if_exists "shared/gold_trial_callout"
+
- if current_user.todos.any?
.top-area
%ul.nav-links.mobile-separator.nav.nav-tabs
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index 387c37b7a91..1d8b9c5bc8f 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -2,6 +2,9 @@
- page_title _("Groups")
- header_title _("Groups"), dashboard_groups_path
+= content_for :above_breadcrumbs_content do
+ = render_if_exists "shared/gold_trial_callout"
+
- if current_user
= render 'dashboard/groups_head'
- else
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index 452f390695c..16be5791f83 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -2,6 +2,9 @@
- page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path
+= content_for :above_breadcrumbs_content do
+ = render_if_exists "shared/gold_trial_callout"
+
- if current_user
= render 'dashboard/projects_head'
- else
diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml
index 452f390695c..16be5791f83 100644
--- a/app/views/explore/projects/starred.html.haml
+++ b/app/views/explore/projects/starred.html.haml
@@ -2,6 +2,9 @@
- page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path
+= content_for :above_breadcrumbs_content do
+ = render_if_exists "shared/gold_trial_callout"
+
- if current_user
= render 'dashboard/projects_head'
- else
diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml
index 452f390695c..16be5791f83 100644
--- a/app/views/explore/projects/trending.html.haml
+++ b/app/views/explore/projects/trending.html.haml
@@ -2,6 +2,9 @@
- page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path
+= content_for :above_breadcrumbs_content do
+ = render_if_exists "shared/gold_trial_callout"
+
- if current_user
= render 'dashboard/projects_head'
- else
diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml
index 003bd25dd06..5b78ce910b8 100644
--- a/app/views/groups/labels/index.html.haml
+++ b/app/views/groups/labels/index.html.haml
@@ -3,29 +3,23 @@
- can_admin_label = can?(current_user, :admin_label, @group)
- issuables = ['issues', 'merge requests']
- search = params[:search]
+- subscribed = params[:subscribed]
+- labels_or_filters = @labels.exists? || search.present? || subscribed.present?
- if can_admin_label
- content_for(:header_content) do
.nav-controls
= link_to _('New label'), new_group_label_path(@group), class: "btn btn-success"
-- if @labels.exists? || search.present?
+- if labels_or_filters
#promote-label-modal
%div{ class: container_class }
- .top-area.adjust
- .nav-text
- = _('Labels can be applied to %{features}. Group labels are available for any project within the group.') % { features: issuables.to_sentence }
- .nav-controls
- = form_tag group_labels_path(@group), method: :get do
- .input-group
- = search_field_tag :search, params[:search], { placeholder: _('Filter'), id: 'label-search', class: 'form-control search-text-input input-short', spellcheck: false }
- %span.input-group-append
- %button.btn.btn-default{ type: "submit", "aria-label" => _('Submit search') }
- = icon("search")
- = render 'shared/labels/sort_dropdown'
+ = render 'shared/labels/nav'
.labels-container.prepend-top-5
- if @labels.any?
+ .text-muted
+ = _('Labels can be applied to %{features}. Group labels are available for any project within the group.') % { features: issuables.to_sentence }
.other-labels
%h5= _('Labels')
%ul.content-list.manage-labels-list.js-other-labels
@@ -34,6 +28,9 @@
- elsif search.present?
.nothing-here-block
= _('No labels with such name or description')
+ - elsif subscribed.present?
+ .nothing-here-block
+ = _('You do not have any subscriptions yet')
- else
= render 'shared/empty_states/labels'
diff --git a/app/views/groups/runners/_group_runners.html.haml b/app/views/groups/runners/_group_runners.html.haml
index e6c089c3494..bcfb6d99716 100644
--- a/app/views/groups/runners/_group_runners.html.haml
+++ b/app/views/groups/runners/_group_runners.html.haml
@@ -11,7 +11,9 @@
-# https://gitlab.com/gitlab-org/gitlab-ce/issues/45894
- if can?(current_user, :admin_pipeline, @group)
= render partial: 'ci/runner/how_to_setup_runner',
- locals: { registration_token: @group.runners_token, type: 'group' }
+ locals: { registration_token: @group.runners_token,
+ type: 'group',
+ reset_token_url: reset_registration_token_group_settings_ci_cd_path }
- if @group.runners.empty?
%h4.underlined-title
diff --git a/app/views/groups/settings/_advanced.html.haml b/app/views/groups/settings/_advanced.html.haml
index b7c673db705..3814d45929d 100644
--- a/app/views/groups/settings/_advanced.html.haml
+++ b/app/views/groups/settings/_advanced.html.haml
@@ -12,8 +12,8 @@
.group-root-path.input-group-prepend.has-tooltip{ title: group_path(@group), :'data-placement' => 'bottom' }
.input-group-text
%span>= root_url
- - if parent
- %strong= parent.full_path + '/'
+ - if @group.parent
+ %strong= @group.parent.full_path + '/'
= f.hidden_field :parent_id
= f.text_field :path, placeholder: 'open-source', class: 'form-control',
autofocus: local_assigns[:autofocus] || false, required: true,
diff --git a/app/views/layouts/header/_current_user_dropdown.html.haml b/app/views/layouts/header/_current_user_dropdown.html.haml
index 9ed05d6e3d0..261d758622b 100644
--- a/app/views/layouts/header/_current_user_dropdown.html.haml
+++ b/app/views/layouts/header/_current_user_dropdown.html.haml
@@ -5,7 +5,14 @@
.user-name.bold
= current_user.name
= current_user.to_reference
+ - if current_user.status
+ .user-status-emoji.str-truncated.has-tooltip{ title: current_user.status.message_html, data: { html: 'true', placement: 'bottom' } }
+ = emoji_icon current_user.status.emoji
+ = current_user.status.message_html.html_safe
%li.divider
+ - if can?(current_user, :update_user_status, current_user)
+ %li
+ .js-set-status-modal-trigger{ data: { has_status: current_user.status.present? ? 'true' : 'false' } }
- if current_user_menu?(:profile)
%li
= link_to s_("CurrentUser|Profile"), current_user, class: 'profile-link', data: { user: current_user.username }
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 044b49c12cc..39604611440 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -74,3 +74,6 @@
%span.sr-only= _('Toggle navigation')
= sprite_icon('ellipsis_h', size: 12, css_class: 'more-icon js-navbar-toggle-right')
= sprite_icon('close', size: 12, css_class: 'close-icon js-navbar-toggle-left')
+
+- if can?(current_user, :update_user_status, current_user)
+ .js-set-status-modal-wrapper{ data: { current_emoji: current_user.status.present? ? current_user.status.emoji : '', current_message: current_user.status.present? ? current_user.status.message : '' } }
diff --git a/app/views/layouts/nav/_breadcrumbs.html.haml b/app/views/layouts/nav/_breadcrumbs.html.haml
index f53bd2b5e4d..c35451827c8 100644
--- a/app/views/layouts/nav/_breadcrumbs.html.haml
+++ b/app/views/layouts/nav/_breadcrumbs.html.haml
@@ -1,6 +1,7 @@
- container = @no_breadcrumb_container ? 'container-fluid' : container_class
- hide_top_links = @hide_top_links || false
+= yield :above_breadcrumbs_content
%nav.breadcrumbs{ role: "navigation", class: [container, @content_class] }
.breadcrumbs-container
- if defined?(@left_sidebar)
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index 25cd53b378a..48025f9bd20 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -216,7 +216,7 @@
= _('Metrics')
= nav_link(controller: :environments, action: [:index, :folder, :show, :new, :edit, :create, :update, :stop, :terminal]) do
- = link_to project_environments_path(@project), title: _('Environments'), class: 'shortcuts-environments' do
+ = link_to project_environments_path(@project), title: _('Environments'), class: 'shortcuts-environments qa-operations-environments-link' do
%span
= _('Environments')
diff --git a/app/views/profiles/two_factor_auths/_codes.html.haml b/app/views/profiles/two_factor_auths/_codes.html.haml
index fb4fff12027..759d39cf5f5 100644
--- a/app/views/profiles/two_factor_auths/_codes.html.haml
+++ b/app/views/profiles/two_factor_auths/_codes.html.haml
@@ -1,5 +1,5 @@
%p.slead
- Should you ever lose your phone, each of these recovery codes can be used one
+ Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one
time each to regain access to your account. Please save them in a safe place, or you
%b will
lose access to your account.
diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml
index cd10b8758f6..94ec0cc5db8 100644
--- a/app/views/profiles/two_factor_auths/show.html.haml
+++ b/app/views/profiles/two_factor_auths/show.html.haml
@@ -6,13 +6,13 @@
.row.prepend-top-default
.col-lg-4
%h4.prepend-top-0
- Register Two-Factor Authentication App
+ Register Two-Factor Authenticator
%p
- Use an app on your mobile device to enable two-factor authentication (2FA).
+ Use an one time password authenticator on your mobile device or computer to enable two-factor authentication (2FA).
.col-lg-8
- if current_user.two_factor_otp_enabled?
%p
- You've already enabled two-factor authentication using mobile authenticator applications. In order to register a different device, you must first disable two-factor authentication.
+ You've already enabled two-factor authentication using one time password authenticators. In order to register a different device, you must first disable two-factor authentication.
%p
If you lose your recovery codes you can generate new ones, invalidating all previous codes.
%div
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
index 5436806162d..f398d97028b 100644
--- a/app/views/projects/_md_preview.html.haml
+++ b/app/views/projects/_md_preview.html.haml
@@ -26,6 +26,7 @@
= markdown_toolbar_button({ icon: "list-bulleted", data: { "md-tag" => "* ", "md-prepend" => true }, title: s_("MarkdownToolbar|Add a bullet list") })
= markdown_toolbar_button({ icon: "list-numbered", data: { "md-tag" => "1. ", "md-prepend" => true }, title: s_("MarkdownToolbar|Add a numbered list") })
= markdown_toolbar_button({ icon: "task-done", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: s_("MarkdownToolbar|Add a task list") })
+ = markdown_toolbar_button({ icon: "table", data: { "md-tag" => "| header | header |\n| ------ | ------ |\n| cell | cell |\n| cell | cell |", "md-prepend" => true }, title: s_("MarkdownToolbar|Add a table") })
%button.toolbar-btn.toolbar-fullscreen-btn.js-zen-enter.has-tooltip{ type: "button", tabindex: -1, "aria-label": "Go full screen", title: s_("MarkdownToolbar|Go full screen"), data: { container: "body" } }
= sprite_icon("screen-full")
diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml
index e1f28364e19..ba7d3154326 100644
--- a/app/views/projects/_new_project_fields.html.haml
+++ b/app/views/projects/_new_project_fields.html.haml
@@ -33,7 +33,7 @@
.form-group.project-path.col-sm-6
= f.label :path, class: 'label-bold' do
%span= _("Project slug")
- = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, required: true
+ = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", required: true
- if current_user.can_create_group?
.form-text.text-muted
Want to house several dependent projects under the same namespace?
@@ -61,5 +61,5 @@
.option-description
Allows you to immediately clone this project’s repository. Skip this if you plan to push up an existing repository.
-= f.submit 'Create project', class: "btn btn-success project-submit", tabindex: 4, data: { track_label: "#{track_label}", track_event: "click_button", track_property: "create_project", track_value: "" }
+= f.submit 'Create project', class: "btn btn-success project-submit", data: { track_label: "#{track_label}", track_event: "click_button", track_property: "create_project", track_value: "" }
= link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "cancel" }
diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml
index 59c297c46a5..95828626bd9 100644
--- a/app/views/projects/ci/builds/_build.html.haml
+++ b/app/views/projects/ci/builds/_build.html.haml
@@ -101,7 +101,7 @@
= sprite_icon('download')
- if can?(current_user, :update_build, job)
- if job.active?
- = link_to cancel_project_job_path(job.project, job, return_to: request.original_url), method: :post, title: 'Cancel', class: 'btn btn-build' do
+ = link_to cancel_project_job_path(job.project, job, continue: { to: request.fullpath }), method: :post, title: 'Cancel', class: 'btn btn-build' do
= icon('remove', class: 'cred')
- elsif job.scheduled?
.btn-group
diff --git a/app/views/projects/clusters/_banner.html.haml b/app/views/projects/clusters/_banner.html.haml
index 73cfea0ef92..141314b4e4e 100644
--- a/app/views/projects/clusters/_banner.html.haml
+++ b/app/views/projects/clusters/_banner.html.haml
@@ -9,7 +9,7 @@
= s_("ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details")
- if show_cluster_security_warning?
- .js-cluster-security-warning.alert.alert-block.alert-dismissable.bs-callout.bs-callout-warning
- %button.close{ type: "button", data: { feature_id: UserCalloutsHelper::CLUSTER_SECURITY_WARNING, dismiss_endpoint: user_callouts_path } } &times;
+ .js-cluster-security-warning.alert.alert-block.alert-dismissable.bs-callout.bs-callout-warning{ data: { feature_id: UserCalloutsHelper::CLUSTER_SECURITY_WARNING, dismiss_endpoint: user_callouts_path } }
+ %button.close.js-close{ type: "button" } &times;
= s_("ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application.")
= link_to s_("More information"), help_page_path('user/project/clusters/index.md', anchor: 'security-implications')
diff --git a/app/views/projects/clusters/_gcp_signup_offer_banner.html.haml b/app/views/projects/clusters/_gcp_signup_offer_banner.html.haml
index 73b11d509d3..85d1002243b 100644
--- a/app/views/projects/clusters/_gcp_signup_offer_banner.html.haml
+++ b/app/views/projects/clusters/_gcp_signup_offer_banner.html.haml
@@ -1,6 +1,6 @@
- link = link_to(s_('ClusterIntegration|sign up'), 'https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral', target: '_blank', rel: 'noopener noreferrer')
-.bs-callout.gcp-signup-offer.alert.alert-block.alert-dismissable.prepend-top-default.append-bottom-default{ role: 'alert' }
- %button.close{ type: "button", data: { feature_id: UserCalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: user_callouts_path } } &times;
+.bs-callout.gcp-signup-offer.alert.alert-block.alert-dismissable.prepend-top-default.append-bottom-default{ role: 'alert', data: { feature_id: UserCalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: user_callouts_path } }
+ %button.close.js-close{ type: "button" } &times;
.gcp-signup-offer--content
.gcp-signup-offer--icon.append-right-8
= sprite_icon("information", size: 16)
diff --git a/app/views/projects/clusters/gcp/_form.html.haml b/app/views/projects/clusters/gcp/_form.html.haml
index 0222bbf7338..171ceeceb68 100644
--- a/app/views/projects/clusters/gcp/_form.html.haml
+++ b/app/views/projects/clusters/gcp/_form.html.haml
@@ -61,15 +61,14 @@
%p.form-text.text-muted
= s_('ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}.').html_safe % { help_link_start_machine_type: help_link_start % { url: machine_type_link_url }, help_link_start_pricing: help_link_start % { url: pricing_link_url }, help_link_end: help_link_end }
- - if rbac_clusters_feature_enabled?
- .form-group
- .form-check
- = provider_gcp_field.check_box :legacy_abac, { class: 'form-check-input' }, false, true
- = provider_gcp_field.label :legacy_abac, s_('ClusterIntegration|RBAC-enabled cluster (experimental)'), class: 'form-check-label label-bold'
- .form-text.text-muted
- = s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
- = s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
- = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'role-based-access-control-rbac-experimental-support'), target: '_blank'
+ .form-group
+ .form-check
+ = provider_gcp_field.check_box :legacy_abac, { class: 'form-check-input' }, false, true
+ = provider_gcp_field.label :legacy_abac, s_('ClusterIntegration|RBAC-enabled cluster (experimental)'), class: 'form-check-label label-bold'
+ .form-text.text-muted
+ = s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
+ = s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
+ = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'role-based-access-control-rbac-core-only'), target: '_blank'
.form-group
= field.submit s_('ClusterIntegration|Create Kubernetes cluster'), class: 'js-gke-cluster-creation-submit btn btn-success', disabled: true
diff --git a/app/views/projects/clusters/gcp/_show.html.haml b/app/views/projects/clusters/gcp/_show.html.haml
index be84f2ae67c..779c9c245c1 100644
--- a/app/views/projects/clusters/gcp/_show.html.haml
+++ b/app/views/projects/clusters/gcp/_show.html.haml
@@ -37,14 +37,13 @@
= platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)')
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
- - if rbac_clusters_feature_enabled?
- .form-group
- .form-check
- = platform_kubernetes_field.check_box :authorization_type, { class: 'form-check-input', disabled: true }, 'rbac', 'abac'
- = platform_kubernetes_field.label :authorization_type, s_('ClusterIntegration|RBAC-enabled cluster (experimental)'), class: 'form-check-label label-bold'
- .form-text.text-muted
- = s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
- = s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
+ .form-group
+ .form-check
+ = platform_kubernetes_field.check_box :authorization_type, { class: 'form-check-input', disabled: true }, 'rbac', 'abac'
+ = platform_kubernetes_field.label :authorization_type, s_('ClusterIntegration|RBAC-enabled cluster (experimental)'), class: 'form-check-label label-bold'
+ .form-text.text-muted
+ = s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
+ = s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
.form-group
= field.submit s_('ClusterIntegration|Save changes'), class: 'btn btn-success'
diff --git a/app/views/projects/clusters/user/_form.html.haml b/app/views/projects/clusters/user/_form.html.haml
index f497f5b606c..54a6e685bb0 100644
--- a/app/views/projects/clusters/user/_form.html.haml
+++ b/app/views/projects/clusters/user/_form.html.haml
@@ -25,15 +25,14 @@
= platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-bold'
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
- - if rbac_clusters_feature_enabled?
- .form-group
- .form-check
- = platform_kubernetes_field.check_box :authorization_type, { class: 'form-check-input' }, 'rbac', 'abac'
- = platform_kubernetes_field.label :authorization_type, s_('ClusterIntegration|RBAC-enabled cluster (experimental)'), class: 'form-check-label label-bold'
- .form-text.text-muted
- = s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
- = s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
- = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'role-based-access-control-rbac-experimental-support'), target: '_blank'
+ .form-group
+ .form-check
+ = platform_kubernetes_field.check_box :authorization_type, { class: 'form-check-input qa-rbac-checkbox' }, 'rbac', 'abac'
+ = platform_kubernetes_field.label :authorization_type, s_('ClusterIntegration|RBAC-enabled cluster (experimental)'), class: 'form-check-label label-bold'
+ .form-text.text-muted
+ = s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
+ = s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
+ = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'role-based-access-control-rbac-core-only'), target: '_blank'
.form-group
= field.submit s_('ClusterIntegration|Add Kubernetes cluster'), class: 'btn btn-success'
diff --git a/app/views/projects/clusters/user/_show.html.haml b/app/views/projects/clusters/user/_show.html.haml
index 56b597d295a..5b57f7ceb7d 100644
--- a/app/views/projects/clusters/user/_show.html.haml
+++ b/app/views/projects/clusters/user/_show.html.haml
@@ -26,14 +26,13 @@
= platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-bold'
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
- - if rbac_clusters_feature_enabled?
- .form-group
- .form-check
- = platform_kubernetes_field.check_box :authorization_type, { class: 'form-check-input', disabled: true }, 'rbac', 'abac'
- = platform_kubernetes_field.label :authorization_type, s_('ClusterIntegration|RBAC-enabled cluster (experimental)'), class: 'form-check-label label-bold'
- .form-text.text-muted
- = s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
- = s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
+ .form-group
+ .form-check
+ = platform_kubernetes_field.check_box :authorization_type, { class: 'form-check-input', disabled: true }, 'rbac', 'abac'
+ = platform_kubernetes_field.label :authorization_type, s_('ClusterIntegration|RBAC-enabled cluster (experimental)'), class: 'form-check-label label-bold'
+ .form-text.text-muted
+ = s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
+ = s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
.form-group
= field.submit s_('ClusterIntegration|Save changes'), class: 'btn btn-success'
diff --git a/app/views/projects/environments/_external_url.html.haml b/app/views/projects/environments/_external_url.html.haml
index 4694bc39d54..b3a82d1ef41 100644
--- a/app/views/projects/environments/_external_url.html.haml
+++ b/app/views/projects/environments/_external_url.html.haml
@@ -1,4 +1,4 @@
- if environment.external_url && can?(current_user, :read_environment, environment)
- = link_to environment.external_url, target: '_blank', rel: 'noopener noreferrer', class: 'btn external-url has-tooltip', title: s_('Environments|Open live environment') do
+ = link_to environment.external_url, target: '_blank', rel: 'noopener noreferrer', class: 'btn external-url has-tooltip qa-view-deployment', title: s_('Environments|Open live environment') do
= sprite_icon('external-link')
View deployment
diff --git a/app/views/projects/environments/empty.html.haml b/app/views/projects/environments/empty.html.haml
index 1413930ebdb..129dbbf4e56 100644
--- a/app/views/projects/environments/empty.html.haml
+++ b/app/views/projects/environments/empty.html.haml
@@ -1,14 +1,14 @@
- page_title _("Metrics")
-.row
+.row.empty-state
.col-sm-12
.svg-content
= image_tag 'illustrations/operations_metrics_empty.svg'
-.row.empty-environments
- .col-sm-12.text-center
- %h4
- = s_('Metrics|No deployed environments')
- .state-description
- = s_('Metrics|Check out the CI/CD documentation on deploying to an environment')
- .prepend-top-10
- = link_to s_("Metrics|Learn about environments"), help_page_path('ci/environments'), class: 'btn btn-success'
+ .col-12
+ .text-content
+ %h4.text-center
+ = s_('Metrics|No deployed environments')
+ %p.state-description
+ = s_('Metrics|Check out the CI/CD documentation on deploying to an environment')
+ .text-center
+ = link_to s_("Metrics|Learn about environments"), help_page_path('ci/environments'), class: 'btn btn-success'
diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml
index c7890b37381..8c5b6e089ea 100644
--- a/app/views/projects/environments/show.html.haml
+++ b/app/views/projects/environments/show.html.haml
@@ -49,15 +49,16 @@
.environments-container
- if @deployments.blank?
- .blank-state-row
- .blank-state-center
- %h2.blank-state-title
+ .empty-state
+ .text-content
+ %h4.state-title
You don't have any deployments right now.
%p.blank-state-text
Define environments in the deploy stage(s) in
%code .gitlab-ci.yml
to track deployments here.
- = link_to "Read more", help_page_path("ci/environments"), class: "btn btn-success"
+ .text-center
+ = link_to _("Read more"), help_page_path("ci/environments"), class: "btn btn-success"
- else
.table-holder
.ci-table.environments{ role: 'grid' }
diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml
index 665968a64e1..28998acdc13 100644
--- a/app/views/projects/issues/_discussion.html.haml
+++ b/app/views/projects/issues/_discussion.html.haml
@@ -6,7 +6,7 @@
= 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
- #js-vue-notes{ data: { notes_data: notes_data(@issue),
+ #js-vue-notes{ data: { notes_data: notes_data(@issue).to_json,
noteable_data: serialize_issuable(@issue),
noteable_type: 'Issue',
target_type: 'issue',
diff --git a/app/views/projects/jobs/_empty_state.html.haml b/app/views/projects/jobs/_empty_state.html.haml
deleted file mode 100644
index ea552c73c92..00000000000
--- a/app/views/projects/jobs/_empty_state.html.haml
+++ /dev/null
@@ -1,18 +0,0 @@
-- illustration = local_assigns.fetch(:illustration)
-- illustration_size = local_assigns.fetch(:illustration_size)
-- title = local_assigns.fetch(:title)
-- content = local_assigns.fetch(:content, nil)
-- action = local_assigns.fetch(:action, nil)
-
-.row.empty-state
- .col-12
- .svg-content{ class: illustration_size }
- = image_tag illustration
- .col-12
- .text-content
- %h4.text-center= title
- - if content
- %p= content
- - if action
- .text-center
- = action
diff --git a/app/views/projects/jobs/_empty_states.html.haml b/app/views/projects/jobs/_empty_states.html.haml
deleted file mode 100644
index e5198d047df..00000000000
--- a/app/views/projects/jobs/_empty_states.html.haml
+++ /dev/null
@@ -1,9 +0,0 @@
-- detailed_status = @build.detailed_status(current_user)
-- illustration = detailed_status.illustration
-
-= render 'empty_state',
- illustration: illustration[:image],
- illustration_size: illustration[:size],
- title: illustration[:title],
- content: illustration[:content],
- action: detailed_status.has_action? ? link_to(detailed_status.action_button_title, detailed_status.action_path, method: detailed_status.action_method, class: 'btn btn-primary', title: detailed_status.action_button_title) : nil
diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml
index ab7963737ca..a5f814b722d 100644
--- a/app/views/projects/jobs/show.html.haml
+++ b/app/views/projects/jobs/show.html.haml
@@ -42,8 +42,6 @@
= custom_icon('scroll_down')
= render 'shared/builds/build_output'
- - else
- = render "empty_states"
#js-details-block-vue{ data: { terminal_path: can?(current_user, :create_build_terminal, @build) && @build.has_terminal? ? terminal_project_job_path(@project, @build) : nil } }
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index 683dda4f166..11a05eada30 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -2,32 +2,25 @@
- page_title "Labels"
- can_admin_label = can?(current_user, :admin_label, @project)
- search = params[:search]
+- subscribed = params[:subscribed]
+- labels_or_filters = @labels.exists? || @prioritized_labels.exists? || search.present? || subscribed.present?
- if can_admin_label
- content_for(:header_content) do
.nav-controls
= link_to _('New label'), new_project_label_path(@project), class: "btn btn-success"
-- if @labels.exists? || @prioritized_labels.exists? || search.present?
+- if labels_or_filters
#promote-label-modal
%div{ class: container_class }
- .top-area.adjust
- .nav-text
- = _('Labels can be applied to issues and merge requests.')
-
- .nav-controls
- = form_tag project_labels_path(@project), method: :get do
- .input-group
- = search_field_tag :search, params[:search], { placeholder: _('Filter'), id: 'label-search', class: 'form-control search-text-input input-short', spellcheck: false }
- %span.input-group-append
- %button.btn.btn-default{ type: "submit", "aria-label" => _('Submit search') }
- = icon("search")
- = render 'shared/labels/sort_dropdown'
+ = render 'shared/labels/nav'
.labels-container.prepend-top-10
- if can_admin_label
- if search.blank?
%p.text-muted
+ = _('Labels can be applied to issues and merge requests.')
+ %br
= _('Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging.')
-# Only show it in the first page
- hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1')
@@ -59,7 +52,9 @@
- else
.nothing-here-block
= _('No labels with such name or description')
-
+ - elsif subscribed.present?
+ .nothing-here-block
+ = _('You do not have any subscriptions yet')
- else
= render 'shared/empty_states/labels'
diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml
index b23baa22d8b..ef2fa8668c0 100644
--- a/app/views/projects/merge_requests/show.html.haml
+++ b/app/views/projects/merge_requests/show.html.haml
@@ -60,7 +60,7 @@
%section.col-md-12
%script.js-notes-data{ type: "application/json" }= initial_notes_data(true).to_json.html_safe
.issuable-discussion.js-vue-notes-event
- #js-vue-mr-discussions{ data: { notes_data: notes_data(@merge_request),
+ #js-vue-mr-discussions{ data: { notes_data: notes_data(@merge_request).to_json,
noteable_data: serialize_issuable(@merge_request),
noteable_type: 'MergeRequest',
target_type: 'merge_request',
diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml
index 314af44490e..ec503cd8bef 100644
--- a/app/views/projects/runners/_specific_runners.html.haml
+++ b/app/views/projects/runners/_specific_runners.html.haml
@@ -1,8 +1,34 @@
%h3
= _('Specific Runners')
-= render partial: 'ci/runner/how_to_setup_specific_runner',
- locals: { registration_token: @project.runners_token }
+.bs-callout.help-callout
+ .append-bottom-10
+ %h4= _('Set up a specific Runner automatically')
+
+ %p
+ - link_to_help_page = link_to(_('Learn more about Kubernetes'),
+ help_page_path('user/project/clusters/index'),
+ target: '_blank',
+ rel: 'noopener noreferrer')
+
+ = _('You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page }
+
+ %ol
+ %li
+ = _('Click the button below to begin the install process by navigating to the Kubernetes page')
+ %li
+ = _('Select an existing Kubernetes cluster or create a new one')
+ %li
+ = _('From the Kubernetes cluster details view, install Runner from the applications list')
+
+ = link_to _('Install Runner on Kubernetes'),
+ project_clusters_path(@project),
+ class: 'btn btn-info'
+ %hr
+ = render partial: 'ci/runner/how_to_setup_runner',
+ locals: { registration_token: @project.runners_token,
+ type: 'specific',
+ reset_token_url: reset_registration_token_namespace_project_settings_ci_cd_path }
- if @project_runners.any?
%h4.underlined-title Runners activated for this project
diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
index ab92b757836..5ec5a06396e 100644
--- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
@@ -39,10 +39,17 @@
= form.label :deploy_strategy_continuous, class: 'form-check-label' do
= s_('CICD|Continuous deployment to production')
= link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'auto-deploy'), target: '_blank'
+
+ .form-check
+ = form.radio_button :deploy_strategy, 'timed_incremental', class: 'form-check-input'
+ = form.label :deploy_strategy_timed_incremental, class: 'form-check-label' do
+ = s_('CICD|Continuous deployment to production using timed incremental rollout')
+ = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'timed-incremental-rollout-to-production'), target: '_blank'
+
.form-check
= form.radio_button :deploy_strategy, 'manual', class: 'form-check-input'
= form.label :deploy_strategy_manual, class: 'form-check-label' do
= s_('CICD|Automatic deployment to staging, manual deployment to production')
- = link_to icon('question-circle'), help_page_path('ci/environments.md', anchor: 'manually-deploying-to-environments'), target: '_blank'
+ = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'incremental-rollout-to-production'), target: '_blank'
= f.submit _('Save changes'), class: "btn btn-success prepend-top-15"
diff --git a/app/views/projects/settings/ci_cd/_form.html.haml b/app/views/projects/settings/ci_cd/_form.html.haml
index ae923d8e6dc..41afaa9ffc0 100644
--- a/app/views/projects/settings/ci_cd/_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_form.html.haml
@@ -3,16 +3,6 @@
= form_for @project, url: project_settings_ci_cd_path(@project, anchor: 'js-general-pipeline-settings') do |f|
= form_errors(@project)
%fieldset.builds-feature
- .form-group.append-bottom-default.js-secret-runner-token
- = f.label :runners_token, _("Runner token"), class: 'label-bold'
- .form-control.js-secret-value-placeholder
- = '*' * 20
- = f.text_field :runners_token, class: "form-control hide js-secret-value", placeholder: 'xEeFCaDAB89'
- %p.form-text.text-muted= _("The secure token used by the Runner to checkout the project")
- %button.btn.btn-info.prepend-top-10.js-secret-value-reveal-button{ type: 'button', data: { secret_reveal_status: 'false' } }
- = _('Reveal value')
-
- %hr
.form-group
%h5.prepend-top-0
= _("Git strategy for pipelines")
diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml
index 16961784e00..98e2829ba43 100644
--- a/app/views/projects/settings/ci_cd/show.html.haml
+++ b/app/views/projects/settings/ci_cd/show.html.haml
@@ -12,7 +12,7 @@
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
- = _("Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report.")
+ = _("Customize your pipeline configuration, view your pipeline status and coverage report.")
.settings-content
= render 'form'
diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml
index 28e6fe1b16d..0d2f6bb77d6 100644
--- a/app/views/shared/boards/_show.html.haml
+++ b/app/views/shared/boards/_show.html.haml
@@ -33,7 +33,7 @@
- if @project
%board-add-issues-modal{ "new-issue-path" => new_project_issue_path(@project),
"milestone-path" => milestones_filter_dropdown_path,
- "label-path" => labels_filter_path,
+ "label-path" => labels_filter_path_with_defaults,
"empty-state-svg" => image_path('illustrations/issues.svg'),
":issue-link-base" => "issueLinkBase",
":root-path" => "rootPath",
diff --git a/app/views/shared/boards/components/sidebar/_labels.html.haml b/app/views/shared/boards/components/sidebar/_labels.html.haml
index 532045f3697..6138914206b 100644
--- a/app/views/shared/boards/components/sidebar/_labels.html.haml
+++ b/app/views/shared/boards/components/sidebar/_labels.html.haml
@@ -25,7 +25,7 @@
show_no: "true",
show_any: "true",
project_id: @project&.try(:id),
- labels: labels_filter_path(false),
+ labels: labels_filter_path_with_defaults,
namespace_path: @namespace_path,
project_path: @project.try(:path) } }
%span.dropdown-toggle-text
diff --git a/app/views/shared/issuable/_label_dropdown.html.haml b/app/views/shared/issuable/_label_dropdown.html.haml
index 0b42b33581a..6eb1f8f0853 100644
--- a/app/views/shared/issuable/_label_dropdown.html.haml
+++ b/app/views/shared/issuable/_label_dropdown.html.haml
@@ -8,7 +8,7 @@
- classes = local_assigns.fetch(:classes, [])
- selected = local_assigns.fetch(:selected, nil)
- dropdown_title = local_assigns.fetch(:dropdown_title, "Filter by label")
-- dropdown_data = {toggle: 'dropdown', field_name: "label_name[]", show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path), labels: labels_filter_path, default_label: "Labels"}
+- dropdown_data = {toggle: 'dropdown', field_name: "label_name[]", show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path), labels: labels_filter_path_with_defaults, default_label: "Labels"}
- dropdown_data.merge!(data_options)
- label_name = local_assigns.fetch(:label_name, "Labels")
- no_default_styles = local_assigns.fetch(:no_default_styles, false)
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 32b609eed0d..aa136af1955 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -109,7 +109,7 @@
- selected_labels.each do |label|
= hidden_field_tag "#{issuable.to_ability_name}[label_names][]", label.id, id: nil
.dropdown
- %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{ type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (labels_filter_path(false) if @project), display: 'static' } }
+ %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{ type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (labels_filter_path_with_defaults if @project), display: 'static' } }
%span.dropdown-toggle-text{ class: ("is-default" if selected_labels.empty?) }
= multi_label_name(selected_labels, "Labels")
= icon('chevron-down', 'aria-hidden': 'true')
diff --git a/app/views/shared/labels/_nav.html.haml b/app/views/shared/labels/_nav.html.haml
new file mode 100644
index 00000000000..98572db738b
--- /dev/null
+++ b/app/views/shared/labels/_nav.html.haml
@@ -0,0 +1,20 @@
+- subscribed = params[:subscribed]
+
+.top-area.adjust
+ %ul.nav-links.nav.nav-tabs
+ %li{ class: active_when(subscribed != 'true') }>
+ = link_to labels_filter_path do
+ = _('All')
+ - if current_user
+ %li{ class: active_when(subscribed == 'true') }>
+ = link_to labels_filter_path(subscribed: 'true') do
+ = _('Subscribed')
+ .nav-controls
+ = form_tag labels_filter_path, method: :get do
+ = hidden_field_tag :subscribed, params[:subscribed]
+ .input-group
+ = search_field_tag :search, params[:search], { placeholder: _('Filter'), id: 'label-search', class: 'form-control search-text-input input-short', spellcheck: false }
+ %span.input-group-append
+ %button.btn.btn-default{ type: "submit", "aria-label" => _('Submit search') }
+ = icon("search")
+ = render 'shared/labels/sort_dropdown'
diff --git a/app/views/shared/labels/_sort_dropdown.html.haml b/app/views/shared/labels/_sort_dropdown.html.haml
index ff6e2947ffd..8a7d037e15b 100644
--- a/app/views/shared/labels/_sort_dropdown.html.haml
+++ b/app/views/shared/labels/_sort_dropdown.html.haml
@@ -6,4 +6,4 @@
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-sort
%li
- label_sort_options_hash.each do |value, title|
- = sortable_item(title, page_filter_path(sort: value, label: true), sort_title)
+ = sortable_item(title, page_filter_path(sort: value, label: true, subscribed: params[:subscribed]), sort_title)
diff --git a/app/views/users/_overview.html.haml b/app/views/users/_overview.html.haml
new file mode 100644
index 00000000000..f8b3754840d
--- /dev/null
+++ b/app/views/users/_overview.html.haml
@@ -0,0 +1,32 @@
+.row
+ .col-md-12.col-lg-6
+ .calendar-block
+ .content-block.hide-bottom-border
+ %h4
+ = s_('UserProfile|Activity')
+ .user-calendar.d-none.d-sm-block.text-left{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } }
+ %h4.center.light
+ %i.fa.fa-spinner.fa-spin
+ .user-calendar-activities.d-none.d-sm-block
+
+ - if can?(current_user, :read_cross_project)
+ .activities-block
+ .content-block
+ %h5.prepend-top-10
+ = s_('UserProfile|Recent contributions')
+ .overview-content-list{ data: { href: user_path } }
+ .center.light.loading
+ %i.fa.fa-spinner.fa-spin
+ .prepend-top-10
+ = link_to s_('UserProfile|View all'), user_activity_path, class: "hide js-view-all"
+
+ .col-md-12.col-lg-6
+ .projects-block
+ .content-block
+ %h4
+ = s_('UserProfile|Personal projects')
+ .overview-content-list{ data: { href: user_projects_path } }
+ .center.light.loading
+ %i.fa.fa-spinner.fa-spin
+ .prepend-top-10
+ = link_to s_('UserProfile|View all'), user_projects_path, class: "hide js-view-all"
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 7a38d290915..d6c8420b744 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -12,22 +12,22 @@
.cover-block.user-cover-block.top-area
.cover-controls
- if @user == current_user
- = link_to profile_path, class: 'btn btn-default has-tooltip', title: 'Edit profile', 'aria-label': 'Edit profile' do
+ = link_to profile_path, class: 'btn btn-default has-tooltip', title: s_('UserProfile|Edit profile'), 'aria-label': 'Edit profile' do
= icon('pencil')
- elsif current_user
- if @user.abuse_report
- %button.btn.btn-danger{ title: 'Already reported for abuse',
+ %button.btn.btn-danger{ title: s_('UserProfile|Already reported for abuse'),
data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } }
= icon('exclamation-circle')
- else
= link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn',
- title: 'Report abuse', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
+ title: s_('UserProfile|Report abuse'), data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= icon('exclamation-circle')
- if can?(current_user, :read_user_profile, @user)
- = link_to user_path(@user, rss_url_options), class: 'btn btn-default has-tooltip', title: 'Subscribe', 'aria-label': 'Subscribe' do
+ = link_to user_path(@user, rss_url_options), class: 'btn btn-default has-tooltip', title: s_('UserProfile|Subscribe'), 'aria-label': 'Subscribe' do
= icon('rss')
- if current_user && current_user.admin?
- = link_to [:admin, @user], class: 'btn btn-default', title: 'View user in admin area',
+ = link_to [:admin, @user], class: 'btn btn-default', title: s_('UserProfile|View user in admin area'),
data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('users')
@@ -51,7 +51,7 @@
@#{@user.username}
- if can?(current_user, :read_user_profile, @user)
%span.middle-dot-divider
- Member since #{@user.created_at.to_date.to_s(:long)}
+ = s_('Member since %{date}') % { date: @user.created_at.to_date.to_s(:long) }
.cover-desc
- unless @user.public_email.blank?
@@ -91,32 +91,40 @@
.fade-left= icon('angle-left')
.fade-right= icon('angle-right')
%ul.nav-links.user-profile-nav.scrolling-tabs.nav.nav-tabs
+ - if profile_tab?(:overview)
+ %li.js-overview-tab
+ = link_to user_path, data: { target: 'div#js-overview', action: 'overview', toggle: 'tab' } do
+ = s_('UserProfile|Overview')
- if profile_tab?(:activity)
%li.js-activity-tab
- = link_to user_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do
- Activity
+ = link_to user_activity_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do
+ = s_('UserProfile|Activity')
- if profile_tab?(:groups)
%li.js-groups-tab
= link_to user_groups_path, data: { target: 'div#groups', action: 'groups', toggle: 'tab', endpoint: user_groups_path(format: :json) } do
- Groups
+ = s_('UserProfile|Groups')
- if profile_tab?(:contributed)
%li.js-contributed-tab
= link_to user_contributed_projects_path, data: { target: 'div#contributed', action: 'contributed', toggle: 'tab', endpoint: user_contributed_projects_path(format: :json) } do
- Contributed projects
+ = s_('UserProfile|Contributed projects')
- if profile_tab?(:projects)
%li.js-projects-tab
= link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do
- Personal projects
+ = s_('UserProfile|Personal projects')
- if profile_tab?(:snippets)
%li.js-snippets-tab
= link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do
- Snippets
+ = s_('UserProfile|Snippets')
%div{ class: container_class }
.tab-content
+ - if profile_tab?(:overview)
+ #js-overview.tab-pane
+ = render "users/overview"
+
- if profile_tab?(:activity)
#activity.tab-pane
- .row-content-block.calender-block.white.second-block.d-none.d-sm-block
+ .row-content-block.calendar-block.white.second-block.d-none.d-sm-block
.user-calendar{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } }
%h4.center.light
%i.fa.fa-spinner.fa-spin
@@ -124,7 +132,7 @@
- if can?(current_user, :read_cross_project)
%h4.prepend-top-20
- Most Recent Activity
+ = s_('UserProfile|Most Recent Activity')
.content_list{ data: { href: user_path } }
= spinner
@@ -155,4 +163,4 @@
.col-12.text-center
.text-content
%h4
- This user has a private profile
+ = s_('UserProfile|This user has a private profile')
diff --git a/app/workers/prune_old_events_worker.rb b/app/workers/prune_old_events_worker.rb
index d44ad0d8030..dc4b7670131 100644
--- a/app/workers/prune_old_events_worker.rb
+++ b/app/workers/prune_old_events_worker.rb
@@ -6,14 +6,13 @@ class PruneOldEventsWorker
# rubocop: disable CodeReuse/ActiveRecord
def perform
- # Contribution calendar shows maximum 12 months of events.
- # Double nested query is used because MySQL doesn't allow DELETE subqueries
- # on the same table.
+ # Contribution calendar shows maximum 12 months of events, we retain 2 years for data integrity.
+ # Double nested query is used because MySQL doesn't allow DELETE subqueries on the same table.
Event.unscoped.where(
'(id IN (SELECT id FROM (?) ids_to_remove))',
Event.unscoped.where(
'created_at < ?',
- (12.months + 1.day).ago)
+ (2.years + 1.day).ago)
.select(:id)
.limit(10_000))
.delete_all
diff --git a/changelogs/unreleased/-51457-Show-percentage-of-language-detection-on-the-language-bar.yml b/changelogs/unreleased/-51457-Show-percentage-of-language-detection-on-the-language-bar.yml
new file mode 100644
index 00000000000..074cc9d642b
--- /dev/null
+++ b/changelogs/unreleased/-51457-Show-percentage-of-language-detection-on-the-language-bar.yml
@@ -0,0 +1,5 @@
+---
+title: Show percentage of language detection on the language bar
+merge_request: 22056
+author: Johann Hubert Sonntagbauer
+type: added
diff --git a/changelogs/unreleased/22104-fix-environment-name-overlap.yml b/changelogs/unreleased/22104-fix-environment-name-overlap.yml
new file mode 100644
index 00000000000..aaa1a1709c8
--- /dev/null
+++ b/changelogs/unreleased/22104-fix-environment-name-overlap.yml
@@ -0,0 +1,4 @@
+---
+title: "Fix the issue where long environment names aren't being truncated, causing the environment name to overlap into the column next to it."
+merge_request: 22104
+type: fixed
diff --git a/changelogs/unreleased/22188-drop-down-filter-for-project-snippets.yml b/changelogs/unreleased/22188-drop-down-filter-for-project-snippets.yml
new file mode 100644
index 00000000000..e24c55e3bad
--- /dev/null
+++ b/changelogs/unreleased/22188-drop-down-filter-for-project-snippets.yml
@@ -0,0 +1,5 @@
+---
+title: Add autocomplete drop down filter for project snippets
+merge_request: 21458
+author: Fabian Schneider
+type: added
diff --git a/changelogs/unreleased/40140-2FA-mobile-options-should-be-rephrased.yml b/changelogs/unreleased/40140-2FA-mobile-options-should-be-rephrased.yml
new file mode 100644
index 00000000000..8131e2ff54f
--- /dev/null
+++ b/changelogs/unreleased/40140-2FA-mobile-options-should-be-rephrased.yml
@@ -0,0 +1,5 @@
+---
+title: Rephrase 2FA and TOTP documentation and view
+merge_request: 21998
+author: Marc Schwede
+type: other
diff --git a/changelogs/unreleased/40636-instance-configuration-shows-incorrect-ssh-fingerprints.yml b/changelogs/unreleased/40636-instance-configuration-shows-incorrect-ssh-fingerprints.yml
new file mode 100644
index 00000000000..1ebad500e9f
--- /dev/null
+++ b/changelogs/unreleased/40636-instance-configuration-shows-incorrect-ssh-fingerprints.yml
@@ -0,0 +1,5 @@
+---
+title: Instance Configuration page now displays correct SSH fingerprints
+merge_request: 22081
+author:
+type: fixed
diff --git a/changelogs/unreleased/41922-simplify-runner-registration-token-resetting.yml b/changelogs/unreleased/41922-simplify-runner-registration-token-resetting.yml
new file mode 100644
index 00000000000..582d7824d27
--- /dev/null
+++ b/changelogs/unreleased/41922-simplify-runner-registration-token-resetting.yml
@@ -0,0 +1,5 @@
+---
+title: Simplify runner registration token resetting
+merge_request: 21658
+author:
+type: changed
diff --git a/changelogs/unreleased/46050_add_new_ci_predefined_variables_for_gitlab_version.yml b/changelogs/unreleased/46050_add_new_ci_predefined_variables_for_gitlab_version.yml
new file mode 100644
index 00000000000..dd230d5f35e
--- /dev/null
+++ b/changelogs/unreleased/46050_add_new_ci_predefined_variables_for_gitlab_version.yml
@@ -0,0 +1,5 @@
+---
+title: Add GitLab version components to CI environment variables
+merge_request: 21853
+author:
+type: added
diff --git a/changelogs/unreleased/47496-more-n-1s-in-calculating-notification-recipients.yml b/changelogs/unreleased/47496-more-n-1s-in-calculating-notification-recipients.yml
new file mode 100644
index 00000000000..f70011ac827
--- /dev/null
+++ b/changelogs/unreleased/47496-more-n-1s-in-calculating-notification-recipients.yml
@@ -0,0 +1,5 @@
+---
+title: Reduce queries needed to compute notification recipients
+merge_request: 22050
+author:
+type: performance
diff --git a/changelogs/unreleased/48222-fix-todos-status-button.yml b/changelogs/unreleased/48222-fix-todos-status-button.yml
new file mode 100644
index 00000000000..2f7c79a07d0
--- /dev/null
+++ b/changelogs/unreleased/48222-fix-todos-status-button.yml
@@ -0,0 +1,6 @@
+---
+title: Fix the state of the Done button when there is an error in the GitLab Todos
+ section
+merge_request:
+author: marcos8896
+type: fixed
diff --git a/changelogs/unreleased/48494-fix-merge-request-buttons-spacing.yml b/changelogs/unreleased/48494-fix-merge-request-buttons-spacing.yml
new file mode 100644
index 00000000000..41cc024b8ac
--- /dev/null
+++ b/changelogs/unreleased/48494-fix-merge-request-buttons-spacing.yml
@@ -0,0 +1,5 @@
+---
+title: Fix incorrect spacing between buttons when commenting on a MR.
+merge_request: 22135
+author:
+type: fixed
diff --git a/changelogs/unreleased/49075-add-status-message-from-within-user-menu.yml b/changelogs/unreleased/49075-add-status-message-from-within-user-menu.yml
new file mode 100644
index 00000000000..2c65c92dd8b
--- /dev/null
+++ b/changelogs/unreleased/49075-add-status-message-from-within-user-menu.yml
@@ -0,0 +1,5 @@
+---
+title: Set user status from within user menu
+merge_request: 21643
+author:
+type: added
diff --git a/changelogs/unreleased/49801-add-new-overview-tab-on-user-profile-page.yml b/changelogs/unreleased/49801-add-new-overview-tab-on-user-profile-page.yml
new file mode 100644
index 00000000000..5e2be42c8b7
--- /dev/null
+++ b/changelogs/unreleased/49801-add-new-overview-tab-on-user-profile-page.yml
@@ -0,0 +1,5 @@
+---
+title: Adds new 'Overview' tab on user profile page
+merge_request: 21663
+author:
+type: other
diff --git a/changelogs/unreleased/50246-can-t-sort-group-issues-by-popularity-when-searching.yml b/changelogs/unreleased/50246-can-t-sort-group-issues-by-popularity-when-searching.yml
new file mode 100644
index 00000000000..cc7a79d25e5
--- /dev/null
+++ b/changelogs/unreleased/50246-can-t-sort-group-issues-by-popularity-when-searching.yml
@@ -0,0 +1,6 @@
+---
+title: Fix sorting by priority or popularity on group issues page, when also searching
+ issue content
+merge_request: 21521
+author:
+type: fixed
diff --git a/changelogs/unreleased/50359-activerecord-statementinvalid-pg-querycanceled-error-canceling-statement-due-to-statement-timeout.yml b/changelogs/unreleased/50359-activerecord-statementinvalid-pg-querycanceled-error-canceling-statement-due-to-statement-timeout.yml
new file mode 100644
index 00000000000..09ec4b8d73d
--- /dev/null
+++ b/changelogs/unreleased/50359-activerecord-statementinvalid-pg-querycanceled-error-canceling-statement-due-to-statement-timeout.yml
@@ -0,0 +1,5 @@
+---
+title: Fix timeout when running the RemoveRestrictedTodos background migration
+merge_request: 21893
+author:
+type: fixed
diff --git a/changelogs/unreleased/50552-unable-to-close-performance-bar.yml b/changelogs/unreleased/50552-unable-to-close-performance-bar.yml
new file mode 100644
index 00000000000..e3619149d2a
--- /dev/null
+++ b/changelogs/unreleased/50552-unable-to-close-performance-bar.yml
@@ -0,0 +1,5 @@
+---
+title: Fix performance bar modal position
+merge_request: 21577
+author:
+type: fixed
diff --git a/changelogs/unreleased/51009-remove-rbac-clusters-feature-flag.yml b/changelogs/unreleased/51009-remove-rbac-clusters-feature-flag.yml
new file mode 100644
index 00000000000..99946b954ce
--- /dev/null
+++ b/changelogs/unreleased/51009-remove-rbac-clusters-feature-flag.yml
@@ -0,0 +1,5 @@
+---
+title: Remove 'rbac_clusters' feature flag
+merge_request: 22096
+author:
+type: changed
diff --git a/changelogs/unreleased/51748-filter-any-milestone-via-api.yml b/changelogs/unreleased/51748-filter-any-milestone-via-api.yml
new file mode 100644
index 00000000000..30304e5a4ac
--- /dev/null
+++ b/changelogs/unreleased/51748-filter-any-milestone-via-api.yml
@@ -0,0 +1,5 @@
+---
+title: Allows to filter issues by Any milestone in the API
+merge_request: 22080
+author: Jacopo Beschi @jacopo-beschi
+type: added
diff --git a/changelogs/unreleased/51803-include-commits-stats-in-projects-api.yml b/changelogs/unreleased/51803-include-commits-stats-in-projects-api.yml
new file mode 100644
index 00000000000..e67cc27f852
--- /dev/null
+++ b/changelogs/unreleased/51803-include-commits-stats-in-projects-api.yml
@@ -0,0 +1,5 @@
+---
+title: Includes commit stats in POST project commits API
+merge_request: 21968
+author: Jacopo Beschi @jacopo-beschi
+type: fixed
diff --git a/changelogs/unreleased/51958-fix-mr-discussion-loading.yml b/changelogs/unreleased/51958-fix-mr-discussion-loading.yml
new file mode 100644
index 00000000000..f80ee51291d
--- /dev/null
+++ b/changelogs/unreleased/51958-fix-mr-discussion-loading.yml
@@ -0,0 +1,5 @@
+---
+title: Fix loading issue on some merge request discussion
+merge_request: 21982
+author:
+type: fixed
diff --git a/changelogs/unreleased/52178-markdown-table-borders.yml b/changelogs/unreleased/52178-markdown-table-borders.yml
new file mode 100644
index 00000000000..965f21f2a97
--- /dev/null
+++ b/changelogs/unreleased/52178-markdown-table-borders.yml
@@ -0,0 +1,5 @@
+---
+title: Add borders and white background to markdown tables
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/52193-Pipeline-graph-is-not-vertically-aligned-in-commit-page.yml b/changelogs/unreleased/52193-Pipeline-graph-is-not-vertically-aligned-in-commit-page.yml
new file mode 100644
index 00000000000..2d3ac49807a
--- /dev/null
+++ b/changelogs/unreleased/52193-Pipeline-graph-is-not-vertically-aligned-in-commit-page.yml
@@ -0,0 +1,5 @@
+---
+title: Vertical align Pipeline Graph in Commit Page
+merge_request: 22173
+author: Johann Hubert Sonntagbauer
+type: fixed
diff --git a/changelogs/unreleased/52194-trim-extra-whitespace-invite-member.yml b/changelogs/unreleased/52194-trim-extra-whitespace-invite-member.yml
new file mode 100644
index 00000000000..d96c2bc7acd
--- /dev/null
+++ b/changelogs/unreleased/52194-trim-extra-whitespace-invite-member.yml
@@ -0,0 +1,5 @@
+---
+title: Trim whitespace when inviting a new user by email
+merge_request: 22119
+author: Jacopo Beschi @jacopo-beschi
+type: fixed
diff --git a/changelogs/unreleased/52242-ui-ux-bug-in-change-group-path.yml b/changelogs/unreleased/52242-ui-ux-bug-in-change-group-path.yml
new file mode 100644
index 00000000000..3fea6f33451
--- /dev/null
+++ b/changelogs/unreleased/52242-ui-ux-bug-in-change-group-path.yml
@@ -0,0 +1,5 @@
+---
+title: Fix incorrect parent path on group settings page
+merge_request: 22142
+author:
+type: fixed
diff --git a/changelogs/unreleased/52353-keyboard-navigation-project-slug-is-not-focused-on-new-project-page.yml b/changelogs/unreleased/52353-keyboard-navigation-project-slug-is-not-focused-on-new-project-page.yml
new file mode 100644
index 00000000000..ffcc0cc08a0
--- /dev/null
+++ b/changelogs/unreleased/52353-keyboard-navigation-project-slug-is-not-focused-on-new-project-page.yml
@@ -0,0 +1,5 @@
+---
+title: Focus project slug on tab navigation
+merge_request: 22198
+author:
+type: other
diff --git a/changelogs/unreleased/52367-cleanup-web-hooks-columns.yml b/changelogs/unreleased/52367-cleanup-web-hooks-columns.yml
new file mode 100644
index 00000000000..d1f3ca83613
--- /dev/null
+++ b/changelogs/unreleased/52367-cleanup-web-hooks-columns.yml
@@ -0,0 +1,5 @@
+---
+title: Remove legacy unencrypted webhook columns from the database
+merge_request: 22199
+author:
+type: changed
diff --git a/changelogs/unreleased/52408-pip-cache-dir-to-cache-python-dependencies.yml b/changelogs/unreleased/52408-pip-cache-dir-to-cache-python-dependencies.yml
new file mode 100644
index 00000000000..19d3e35c15c
--- /dev/null
+++ b/changelogs/unreleased/52408-pip-cache-dir-to-cache-python-dependencies.yml
@@ -0,0 +1,5 @@
+---
+title: Use the standard PIP_CACHE_DIR for Python dependency caching template
+merge_request: 22211
+author: Takuya Noguchi
+type: fixed
diff --git a/changelogs/unreleased/5987-project-templates-api.yml b/changelogs/unreleased/5987-project-templates-api.yml
new file mode 100644
index 00000000000..a627ba9f0de
--- /dev/null
+++ b/changelogs/unreleased/5987-project-templates-api.yml
@@ -0,0 +1,5 @@
+---
+title: Allow file templates to be requested at the project level
+merge_request: 7776
+author:
+type: added
diff --git a/changelogs/unreleased/Fix-pipeline-redirect.yml b/changelogs/unreleased/Fix-pipeline-redirect.yml
new file mode 100644
index 00000000000..459273c7740
--- /dev/null
+++ b/changelogs/unreleased/Fix-pipeline-redirect.yml
@@ -0,0 +1,5 @@
+---
+title: Redirect to the pipeline builds page when a build is canceled
+merge_request:
+author: Eva Kadlecova
+type: fixed
diff --git a/changelogs/unreleased/add-button-to-insert-table-in-markdown.yml b/changelogs/unreleased/add-button-to-insert-table-in-markdown.yml
new file mode 100644
index 00000000000..69432c0d20c
--- /dev/null
+++ b/changelogs/unreleased/add-button-to-insert-table-in-markdown.yml
@@ -0,0 +1,5 @@
+---
+title: Add markdown header toolbar button to insert table
+merge_request: 18480
+author: George Tsiolis
+type: added
diff --git a/changelogs/unreleased/add-installation-type-backup-information.yml b/changelogs/unreleased/add-installation-type-backup-information.yml
new file mode 100644
index 00000000000..24cf4cc21f4
--- /dev/null
+++ b/changelogs/unreleased/add-installation-type-backup-information.yml
@@ -0,0 +1,5 @@
+---
+title: Add installation type to backup information file
+merge_request: 22150
+author:
+type: changed
diff --git a/changelogs/unreleased/add_reliable_fetcher.yml b/changelogs/unreleased/add_reliable_fetcher.yml
new file mode 100644
index 00000000000..c08c755e546
--- /dev/null
+++ b/changelogs/unreleased/add_reliable_fetcher.yml
@@ -0,0 +1,5 @@
+---
+title: Use Reliable Sidekiq fetch
+merge_request: 21715
+author:
+type: fixed
diff --git a/changelogs/unreleased/auth.yml b/changelogs/unreleased/auth.yml
new file mode 100644
index 00000000000..cd4bbf0059e
--- /dev/null
+++ b/changelogs/unreleased/auth.yml
@@ -0,0 +1,5 @@
+---
+title: Add access control to GitLab pages and make it possible to enable/disable it in project settings
+merge_request: 18589
+author: Tuomo Ala-Vannesluoma
+type: added
diff --git a/changelogs/unreleased/autodevops-timed-incremental-rollout.yml b/changelogs/unreleased/autodevops-timed-incremental-rollout.yml
new file mode 100644
index 00000000000..72c7b41177d
--- /dev/null
+++ b/changelogs/unreleased/autodevops-timed-incremental-rollout.yml
@@ -0,0 +1,5 @@
+---
+title: Add timed incremental rollout to Auto DevOps
+merge_request: 22023
+author:
+type: added
diff --git a/changelogs/unreleased/clone-nurtch-demo-repo.yml b/changelogs/unreleased/clone-nurtch-demo-repo.yml
new file mode 100644
index 00000000000..c77138d27f0
--- /dev/null
+++ b/changelogs/unreleased/clone-nurtch-demo-repo.yml
@@ -0,0 +1,5 @@
+---
+title: Copy nurtch demo notebooks at Jupyter startup
+merge_request: 21698
+author: Amit Rathi
+type: added
diff --git a/changelogs/unreleased/copy-changes-for-abuse-clarity.yml b/changelogs/unreleased/copy-changes-for-abuse-clarity.yml
new file mode 100644
index 00000000000..00d9fec5e42
--- /dev/null
+++ b/changelogs/unreleased/copy-changes-for-abuse-clarity.yml
@@ -0,0 +1,5 @@
+---
+title: Increased retained event data by extending events pruner timeframe to 2 years
+merge_request: 22145
+author:
+type: changed
diff --git a/changelogs/unreleased/dz-labels-subscribe-filter.yml b/changelogs/unreleased/dz-labels-subscribe-filter.yml
new file mode 100644
index 00000000000..768c20c77c7
--- /dev/null
+++ b/changelogs/unreleased/dz-labels-subscribe-filter.yml
@@ -0,0 +1,5 @@
+---
+title: Add subscribe filter to group and project labels pages
+merge_request: 21965
+author:
+type: added
diff --git a/changelogs/unreleased/feature-gb-pipeline-only-except-with-modified-paths.yml b/changelogs/unreleased/feature-gb-pipeline-only-except-with-modified-paths.yml
new file mode 100644
index 00000000000..62676cdad62
--- /dev/null
+++ b/changelogs/unreleased/feature-gb-pipeline-only-except-with-modified-paths.yml
@@ -0,0 +1,5 @@
+---
+title: Add support for pipeline only/except policy for modified paths
+merge_request: 21981
+author:
+type: added
diff --git a/changelogs/unreleased/features-unauth-access-ssh-keys.yml b/changelogs/unreleased/features-unauth-access-ssh-keys.yml
new file mode 100644
index 00000000000..bae2bcfaabd
--- /dev/null
+++ b/changelogs/unreleased/features-unauth-access-ssh-keys.yml
@@ -0,0 +1,5 @@
+---
+title: Enable unauthenticated access to public SSH keys via the API
+merge_request: 20118
+author: Ronald Claveau
+type: changed
diff --git a/changelogs/unreleased/gt-remove-duplicate-button-from-the-md-header-toolbar.yml b/changelogs/unreleased/gt-remove-duplicate-button-from-the-md-header-toolbar.yml
new file mode 100644
index 00000000000..c2e828eb697
--- /dev/null
+++ b/changelogs/unreleased/gt-remove-duplicate-button-from-the-md-header-toolbar.yml
@@ -0,0 +1,5 @@
+---
+title: Remove duplicate button from the markdown header toolbar
+merge_request: 22192
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/load_project_features.yml b/changelogs/unreleased/load_project_features.yml
new file mode 100644
index 00000000000..0cf7f0e3a74
--- /dev/null
+++ b/changelogs/unreleased/load_project_features.yml
@@ -0,0 +1,5 @@
+---
+title: Mitigate N+1 queries when parsing commit references in comments.
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/mao-48221-issues_show_sql_count.yml b/changelogs/unreleased/mao-48221-issues_show_sql_count.yml
new file mode 100644
index 00000000000..d634d15946e
--- /dev/null
+++ b/changelogs/unreleased/mao-48221-issues_show_sql_count.yml
@@ -0,0 +1,5 @@
+---
+title: Banzai label ref finder - minimize SQL calls by sharing context more aggresively
+merge_request: 22070
+author:
+type: performance
diff --git a/changelogs/unreleased/more-frozen-string-enable-lib.yml b/changelogs/unreleased/more-frozen-string-enable-lib.yml
new file mode 100644
index 00000000000..9598c53b7fd
--- /dev/null
+++ b/changelogs/unreleased/more-frozen-string-enable-lib.yml
@@ -0,0 +1,5 @@
+---
+title: Enable more frozen string in lib/**/*.rb
+merge_request:
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/osw-fix-lfs-images-not-rendering.yml b/changelogs/unreleased/osw-fix-lfs-images-not-rendering.yml
new file mode 100644
index 00000000000..5dde22d3158
--- /dev/null
+++ b/changelogs/unreleased/osw-fix-lfs-images-not-rendering.yml
@@ -0,0 +1,5 @@
+---
+title: Fix LFS uploaded images not being rendered
+merge_request: 22092
+author:
+type: fixed
diff --git a/changelogs/unreleased/osw-remove-dead-code-on-mr-show.yml b/changelogs/unreleased/osw-remove-dead-code-on-mr-show.yml
new file mode 100644
index 00000000000..d4e2641daf5
--- /dev/null
+++ b/changelogs/unreleased/osw-remove-dead-code-on-mr-show.yml
@@ -0,0 +1,5 @@
+---
+title: Removes expensive dead code on main MR page request
+merge_request: 22153
+author:
+type: performance
diff --git a/changelogs/unreleased/rails5-fix-artifacts-controller-spec.yml b/changelogs/unreleased/rails5-fix-artifacts-controller-spec.yml
new file mode 100644
index 00000000000..3a399bb836e
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-artifacts-controller-spec.yml
@@ -0,0 +1,6 @@
+---
+title: 'Rails5: fix artifacts controller download spec Rails5 has params[:file_type]
+ as '''' if file_type is included as nil in the request'
+merge_request: 22123
+author: Jasper Maes
+type: other
diff --git a/changelogs/unreleased/rails5-mysql-schedule-build.yml b/changelogs/unreleased/rails5-mysql-schedule-build.yml
new file mode 100644
index 00000000000..cbc481fbf89
--- /dev/null
+++ b/changelogs/unreleased/rails5-mysql-schedule-build.yml
@@ -0,0 +1,5 @@
+---
+title: 'Rails 5: fix mysql milliseconds problems in scheduled build specs'
+merge_request: 22170
+author: Jasper Maes
+type: other
diff --git a/changelogs/unreleased/rails5-user-status-spec.yml b/changelogs/unreleased/rails5-user-status-spec.yml
new file mode 100644
index 00000000000..818d480e9fc
--- /dev/null
+++ b/changelogs/unreleased/rails5-user-status-spec.yml
@@ -0,0 +1,5 @@
+---
+title: 'Rails5: fix user edit profile clear status spec'
+merge_request: 22169
+author: Jasper Maes
+type: other
diff --git a/changelogs/unreleased/security-bw-confidential-titles-through-markdown-api.yml b/changelogs/unreleased/security-bw-confidential-titles-through-markdown-api.yml
new file mode 100644
index 00000000000..e0231b7962f
--- /dev/null
+++ b/changelogs/unreleased/security-bw-confidential-titles-through-markdown-api.yml
@@ -0,0 +1,5 @@
+---
+title: Markdown API no longer displays confidential title references unless authorized
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/security-fix-leaking-private-project-namespace.yml b/changelogs/unreleased/security-fix-leaking-private-project-namespace.yml
new file mode 100644
index 00000000000..589d16c0c35
--- /dev/null
+++ b/changelogs/unreleased/security-fix-leaking-private-project-namespace.yml
@@ -0,0 +1,5 @@
+---
+title: Properly filter private references from system notes
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/security-osw-user-info-leak-discussions.yml b/changelogs/unreleased/security-osw-user-info-leak-discussions.yml
new file mode 100644
index 00000000000..5acbb80fc3d
--- /dev/null
+++ b/changelogs/unreleased/security-osw-user-info-leak-discussions.yml
@@ -0,0 +1,5 @@
+---
+title: Filter user sensitive data from discussions JSON
+merge_request: 2536
+author:
+type: security
diff --git a/changelogs/unreleased/sh-handle-invalid-comparison.yml b/changelogs/unreleased/sh-handle-invalid-comparison.yml
new file mode 100644
index 00000000000..30b5b3d8198
--- /dev/null
+++ b/changelogs/unreleased/sh-handle-invalid-comparison.yml
@@ -0,0 +1,5 @@
+---
+title: Reject invalid branch names in repository compare controller
+merge_request: 22186
+author:
+type: fixed
diff --git a/changelogs/unreleased/update-operations-metrics-empty-state.yml b/changelogs/unreleased/update-operations-metrics-empty-state.yml
new file mode 100644
index 00000000000..51f3935b769
--- /dev/null
+++ b/changelogs/unreleased/update-operations-metrics-empty-state.yml
@@ -0,0 +1,5 @@
+---
+title: Update operations metrics empty state
+merge_request: 21974
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/zj-render-log-artifacts.yml b/changelogs/unreleased/zj-render-log-artifacts.yml
new file mode 100644
index 00000000000..82f29b62300
--- /dev/null
+++ b/changelogs/unreleased/zj-render-log-artifacts.yml
@@ -0,0 +1,5 @@
+---
+title: Render log artifact files in GitLab
+merge_request:
+author:
+type: added
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 67337f4b82f..749cdd0f869 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -210,6 +210,7 @@ production: &base
## GitLab Pages
pages:
enabled: false
+ access_control: false
# The location where pages are stored (default: shared/pages).
# path: shared/pages
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 0caa4962128..bd02b85c7ce 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -200,6 +200,7 @@ Settings.registry['path'] = Settings.absolute(Settings.registry['path
#
Settings['pages'] ||= Settingslogic.new({})
Settings.pages['enabled'] = false if Settings.pages['enabled'].nil?
+Settings.pages['access_control'] = false if Settings.pages['access_control'].nil?
Settings.pages['path'] = Settings.absolute(Settings.pages['path'] || File.join(Settings.shared['path'], "pages"))
Settings.pages['https'] = false if Settings.pages['https'].nil?
Settings.pages['host'] ||= "example.com"
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index 6c1079faad1..bc6b7aed6aa 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -40,6 +40,10 @@ Sidekiq.configure_server do |config|
ActiveRecord::Base.clear_all_connections!
end
+ if Feature.enabled?(:gitlab_sidekiq_reliable_fetcher)
+ Sidekiq::ReliableFetcher.setup_reliable_fetch!(config)
+ end
+
# Sidekiq-cron: load recurring jobs from gitlab.yml
# UGLY Hack to get nested hash from settingslogic
cron_jobs = JSON.parse(Gitlab.config.cron_jobs.to_json)
@@ -57,10 +61,10 @@ Sidekiq.configure_server do |config|
Gitlab::SidekiqVersioning.install!
- config = Gitlab::Database.config ||
+ db_config = Gitlab::Database.config ||
Rails.application.config.database_configuration[Rails.env]
- config['pool'] = Sidekiq.options[:concurrency]
- ActiveRecord::Base.establish_connection(config)
+ db_config['pool'] = Sidekiq.options[:concurrency]
+ ActiveRecord::Base.establish_connection(db_config)
Rails.logger.debug("Connection Pool size for Sidekiq Server is now: #{ActiveRecord::Base.connection.pool.instance_variable_get('@size')}")
# Avoid autoload issue such as 'Mail::Parsers::AddressStruct'
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index 7489b01ded6..7cdaa2daa14 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -107,7 +107,7 @@ namespace :admin do
resource :application_settings, only: [:show, :update] do
resources :services, only: [:index, :edit, :update]
get :usage_data
- put :reset_runners_token
+ put :reset_registration_token
put :reset_health_check_token
put :clear_repository_check_states
get :integrations, :repository, :templates, :ci_cd, :reporting, :metrics_and_profiling, :network, :geo, :preferences
diff --git a/config/routes/group.rb b/config/routes/group.rb
index 893ec8a4e58..602bbe837cf 100644
--- a/config/routes/group.rb
+++ b/config/routes/group.rb
@@ -27,7 +27,9 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
as: :group,
constraints: { group_id: Gitlab::PathRegex.full_namespace_route_regex }) do
namespace :settings do
- resource :ci_cd, only: [:show], controller: 'ci_cd'
+ resource :ci_cd, only: [:show], controller: 'ci_cd' do
+ put :reset_registration_token
+ end
end
resource :variables, only: [:show, :update]
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 04a270c5cc9..9cbd5b644f6 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -32,6 +32,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get 'labels'
get 'milestones'
get 'commands'
+ get 'snippets'
end
end
@@ -366,7 +367,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get :discussions, format: :json
end
collection do
- post :bulk_update
+ post :bulk_update
end
end
@@ -438,6 +439,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get :members, to: redirect("%{namespace_id}/%{project_id}/project_members")
resource :ci_cd, only: [:show, :update], controller: 'ci_cd' do
post :reset_cache
+ put :reset_registration_token
end
resource :integrations, only: [:show]
resource :repository, only: [:show], controller: :repository do
diff --git a/config/routes/user.rb b/config/routes/user.rb
index bc7df5e7584..e0ae264e2c0 100644
--- a/config/routes/user.rb
+++ b/config/routes/user.rb
@@ -45,6 +45,7 @@ scope(constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }) d
get :contributed, as: :contributed_projects
get :snippets
get :exists
+ get :activity
get '/', to: redirect('%{username}'), as: nil
end
diff --git a/db/migrate/20170506185517_add_foreign_key_pipeline_schedules_and_pipelines.rb b/db/migrate/20170506185517_add_foreign_key_pipeline_schedules_and_pipelines.rb
index 55bf40ba24d..cc5cb355579 100644
--- a/db/migrate/20170506185517_add_foreign_key_pipeline_schedules_and_pipelines.rb
+++ b/db/migrate/20170506185517_add_foreign_key_pipeline_schedules_and_pipelines.rb
@@ -13,7 +13,7 @@ class AddForeignKeyPipelineSchedulesAndPipelines < ActiveRecord::Migration
'SET NULL'
end
- add_concurrent_foreign_key :ci_pipelines, :ci_pipeline_schedules,
+ add_concurrent_foreign_key :ci_pipelines, :ci_pipeline_schedules,
column: :pipeline_schedule_id, on_delete: on_delete
end
diff --git a/db/migrate/20180423204600_add_pages_access_level_to_project_feature.rb b/db/migrate/20180423204600_add_pages_access_level_to_project_feature.rb
new file mode 100644
index 00000000000..1d2f8cf9c76
--- /dev/null
+++ b/db/migrate/20180423204600_add_pages_access_level_to_project_feature.rb
@@ -0,0 +1,16 @@
+class AddPagesAccessLevelToProjectFeature < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+
+ def up
+ add_column_with_default(:project_features, :pages_access_level, :integer, default: ProjectFeature::PUBLIC, allow_null: false)
+
+ change_column_default(:project_features, :pages_access_level, ProjectFeature::ENABLED)
+ end
+
+ def down
+ remove_column :project_features, :pages_access_level
+ end
+end
diff --git a/db/migrate/20180924201039_add_partial_index_to_scheduled_at.rb b/db/migrate/20180924201039_add_partial_index_to_scheduled_at.rb
index c2e62dede8a..81bf0d94e11 100644
--- a/db/migrate/20180924201039_add_partial_index_to_scheduled_at.rb
+++ b/db/migrate/20180924201039_add_partial_index_to_scheduled_at.rb
@@ -9,7 +9,7 @@ class AddPartialIndexToScheduledAt < ActiveRecord::Migration
disable_ddl_transaction!
def up
- add_concurrent_index(:ci_builds, [:scheduled_at, :id], where: "scheduled_at IS NOT NULL", name: INDEX_NAME)
+ add_concurrent_index(:ci_builds, :scheduled_at, where: "scheduled_at IS NOT NULL AND type = 'Ci::Build' AND status = 'scheduled'", name: INDEX_NAME)
end
def down
diff --git a/db/migrate/20181002172433_remove_restricted_todos_with_cte.rb b/db/migrate/20181002172433_remove_restricted_todos_with_cte.rb
new file mode 100644
index 00000000000..0a8f4a12266
--- /dev/null
+++ b/db/migrate/20181002172433_remove_restricted_todos_with_cte.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+# rescheduling of the revised RemoveRestrictedTodos background migration
+class RemoveRestrictedTodosWithCte < ActiveRecord::Migration
+ DOWNTIME = false
+ disable_ddl_transaction!
+
+ MIGRATION = 'RemoveRestrictedTodos'.freeze
+ BATCH_SIZE = 1000
+ DELAY_INTERVAL = 5.minutes.to_i
+
+ class Project < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'projects'
+ end
+
+ def up
+ Project.where('EXISTS (SELECT 1 FROM todos WHERE todos.project_id = projects.id)')
+ .each_batch(of: BATCH_SIZE) do |batch, index|
+ range = batch.pluck('MIN(id)', 'MAX(id)').first
+
+ BackgroundMigrationWorker.perform_in(index * DELAY_INTERVAL, MIGRATION, range)
+ end
+ end
+
+ def down
+ # nothing to do
+ end
+end
diff --git a/db/post_migrate/20161221153951_rename_reserved_project_names.rb b/db/post_migrate/20161221153951_rename_reserved_project_names.rb
index 017c58477ac..08d7f499eec 100644
--- a/db/post_migrate/20161221153951_rename_reserved_project_names.rb
+++ b/db/post_migrate/20161221153951_rename_reserved_project_names.rb
@@ -1,5 +1,3 @@
-require 'thread'
-
class RenameReservedProjectNames < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
include Gitlab::ShellAdapter
diff --git a/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb b/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
index 3e8ccfdb899..43a37667250 100644
--- a/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
+++ b/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
@@ -1,5 +1,3 @@
-require 'thread'
-
class RenameMoreReservedProjectNames < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
include Gitlab::ShellAdapter
diff --git a/db/post_migrate/20181008145341_steal_encrypt_columns.rb b/db/post_migrate/20181008145341_steal_encrypt_columns.rb
new file mode 100644
index 00000000000..c107ac72913
--- /dev/null
+++ b/db/post_migrate/20181008145341_steal_encrypt_columns.rb
@@ -0,0 +1,15 @@
+class StealEncryptColumns < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ Gitlab::BackgroundMigration.steal('EncryptColumns')
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20181008145359_remove_web_hooks_token_and_url.rb b/db/post_migrate/20181008145359_remove_web_hooks_token_and_url.rb
new file mode 100644
index 00000000000..0c44bca5f1a
--- /dev/null
+++ b/db/post_migrate/20181008145359_remove_web_hooks_token_and_url.rb
@@ -0,0 +1,10 @@
+class RemoveWebHooksTokenAndUrl < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ remove_column :web_hooks, :token, :string
+ remove_column :web_hooks, :url, :string, limit: 2000
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index f14859d9eac..d47156c6da4 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20180924201039) do
+ActiveRecord::Schema.define(version: 20181008145359) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -347,7 +347,7 @@ ActiveRecord::Schema.define(version: 20180924201039) do
add_index "ci_builds", ["project_id", "id"], name: "index_ci_builds_on_project_id_and_id", using: :btree
add_index "ci_builds", ["protected"], name: "index_ci_builds_on_protected", using: :btree
add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree
- add_index "ci_builds", ["scheduled_at", "id"], name: "partial_index_ci_builds_on_scheduled_at_with_scheduled_jobs", where: "(scheduled_at IS NOT NULL)", using: :btree
+ add_index "ci_builds", ["scheduled_at"], name: "partial_index_ci_builds_on_scheduled_at_with_scheduled_jobs", where: "((scheduled_at IS NOT NULL) AND ((type)::text = 'Ci::Build'::text) AND ((status)::text = 'scheduled'::text))", using: :btree
add_index "ci_builds", ["stage_id", "stage_idx"], name: "tmp_build_stage_position_index", where: "(stage_idx IS NOT NULL)", using: :btree
add_index "ci_builds", ["stage_id"], name: "index_ci_builds_on_stage_id", using: :btree
add_index "ci_builds", ["status", "type", "runner_id"], name: "index_ci_builds_on_status_and_type_and_runner_id", using: :btree
@@ -1580,6 +1580,7 @@ ActiveRecord::Schema.define(version: 20180924201039) do
t.datetime "created_at"
t.datetime "updated_at"
t.integer "repository_access_level", default: 20, null: false
+ t.integer "pages_access_level", default: 20, null: false
end
add_index "project_features", ["project_id"], name: "index_project_features_on_project_id", unique: true, using: :btree
@@ -2255,7 +2256,6 @@ ActiveRecord::Schema.define(version: 20180924201039) do
add_index "web_hook_logs", ["web_hook_id"], name: "index_web_hook_logs_on_web_hook_id", using: :btree
create_table "web_hooks", force: :cascade do |t|
- t.string "url", limit: 2000
t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
@@ -2268,7 +2268,6 @@ ActiveRecord::Schema.define(version: 20180924201039) do
t.boolean "note_events", default: false, null: false
t.boolean "enable_ssl_verification", default: true
t.boolean "wiki_page_events", default: false, null: false
- t.string "token"
t.boolean "pipeline_events", default: false, null: false
t.boolean "confidential_issues_events", default: false, null: false
t.boolean "repository_update_events", default: false, null: false
diff --git a/doc/administration/custom_hooks.md b/doc/administration/custom_hooks.md
index 9b0fabb9259..c58ced7d520 100644
--- a/doc/administration/custom_hooks.md
+++ b/doc/administration/custom_hooks.md
@@ -50,6 +50,9 @@ Hooks can be also placed in `hooks/<hook_name>.d` (global) or
`custom_hooks/<hook_name>.d` (per project) directories supporting chained
execution of the hooks.
+NOTE: **Note:** `<hook_name>.d` would need to be either `pre-receive.d`,
+`post-receive.d`, or `update.d` to work properly. Any other names will be ignored.
+
To look in a different directory for the global custom hooks (those in
`hooks/<hook_name.d>`), set `custom_hooks_dir` in gitlab-shell config. For
Omnibus installations, this can be set in `gitlab.rb`; and in source
diff --git a/doc/administration/operations/moving_repositories.md b/doc/administration/operations/moving_repositories.md
index 54adb99386a..ec11a92db1b 100644
--- a/doc/administration/operations/moving_repositories.md
+++ b/doc/administration/operations/moving_repositories.md
@@ -22,9 +22,8 @@ However, it is not possible to resume an interrupted tar pipe: if
that happens then all data must be copied again.
```
-# As the git user
-tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\
- tar -C /mnt/gitlab/repositories -xf -
+sudo -u git sh -c 'tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\
+ tar -C /mnt/gitlab/repositories -xf -'
```
If you want to see progress, replace `-xf` with `-xvf`.
@@ -36,9 +35,8 @@ You can also use a tar pipe to copy data to another server. If your
can pipe the data through SSH.
```
-# As the git user
-tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\
- ssh git@newserver tar -C /mnt/gitlab/repositories -xf -
+sudo -u git sh -c 'tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\
+ ssh git@newserver tar -C /mnt/gitlab/repositories -xf -'
```
If you want to compress the data before it goes over the network
@@ -53,9 +51,8 @@ is either already installed on your system or easily installable
via apt, yum etc.
```
-# As the 'git' user
-rsync -a --delete /var/opt/gitlab/git-data/repositories/. \
- /mnt/gitlab/repositories
+sudo -u git sh -c 'rsync -a --delete /var/opt/gitlab/git-data/repositories/. \
+ /mnt/gitlab/repositories'
```
The `/.` in the command above is very important, without it you can
@@ -68,9 +65,8 @@ If the 'git' user on your source system has SSH access to the target
server you can send the repositories over the network with rsync.
```
-# As the 'git' user
-rsync -a --delete /var/opt/gitlab/git-data/repositories/. \
- git@newserver:/mnt/gitlab/repositories
+sudo -u git sh -c 'rsync -a --delete /var/opt/gitlab/git-data/repositories/. \
+ git@newserver:/mnt/gitlab/repositories'
```
## Thousands of Git repositories: use one rsync per repository
@@ -125,7 +121,7 @@ sudo -u git -H sh -c 'bundle exec rake gitlab:list_repos > /home/git/transfer-lo
Now we can start the transfer. The command below is idempotent, and
the number of jobs done by GNU Parallel should converge to zero. If it
-does not some repositories listed in all-repos-1234.txt may have been
+does not, some repositories listed in `all-repos-1234.txt` may have been
deleted/renamed before they could be copied.
```
@@ -155,8 +151,8 @@ cat /home/git/transfer-logs/* | sort | uniq -u |\
Suppose you have already done one sync that started after 2015-10-1 12:00 UTC.
Then you might only want to sync repositories that were changed via GitLab
-_after_ that time. You can use the 'SINCE' variable to tell 'rake
-gitlab:list_repos' to only print repositories with recent activity.
+_after_ that time. You can use the `SINCE` variable to tell `rake
+gitlab:list_repos` to only print repositories with recent activity.
```
# Omnibus
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index 3af0a5759a7..2952a98626a 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -92,9 +92,8 @@ where `example.io` is the domain under which GitLab Pages will be served
and `192.0.2.1` is the IPv4 address of your GitLab instance and `2001::1` is the
IPv6 address. If you don't have IPv6, you can omit the AAAA record.
-> **Note:**
-You should not use the GitLab domain to serve user pages. For more information
-see the [security section](#security).
+NOTE: **Note:**
+You should not use the GitLab domain to serve user pages. For more information see the [security section](#security).
[wiki-wildcard-dns]: https://en.wikipedia.org/wiki/Wildcard_DNS_record
@@ -107,12 +106,13 @@ since that is needed in all configurations.
### Wildcard domains
-> **Requirements:**
-> - [Wildcard DNS setup](#dns-configuration)
->
-> ---
->
-> URL scheme: `http://page.example.io`
+**Requirements:**
+
+- [Wildcard DNS setup](#dns-configuration)
+
+---
+
+URL scheme: `http://page.example.io`
This is the minimum setup that you can use Pages with. It is the base for all
other setups as described below. Nginx will proxy all requests to the daemon.
@@ -126,18 +126,18 @@ The Pages daemon doesn't listen to the outside world.
1. [Reconfigure GitLab][reconfigure]
-
Watch the [video tutorial][video-admin] for this configuration.
### Wildcard domains with TLS support
-> **Requirements:**
-> - [Wildcard DNS setup](#dns-configuration)
-> - Wildcard TLS certificate
->
-> ---
->
-> URL scheme: `https://page.example.io`
+**Requirements:**
+
+- [Wildcard DNS setup](#dns-configuration)
+- Wildcard TLS certificate
+
+---
+
+URL scheme: `https://page.example.io`
Nginx will proxy all requests to the daemon. Pages daemon doesn't listen to the
outside world.
@@ -168,13 +168,14 @@ you have IPv6 as well as IPv4 addresses, you can use them both.
### Custom domains
-> **Requirements:**
-> - [Wildcard DNS setup](#dns-configuration)
-> - Secondary IP
->
-> ---
->
-> URL scheme: `http://page.example.io` and `http://domain.com`
+**Requirements:**
+
+- [Wildcard DNS setup](#dns-configuration)
+- Secondary IP
+
+---
+
+URL scheme: `http://page.example.io` and `http://domain.com`
In that case, the Pages daemon is running, Nginx still proxies requests to
the daemon but the daemon is also able to receive requests from the outside
@@ -197,14 +198,15 @@ world. Custom domains are supported, but no TLS.
### Custom domains with TLS support
-> **Requirements:**
-> - [Wildcard DNS setup](#dns-configuration)
-> - Wildcard TLS certificate
-> - Secondary IP
->
-> ---
->
-> URL scheme: `https://page.example.io` and `https://domain.com`
+**Requirements:**
+
+- [Wildcard DNS setup](#dns-configuration)
+- Wildcard TLS certificate
+- Secondary IP
+
+---
+
+URL scheme: `https://page.example.io` and `https://domain.com`
In that case, the Pages daemon is running, Nginx still proxies requests to
the daemon but the daemon is also able to receive requests from the outside
@@ -320,12 +322,12 @@ latest previous version.
---
-**GitLab 8.17 ([documentation][8-17-docs])**
+**GitLab 8.17 ([documentation](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-17-stable/doc/administration/pages/index.md))**
- GitLab Pages were ported to Community Edition in GitLab 8.17.
- Documentation was refactored to be more modular and easy to follow.
-**GitLab 8.5 ([documentation][8-5-docs])**
+**GitLab 8.5 ([documentation](https://gitlab.com/gitlab-org/gitlab-ee/blob/8-5-stable-ee/doc/pages/administration.md))**
- In GitLab 8.5 we introduced the [gitlab-pages][] daemon which is now the
recommended way to set up GitLab Pages.
@@ -334,13 +336,10 @@ latest previous version.
- Custom CNAME and TLS certificates support.
- Documentation was moved to one place.
-**GitLab 8.3 ([documentation][8-3-docs])**
+**GitLab 8.3 ([documentation](https://gitlab.com/gitlab-org/gitlab-ee/blob/8-3-stable-ee/doc/pages/administration.md))**
- GitLab Pages feature was introduced.
-[8-3-docs]: https://gitlab.com/gitlab-org/gitlab-ee/blob/8-3-stable-ee/doc/pages/administration.md
-[8-5-docs]: https://gitlab.com/gitlab-org/gitlab-ee/blob/8-5-stable-ee/doc/pages/administration.md
-[8-17-docs]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-17-stable-ce/doc/administration/pages/index.md
[backup]: ../../raketasks/backup_restore.md
[ce-14605]: https://gitlab.com/gitlab-org/gitlab-ce/issues/14605
[ee-80]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/80
diff --git a/doc/administration/raketasks/github_import.md b/doc/administration/raketasks/github_import.md
index 6b8ad1b039b..ccd9c0afb1d 100644
--- a/doc/administration/raketasks/github_import.md
+++ b/doc/administration/raketasks/github_import.md
@@ -1,37 +1,41 @@
# GitHub import
->**Note:**
->
-> - [Introduced][ce-10308] in GitLab 9.1.
-> - You need a personal access token in order to retrieve and import GitHub
-> projects. You can get it from: https://github.com/settings/tokens
-> - You also need to pass an username as the second argument to the rake task
-> which will become the owner of the project.
-> - You can also resume an import with the same command.
+> [Introduced]( https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10308) in GitLab 9.1.
+
+In order to retrieve and import GitHub repositories, you will need a
+[GitHub personal access token](https://github.com/settings/tokens).
+A username should be passed as the second argument to the rake task
+which will become the owner of the project. You can resume an import
+with the same command.
+
+Bear in mind that the syntax is very specific. Remove any spaces within the argument block and
+before/after the brackets. Also, Some shells (e.g., zsh) can interpret the open/close brackets
+(`[]`) separately. You may need to either escape the brackets or use double quotes.
+
+## Importing multiple projects
To import a project from the list of your GitHub projects available:
```bash
# Omnibus installations
-sudo gitlab-rake import:github[access_token,root,foo/bar]
+sudo gitlab-rake "import:github[access_token,root,foo/bar]"
# Installations from source
-bundle exec rake import:github[access_token,root,foo/bar] RAILS_ENV=production
+bundle exec rake "import:github[access_token,root,foo/bar]" RAILS_ENV=production
```
In this case, `access_token` is your GitHub personal access token, `root`
is your GitLab username, and `foo/bar` is the new GitLab namespace/project that
will get created from your GitHub project. Subgroups are also possible: `foo/foo/bar`.
+## Importing a single project
To import a specific GitHub project (named `foo/github_repo` here):
```bash
# Omnibus installations
-sudo gitlab-rake import:github[access_token,root,foo/bar,foo/github_repo]
+sudo gitlab-rake "import:github[access_token,root,foo/bar,foo/github_repo]"
# Installations from source
-bundle exec rake import:github[access_token,root,foo/bar,foo/github_repo] RAILS_ENV=production
+bundle exec rake "import:github[access_token,root,foo/bar,foo/github_repo]" RAILS_ENV=production
```
-
-[ce-10308]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10308
diff --git a/doc/api/README.md b/doc/api/README.md
index a3589377e9d..a351db99bbd 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -20,10 +20,11 @@ following locations:
- [Custom Attributes](custom_attributes.md)
- [Deployments](deployments.md)
- [Deploy Keys](deploy_keys.md)
+- [Dockerfile templates](templates/dockerfiles.md)
- [Environments](environments.md)
- [Events](events.md)
- [Feature flags](features.md)
-- [Gitignores templates](templates/gitignores.md)
+- [Gitignore templates](templates/gitignores.md)
- [GitLab CI Config templates](templates/gitlab_ci_ymls.md)
- [Groups](groups.md)
- [Group Access Requests](access_requests.md)
@@ -55,6 +56,7 @@ following locations:
- [Project import/export](project_import_export.md)
- [Project Members](members.md)
- [Project Snippets](project_snippets.md)
+- [Project Templates](project_templates.md)
- [Protected Branches](protected_branches.md)
- [Protected Tags](protected_tags.md)
- [Repositories](repositories.md)
diff --git a/doc/api/commits.md b/doc/api/commits.md
index 5ff1e1f60e0..9b7ca4b6e70 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -79,6 +79,7 @@ POST /projects/:id/repository/commits
| `actions[]` | array | yes | An array of action hashes to commit as a batch. See the next table for what attributes it can take. |
| `author_email` | string | no | Specify the commit author's email address |
| `author_name` | string | no | Specify the commit author's name |
+| `stats` | boolean | no | Include commit stats. Default is true |
| `actions[]` Attribute | Type | Required | Description |
diff --git a/doc/api/events.md b/doc/api/events.md
index fb5ebb71a86..cd84b32029e 100644
--- a/doc/api/events.md
+++ b/doc/api/events.md
@@ -44,7 +44,7 @@ YYYY-MM-DD
### Event Time Period Limit
-GitLab removes events older than 1 year from the events table for performance reasons. The range of 1 year was chosen because user contribution calendars only show contributions of the past year.
+GitLab removes events older than 2 years from the events table for performance reasons.
## List currently authenticated user's events
diff --git a/doc/api/issues.md b/doc/api/issues.md
index f4c0f4ea65b..cc1d6834a20 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -37,7 +37,7 @@ GET /issues?my_reaction_emoji=star
| ------------------- | ---------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
-| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone |
+| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone. `Any+Milestone` lists all issues that have an assigned milestone |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned_to_me`. _([Introduced][ce-13004] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index b37e7698ab4..862ee398a84 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -54,35 +54,38 @@ Parameters:
{
"id": 1,
"iid": 1,
- "target_branch": "master",
- "source_branch": "test1",
"project_id": 3,
"title": "test1",
+ "description": "fixed login page css paddings",
"state": "opened",
"created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 2,
"target_project_id": 3,
- "labels": [ ],
- "description": "fixed login page css paddings",
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
"id": 5,
@@ -93,23 +96,28 @@ Parameters:
"state": "closed",
"created_at": "2015-02-02T19:49:26.013Z",
"updated_at": "2015-02-02T19:49:26.013Z",
- "due_date": null
+ "due_date": "2018-09-22",
+ "start_date": "2018-08-08",
+ "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
+ "discussion_locked": null,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
- "squash": false,
- "web_url": "http://example.com/example/example/merge_requests/1",
+ "allow_collaboration": false,
+ "allow_maintainer_to_push": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"time_stats": {
"time_estimate": 0,
"total_time_spent": 0,
"human_time_estimate": null,
"human_total_time_spent": null
- }
+ },
+ "squash": false
}
]
```
@@ -169,35 +177,38 @@ Parameters:
{
"id": 1,
"iid": 1,
- "target_branch": "master",
- "source_branch": "test1",
"project_id": 3,
"title": "test1",
+ "description": "fixed login page css paddings",
"state": "opened",
"created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 2,
"target_project_id": 3,
- "labels": [ ],
- "description": "fixed login page css paddings",
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
"id": 5,
@@ -208,24 +219,28 @@ Parameters:
"state": "closed",
"created_at": "2015-02-02T19:49:26.013Z",
"updated_at": "2015-02-02T19:49:26.013Z",
- "due_date": null
+ "due_date": "2018-09-22",
+ "start_date": "2018-08-08",
+ "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
+ "discussion_locked": null,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
- "squash": false,
- "web_url": "http://example.com/example/example/merge_requests/1",
- "discussion_locked": false,
+ "allow_collaboration": false,
+ "allow_maintainer_to_push": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"time_stats": {
"time_estimate": 0,
"total_time_spent": 0,
"human_time_estimate": null,
"human_total_time_spent": null
- }
+ },
+ "squash": false
}
]
```
@@ -275,35 +290,38 @@ Parameters:
{
"id": 1,
"iid": 1,
- "target_branch": "master",
- "source_branch": "test1",
"project_id": 3,
"title": "test1",
+ "description": "fixed login page css paddings",
"state": "opened",
"created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 2,
"target_project_id": 3,
- "labels": [ ],
- "description": "fixed login page css paddings",
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
"id": 5,
@@ -314,23 +332,26 @@ Parameters:
"state": "closed",
"created_at": "2015-02-02T19:49:26.013Z",
"updated_at": "2015-02-02T19:49:26.013Z",
- "due_date": null
+ "due_date": "2018-10-22",
+ "start_date": "2018-09-08",
+ "web_url": "gitlab.example.com/my-group/my-project/milestones/1"
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
+ "discussion_locked": null,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
- "web_url": "http://example.com/example/example/merge_requests/1",
- "discussion_locked": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"time_stats": {
"time_estimate": 0,
"total_time_spent": 0,
"human_time_estimate": null,
"human_total_time_spent": null
- }
+ },
+ "squash": false
}
]
```
@@ -359,35 +380,38 @@ Parameters:
{
"id": 1,
"iid": 1,
- "target_branch": "master",
- "source_branch": "test1",
"project_id": 3,
"title": "test1",
- "state": "merged",
+ "description": "fixed login page css paddings",
+ "state": "opened",
"created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
- "state" : "active",
- "web_url" : "https://gitlab.example.com/root",
- "avatar_url" : null,
- "username" : "root",
- "id" : 1,
- "name" : "Administrator"
+ "id": 1,
+ "name": "Administrator",
+ "username": "admin",
+ "state": "active",
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
- "state" : "active",
- "web_url" : "https://gitlab.example.com/root",
- "avatar_url" : null,
- "username" : "root",
- "id" : 1,
- "name" : "Administrator"
+ "id": 1,
+ "name": "Administrator",
+ "username": "admin",
+ "state": "active",
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 2,
"target_project_id": 3,
- "labels": [ ],
- "description": "fixed login page css paddings",
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
"id": 5,
@@ -398,50 +422,55 @@ Parameters:
"state": "closed",
"created_at": "2015-02-02T19:49:26.013Z",
"updated_at": "2015-02-02T19:49:26.013Z",
- "due_date": null
+ "due_date": "2018-09-22",
+ "start_date": "2018-08-08",
+ "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
- "subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
- "merge_commit_sha": "9999999999999999999999999999999999999999",
+ "merge_commit_sha": null,
"user_notes_count": 1,
- "changes_count": "1",
+ "discussion_locked": null,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
- "squash": false,
- "web_url": "http://example.com/example/example/merge_requests/1",
- "discussion_locked": false,
+ "allow_collaboration": false,
+ "allow_maintainer_to_push": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"time_stats": {
"time_estimate": 0,
"total_time_spent": 0,
"human_time_estimate": null,
"human_total_time_spent": null
},
- "closed_at": "2018-01-19T14:36:11.086Z",
- "latest_build_started_at": null,
- "latest_build_finished_at": null,
+ "squash": false,
+ "subscribed": false,
+ "changes_count": "1",
+ "merged_by": {
+ "id": 87854,
+ "name": "Douwe Maan",
+ "username": "DouweM",
+ "state": "active",
+ "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
+ "web_url": "https://gitlab.com/DouweM"
+ },
+ "merged_at": "2018-09-07T11:16:17.520Z",
+ "closed_by": null,
+ "closed_at": null,
+ "latest_build_started_at": "2018-09-07T07:27:38.472Z",
+ "latest_build_finished_at": "2018-09-07T08:07:06.012Z",
"first_deployed_to_production_at": null,
"pipeline": {
- "id": 8,
- "ref": "master",
- "sha": "2dc6aa325a317eda67812f05600bdf0fcdc70ab0",
- "status": "created"
- },
- "merged_by": null,
- "merged_at": null,
- "closed_by": {
- "state" : "active",
- "web_url" : "https://gitlab.example.com/root",
- "avatar_url" : null,
- "username" : "root",
- "id" : 1,
- "name" : "Administrator"
+ "id": 29626725,
+ "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "ref": "patch-28",
+ "status": "success",
+ "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725"
},
"diff_refs": {
- "base_sha": "1111111111111111111111111111111111111111",
- "head_sha": "2222222222222222222222222222222222222222",
- "start_sha": "3333333333333333333333333333333333333333"
+ "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00",
+ "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
},
"diverged_commits_count": 2
}
@@ -663,65 +692,99 @@ POST /projects/:id/merge_requests
{
"id": 1,
"iid": 1,
- "target_branch": "master",
- "source_branch": "test1",
- "project_id": 4,
+ "project_id": 3,
"title": "test1",
+ "description": "fixed login page css paddings",
"state": "opened",
+ "created_at": "2017-04-29T08:46:00Z",
+ "updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
- "source_project_id": 3,
- "target_project_id": 4,
- "labels": [ ],
- "description": "fixed login page css paddings",
+ "source_project_id": 2,
+ "target_project_id": 3,
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
"id": 5,
"iid": 1,
- "project_id": 4,
+ "project_id": 3,
"title": "v2.0",
"description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
"state": "closed",
"created_at": "2015-02-02T19:49:26.013Z",
"updated_at": "2015-02-02T19:49:26.013Z",
- "due_date": null
+ "due_date": "2018-09-22",
+ "start_date": "2018-08-08",
+ "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
- "subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
- "user_notes_count": 0,
- "changes_count": "1",
+ "user_notes_count": 1,
+ "discussion_locked": null,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
- "squash": false,
- "web_url": "http://example.com/example/example/merge_requests/1",
- "discussion_locked": false,
"allow_collaboration": false,
"allow_maintainer_to_push": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"time_stats": {
"time_estimate": 0,
"total_time_spent": 0,
"human_time_estimate": null,
"human_total_time_spent": null
- }
+ },
+ "squash": false,
+ "subscribed": false,
+ "changes_count": "1",
+ "merged_by": {
+ "id": 87854,
+ "name": "Douwe Maan",
+ "username": "DouweM",
+ "state": "active",
+ "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
+ "web_url": "https://gitlab.com/DouweM"
+ },
+ "merged_at": "2018-09-07T11:16:17.520Z",
+ "closed_by": null,
+ "closed_at": null,
+ "latest_build_started_at": "2018-09-07T07:27:38.472Z",
+ "latest_build_finished_at": "2018-09-07T08:07:06.012Z",
+ "first_deployed_to_production_at": null,
+ "pipeline": {
+ "id": 29626725,
+ "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "ref": "patch-28",
+ "status": "success",
+ "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725"
+ },
+ "diff_refs": {
+ "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00",
+ "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
+ },
+ "diverged_commits_count": 2
}
```
@@ -756,64 +819,99 @@ Must include at least one non-required attribute from above.
{
"id": 1,
"iid": 1,
- "target_branch": "master",
- "project_id": 4,
+ "project_id": 3,
"title": "test1",
+ "description": "fixed login page css paddings",
"state": "opened",
+ "created_at": "2017-04-29T08:46:00Z",
+ "updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
- "source_project_id": 3,
- "target_project_id": 4,
- "labels": [ ],
- "description": "description1",
+ "source_project_id": 2,
+ "target_project_id": 3,
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
"id": 5,
"iid": 1,
- "project_id": 4,
+ "project_id": 3,
"title": "v2.0",
"description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
"state": "closed",
"created_at": "2015-02-02T19:49:26.013Z",
"updated_at": "2015-02-02T19:49:26.013Z",
- "due_date": null
+ "due_date": "2018-09-22",
+ "start_date": "2018-08-08",
+ "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
- "subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
- "changes_count": "1",
+ "discussion_locked": null,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
- "squash": false,
- "web_url": "http://example.com/example/example/merge_requests/1",
- "discussion_locked": false,
"allow_collaboration": false,
"allow_maintainer_to_push": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"time_stats": {
"time_estimate": 0,
"total_time_spent": 0,
"human_time_estimate": null,
"human_total_time_spent": null
- }
+ },
+ "squash": false,
+ "subscribed": false,
+ "changes_count": "1",
+ "merged_by": {
+ "id": 87854,
+ "name": "Douwe Maan",
+ "username": "DouweM",
+ "state": "active",
+ "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
+ "web_url": "https://gitlab.com/DouweM"
+ },
+ "merged_at": "2018-09-07T11:16:17.520Z",
+ "closed_by": null,
+ "closed_at": null,
+ "latest_build_started_at": "2018-09-07T07:27:38.472Z",
+ "latest_build_finished_at": "2018-09-07T08:07:06.012Z",
+ "first_deployed_to_production_at": null,
+ "pipeline": {
+ "id": 29626725,
+ "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "ref": "patch-28",
+ "status": "success",
+ "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725"
+ },
+ "diff_refs": {
+ "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00",
+ "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
+ },
+ "diverged_commits_count": 2
}
```
@@ -857,70 +955,106 @@ Parameters:
- `merge_request_iid` (required) - Internal ID of MR
- `merge_commit_message` (optional) - Custom merge commit message
- `should_remove_source_branch` (optional) - if `true` removes the source branch
-- `merge_when_pipeline_succeeds` (optional) - if `true` the MR is merged when the pipeline succeeds
+- `merge_when_pipeline_succeeds` (optional) - if `true` the MR is merged when the pipeline succeeds
- `sha` (optional) - if present, then this SHA must match the HEAD of the source branch, otherwise the merge will fail
```json
{
"id": 1,
"iid": 1,
- "target_branch": "master",
- "source_branch": "test1",
"project_id": 3,
"title": "test1",
- "state": "merged",
+ "description": "fixed login page css paddings",
+ "state": "opened",
+ "created_at": "2017-04-29T08:46:00Z",
+ "updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
- "source_project_id": 4,
- "target_project_id": 4,
- "labels": [ ],
- "description": "fixed login page css paddings",
+ "source_project_id": 2,
+ "target_project_id": 3,
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
"id": 5,
"iid": 1,
- "project_id": 4,
+ "project_id": 3,
"title": "v2.0",
"description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
"state": "closed",
"created_at": "2015-02-02T19:49:26.013Z",
"updated_at": "2015-02-02T19:49:26.013Z",
- "due_date": null
+ "due_date": "2018-09-22",
+ "start_date": "2018-08-08",
+ "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
- "subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
- "merge_commit_sha": "9999999999999999999999999999999999999999",
+ "merge_commit_sha": null,
"user_notes_count": 1,
- "changes_count": "1",
+ "discussion_locked": null,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
- "squash": false,
- "web_url": "http://example.com/example/example/merge_requests/1",
- "discussion_locked": false,
+ "allow_collaboration": false,
+ "allow_maintainer_to_push": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"time_stats": {
"time_estimate": 0,
"total_time_spent": 0,
"human_time_estimate": null,
"human_total_time_spent": null
- }
+ },
+ "squash": false,
+ "subscribed": false,
+ "changes_count": "1",
+ "merged_by": {
+ "id": 87854,
+ "name": "Douwe Maan",
+ "username": "DouweM",
+ "state": "active",
+ "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
+ "web_url": "https://gitlab.com/DouweM"
+ },
+ "merged_at": "2018-09-07T11:16:17.520Z",
+ "closed_by": null,
+ "closed_at": null,
+ "latest_build_started_at": "2018-09-07T07:27:38.472Z",
+ "latest_build_finished_at": "2018-09-07T08:07:06.012Z",
+ "first_deployed_to_production_at": null,
+ "pipeline": {
+ "id": 29626725,
+ "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "ref": "patch-28",
+ "status": "success",
+ "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725"
+ },
+ "diff_refs": {
+ "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00",
+ "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
+ },
+ "diverged_commits_count": 2
}
```
@@ -937,69 +1071,105 @@ PUT /projects/:id/merge_requests/:merge_request_iid/cancel_merge_when_pipeline_s
Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
-- `merge_request_iid` (required) - Internal ID of MR
+- `merge_request_iid` (required) - Internal ID of MR
```json
{
"id": 1,
"iid": 1,
- "target_branch": "master",
- "source_branch": "test1",
"project_id": 3,
"title": "test1",
+ "description": "fixed login page css paddings",
"state": "opened",
+ "created_at": "2017-04-29T08:46:00Z",
+ "updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
- "username": "admin",
"name": "Administrator",
+ "username": "admin",
"state": "active",
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
- "source_project_id": 4,
- "target_project_id": 4,
- "labels": [ ],
- "description": "fixed login page css paddings",
+ "source_project_id": 2,
+ "target_project_id": 3,
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
"id": 5,
"iid": 1,
- "project_id": 4,
+ "project_id": 3,
"title": "v2.0",
"description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
"state": "closed",
"created_at": "2015-02-02T19:49:26.013Z",
"updated_at": "2015-02-02T19:49:26.013Z",
- "due_date": null
+ "due_date": "2018-09-22",
+ "start_date": "2018-08-08",
+ "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
},
- "merge_when_pipeline_succeeds": true,
+ "merge_when_pipeline_succeeds": false,
"merge_status": "can_be_merged",
- "subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
- "changes_count": "1",
+ "discussion_locked": null,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
- "squash": false,
- "web_url": "http://example.com/example/example/merge_requests/1",
- "discussion_locked": false,
+ "allow_collaboration": false,
+ "allow_maintainer_to_push": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"time_stats": {
"time_estimate": 0,
"total_time_spent": 0,
"human_time_estimate": null,
"human_total_time_spent": null
- }
+ },
+ "squash": false,
+ "subscribed": false,
+ "changes_count": "1",
+ "merged_by": {
+ "id": 87854,
+ "name": "Douwe Maan",
+ "username": "DouweM",
+ "state": "active",
+ "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
+ "web_url": "https://gitlab.com/DouweM"
+ },
+ "merged_at": "2018-09-07T11:16:17.520Z",
+ "closed_by": null,
+ "closed_at": null,
+ "latest_build_started_at": "2018-09-07T07:27:38.472Z",
+ "latest_build_finished_at": "2018-09-07T08:07:06.012Z",
+ "first_deployed_to_production_at": null,
+ "pipeline": {
+ "id": 29626725,
+ "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "ref": "patch-28",
+ "status": "success",
+ "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725"
+ },
+ "diff_refs": {
+ "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00",
+ "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
+ },
+ "diverged_commits_count": 2
}
```
@@ -1067,7 +1237,7 @@ Example response when the GitLab issue tracker is used:
"labels" : [],
"user_notes_count": 1,
"changes_count": "1"
- },
+ }
]
```
@@ -1104,54 +1274,101 @@ Example response:
```json
{
- "id": 17,
+ "id": 1,
"iid": 1,
- "project_id": 5,
- "title": "Et et sequi est impedit nulla ut rem et voluptatem.",
- "description": "Consequatur velit eos rerum optio autem. Quia id officia quaerat dolorum optio. Illo laudantium aut ipsum dolorem.",
+ "project_id": 3,
+ "title": "test1",
+ "description": "fixed login page css paddings",
"state": "opened",
- "created_at": "2016-04-05T21:42:23.233Z",
- "updated_at": "2016-04-05T22:11:52.900Z",
- "target_branch": "ui-dev-kit",
- "source_branch": "version-1-9",
+ "created_at": "2017-04-29T08:46:00Z",
+ "updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
- "name": "Eileen Skiles",
- "username": "leila",
- "id": 19,
+ "id": 1,
+ "name": "Administrator",
+ "username": "admin",
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/39ce4a2822cc896933ffbd68c1470e55?s=80&d=identicon",
- "web_url": "https://gitlab.example.com/leila"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
- "name": "Celine Wehner",
- "username": "carli",
- "id": 16,
+ "id": 1,
+ "name": "Administrator",
+ "username": "admin",
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/f4cd5605b769dd2ce405a27c6e6f2684?s=80&d=identicon",
- "web_url": "https://gitlab.example.com/carli"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
- "source_project_id": 5,
- "target_project_id": 5,
- "labels": [],
+ "source_project_id": 2,
+ "target_project_id": 3,
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
- "id": 7,
+ "id": 5,
"iid": 1,
- "project_id": 5,
+ "project_id": 3,
"title": "v2.0",
- "description": "Corrupti eveniet et velit occaecati dolorem est rerum aut.",
+ "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
"state": "closed",
- "created_at": "2016-04-05T21:41:40.905Z",
- "updated_at": "2016-04-05T21:41:40.905Z",
- "due_date": null
+ "created_at": "2015-02-02T19:49:26.013Z",
+ "updated_at": "2015-02-02T19:49:26.013Z",
+ "due_date": "2018-09-22",
+ "start_date": "2018-08-08",
+ "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
},
- "merge_when_pipeline_succeeds": false,
- "merge_status": "cannot_be_merged",
- "subscribed": true,
+ "merge_when_pipeline_succeeds": true,
+ "merge_status": "can_be_merged",
"sha": "8888888888888888888888888888888888888888",
- "merge_commit_sha": null
+ "merge_commit_sha": null,
+ "user_notes_count": 1,
+ "discussion_locked": null,
+ "should_remove_source_branch": true,
+ "force_remove_source_branch": false,
+ "allow_collaboration": false,
+ "allow_maintainer_to_push": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
+ "time_stats": {
+ "time_estimate": 0,
+ "total_time_spent": 0,
+ "human_time_estimate": null,
+ "human_total_time_spent": null
+ },
+ "squash": false,
+ "subscribed": false,
+ "changes_count": "1",
+ "merged_by": {
+ "id": 87854,
+ "name": "Douwe Maan",
+ "username": "DouweM",
+ "state": "active",
+ "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
+ "web_url": "https://gitlab.com/DouweM"
+ },
+ "merged_at": "2018-09-07T11:16:17.520Z",
+ "closed_by": null,
+ "closed_at": null,
+ "latest_build_started_at": "2018-09-07T07:27:38.472Z",
+ "latest_build_finished_at": "2018-09-07T08:07:06.012Z",
+ "first_deployed_to_production_at": null,
+ "pipeline": {
+ "id": 29626725,
+ "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "ref": "patch-28",
+ "status": "success",
+ "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725"
+ },
+ "diff_refs": {
+ "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00",
+ "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
+ },
+ "diverged_commits_count": 2
}
```
@@ -1178,54 +1395,101 @@ Example response:
```json
{
- "id": 17,
+ "id": 1,
"iid": 1,
- "project_id": 5,
- "title": "Et et sequi est impedit nulla ut rem et voluptatem.",
- "description": "Consequatur velit eos rerum optio autem. Quia id officia quaerat dolorum optio. Illo laudantium aut ipsum dolorem.",
+ "project_id": 3,
+ "title": "test1",
+ "description": "fixed login page css paddings",
"state": "opened",
- "created_at": "2016-04-05T21:42:23.233Z",
- "updated_at": "2016-04-05T22:11:52.900Z",
- "target_branch": "ui-dev-kit",
- "source_branch": "version-1-9",
+ "created_at": "2017-04-29T08:46:00Z",
+ "updated_at": "2017-04-29T08:46:00Z",
+ "target_branch": "master",
+ "source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
- "name": "Eileen Skiles",
- "username": "leila",
- "id": 19,
+ "id": 1,
+ "name": "Administrator",
+ "username": "admin",
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/39ce4a2822cc896933ffbd68c1470e55?s=80&d=identicon",
- "web_url": "https://gitlab.example.com/leila"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
- "name": "Celine Wehner",
- "username": "carli",
- "id": 16,
+ "id": 1,
+ "name": "Administrator",
+ "username": "admin",
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/f4cd5605b769dd2ce405a27c6e6f2684?s=80&d=identicon",
- "web_url": "https://gitlab.example.com/carli"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
- "source_project_id": 5,
- "target_project_id": 5,
- "labels": [],
+ "source_project_id": 2,
+ "target_project_id": 3,
+ "labels": [
+ "Community contribution",
+ "Manage"
+ ],
"work_in_progress": false,
"milestone": {
- "id": 7,
+ "id": 5,
"iid": 1,
- "project_id": 5,
+ "project_id": 3,
"title": "v2.0",
- "description": "Corrupti eveniet et velit occaecati dolorem est rerum aut.",
+ "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
"state": "closed",
- "created_at": "2016-04-05T21:41:40.905Z",
- "updated_at": "2016-04-05T21:41:40.905Z",
- "due_date": null
+ "created_at": "2015-02-02T19:49:26.013Z",
+ "updated_at": "2015-02-02T19:49:26.013Z",
+ "due_date": "2018-09-22",
+ "start_date": "2018-08-08",
+ "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1"
},
- "merge_when_pipeline_succeeds": false,
- "merge_status": "cannot_be_merged",
- "subscribed": false,
+ "merge_when_pipeline_succeeds": true,
+ "merge_status": "can_be_merged",
"sha": "8888888888888888888888888888888888888888",
- "merge_commit_sha": null
+ "merge_commit_sha": null,
+ "user_notes_count": 1,
+ "discussion_locked": null,
+ "should_remove_source_branch": true,
+ "force_remove_source_branch": false,
+ "allow_collaboration": false,
+ "allow_maintainer_to_push": false,
+ "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
+ "time_stats": {
+ "time_estimate": 0,
+ "total_time_spent": 0,
+ "human_time_estimate": null,
+ "human_total_time_spent": null
+ },
+ "squash": false,
+ "subscribed": false,
+ "changes_count": "1",
+ "merged_by": {
+ "id": 87854,
+ "name": "Douwe Maan",
+ "username": "DouweM",
+ "state": "active",
+ "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
+ "web_url": "https://gitlab.com/DouweM"
+ },
+ "merged_at": "2018-09-07T11:16:17.520Z",
+ "closed_by": null,
+ "closed_at": null,
+ "latest_build_started_at": "2018-09-07T07:27:38.472Z",
+ "latest_build_finished_at": "2018-09-07T08:07:06.012Z",
+ "first_deployed_to_production_at": null,
+ "pipeline": {
+ "id": 29626725,
+ "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "ref": "patch-28",
+ "status": "success",
+ "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725"
+ },
+ "diff_refs": {
+ "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00",
+ "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
+ "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
+ },
+ "diverged_commits_count": 2
}
```
diff --git a/doc/api/project_templates.md b/doc/api/project_templates.md
new file mode 100644
index 00000000000..ebdfa975849
--- /dev/null
+++ b/doc/api/project_templates.md
@@ -0,0 +1,135 @@
+# Project templates API
+
+This API is a project-specific implementation of these endpoints:
+
+- [Dockerfile templates](templates/dockerfiles.md)
+- [Gitignore templates](templates/gitignores.md)
+- [GitLab CI Config templates](templates/gitlab_ci_ymls.md)
+- [Open source license templates](templates/licenses.md)
+
+It deprecates those endpoints, which will be removed for API version 5.
+
+Project-specific templates will be added to this API in time. This includes, but
+is not limited to:
+
+- [Issue and Merge Request templates](../user/project/description_templates.html)
+- [Group level file templates](https://gitlab.com/gitlab-org/gitlab-ee/issues/5987) **(Premium)**
+
+## Get all templates of a particular type
+
+```
+GET /projects/:id/templates/:type
+```
+
+| Attribute | Type | Required | Description |
+| ---------- | ------ | -------- | ----------- |
+| `id ` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `type` | string | yes| The type `(dockerfiles|gitignores|gitlab_ci_ymls|licenses)` of the template |
+
+Example response (licenses):
+
+```json
+[
+ {
+ "key": "epl-1.0",
+ "name": "Eclipse Public License 1.0"
+ },
+ {
+ "key": "lgpl-3.0",
+ "name": "GNU Lesser General Public License v3.0"
+ },
+ {
+ "key": "unlicense",
+ "name": "The Unlicense"
+ },
+ {
+ "key": "agpl-3.0",
+ "name": "GNU Affero General Public License v3.0"
+ },
+ {
+ "key": "gpl-3.0",
+ "name": "GNU General Public License v3.0"
+ },
+ {
+ "key": "bsd-3-clause",
+ "name": "BSD 3-clause \"New\" or \"Revised\" License"
+ },
+ {
+ "key": "lgpl-2.1",
+ "name": "GNU Lesser General Public License v2.1"
+ },
+ {
+ "key": "mit",
+ "name": "MIT License"
+ },
+ {
+ "key": "apache-2.0",
+ "name": "Apache License 2.0"
+ },
+ {
+ "key": "bsd-2-clause",
+ "name": "BSD 2-clause \"Simplified\" License"
+ },
+ {
+ "key": "mpl-2.0",
+ "name": "Mozilla Public License 2.0"
+ },
+ {
+ "key": "gpl-2.0",
+ "name": "GNU General Public License v2.0"
+ }
+]
+```
+
+## Get one template of a particular type
+
+```
+GET /projects/:id/templates/:type/:key
+```
+
+| Attribute | Type | Required | Description |
+| ---------- | ------ | -------- | ----------- |
+| `id ` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `type` | string | yes| The type `(dockerfiles|gitignores|gitlab_ci_ymls|licenses)` of the template |
+| `key` | string | yes | The key of the template, as obtained from the collection endpoint |
+| `project` | string | no | The project name to use when expanding placeholders in the template. Only affects licenses |
+| `fullname` | string | no | The full name of the copyright holder to use when expanding placeholders in the template. Only affects licenses |
+
+Example response (Dockerfile):
+
+
+```json
+{
+ "name": "Binary",
+ "content": "# This file is a template, and might need editing before it works on your project.\n# This Dockerfile installs a compiled binary into a bare system.\n# You must either commit your compiled binary into source control (not recommended)\n# or build the binary first as part of a CI/CD pipeline.\n\nFROM buildpack-deps:jessie\n\nWORKDIR /usr/local/bin\n\n# Change `app` to whatever your binary is called\nAdd app .\nCMD [\"./app\"]\n"
+}
+
+```
+
+Example response (license):
+
+```json
+{
+ "key": "mit",
+ "name": "MIT License",
+ "nickname": null,
+ "popular": true,
+ "html_url": "http://choosealicense.com/licenses/mit/",
+ "source_url": "https://opensource.org/licenses/MIT",
+ "description": "A short and simple permissive license with conditions only requiring preservation of copyright and license notices. Licensed works, modifications, and larger works may be distributed under different terms and without source code.",
+ "conditions": [
+ "include-copyright"
+ ],
+ "permissions": [
+ "commercial-use",
+ "modifications",
+ "distribution",
+ "private-use"
+ ],
+ "limitations": [
+ "liability",
+ "warranty"
+ ],
+ "content": "MIT License\n\nCopyright (c) 2018 [fullname]\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
+}
+```
diff --git a/doc/api/repository_files.md b/doc/api/repository_files.md
index 0623a6b02ae..c3624f1a535 100644
--- a/doc/api/repository_files.md
+++ b/doc/api/repository_files.md
@@ -97,7 +97,10 @@ POST /projects/:id/repository/files/:file_path
```
```bash
-curl --request POST --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/files/app%2Fprojectrb%2E?branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&content=some%20content&commit_message=create%20a%20new%20file'
+curl --request POST --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' --header "Content-Type: application/json" \
+ --data '{"branch": "master", "author_email": "author@example.com", "author_name": "Firstname Lastname", \
+ "content": "some content", "commit_message": "create a new file"}' \
+ 'https://gitlab.example.com/api/v4/projects/13083/repository/files/app%2Fproject%2Erb'
```
Example response:
@@ -129,7 +132,10 @@ PUT /projects/:id/repository/files/:file_path
```
```bash
-curl --request PUT --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/files/app%2Fproject%2Erb?branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&content=some%20other%20content&commit_message=update%20file'
+curl --request PUT --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' --header "Content-Type: application/json" \
+ --data '{"branch": "master", "author_email": "author@example.com", "author_name": "Firstname Lastname", \
+ "content": "some content", "commit_message": "update file"}' \
+ 'https://gitlab.example.com/api/v4/projects/13083/repository/files/app%2Fproject%2Erb'
```
Example response:
@@ -171,7 +177,10 @@ DELETE /projects/:id/repository/files/:file_path
```
```bash
-curl --request DELETE --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/files/app%2Fproject%2Erb?branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&commit_message=delete%20file'
+curl --request DELETE --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' --header "Content-Type: application/json" \
+ --data '{"branch": "master", "author_email": "author@example.com", "author_name": "Firstname Lastname", \
+ "commit_message": "delete file"}' \
+ 'https://gitlab.example.com/api/v4/projects/13083/repository/files/app%2Fproject%2Erb'
```
Parameters:
diff --git a/doc/api/templates/dockerfiles.md b/doc/api/templates/dockerfiles.md
new file mode 100644
index 00000000000..a08b8d33693
--- /dev/null
+++ b/doc/api/templates/dockerfiles.md
@@ -0,0 +1,113 @@
+# Dockerfiles API
+
+## List Dockerfile templates
+
+Get all Dockerfile templates.
+
+```
+GET /templates/dockerfiles
+```
+
+```bash
+curl https://gitlab.example.com/api/v4/templates/dockerfiles
+```
+
+Example response:
+
+```json
+[
+ {
+ "key": "Binary",
+ "name": "Binary"
+ },
+ {
+ "key": "Binary-alpine",
+ "name": "Binary-alpine"
+ },
+ {
+ "key": "Binary-scratch",
+ "name": "Binary-scratch"
+ },
+ {
+ "key": "Golang",
+ "name": "Golang"
+ },
+ {
+ "key": "Golang-alpine",
+ "name": "Golang-alpine"
+ },
+ {
+ "key": "Golang-scratch",
+ "name": "Golang-scratch"
+ },
+ {
+ "key": "HTTPd",
+ "name": "HTTPd"
+ },
+ {
+ "key": "Node",
+ "name": "Node"
+ },
+ {
+ "key": "Node-alpine",
+ "name": "Node-alpine"
+ },
+ {
+ "key": "OpenJDK",
+ "name": "OpenJDK"
+ },
+ {
+ "key": "OpenJDK-alpine",
+ "name": "OpenJDK-alpine"
+ },
+ {
+ "key": "PHP",
+ "name": "PHP"
+ },
+ {
+ "key": "Python",
+ "name": "Python"
+ },
+ {
+ "key": "Python-alpine",
+ "name": "Python-alpine"
+ },
+ {
+ "key": "Python2",
+ "name": "Python2"
+ },
+ {
+ "key": "Ruby",
+ "name": "Ruby"
+ },
+ {
+ "key": "Ruby-alpine",
+ "name": "Ruby-alpine"
+ }
+]
+```
+
+## Single Dockerfile template
+
+Get a single Dockerfile template.
+
+```
+GET /templates/dockerfiles/:key
+```
+
+| Attribute | Type | Required | Description |
+| ---------- | ------ | -------- | ----------- |
+| `key` | string | yes | The key of the Dockerfile template |
+
+```bash
+curl https://gitlab.example.com/api/v4/templates/dockerfiles/Binary
+```
+
+Example response:
+
+```json
+{
+ "name": "Binary",
+ "content": "# This file is a template, and might need editing before it works on your project.\n# This Dockerfile installs a compiled binary into a bare system.\n# You must either commit your compiled binary into source control (not recommended)\n# or build the binary first as part of a CI/CD pipeline.\n\nFROM buildpack-deps:jessie\n\nWORKDIR /usr/local/bin\n\n# Change `app` to whatever your binary is called\nAdd app .\nCMD [\"./app\"]\n"
+}
+```
diff --git a/doc/api/templates/gitignores.md b/doc/api/templates/gitignores.md
index d3f5c88ca90..3804855129c 100644
--- a/doc/api/templates/gitignores.md
+++ b/doc/api/templates/gitignores.md
@@ -17,538 +17,84 @@ Example response:
```json
[
{
- "name": "AppEngine"
- },
- {
- "name": "Laravel"
- },
- {
- "name": "Elisp"
- },
- {
- "name": "SketchUp"
+ "key": "Actionscript",
+ "name": "Actionscript"
},
{
+ "key": "Ada",
"name": "Ada"
},
{
- "name": "Ruby"
- },
- {
- "name": "Kohana"
- },
- {
- "name": "Nanoc"
- },
- {
- "name": "Erlang"
- },
- {
- "name": "OCaml"
- },
- {
- "name": "Lithium"
- },
- {
- "name": "Fortran"
- },
- {
- "name": "Scala"
- },
- {
- "name": "Node"
- },
- {
- "name": "Fancy"
- },
- {
- "name": "Perl"
- },
- {
- "name": "Zephir"
- },
- {
- "name": "WordPress"
- },
- {
- "name": "Symfony"
- },
- {
- "name": "FuelPHP"
- },
- {
- "name": "DM"
- },
- {
- "name": "Sdcc"
- },
- {
- "name": "Rust"
- },
- {
- "name": "C"
- },
- {
- "name": "Umbraco"
- },
- {
- "name": "Actionscript"
+ "key": "Agda",
+ "name": "Agda"
},
{
+ "key": "Android",
"name": "Android"
},
{
- "name": "Grails"
- },
- {
- "name": "Composer"
- },
- {
- "name": "ExpressionEngine"
- },
- {
- "name": "Gcov"
- },
- {
- "name": "Qt"
+ "key": "AppEngine",
+ "name": "AppEngine"
},
{
- "name": "Phalcon"
+ "key": "AppceleratorTitanium",
+ "name": "AppceleratorTitanium"
},
{
+ "key": "ArchLinuxPackages",
"name": "ArchLinuxPackages"
},
{
- "name": "TeX"
- },
- {
- "name": "SCons"
- },
- {
- "name": "Lilypond"
- },
- {
- "name": "CommonLisp"
- },
- {
- "name": "Rails"
- },
- {
- "name": "Mercury"
- },
- {
- "name": "Magento"
- },
- {
- "name": "ChefCookbook"
- },
- {
- "name": "GitBook"
- },
- {
- "name": "C++"
- },
- {
- "name": "Eagle"
- },
- {
- "name": "Go"
- },
- {
- "name": "OpenCart"
- },
- {
- "name": "Scheme"
- },
- {
- "name": "Typo3"
- },
- {
- "name": "SeamGen"
- },
- {
- "name": "Swift"
- },
- {
- "name": "Elm"
- },
- {
- "name": "Unity"
- },
- {
- "name": "Agda"
- },
- {
- "name": "CUDA"
- },
- {
- "name": "VVVV"
- },
- {
- "name": "Finale"
- },
- {
- "name": "LemonStand"
- },
- {
- "name": "Textpattern"
- },
- {
- "name": "Julia"
- },
- {
- "name": "Packer"
- },
- {
- "name": "Scrivener"
- },
- {
- "name": "Dart"
- },
- {
- "name": "Plone"
- },
- {
- "name": "Jekyll"
- },
- {
- "name": "Xojo"
- },
- {
- "name": "LabVIEW"
- },
- {
+ "key": "Autotools",
"name": "Autotools"
},
{
- "name": "KiCad"
- },
- {
- "name": "Prestashop"
- },
- {
- "name": "ROS"
- },
- {
- "name": "Smalltalk"
- },
- {
- "name": "GWT"
- },
- {
- "name": "OracleForms"
- },
- {
- "name": "SugarCRM"
- },
- {
- "name": "Nim"
- },
- {
- "name": "SymphonyCMS"
+ "key": "C",
+ "name": "C"
},
{
- "name": "Maven"
+ "key": "C++",
+ "name": "C++"
},
{
+ "key": "CFWheels",
"name": "CFWheels"
},
{
- "name": "Python"
- },
- {
- "name": "ZendFramework"
- },
- {
- "name": "CakePHP"
- },
- {
- "name": "Concrete5"
- },
- {
- "name": "PlayFramework"
- },
- {
- "name": "Terraform"
- },
- {
- "name": "Elixir"
- },
- {
+ "key": "CMake",
"name": "CMake"
},
{
- "name": "Joomla"
- },
- {
- "name": "Coq"
- },
- {
- "name": "Delphi"
- },
- {
- "name": "Haskell"
- },
- {
- "name": "Yii"
- },
- {
- "name": "Java"
- },
- {
- "name": "UnrealEngine"
- },
- {
- "name": "AppceleratorTitanium"
- },
- {
- "name": "CraftCMS"
- },
- {
- "name": "ForceDotCom"
- },
- {
- "name": "ExtJs"
- },
- {
- "name": "MetaProgrammingSystem"
- },
- {
- "name": "D"
- },
- {
- "name": "Objective-C"
- },
- {
- "name": "RhodesRhomobile"
- },
- {
- "name": "R"
- },
- {
- "name": "EPiServer"
- },
- {
- "name": "Yeoman"
- },
- {
- "name": "VisualStudio"
- },
- {
- "name": "Processing"
- },
- {
- "name": "Leiningen"
- },
- {
- "name": "Stella"
- },
- {
- "name": "Opa"
- },
- {
- "name": "Drupal"
- },
- {
- "name": "TurboGears2"
- },
- {
- "name": "Idris"
- },
- {
- "name": "Jboss"
- },
- {
- "name": "CodeIgniter"
- },
- {
- "name": "Qooxdoo"
- },
- {
- "name": "Waf"
+ "key": "CUDA",
+ "name": "CUDA"
},
{
- "name": "Sass"
+ "key": "CakePHP",
+ "name": "CakePHP"
},
{
- "name": "Lua"
+ "key": "ChefCookbook",
+ "name": "ChefCookbook"
},
{
+ "key": "Clojure",
"name": "Clojure"
},
{
- "name": "IGORPro"
- },
- {
- "name": "Gradle"
- },
- {
- "name": "Archives"
- },
- {
- "name": "SynopsysVCS"
- },
- {
- "name": "Ninja"
- },
- {
- "name": "Tags"
- },
- {
- "name": "OSX"
- },
- {
- "name": "Dreamweaver"
- },
- {
- "name": "CodeKit"
- },
- {
- "name": "NotepadPP"
- },
- {
- "name": "VisualStudioCode"
- },
- {
- "name": "Mercurial"
- },
- {
- "name": "BricxCC"
- },
- {
- "name": "DartEditor"
- },
- {
- "name": "Eclipse"
- },
- {
- "name": "Cloud9"
- },
- {
- "name": "TortoiseGit"
- },
- {
- "name": "NetBeans"
- },
- {
- "name": "GPG"
- },
- {
- "name": "Espresso"
- },
- {
- "name": "Redcar"
- },
- {
- "name": "Xcode"
- },
- {
- "name": "Matlab"
- },
- {
- "name": "LyX"
- },
- {
- "name": "SlickEdit"
- },
- {
- "name": "Dropbox"
- },
- {
- "name": "CVS"
- },
- {
- "name": "Calabash"
- },
- {
- "name": "JDeveloper"
- },
- {
- "name": "Vagrant"
- },
- {
- "name": "IPythonNotebook"
- },
- {
- "name": "TextMate"
- },
- {
- "name": "Ensime"
- },
- {
- "name": "WebMethods"
- },
- {
- "name": "VirtualEnv"
- },
- {
- "name": "Emacs"
- },
- {
- "name": "Momentics"
- },
- {
- "name": "JetBrains"
- },
- {
- "name": "SublimeText"
- },
- {
- "name": "Kate"
- },
- {
- "name": "ModelSim"
- },
- {
- "name": "Redis"
- },
- {
- "name": "KDevelop4"
- },
- {
- "name": "Bazaar"
- },
- {
- "name": "Linux"
- },
- {
- "name": "Windows"
- },
- {
- "name": "XilinxISE"
- },
- {
- "name": "Lazarus"
- },
- {
- "name": "EiffelStudio"
- },
- {
- "name": "Anjuta"
- },
- {
- "name": "Vim"
- },
- {
- "name": "Otto"
- },
- {
- "name": "MicrosoftOffice"
- },
- {
- "name": "LibreOffice"
- },
- {
- "name": "SBT"
+ "key": "CodeIgniter",
+ "name": "CodeIgniter"
},
{
- "name": "MonoDevelop"
+ "key": "CommonLisp",
+ "name": "CommonLisp"
},
{
- "name": "SVN"
+ "key": "Composer",
+ "name": "Composer"
},
{
- "name": "FlexBuilder"
+ "key": "Concrete5",
+ "name": "Concrete5"
}
]
```
diff --git a/doc/api/templates/gitlab_ci_ymls.md b/doc/api/templates/gitlab_ci_ymls.md
index bdb128fc336..cecfc8cd9b9 100644
--- a/doc/api/templates/gitlab_ci_ymls.md
+++ b/doc/api/templates/gitlab_ci_ymls.md
@@ -17,79 +17,84 @@ Example response:
```json
[
{
- "name": "C++"
- },
- {
- "name": "Docker"
- },
- {
- "name": "Elixir"
- },
- {
- "name": "LaTeX"
- },
- {
- "name": "Grails"
- },
- {
- "name": "Rust"
+ "key": "Android",
+ "name": "Android"
},
{
- "name": "Nodejs"
+ "key": "Auto-DevOps",
+ "name": "Auto-DevOps"
},
{
- "name": "Ruby"
+ "key": "Bash",
+ "name": "Bash"
},
{
- "name": "Scala"
+ "key": "C++",
+ "name": "C++"
},
{
- "name": "Maven"
+ "key": "Chef",
+ "name": "Chef"
},
{
- "name": "Harp"
+ "key": "Clojure",
+ "name": "Clojure"
},
{
- "name": "Pelican"
+ "key": "Crystal",
+ "name": "Crystal"
},
{
- "name": "Hyde"
+ "key": "Django",
+ "name": "Django"
},
{
- "name": "Nanoc"
+ "key": "Docker",
+ "name": "Docker"
},
{
- "name": "Octopress"
+ "key": "Elixir",
+ "name": "Elixir"
},
{
- "name": "JBake"
+ "key": "Go",
+ "name": "Go"
},
{
- "name": "HTML"
+ "key": "Gradle",
+ "name": "Gradle"
},
{
- "name": "Hugo"
+ "key": "Grails",
+ "name": "Grails"
},
{
- "name": "Metalsmith"
+ "key": "Julia",
+ "name": "Julia"
},
{
- "name": "Hexo"
+ "key": "LaTeX",
+ "name": "LaTeX"
},
{
- "name": "Lektor"
+ "key": "Laravel",
+ "name": "Laravel"
},
{
- "name": "Doxygen"
+ "key": "Maven",
+ "name": "Maven"
},
{
- "name": "Brunch"
+ "key": "Mono",
+ "name": "Mono"
},
{
- "name": "Jekyll"
+ "key": "Nodejs",
+ "name": "Nodejs"
},
{
- "name": "Middleman"
+ "key": "OpenShift",
+ "name": "OpenShift"
}
]
```
diff --git a/doc/api/users.md b/doc/api/users.md
index 3b41e0f7ec6..07f03f9c827 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -558,7 +558,7 @@ Parameters:
## List SSH keys for user
-Get a list of a specified user's SSH keys. Available only for admin
+Get a list of a specified user's SSH keys.
```
GET /users/:id/keys
diff --git a/doc/ci/caching/index.md b/doc/ci/caching/index.md
index f479dc74d1f..758ab37861b 100644
--- a/doc/ci/caching/index.md
+++ b/doc/ci/caching/index.md
@@ -253,7 +253,7 @@ image: python:latest
# Change pip's cache directory to be inside the project directory since we can
# only cache local items.
variables:
- PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache"
+ PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
# Pip's cache doesn't store the python packages
# https://pip.pypa.io/en/stable/reference/pip_install/#caching
@@ -262,7 +262,7 @@ variables:
# them in a virtualenv and cache it as well.
cache:
paths:
- - .cache/
+ - .cache/pip
- venv/
before_script:
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index 9abedcc6acb..31649ee2792 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -461,25 +461,25 @@ that runner.
> - If the repository is private you need to authenticate your GitLab Runner in the
> registry. Learn more about how [GitLab Runner works in this case][runner-priv-reg].
-As an example, let's assume that you want to use the `registry.example.com/private/image:latest`
+As an example, let's assume that you want to use the `registry.example.com:5000/private/image:latest`
image which is private and requires you to login into a private container registry.
Let's also assume that these are the login credentials:
-| Key | Value |
-|----------|----------------------|
-| registry | registry.example.com |
-| username | my_username |
-| password | my_password |
+| Key | Value |
+|----------|---------------------------|
+| registry | registry.example.com:5000 |
+| username | my_username |
+| password | my_password |
-To configure access for `registry.example.com`, follow these steps:
+To configure access for `registry.example.com:5000`, follow these steps:
1. Find what the value of `DOCKER_AUTH_CONFIG` should be. There are two ways to
accomplish this:
- **First way -** Do a `docker login` on your local machine:
```bash
- docker login registry.example.com --username my_username --password my_password
+ docker login registry.example.com:5000 --username my_username --password my_password
```
Then copy the content of `~/.docker/config.json`.
@@ -503,7 +503,7 @@ To configure access for `registry.example.com`, follow these steps:
```json
{
"auths": {
- "registry.example.com": {
+ "registry.example.com:5000": {
"auth": "bXlfdXNlcm5hbWU6bXlfcGFzc3dvcmQ="
}
}
@@ -515,22 +515,28 @@ To configure access for `registry.example.com`, follow these steps:
registry from it:
```bash
- docker logout registry.example.com
+ docker logout registry.example.com:5000
```
-1. You can now use any private image from `registry.example.com` defined in
+1. You can now use any private image from `registry.example.com:5000` defined in
`image` and/or `services` in your `.gitlab-ci.yml` file:
```yaml
- image: my.registry.tld:5000/namespace/image:tag
+ image: registry.example.com:5000/namespace/image:tag
```
- In the example above, GitLab Runner will look at `my.registry.tld:5000` for the
+ In the example above, GitLab Runner will look at `registry.example.com:5000` for the
image `namespace/image:tag`.
You can add configuration for as many registries as you want, adding more
registries to the `"auths"` hash as described above.
+NOTE: **Note:** The full `hostname:port` combination is required everywhere
+for the Runner to match the `DOCKER_AUTH_CONFIG`. For example, if
+`registry.example.com:5000/namespace/image:tag` is specified in `.gitlab-ci.yml`,
+then the `DOCKER_AUTH_CONFIG` must also specify `registry.example.com:5000`.
+Specifying only `registry.example.com` will not work.
+
## Configuring services
Many services accept environment variables which allow you to easily change
diff --git a/doc/ci/examples/deployment/README.md b/doc/ci/examples/deployment/README.md
index bd60d641493..f53f7c50281 100644
--- a/doc/ci/examples/deployment/README.md
+++ b/doc/ci/examples/deployment/README.md
@@ -5,7 +5,7 @@ continuous deployment that's developed and used by Travis CI, but can also be
used with GitLab CI.
>**Note:**
-We recommend to use Dpl if you're deploying to any of these of these services:
+We recommend to use Dpl if you're deploying to any of these services:
https://github.com/travis-ci/dpl#supported-providers.
## Requirements
diff --git a/doc/ci/interactive_web_terminal/index.md b/doc/ci/interactive_web_terminal/index.md
index df83f30fbb7..1ddc1bf4d7e 100644
--- a/doc/ci/interactive_web_terminal/index.md
+++ b/doc/ci/interactive_web_terminal/index.md
@@ -25,6 +25,12 @@ Two things need to be configured for the interactive web terminal to work:
NOTE: **Note:** Not all executors are
[supported](https://docs.gitlab.com/runner/executors/#compatibility-chart).
+NOTE: **Note:** The `docker` executor does not keep running
+after the build script is finished. At that point, the terminal will automatically
+disconnect and will not wait for the user to finish. Please follow [this
+issue](https://gitlab.com/gitlab-org/gitlab-runner/issues/3605) for updates on
+improving this behavior.
+
Sometimes, when a job is running, things don't go as you would expect, and it
would be helpful if one can have a shell to aid debugging. When a job is
running, on the right panel you can see a button `debug` that will open the terminal
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index f11949da64e..2d23bf6d2fd 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -94,6 +94,9 @@ future GitLab releases.**
| **CI_SERVER_NAME** | all | all | The name of CI server that is used to coordinate jobs |
| **CI_SERVER_REVISION** | all | all | GitLab revision that is used to schedule jobs |
| **CI_SERVER_VERSION** | all | all | GitLab version that is used to schedule jobs |
+| **CI_SERVER_VERSION_MAJOR** | 11.4 | all | GitLab version major component |
+| **CI_SERVER_VERSION_MINOR** | 11.4 | all | GitLab version minor component |
+| **CI_SERVER_VERSION_PATCH** | 11.4 | all | GitLab version patch component |
| **CI_SHARED_ENVIRONMENT** | all | 10.1 | Marks that the job is executed in a shared environment (something that is persisted across CI invocations like `shell` or `ssh` executor). If the environment is shared, it is set to true, otherwise it is not defined at all. |
| **GET_SOURCES_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to fetch sources running a job |
| **GITLAB_CI** | all | all | Mark that job is executed in GitLab CI environment |
@@ -194,7 +197,7 @@ Likewise, group-level variables can be added by going to your group's
**Settings > CI/CD**, then finding the section called **Variables**.
Any variables of [subgroups] will be inherited recursively.
-![Variables](img/secret_variables.png)
+![Variables](img/variables.png)
Once you set them, they will be available for all subsequent pipelines. You can also
[protect your variables](#protected-variables).
@@ -323,6 +326,12 @@ Running on runner-8a2f473d-project-1796893-concurrent-0 via runner-8a2f473d-mach
++ CI_SERVER_NAME='GitLab CI'
++ export CI_SERVER_VERSION=
++ CI_SERVER_VERSION=
+++ export CI_SERVER_VERSION_MAJOR=
+++ CI_SERVER_VERSION_MAJOR=
+++ export CI_SERVER_VERSION_MINOR=
+++ CI_SERVER_VERSION_MINOR=
+++ export CI_SERVER_VERSION_PATCH=
+++ CI_SERVER_VERSION_PATCH=
++ export CI_SERVER_REVISION=
++ CI_SERVER_REVISION=
++ export GITLAB_CI=true
@@ -468,6 +477,9 @@ export CI_SERVER="yes"
export CI_SERVER_NAME="GitLab"
export CI_SERVER_REVISION="70606bf"
export CI_SERVER_VERSION="8.9.0"
+export CI_SERVER_VERSION_MAJOR="8"
+export CI_SERVER_VERSION_MINOR="9"
+export CI_SERVER_VERSION_PATCH="0"
export GITLAB_USER_ID="42"
export GITLAB_USER_EMAIL="user@example.com"
export CI_REGISTRY_USER="gitlab-ci-token"
diff --git a/doc/ci/variables/img/secret_variables.png b/doc/ci/variables/img/secret_variables.png
deleted file mode 100644
index 3c1aa361dc2..00000000000
--- a/doc/ci/variables/img/secret_variables.png
+++ /dev/null
Binary files differ
diff --git a/doc/ci/variables/img/variables.png b/doc/ci/variables/img/variables.png
new file mode 100644
index 00000000000..d2dc99bbac0
--- /dev/null
+++ b/doc/ci/variables/img/variables.png
Binary files differ
diff --git a/doc/ci/variables/where_variables_can_be_used.md b/doc/ci/variables/where_variables_can_be_used.md
index b0d2ea6484d..4e8ce10c9cb 100644
--- a/doc/ci/variables/where_variables_can_be_used.md
+++ b/doc/ci/variables/where_variables_can_be_used.md
@@ -17,8 +17,8 @@ There are two places defined variables can be used. On the:
| Definition | Can be expanded? | Expansion place | Description |
|--------------------------------------|-------------------|-----------------|--------------|
-| `environment:url` | yes | GitLab | The variable expansion is made by GitLab's [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism).<ul><li>**Supported:** all variables defined for a job (project/group variables, variables from `.gitlab-ci.yml`, variables from triggers, variables from pipeline schedules)</li><li>**Not suported:** variables defined in Runner's `config.toml` and variables created in job's `script`</li></ul> |
-| `environment:name` | yes | GitLab | Similar to `environment:url`, but the variables expansion **doesn't support**: <ul><li>variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`)</li><li>any other variables related to environment (currently only `CI_ENVIRONMENT_URL`)</li><li>[persisted variables](#persisted-variables)</li></ul> |
+| `environment:url` | yes | GitLab | The variable expansion is made by GitLab's [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism).<ul><li>Supported: all variables defined for a job (project/group variables, variables from `.gitlab-ci.yml`, variables from triggers, variables from pipeline schedules)</li><li>Not suported: variables defined in Runner's `config.toml` and variables created in job's `script`</li></ul> |
+| `environment:name` | yes | GitLab | Similar to `environment:url`, but the variables expansion doesn't support: <ul><li>variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`)</li><li>any other variables related to environment (currently only `CI_ENVIRONMENT_URL`)</li><li>[persisted variables](#persisted-variables)</li></ul> |
| `variables` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
| `image` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
| `services:[]` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
@@ -26,7 +26,7 @@ There are two places defined variables can be used. On the:
| `cache:key` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
| `artifacts:name` | yes | Runner | The variable expansion is made by GitLab Runner's shell environment |
| `script`, `before_script`, `after_script` | yes | Script execution shell | The variable expansion is made by the [execution shell environment](#execution-shell-environment) |
-| `only:variables:[]`, `except:variables:[]` | no | n/a | The variable must be in the form of `$variable`.<br/>**Not supported:**<ul><li>variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`)</li><li>any other variables related to environment (currently only `CI_ENVIRONMENT_URL`)</li><li>[persisted variables](#persisted-variables)</li></ul> |
+| `only:variables:[]`, `except:variables:[]` | no | n/a | The variable must be in the form of `$variable`.<br/>Not supported:<ul><li>variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`)</li><li>any other variables related to environment (currently only `CI_ENVIRONMENT_URL`)</li><li>[persisted variables](#persisted-variables)</li></ul> |
### `config.toml` file
@@ -55,9 +55,9 @@ since the expansion is done in GitLab before any Runner will get the job.
### GitLab Runner internal variable expansion mechanism
-- **Supported:** project/group variables, `.gitlab-ci.yml` variables, `config.toml` variables, and
+- Supported: project/group variables, `.gitlab-ci.yml` variables, `config.toml` variables, and
variables from triggers, pipeline schedules, and manual pipelines.
-- **Not supported:** variables defined inside of scripts (e.g., `export MY_VARIABLE="test"`).
+- Not supported: variables defined inside of scripts (e.g., `export MY_VARIABLE="test"`).
The Runner uses Go's `os.Expand()` method for variable expansion. It means that it will handle
only variables defined as `$variable` and `${variable}`. What's also important, is that
@@ -73,7 +73,7 @@ by bash/sh (leaving empty strings or some values depending whether the variables
defined or not), but will not work with Windows' cmd/PowerShell, since these shells
are using a different variables syntax.
-**Supported:**
+Supported:
- The `script` may use all available variables that are default for the shell (e.g., `$PATH` which
should be present in all bash/sh shells) and all variables defined by GitLab CI/CD (project/group variables,
@@ -106,7 +106,9 @@ The following variables are known as "persisted":
They are:
-- **Supported** for all definitions as [described in the table](#gitlab-ci-yml-file) where the "Expansion place" is "Runner".
-- **Not supported:**
- - By the definitions [described in the table](#gitlab-ci-yml-file) where the "Expansion place" is "GitLab".
+- Supported for definitions where the ["Expansion place"](#gitlab-ci-yml-file) is:
+ - Runner.
+ - Script execution shell.
+- Not supported:
+ - For definitions where the ["Expansion place"](#gitlab-ci-yml-file) is GitLab.
- In the `only` and `except` [variables expressions](README.md#variables-expressions).
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index e38628b288b..8b770495853 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -102,10 +102,13 @@ rspec:
- $RSPEC
```
-In the example above, the `rspec` job is going to inherit from the `.tests`
-template job. GitLab will perform a reverse deep merge, which means that it will
-merge the `rspec` contents into `.tests` recursively, and this is going to result in
-the following `rspec` job:
+In the example above, the `rspec` job inherits from the `.tests` template job.
+GitLab will perform a reverse deep merge based on the keys. GitLab will:
+
+- Merge the `rspec` contents into `.tests` recursively.
+- Not merge the values of the keys.
+
+This results in the following `rspec` job:
```yaml
rspec:
@@ -118,6 +121,11 @@ rspec:
- $RSPEC
```
+NOTE: **Note:**
+Note that `script: rake test` has been overwritten by `script: rake rspec`.
+
+If you do want to include the `rake test`, have a look at [before_script-and-after_script](#before_script-and-after_script).
+
`.tests` in this example is a [hidden key](#hidden-keys-jobs), but it's
possible to inherit from regular jobs as well.
@@ -387,6 +395,8 @@ except master.
> `refs` and `kubernetes` policies introduced in GitLab 10.0
>
> `variables` policy introduced in 10.7
+>
+> `changes` policy [introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/19232) in 11.4
CAUTION: **Warning:**
This an _alpha_ feature, and it it subject to change at any time without
@@ -398,10 +408,15 @@ policy configuration.
GitLab now supports both, simple and complex strategies, so it is possible to
use an array and a hash configuration scheme.
-Three keys are now available: `refs`, `kubernetes` and `variables`.
+Four keys are now available: `refs`, `kubernetes` and `variables` and `changes`.
+
+### `refs` and `kubernetes`
+
Refs strategy equals to simplified only/except configuration, whereas
kubernetes strategy accepts only `active` keyword.
+### `variables`
+
`variables` keyword is used to define variables expressions. In other words
you can use predefined variables / project / group or
environment-scoped variables to define an expression GitLab is going to
@@ -445,6 +460,46 @@ end-to-end:
Learn more about variables expressions on [a separate page][variables-expressions].
+### `changes`
+
+Using `changes` keyword with `only` or `except` makes it possible to define if
+a job should be created based on files modified by a git push event.
+
+For example:
+
+```yaml
+docker build:
+ script: docker build -t my-image:$CI_COMMIT_REF_SLUG .
+ only:
+ changes:
+ - Dockerfile
+ - docker/scripts/*
+```
+
+In the scenario above, if you are pushing multiple commits to GitLab to an
+existing branch, GitLab creates and triggers `docker build` job, provided that
+one of the commits contains changes to either:
+
+- The `Dockerfile` file.
+- Any of the files inside `docker/scripts/` directory.
+
+CAUTION: **Warning:**
+There are some caveats when using this feature with new branches and tags. See
+the section below.
+
+#### Using `changes` with new branches and tags
+
+If you are pushing a **new** branch or a **new** tag to GitLab, the policy
+always evaluates to true and GitLab will create a job. This feature is not
+connected with merge requests yet, and because GitLab is creating pipelines
+before an user can create a merge request we don't know a target branch at
+this point.
+
+Without a target branch, it is not possible to know what the common ancestor is,
+thus we always create a job in that case. This feature works best for stable
+branches like `master` because in that case GitLab uses the previous commit
+that is present in a branch to compare against the latest SHA that was pushed.
+
## `tags`
`tags` is used to select specific Runners from the list of all Runners that are
diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md
index fed29d37b26..2f06677bfec 100644
--- a/doc/development/contributing/issue_workflow.md
+++ b/doc/development/contributing/issue_workflow.md
@@ -60,7 +60,7 @@ people.
The current team labels are:
-- ~Configuration
+- ~Configure
- ~"CI/CD"
- ~Create
- ~Distribution
diff --git a/doc/development/database_merge_request_checklist.md b/doc/development/database_merge_request_checklist.md
index 75c395b61ef..48864c81592 100644
--- a/doc/development/database_merge_request_checklist.md
+++ b/doc/development/database_merge_request_checklist.md
@@ -1,15 +1,15 @@
# Merge Request Checklist
When creating a merge request that performs database related changes (schema
-changes, adjusting queries to optimise performance, etc) you should use the
-merge request template called "Database Changes". This template contains a
+changes, adjusting queries to optimize performance, etc) you should use the
+merge request template called "Database changes". This template contains a
checklist of steps to follow to make sure the changes are up to snuff.
To use the checklist, create a new merge request and click on the "Choose a
-template" dropdown, then click "Database Changes".
+template" dropdown, then click "Database changes".
An example of this checklist can be found at
-https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12463.
+<https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12463>.
The source code of the checklist can be found in at
-https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab/merge_request_templates/Database%20Changes.md
+<https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab/merge_request_templates/Database%20changes.md>
diff --git a/doc/development/documentation/structure.md b/doc/development/documentation/structure.md
index 1002836096a..01068e23082 100644
--- a/doc/development/documentation/structure.md
+++ b/doc/development/documentation/structure.md
@@ -21,21 +21,21 @@ Before getting started, read through the following docs:
Every document should include the following content in the following sequence:
- **Feature name**: defines an intuitive name for the feature that clearly
-states what it is and is consistent with any relevant UI text.
+ states what it is and is consistent with any relevant UI text.
- **Feature overview** and description: describe what it is, what it does, and in what context it should be used.
- **Use cases**: describes real use case scenarios for that feature.
- **Requirements**: describes what software and/or configuration is required to be able to
-use the feature and, if applicable, prerequisite knowledge for being able to follow/implement the tutorial.
-For example, familiarity with GitLab CI/CD, an account on a third-party service, dependencies installed, etc.
-Link each one to its most relevant resource; i.e., where the reader can go to begin to fullfil that requirement.
-(Another doc page, a third party application's site, etc.)
+ use the feature and, if applicable, prerequisite knowledge for being able to follow/implement the tutorial.
+ For example, familiarity with GitLab CI/CD, an account on a third-party service, dependencies installed, etc.
+ Link each one to its most relevant resource; i.e., where the reader can go to begin to fullfil that requirement.
+ (Another doc page, a third party application's site, etc.)
- **Instructions**: clearly describes the steps to use the feature, leaving no gaps.
- **Troubleshooting** guide (recommended but not required): if you know beforehand what issues
-one might have when setting it up, or when something is changed, or on upgrading, it's
-important to describe those too. Think of things that may go wrong and include them in the
-docs. This is important to minimize requests for support, and to avoid doc comments with
-questions that you know someone might ask. Answering them beforehand only makes your
-document better and more approachable.
+ one might have when setting it up, or when something is changed, or on upgrading, it's
+ important to describe those too. Think of things that may go wrong and include them in the
+ docs. This is important to minimize requests for support, and to avoid doc comments with
+ questions that you know someone might ask. Answering them beforehand only makes your
+ document better and more approachable.
For additional details, see the subsections below, as well as the [Documentation template for new docs](#Documentation-template-for-new-docs).
@@ -55,10 +55,11 @@ You should answer this question: what can you do with this feature/change? Use c
are examples of how this feature or change can be used in real life.
Examples:
-- CE and EE: [Issues](../user/project/issues/index.md#use-cases)
-- CE and EE: [Merge Requests](../user/project/merge_requests/index.md#overview)
-- EE-only: [Geo](https://docs.gitlab.com/ee/gitlab-geo/README.html#overview)
-- EE-only: [Jenkins integration](https://docs.gitlab.com/ee/integration/jenkins.md#overview)
+
+- CE and EE: [Issues](../../user/project/issues/index.md#use-cases)
+- CE and EE: [Merge Requests](../../user/project/merge_requests/index.md)
+- EE-only: [Geo](https://docs.gitlab.com/ee/administration/geo/replication/index.html)
+- EE-only: [Jenkins integration](https://docs.gitlab.com/ee/integration/jenkins.html)
Note that if you don't have anything to add between the doc title (`<h1>`) and
the header `## Overview`, you can omit the header, but keep the content of the
@@ -72,14 +73,14 @@ and for every **major** feature present in Community Edition.
Your new document will be discoverable by the user only if:
- Crosslinked from the higher-level index (e.g., Issue Boards docs
-should be linked from Issues; Prometheus docs should be linked from
-Monitoring; CI/CD tutorials should be linked from CI/CD examples).
+ should be linked from Issues; Prometheus docs should be linked from
+ Monitoring; CI/CD tutorials should be linked from CI/CD examples).
- When referencing other GitLab products and features, link to their
-respective docs; when referencing third-party products or technologies,
-link out to their external sites, documentation, and resources.
+ respective docs; when referencing third-party products or technologies,
+ link out to their external sites, documentation, and resources.
- The headings are clear. E.g., "App testing" is a bad heading, "Testing
-an application with GitLab CI/CD" is much better. Think of something
-someone will search for and use these keywords in the headings.
+ an application with GitLab CI/CD" is much better. Think of something
+ someone will search for and use these keywords in the headings.
## Documentation template for new docs
@@ -133,7 +134,7 @@ is simple and the document is short.
- Be clear, concise, and stick to the goal of the doc: explain how to
use that feature.
- Use inclusive language and avoid jargons, as well as uncommon and
-fancy words. The docs should be clear and very easy to understand.
+fancy words. The docs should be clear and easy to understand.
- Write in the 3rd person (use "we", "you", "us", "one", instead of "I" or "me").
- Always provide internal and external reference links.
- Always link the doc from its higher-level index.
diff --git a/doc/development/rolling_out_changes_using_feature_flags.md b/doc/development/rolling_out_changes_using_feature_flags.md
index 905aa26a40b..dada59ce242 100644
--- a/doc/development/rolling_out_changes_using_feature_flags.md
+++ b/doc/development/rolling_out_changes_using_feature_flags.md
@@ -151,3 +151,27 @@ most cases this will translate to a feature (with a feature flag) being shipped
in RC1, followed by the feature flag being removed in RC2. This in turn means
the feature will be stable by the time we publish a stable package around the
22nd of the month.
+
+## Undefined feature flags default to "on"
+
+By default, the [`Project#feature_available?`][project-fa],
+[`Namespace#feature_available?`][namespace-fa] (EE), and
+[`License.feature_available?`][license-fa] (EE) methods will check if the
+specified feature is behind a feature flag. Unless the feature is explicitly
+disabled or limited to a percentage of users, the feature flag check will
+default to `true`.
+
+As an example, if you were to ship the backend half of a feature behind a flag,
+you'd want to explicitly disable that flag until the frontend half is also ready
+to be shipped. You can do this via ChatOps:
+
+```
+/chatops run feature set some_feature 0
+```
+
+Note that you can do this at any time, even before the merge request using the
+flag has been merged!
+
+[project-fa]: https://gitlab.com/gitlab-org/gitlab-ee/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/app/models/project_feature.rb#L63-68
+[namespace-fa]: https://gitlab.com/gitlab-org/gitlab-ee/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/ee/app/models/ee/namespace.rb#L71-85
+[license-fa]: https://gitlab.com/gitlab-org/gitlab-ee/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/ee/app/models/license.rb#L293-300
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 7df81fbc46f..25aa5d3369d 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -12,7 +12,7 @@ Since installations from source don't have Runit, Sidekiq can't be terminated an
## Select Version to Install
-Make sure you view [this installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md) from the branch (version) of GitLab you would like to install (e.g., `11-3-stable`).
+Make sure you view [this installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md) from the branch (version) of GitLab you would like to install (e.g., `11-4-stable`).
You can select the branch in the version dropdown in the top left corner of GitLab (below the menu bar).
If the highest number stable branch is unclear please check the [GitLab Blog](https://about.gitlab.com/blog/) for installation guide links by version.
@@ -300,9 +300,9 @@ sudo usermod -aG redis git
### Clone the Source
# Clone GitLab repository
- sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 11-3-stable gitlab
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 11-4-stable gitlab
-**Note:** You can change `11-3-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+**Note:** You can change `11-4-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It
diff --git a/doc/topics/authentication/index.md b/doc/topics/authentication/index.md
index a645f65938f..9546f43eea8 100644
--- a/doc/topics/authentication/index.md
+++ b/doc/topics/authentication/index.md
@@ -23,7 +23,7 @@ This page gathers all the resources for the topic **Authentication** within GitL
- [How to Configure LDAP with GitLab CE](../../administration/auth/how_to_configure_ldap_gitlab_ce/index.md)
- [How to Configure LDAP with GitLab EE](https://docs.gitlab.com/ee/articles/how_to_configure_ldap_gitlab_ee/)
- [Feature Highlight: LDAP Integration](https://about.gitlab.com/2014/07/10/feature-highlight-ldap-sync/)
- - [Debugging LDAP](https://about.gitlab.com/handbook/support/workflows/ldap/debugging_ldap.html)
+ - [Debugging LDAP](https://about.gitlab.com/handbook/support/workflows/support-engineering/ldap/debugging_ldap.html)
- **Integrations:**
- [OmniAuth](../../integration/omniauth.md)
- [Authentiq OmniAuth Provider](../../administration/auth/authentiq.md#authentiq-omniauth-provider)
@@ -42,7 +42,7 @@ This page gathers all the resources for the topic **Authentication** within GitL
## Third-party resources
-- [Kanboard Plugin GitLab Authentication](https://kanboard.net/plugin/gitlab-auth)
+- [Kanboard Plugin GitLab Authentication](https://github.com/kanboard/plugin-gitlab-auth)
- [Jenkins GitLab OAuth Plugin](https://wiki.jenkins-ci.org/display/JENKINS/GitLab+OAuth+Plugin)
- [Set up Gitlab CE with Active Directory authentication](https://www.caseylabs.com/setup-gitlab-ce-with-active-directory-authentication/)
- [How to customize GitLab to support OpenID authentication](http://eric.van-der-vlist.com/blog/2013/11/23/how-to-customize-gitlab-to-support-openid-authentication/)
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index b5a9e469965..0d1ba3e8f9a 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -239,14 +239,19 @@ project's **Settings > CI/CD > Auto DevOps**.
The available options are:
-- **Continuous deployment to production** - enables [Auto Deploy](#auto-deploy)
- by setting the [`STAGING_ENABLED`](#deploy-policy-for-staging-and-production-environments) and
- [`INCREMENTAL_ROLLOUT_ENABLED`](#incremental-rollout-to-production) variables
- to false.
-- **Automatic deployment to staging, manual deployment to production** - sets the
+- **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
+ 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_ENABLED`](#incremental-rollout-to-production) variables
- to true, and the user is responsible for manually deploying to staging and production.
+ [`INCREMENTAL_ROLLOUT_MODE`](#incremental-rollout-to-production) variables
+ to `1` and `manual`. This means:
+
+ - `master` branch is directly deployed to staging.
+ - Manual actions are provided for incremental rollout to production.
## Stages of Auto DevOps
@@ -609,7 +614,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `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_ENABLED`| From GitLab 10.8, this variable can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment. |
+| `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> |
| `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. |
@@ -730,9 +735,8 @@ to use an incremental rollout to replace just a few pods with the latest code.
This will allow you to first check how the app is behaving, and later manually
increasing the rollout up to 100%.
-If `INCREMENTAL_ROLLOUT_ENABLED` is defined in your project (e.g., set
-`INCREMENTAL_ROLLOUT_ENABLED` to `1` as a secret variable), then instead of the
-standard `production` job, 4 different
+If `INCREMENTAL_ROLLOUT_MODE` is set to `manual` in your project, then instead
+of the standard `production` job, 4 different
[manual jobs](../../ci/pipelines.md#manual-actions-from-the-pipeline-graph)
will be created:
@@ -756,21 +760,45 @@ environment page.
Below, you can see how the pipeline will look if the rollout or staging
variables are defined.
-- **Without `INCREMENTAL_ROLLOUT_ENABLED` and without `STAGING_ENABLED`**
+Without `INCREMENTAL_ROLLOUT_MODE` and without `STAGING_ENABLED`:
+
+![Staging and rollout disabled](img/rollout_staging_disabled.png)
+
+Without `INCREMENTAL_ROLLOUT_MODE` and with `STAGING_ENABLED`:
- ![Staging and rollout disabled](img/rollout_staging_disabled.png)
+![Staging enabled](img/staging_enabled.png)
-- **Without `INCREMENTAL_ROLLOUT_ENABLED` and with `STAGING_ENABLED`**
+With `INCREMENTAL_ROLLOUT_MODE` set to `manual` and without `STAGING_ENABLED`:
- ![Staging enabled](img/staging_enabled.png)
+![Rollout enabled](img/rollout_enabled.png)
-- **With `INCREMENTAL_ROLLOUT_ENABLED` and without `STAGING_ENABLED`**
+With `INCREMENTAL_ROLLOUT_MODE` set to `manual` and with `STAGING_ENABLED`
+
+![Rollout and staging enabled](img/rollout_staging_enabled.png)
+
+CAUTION: **Caution:**
+Before GitLab 11.4 this feature was enabled by the presence of the
+`INCREMENTAL_ROLLOUT_ENABLED` environment variable.
+This configuration is deprecated and will be removed in the future.
+
+#### Timed incremental rollout to production **[PREMIUM]**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7545) in GitLab 11.4.
+
+TIP: **Tip:**
+You can also set this inside your [project's settings](#deployment-strategy).
- ![Rollout enabled](img/rollout_enabled.png)
+This configuration based on
+[incremental rollout to production](#incremental-rollout-to-production).
-- **With `INCREMENTAL_ROLLOUT_ENABLED` and with `STAGING_ENABLED`**
+Everything behaves the same way, except:
- ![Rollout and staging enabled](img/rollout_staging_enabled.png)
+- It's enabled by setting the `INCREMENTAL_ROLLOUT_MODE` variable to `timed`.
+- Instead of the standard `production` job, the following jobs with a 5 minute delay between each are created:
+ 1. `timed rollout 10%`
+ 1. `timed rollout 25%`
+ 1. `timed rollout 50%`
+ 1. `timed rollout 100%`
## Currently supported languages
diff --git a/doc/topics/autodevops/quick_start_guide.md b/doc/topics/autodevops/quick_start_guide.md
index 7ca441a2f74..b12e86cb0a9 100644
--- a/doc/topics/autodevops/quick_start_guide.md
+++ b/doc/topics/autodevops/quick_start_guide.md
@@ -15,7 +15,7 @@ need to ensure your own [Runners are configured](../../ci/runners/README.md) and
Before creating and connecting your Kubernetes cluster to your GitLab project,
you need a Google Cloud Platform account. If you don't already have one,
-sign up at https://console.cloud.google.com. You'll need to either sign in with an existing
+sign up at <https://console.cloud.google.com>. You'll need to either sign in with an existing
Google account (for example, one that you use to access Gmail, Drive, etc.) or create a new one.
1. Follow the steps as outlined in the ["Before you begin" section of the Kubernetes Engine docs](https://cloud.google.com/kubernetes-engine/docs/quickstart#before-you-begin)
@@ -205,7 +205,7 @@ applications. In the rightmost column for the production environment, you can ma
application is running.
Right below, there is the
-[Deploy Board](https://docs.gitlab.com/ee/user/project/deploy_boards.md).
+[Deploy Board](https://docs.gitlab.com/ee/user/project/deploy_boards.html).
The squares represent pods in your Kubernetes cluster that are associated with
the given environment. Hovering above each square you can see the state of a
deployment and clicking a square will take you to the pod's logs page.
@@ -264,8 +264,8 @@ Let's fix that:
to stage the changes.
1. Write a commit message and click **Commit**.
-Now, if you go back to the merge request you should not only see the test passing,
-but also the application deployed as a [review app](index.md#auto-review-apps). You
+Now, if you go back to the merge request you should not only see the test passing, but
+also the application deployed as a [review app](index.md#auto-review-apps). You
can visit it by following the URL in the merge request. The changes that we
previously made should be there.
diff --git a/doc/update/11.2-to-11-3.md b/doc/update/11.2-to-11.3.md
index d77f879ee57..d77f879ee57 100644
--- a/doc/update/11.2-to-11-3.md
+++ b/doc/update/11.2-to-11.3.md
diff --git a/doc/update/11.3-to-11.4.md b/doc/update/11.3-to-11.4.md
new file mode 100644
index 00000000000..985239369d7
--- /dev/null
+++ b/doc/update/11.3-to-11.4.md
@@ -0,0 +1,378 @@
+---
+comments: false
+---
+
+# From 11.3 to 11.4
+
+Make sure you view this update guide from the branch (version) of GitLab you would
+like to install (e.g., `11-4-stable`. You can select the branch in the version
+dropdown at the top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
+### 1. Stop server
+
+```bash
+sudo service gitlab stop
+```
+
+### 2. Backup
+
+NOTE: If you installed GitLab from source, make sure `rsync` is installed.
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Update Ruby
+
+NOTE: GitLab 11.0 and higher only support Ruby 2.4.x and dropped support for Ruby 2.3.x. Be
+sure to upgrade your interpreter if necessary.
+
+You can check which version you are running with `ruby -v`.
+
+Download Ruby and compile it:
+
+```bash
+mkdir /tmp/ruby && cd /tmp/ruby
+curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.4.tar.gz
+echo 'ec82b0d53bd0adad9b19e6b45e44d54e9ec3f10c ruby-2.4.4.tar.gz' | shasum -c - && tar xzf ruby-2.4.4.tar.gz
+cd ruby-2.4.4
+
+./configure --disable-install-rdoc
+make
+sudo make install
+```
+
+Install Bundler:
+
+```bash
+sudo gem install bundler --no-ri --no-rdoc
+```
+
+### 4. Update Node
+
+GitLab utilizes [webpack](http://webpack.js.org) to compile frontend assets.
+This requires a minimum version of node v6.0.0.
+
+You can check which version you are running with `node -v`. If you are running
+a version older than `v6.0.0` you will need to update to a newer version. You
+can find instructions to install from community maintained packages or compile
+from source at the nodejs.org website.
+
+<https://nodejs.org/en/download/>
+
+GitLab also requires the use of yarn `>= v1.2.0` to manage JavaScript
+dependencies.
+
+```bash
+curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
+echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
+sudo apt-get update
+sudo apt-get install yarn
+```
+
+More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
+
+### 5. Update Go
+
+NOTE: GitLab 11.0 and higher only supports Go 1.9.x and newer, and dropped support for Go
+1.5.x through 1.8.x. Be sure to upgrade your installation if necessary.
+
+You can check which version you are running with `go version`.
+
+Download and install Go:
+
+```bash
+# Remove former Go installation folder
+sudo rm -rf /usr/local/go
+
+curl --remote-name --progress https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz
+echo 'fa1b0e45d3b647c252f51f5e1204aba049cde4af177ef9f2181f43004f901035 go1.10.3.linux-amd64.tar.gz' | shasum -a256 -c - && \
+ sudo tar -C /usr/local -xzf go1.10.3.linux-amd64.tar.gz
+sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
+rm go1.10.3.linux-amd64.tar.gz
+```
+
+### 6. Get latest code
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git fetch --all --prune
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+sudo -u git -H git checkout -- locale
+```
+
+For GitLab Community Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 11-4-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 11-4-stable-ee
+```
+
+### 7. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION)
+sudo -u git -H bin/compile
+```
+
+### 8. Update gitlab-workhorse
+
+Install and compile gitlab-workhorse. GitLab-Workhorse uses
+[GNU Make](https://www.gnu.org/software/make/).
+If you are not using Linux you may have to run `gmake` instead of
+`make` below.
+
+```bash
+cd /home/git/gitlab-workhorse
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_WORKHORSE_VERSION)
+sudo -u git -H make
+```
+
+### 9. Update Gitaly
+
+#### New Gitaly configuration options required
+
+In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell`.
+
+```shell
+echo '
+[gitaly-ruby]
+dir = "/home/git/gitaly/ruby"
+
+[gitlab-shell]
+dir = "/home/git/gitlab-shell"
+' | sudo -u git tee -a /home/git/gitaly/config.toml
+```
+
+#### Check Gitaly configuration
+
+Due to a bug in the `rake gitlab:gitaly:install` script your Gitaly
+configuration file may contain syntax errors. The block name
+`[[storages]]`, which may occur more than once in your `config.toml`
+file, should be `[[storage]]` instead.
+
+```shell
+sudo -u git -H sed -i.pre-10.1 's/\[\[storages\]\]/[[storage]]/' /home/git/gitaly/config.toml
+```
+
+#### Compile Gitaly
+
+```shell
+cd /home/git/gitaly
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITALY_SERVER_VERSION)
+sudo -u git -H make
+```
+
+### 10. Update gitlab-pages
+
+#### Only needed if you use GitLab Pages.
+
+Install and compile gitlab-pages. GitLab-Pages uses
+[GNU Make](https://www.gnu.org/software/make/).
+If you are not using Linux you may have to run `gmake` instead of
+`make` below.
+
+```bash
+cd /home/git/gitlab-pages
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION)
+sudo -u git -H make
+```
+
+### 11. Update MySQL permissions
+
+If you are using MySQL you need to grant the GitLab user the necessary
+permissions on the database:
+
+```bash
+mysql -u root -p -e "GRANT TRIGGER ON \`gitlabhq_production\`.* TO 'git'@'localhost';"
+```
+
+If you use MySQL with replication, or just have MySQL configured with binary logging,
+you will need to also run the following on all of your MySQL servers:
+
+```bash
+mysql -u root -p -e "SET GLOBAL log_bin_trust_function_creators = 1;"
+```
+
+You can make this setting permanent by adding it to your `my.cnf`:
+
+```
+log_bin_trust_function_creators=1
+```
+
+### 12. Update configuration files
+
+#### New configuration options for `gitlab.yml`
+
+There might be configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/11-1-stable:config/gitlab.yml.example origin/11-4-stable:config/gitlab.yml.example
+```
+
+#### Nginx configuration
+
+Ensure you're still up-to-date with the latest NGINX configuration changes:
+
+```sh
+cd /home/git/gitlab
+
+# For HTTPS configurations
+git diff origin/11-1-stable:lib/support/nginx/gitlab-ssl origin/11-4-stable:lib/support/nginx/gitlab-ssl
+
+# For HTTP configurations
+git diff origin/11-1-stable:lib/support/nginx/gitlab origin/11-4-stable:lib/support/nginx/gitlab
+```
+
+If you are using Strict-Transport-Security in your installation to continue using it you must enable it in your Nginx
+configuration as GitLab application no longer handles setting it.
+
+If you are using Apache instead of NGINX please see the updated [Apache templates].
+Also note that because Apache does not support upstreams behind Unix sockets you
+will need to let gitlab-workhorse listen on a TCP port. You can do this
+via [/etc/default/gitlab].
+
+[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
+[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-4-stable/lib/support/init.d/gitlab.default.example#L38
+
+#### SMTP configuration
+
+If you're installing from source and use SMTP to deliver mail, you will need to add the following line
+to config/initializers/smtp_settings.rb:
+
+```ruby
+ActionMailer::Base.delivery_method = :smtp
+```
+
+See [smtp_settings.rb.sample] as an example.
+
+[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-4-stable/config/initializers/smtp_settings.rb.sample#L13
+
+#### Init script
+
+There might be new configuration options available for [`gitlab.default.example`][gl-example]. View them with the command below and apply them manually to your current `/etc/default/gitlab`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/11-1-stable:lib/support/init.d/gitlab.default.example origin/11-4-stable:lib/support/init.d/gitlab.default.example
+```
+
+Ensure you're still up-to-date with the latest init script changes:
+
+```bash
+cd /home/git/gitlab
+
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+```
+
+For Ubuntu 16.04.1 LTS:
+
+```bash
+sudo systemctl daemon-reload
+```
+
+### 13. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Optional: clean up old gems
+sudo -u git -H bundle clean
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Compile GetText PO files
+
+sudo -u git -H bundle exec rake gettext:compile RAILS_ENV=production
+
+# Update node dependencies and recompile assets
+sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
+
+# Clean up cache
+sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
+```
+
+**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md).
+
+### 14. Start application
+
+```bash
+sudo service gitlab start
+sudo service nginx restart
+```
+
+### 15. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+```
+
+To make sure you didn't miss anything run a more thorough check:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+```
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (11.3)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 11.2 to 11.3](11.2-to-11.3.md), except for the
+database migration (the backup is already migrated to the previous version).
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
+
+[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-4-stable/config/gitlab.yml.example
+[gl-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-4-stable/lib/support/init.d/gitlab.default.example
diff --git a/doc/user/admin_area/settings/terms.md b/doc/user/admin_area/settings/terms.md
index aa817c9a209..e2290bf0598 100644
--- a/doc/user/admin_area/settings/terms.md
+++ b/doc/user/admin_area/settings/terms.md
@@ -35,17 +35,17 @@ continue their registration afterwards.
## Accepting terms
-When this feature was enabled, the users that have not accepted the
+When this feature is enabled, the users that have not accepted the
terms of service will be presented with a screen where they can either
accept or decline the terms.
![Respond to terms](img/respond_to_terms.png)
-When the user accepts the terms, they will be directed to where they
+If the user accepts the terms, they will be directed to where they
were going. After a sign-in or sign-up this will most likely be the
dashboard.
-When the user was already logged in when the feature was turned on,
+If the user was already logged in when the feature was turned on,
they will be asked to accept the terms on their next interaction.
-When a user declines the terms, they will be signed out.
+If a user declines the terms, they will be signed out.
diff --git a/doc/user/img/color_inline_colorchip_render_gfm.png b/doc/user/img/color_inline_colorchip_render_gfm.png
new file mode 100644
index 00000000000..6a8a674d6e0
--- /dev/null
+++ b/doc/user/img/color_inline_colorchip_render_gfm.png
Binary files differ
diff --git a/doc/user/img/math_inline_sup_render_gfm.png b/doc/user/img/math_inline_sup_render_gfm.png
new file mode 100644
index 00000000000..bf1464457bc
--- /dev/null
+++ b/doc/user/img/math_inline_sup_render_gfm.png
Binary files differ
diff --git a/doc/user/img/mermaid_diagram_render_gfm.png b/doc/user/img/mermaid_diagram_render_gfm.png
new file mode 100644
index 00000000000..3b3eb3a738a
--- /dev/null
+++ b/doc/user/img/mermaid_diagram_render_gfm.png
Binary files differ
diff --git a/doc/user/img/task_list_ordered_render_gfm.png b/doc/user/img/task_list_ordered_render_gfm.png
new file mode 100644
index 00000000000..fdff8a9886c
--- /dev/null
+++ b/doc/user/img/task_list_ordered_render_gfm.png
Binary files differ
diff --git a/doc/user/img/unordered_check_list_render_gfm.png b/doc/user/img/unordered_check_list_render_gfm.png
new file mode 100644
index 00000000000..2e3fb7cbb79
--- /dev/null
+++ b/doc/user/img/unordered_check_list_render_gfm.png
Binary files differ
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index fb132f0613b..f9bdaea185b 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -112,8 +112,8 @@ GFM will autolink almost any URL you copy and paste into your text:
* https://www.google.com
* https://google.com/
* ftp://ftp.us.debian.org/debian/
-* smb://foo/bar/baz
-* irc://irc.freenode.net/gitlab
+* <a href="smb://foo/bar/baz">smb://foo/bar/baz</a>
+* <a href="irc://irc.freenode.net/gitlab">irc://irc.freenode.net/gitlab</a>
* http://localhost:3000
### Multiline Blockquote
@@ -138,17 +138,13 @@ you can quote that without having to manually prepend `>` to every line!
>>>
```
->>>
-If you paste a message from somewhere else
-
-that
-
-spans
-
-multiple lines,
-
-you can quote that without having to manually prepend `>` to every line!
->>>
+<blockquote dir="auto">
+<p>If you paste a message from somewhere else</p>
+<p>that</p>
+<p>spans</p>
+<p>multiple lines,</p>
+<p>you can quote that without having to manually prepend <code>&gt;</code> to every line!</p>
+</blockquote>
### Code and Syntax Highlighting
@@ -269,15 +265,15 @@ https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#emoji
Ubuntu 18.04 (like many modern Linux distros) has this font installed by default.
-Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you:
+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:
-:zap: You can use emoji anywhere GFM is supported. :v:
+<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">
-You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that.
+You can use it to point out a <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/bug.png" width="20px" height="20px"> or warn about <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/speak_no_evil.png" width="20px" height="20px"> patches. And if someone improves your really <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/snail.png" width="20px" height="20px"> code, send them some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/birthday.png" width="20px" height="20px">. People will <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/heart.png" width="20px" height="20px"> you for that.
-If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up one of the supported codes.
+If you are new to this, don't be <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/fearful.png" width="20px" height="20px">. You can easily join the emoji <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/family.png" width="20px" height="20px">. All you need to do is to look up one of the supported codes.
-Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
+Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/thumbsup.png" width="20px" height="20px">
Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support.
@@ -286,7 +282,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.
-
### Special GitLab References
GFM recognizes special references.
@@ -356,11 +351,7 @@ You can add task lists to issues, merge requests and comments. To create a task
- [ ] Sub-task 3
```
-- [x] Completed task
-- [ ] Incomplete task
- - [ ] Sub-task 1
- - [x] Sub-task 2
- - [ ] Sub-task 3
+![alt unordered-check-list-render-gfm](img/unordered_check_list_render_gfm.png)
Tasks formatted as ordered lists are supported as well:
@@ -371,10 +362,7 @@ Tasks formatted as ordered lists are supported as well:
1. [x] Sub-task 2
```
-1. [x] Completed task
-1. [ ] Incomplete task
- 1. [ ] Sub-task 1
- 1. [x] Sub-task 2
+![alt task-list-ordered-render-gfm](img/task_list_ordered_render_gfm.png)
Task lists can only be created in descriptions, not in titles. Task item state can be managed by editing the description's Markdown or by toggling the rendered check boxes.
@@ -393,7 +381,10 @@ The valid video extensions are `.mp4`, `.m4v`, `.mov`, `.webm`, and `.ogv`.
Here's a sample video:
-![Sample Video](img/markdown_video.mp4)
+<div class="video-container">
+ <video src="img/markdown_video.mp4" width="400" controls="true" data-setup="{}" data-title="Sample Video"></video>
+ <p><a href="img/markdown_video.mp4" target="_blank" rel="noopener noreferrer" title="Download 'Sample Video'">Sample Video</a></p>
+</div>
### Math
@@ -417,12 +408,11 @@ Example:
Becomes:
-This math is inline $`a^2+b^2=c^2`$.
+This math is inline ![alt text](img/math_inline_sup_render_gfm.png).
This is on a separate line
-```math
-a^2+b^2=c^2
-```
+
+<div align="center"><img src="./img/math_inline_sup_render_gfm.png" ></div>
_Be advised that KaTeX only supports a [subset][katex-subset] of LaTeX._
@@ -452,15 +442,7 @@ Examples:
Become:
-`#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)`
+![alt color-inline-colorchip-render-gfm](img/color_inline_colorchip_render_gfm.png)
#### Supported formats:
@@ -492,13 +474,7 @@ Example:
Becomes:
-```mermaid
-graph TD;
- A-->B;
- A-->C;
- B-->D;
- C-->D;
-```
+<img src="./img/mermaid_diagram_render_gfm.png" width="200px" height="400px">
For details see the [Mermaid official page][mermaid].
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 8369cff2386..4359592905d 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -42,6 +42,8 @@ The following table depicts the various user permission levels in a project.
| See a job log | ✓ [^3] | ✓ | ✓ | ✓ | ✓ |
| Download and browse job artifacts | ✓ [^3] | ✓ | ✓ | ✓ | ✓ |
| View wiki pages | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
+| View license management reports **[ULTIMATE]** | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
+| View Security reports **[ULTIMATE]** | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
| Pull project code | [^1] | ✓ | ✓ | ✓ | ✓ |
| Download project | [^1] | ✓ | ✓ | ✓ | ✓ |
| Assign issues | | ✓ | ✓ | ✓ | ✓ |
@@ -57,6 +59,7 @@ The following table depicts the various user permission levels in a project.
| See a list of merge requests | | ✓ | ✓ | ✓ | ✓ |
| Manage related issues **[STARTER]** | | ✓ | ✓ | ✓ | ✓ |
| Lock issue discussions | | ✓ | ✓ | ✓ | ✓ |
+| Create issue from vulnerability **[ULTIMATE]** | | ✓ | ✓ | ✓ | ✓ |
| Lock merge request discussions | | | ✓ | ✓ | ✓ |
| Create new environments | | | ✓ | ✓ | ✓ |
| Stop environments | | | ✓ | ✓ | ✓ |
@@ -73,6 +76,9 @@ The following table depicts the various user permission levels in a project.
| Update a container registry | | | ✓ | ✓ | ✓ |
| Remove a container registry image | | | ✓ | ✓ | ✓ |
| Create/edit/delete project milestones | | | ✓ | ✓ | ✓ |
+| View approved/blacklisted licenses **[ULTIMATE]** | | | ✓ | ✓ | ✓ |
+| Use security dashboard **[ULTIMATE]** | | | ✓ | ✓ | ✓ |
+| Dismiss vulnerability **[ULTIMATE]** | | | ✓ | ✓ | ✓ |
| Use environment terminals | | | | ✓ | ✓ |
| Add new team members | | | | ✓ | ✓ |
| Push to protected branches | | | | ✓ | ✓ |
@@ -90,6 +96,7 @@ The following table depicts the various user permission levels in a project.
| Manage GitLab Pages domains and certificates | | | | ✓ | ✓ |
| Remove GitLab Pages | | | | | ✓ |
| Manage clusters | | | | ✓ | ✓ |
+| Manage license policy **[ULTIMATE]** | | | | ✓ | ✓ |
| Edit comments (posted by any user) | | | | ✓ | ✓ |
| Switch visibility level | | | | | ✓ |
| Transfer project to another namespace | | | | | ✓ |
@@ -98,7 +105,7 @@ The following table depicts the various user permission levels in a project.
| Remove pages | | | | | ✓ |
| Force push to protected branches [^4] | | | | | |
| Remove protected branches [^4] | | | | | |
-| View project Audit Events | | | | ✓ | ✓ |
+| View project Audit Events | | | | ✓ | ✓ |
## Project features permissions
@@ -110,6 +117,7 @@ which visibility level you select on project settings.
- Disabled: disabled for everyone
- Only team members: only team members will see even if your project is public or internal
- Everyone with access: everyone can see depending on your project visibility level
+- Everyone: enabled for everyone (only available for GitLab Pages)
### Protected branches
@@ -242,6 +250,7 @@ which visibility level you select on project settings.
- Disabled: disabled for everyone
- Only team members: only team members will see even if your project is public or internal
- Everyone with access: everyone can see depending on your project visibility level
+- Everyone: enabled for everyone (only available for GitLab Pages)
## GitLab CI/CD permissions
diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md
index e5411662511..bc6ecdf4f32 100644
--- a/doc/user/profile/account/two_factor_authentication.md
+++ b/doc/user/profile/account/two_factor_authentication.md
@@ -2,18 +2,18 @@
Two-factor Authentication (2FA) provides an additional level of security to your
GitLab account. Once enabled, in addition to supplying your username and
-password to login, you'll be prompted for a code generated by an application on
-your phone.
+password to login, you'll be prompted for a code generated by your one time password
+authenticator. For example, a password manager on one of your devices.
By enabling 2FA, the only way someone other than you can log into your account
-is to know your username and password *and* have access to your phone.
+is to know your username and password *and* have access to your one time password secret.
## Overview
> **Note:**
When you enable 2FA, don't forget to back up your recovery codes.
-In addition to a phone application, GitLab supports U2F (universal 2nd factor) devices as
+In addition to one time authenticators (TOTP), GitLab supports U2F (universal 2nd factor) devices as
the second factor of authentication. Once enabled, in addition to supplying your username and
password to login, you'll be prompted to activate your U2F device (usually by pressing
a button on it), and it will perform secure authentication on your behalf.
@@ -24,10 +24,10 @@ from other browsers.
## Enabling 2FA
-There are two ways to enable two-factor authentication: via a mobile application
+There are two ways to enable two-factor authentication: via a one time password authenticator
or a U2F device.
-### Enable 2FA via mobile application
+### Enable 2FA via one time password authenticator
**In GitLab:**
@@ -82,7 +82,7 @@ Click on **Register U2F Device** to complete the process.
> **Note:**
Recovery codes are not generated for U2F devices.
-Should you ever lose access to your phone, you can use one of the ten provided
+Should you ever lose access to your one time password authenticator, you can use one of the ten provided
backup codes to login to your account. We suggest copying or printing them for
storage in a safe place. **Each code can be used only once** to log in to your
account.
@@ -98,7 +98,7 @@ be presented with a second prompt, depending on which type of 2FA you've enabled
### Log in via mobile application
-Enter the pin from your phone's application or a recovery code to log in.
+Enter the pin from your one time password authenticator's application or a recovery code to log in.
![Two-Factor Authentication on sign in via OTP](img/2fa_auth.png)
diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md
index 8604ea27f99..ab62762f343 100644
--- a/doc/user/profile/index.md
+++ b/doc/user/profile/index.md
@@ -115,6 +115,13 @@ Please be aware that your status is publicly visible even if your [profile is pr
To set your current status:
+1. Open the user menu in the top-right corner of the navigation bar.
+1. Hit **Set status**, or **Edit status** if you have already set a status.
+1. Set the emoji and/or status message to your liking.
+1. Hit **Set status**. Alternatively, you can also hit **Remove status** to remove your user status entirely.
+
+or
+
1. Navigate to your personal [profile settings](#profile-settings).
1. In the text field below `Your status`, enter your status message.
1. Select an emoji from the dropdown if you like.
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index 41768998a59..3ec17806490 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -134,36 +134,11 @@ authorization is [experimental](#role-based-access-control-rbac).
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21401) in GitLab 11.4.
CAUTION: **Warning:**
-The RBAC authorization is experimental. To enable it you need access to the
-server where GitLab is installed.
+The RBAC authorization is experimental.
-The support for RBAC-enabled clusters is hidden behind a feature flag. Once
-the feature flag is enabled, GitLab will create the necessary service accounts
+Once RBAC is enabled for a cluster, GitLab will create the necessary service accounts
and privileges in order to install and run [GitLab managed applications](#installing-applications).
-To enable the feature flag:
-
-1. SSH into the server where GitLab is installed.
-1. Enter the Rails console:
-
- **For Omnibus GitLab**
-
- ```sh
- sudo gitlab-rails console
- ```
-
- **For installations from source**
-
- ```sh
- sudo -u git -H bundle exec rails console
- ```
-
-1. Enable the RBAC authorization:
-
- ```ruby
- Feature.enable('rbac_clusters')
- ```
-
If you are creating a [new GKE cluster via
GitLab](#adding-and-creating-a-new-gke-cluster-via-gitlab), you will be
asked if you would like to create an RBAC-enabled cluster. Enabling this
@@ -240,7 +215,7 @@ twice, which can lead to confusion during deployments.
| [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) | 10.2+ | Ingress can provide load balancing, SSL termination, and name-based virtual hosting. It acts as a web proxy for your applications and is useful if you want to use [Auto DevOps] or deploy your own web apps. | [stable/nginx-ingress](https://github.com/helm/charts/tree/master/stable/nginx-ingress) |
| [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications. | [stable/prometheus](https://github.com/helm/charts/tree/master/stable/prometheus) |
| [GitLab Runner](https://docs.gitlab.com/runner/) | 10.6+ | GitLab Runner is the open source project that is used to run your jobs and send the results back to GitLab. It is used in conjunction with [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/), the open-source continuous integration service included with GitLab that coordinates the jobs. When installing the GitLab Runner via the applications, it will run in **privileged mode** by default. Make sure you read the [security implications](#security-implications) before doing so. | [runner/gitlab-runner](https://gitlab.com/charts/gitlab-runner) |
-| [JupyterHub](http://jupyter.org/) | 11.0+ | [JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a multi-user service for managing notebooks across a team. [Jupyter Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/) provide a web-based interactive programming environment used for data analysis, visualization, and machine learning. We use [this](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile) custom Jupyter image that installs additional useful packages on top of the base Jupyter. **Note**: Authentication will be enabled for any user of the GitLab server via OAuth2. HTTPS will be supported in a future release. | [jupyter/jupyterhub](https://jupyterhub.github.io/helm-chart/) |
+| [JupyterHub](http://jupyter.org/) | 11.0+ | [JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a multi-user service for managing notebooks across a team. [Jupyter Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/) provide a web-based interactive programming environment used for data analysis, visualization, and machine learning. We use [this](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile) custom Jupyter image that installs additional useful packages on top of the base Jupyter. You will also see ready-to-use DevOps Runbooks built with [Rubix](https://github.com/amit1rrr/rubix). **Note**: Authentication will be enabled for any user of the GitLab server via OAuth2. HTTPS will be supported in a future release. | [jupyter/jupyterhub](https://jupyterhub.github.io/helm-chart/) |
## Getting the external IP address
diff --git a/doc/user/project/import/svn.md b/doc/user/project/import/svn.md
index 16bc5121027..a5923986292 100644
--- a/doc/user/project/import/svn.md
+++ b/doc/user/project/import/svn.md
@@ -29,7 +29,7 @@ directly in a filesystem level.
1. Install Oracle JRE 1.8 or newer. On Debian-based Linux distributions you can
follow [this article](http://www.webupd8.org/2012/09/install-oracle-java-8-in-ubuntu-via-ppa.html).
-1. Download SubGit from https://subgit.com/download/.
+1. Download SubGit from <https://subgit.com/download/>.
1. Unpack the downloaded SubGit zip archive to the `/opt` directory. The `subgit`
command will be available at `/opt/subgit-VERSION/bin/subgit`.
@@ -71,7 +71,7 @@ edit $GIT_REPO_PATH/subgit/config
```
For more information regarding the SubGit configuration options, refer to
-[SubGit's documentation](https://subgit.com/documentation.html) website.
+[SubGit's documentation](https://subgit.com/documentation/) website.
### Initial translation
@@ -97,7 +97,7 @@ subgit import $GIT_REPO_PATH
### SubGit licensing
Running SubGit in a mirror mode requires a
-[registration](https://subgit.com/pricing.html). Registration is free for open
+[registration](https://subgit.com/pricing/). Registration is free for open
source, academic and startup projects.
We're currently working on deeper GitLab/SubGit integration. You may track our
@@ -179,5 +179,6 @@ git push --tags origin
```
## Contribute to this guide
+
We welcome all contributions that would expand this guide with instructions on
how to migrate from SVN and other version control systems.
diff --git a/doc/user/project/issues/create_new_issue.md b/doc/user/project/issues/create_new_issue.md
index 1688edc1ee2..c33d1365001 100644
--- a/doc/user/project/issues/create_new_issue.md
+++ b/doc/user/project/issues/create_new_issue.md
@@ -58,3 +58,21 @@ body becomes the issue description. [Markdown] and [quick actions] are
supported.
![Bottom of a project issues page](img/new_issue_from_email.png)
+
+## New issue via URL with prefilled fields
+
+You can link directly to the new issue page for a given project, with prefilled
+field values using query string parameters in a URL. This is useful for embedding
+a URL in an external HTML page, and also certain scenarios where you want the user to
+create an issue with certain fields prefilled.
+
+The title, description, and description template fields can be prefilled using
+this method. The description and description template fields cannot be pre-entered
+in the same URL (since a description template just populates the description field).
+
+Follow these examples to form your new issue URL with prefilled fields.
+
+- For a new issue in the GitLab Community Edition project with a pre-entered title
+and a pre-entered description, the URL would be `https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issue[title]=Validate%20new%20concept&issue[description]=Research%20idea`
+- For a new issue in the GitLab Community Edition project with a pre-entered title
+and a pre-entered description template, the URL would be `https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issue[title]=Validate%20new%20concept&issuable_template=Research%20proposal`
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index df045822740..c2f53540089 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -1,9 +1,9 @@
# GitLab quick actions
-Quick actions are textual shortcuts for common actions on issues, epics, merge requests,
+Quick actions are textual shortcuts for common actions on issues, epics, merge requests,
and commits that are usually done by clicking buttons or dropdowns in GitLab's UI.
You can enter these commands while creating a new issue or merge request, or
-in comments of issues, epics, merge requests, and commits. Each command should be
+in comments of issues, epics, merge requests, and commits. Each command should be
on a separate line in order to be properly detected and executed. Once executed,
the commands are removed from the text body and not visible to anyone else.
@@ -38,7 +38,9 @@ discussions, and descriptions:
| `/remove_estimate` | Remove time estimate | ✓ | ✓ |
| <code>/spend &lt;time(1h 30m &#124; -1h 5m)&gt; &lt;date(YYYY-MM-DD)&gt;</code> | Add or subtract spent time; optionally, specify the date that time was spent on | ✓ | ✓ |
| `/remove_time_spent` | Remove time spent | ✓ | ✓ |
-| <code>/due &lt;in 2 days &#124; this Friday &#124; December 31st&gt;</code>| Set due date | ✓ |
+| `/lock` | Lock the discussion | ✓ | ✓ |
+| `/unlock` | Unlock the discussion | ✓ | ✓ |
+| <code>/due &lt;in 2 days &#124; this Friday &#124; December 31st&gt;</code>| Set due date | ✓ | |
| `/remove_due_date` | Remove due date | ✓ | |
| `/weight 0,1,2, ...` | Set weight **[STARTER]** | ✓ | |
| `/clear_weight` | Clears weight **[STARTER]** | ✓ | |
@@ -68,7 +70,7 @@ The following quick actions are applicable for epics threads and description:
|:---------------------------|:----------------------------------------|
| `/tableflip <Comment>` | Append the comment with `(╯°□°)╯︵ â”»â”â”»` |
| `/shrug <Comment>` | Append the comment with `¯\_(ツ)_/¯` |
-| `/todo` | Add a todo |
+| `/todo` | Add a todo |
| `/done` | Mark todo as done |
| `/subscribe` | Subscribe |
| `/unsubscribe` | Unsubscribe |
@@ -78,4 +80,4 @@ The following quick actions are applicable for epics threads and description:
| `/award :emoji:` | Toggle emoji award |
| `/label ~label1 ~label2` | Add label(s) |
| `/unlabel ~label1 ~label2` | Remove all or specific label(s) |
-| `/relabel ~label1 ~label2` | Replace label | \ No newline at end of file
+| `/relabel ~label1 ~label2` | Replace label |
diff --git a/doc/user/project/repository/gpg_signed_commits/index.md b/doc/user/project/repository/gpg_signed_commits/index.md
index 4f076ee01b8..c6239c8e41c 100644
--- a/doc/user/project/repository/gpg_signed_commits/index.md
+++ b/doc/user/project/repository/gpg_signed_commits/index.md
@@ -57,7 +57,7 @@ started:
gpg --full-gen-key
```
-_NOTE: In some cases like Gpg4win on Windows and other Mac OS versions the command here may be ` gpg --gen-key`_
+ _NOTE: In some cases like Gpg4win on Windows and other Mac OS versions the command here may be ` gpg --gen-key`_
This will spawn a series of questions.
diff --git a/lib/after_commit_queue.rb b/lib/after_commit_queue.rb
index a4d8507960e..6fb7985f955 100644
--- a/lib/after_commit_queue.rb
+++ b/lib/after_commit_queue.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module AfterCommitQueue
extend ActiveSupport::Concern
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 06c8b48b8cc..c49c52213bf 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -132,6 +132,7 @@ module API
mount ::API::Projects
mount ::API::ProjectSnapshots
mount ::API::ProjectSnippets
+ mount ::API::ProjectTemplates
mount ::API::ProtectedBranches
mount ::API::ProtectedTags
mount ::API::Repositories
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index ff927d1aa3c..e59abd3e3d0 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -98,6 +98,7 @@ module API
optional :start_branch, type: String, desc: 'Name of the branch to start the new commit from'
optional :author_email, type: String, desc: 'Author email for commit'
optional :author_name, type: String, desc: 'Author name for commit'
+ optional :stats, type: Boolean, default: true, desc: 'Include commit stats'
end
post ':id/repository/commits' do
authorize_push_to_branch!(params[:branch])
@@ -113,7 +114,7 @@ module API
Gitlab::WebIdeCommitsCounter.increment if find_user_from_warden
- present commit_detail, with: Entities::CommitDetail
+ present commit_detail, with: Entities::CommitDetail, stats: params[:stats]
else
render_api_error!(result[:message], 400)
end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index a78a93cbfd9..120545792f2 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -1221,6 +1221,7 @@ module API
end
class TemplatesList < Grape::Entity
+ expose :key
expose :name
end
diff --git a/lib/api/markdown.rb b/lib/api/markdown.rb
index 50d8a1ac596..de77bef43ce 100644
--- a/lib/api/markdown.rb
+++ b/lib/api/markdown.rb
@@ -12,7 +12,8 @@ module API
detail "This feature was introduced in GitLab 11.0."
end
post do
- context = { only_path: false }
+ context = { only_path: false, current_user: current_user }
+ context[:pipeline] = params[:gfm] ? :full : :plain_markdown
if params[:project]
project = Project.find_by_full_path(params[:project])
@@ -24,9 +25,7 @@ module API
context[:skip_project_check] = true
end
- context[:pipeline] = params[:gfm] ? :full : :plain_markdown
-
- { html: Banzai.render(params[:text], context) }
+ { html: Banzai.render_and_post_process(params[:text], context) }
end
end
end
diff --git a/lib/api/project_templates.rb b/lib/api/project_templates.rb
new file mode 100644
index 00000000000..d05ddad7466
--- /dev/null
+++ b/lib/api/project_templates.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+module API
+ class ProjectTemplates < Grape::API
+ include PaginationParams
+
+ TEMPLATE_TYPES = %w[dockerfiles gitignores gitlab_ci_ymls licenses].freeze
+
+ before { authenticate_non_get! }
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ requires :type, type: String, values: TEMPLATE_TYPES, desc: 'The type (dockerfiles|gitignores|gitlab_ci_ymls|licenses) of the template'
+ end
+ resource :projects do
+ desc 'Get a list of templates available to this project' do
+ detail 'This endpoint was introduced in GitLab 11.4'
+ end
+ params do
+ use :pagination
+ end
+ get ':id/templates/:type' do
+ templates = TemplateFinder
+ .build(params[:type], user_project)
+ .execute
+
+ present paginate(::Kaminari.paginate_array(templates)), with: Entities::TemplatesList
+ end
+
+ desc 'Download a template available to this project' do
+ detail 'This endpoint was introduced in GitLab 11.4'
+ end
+ params do
+ requires :name, type: String, desc: 'The name of the template'
+
+ optional :project, type: String, desc: 'The project name to use when expanding placeholders in the template. Only affects licenses'
+ optional :fullname, type: String, desc: 'The full name of the copyright holder to use when expanding placeholders in the template. Only affects licenses'
+ end
+ get ':id/templates/:type/:name', requirements: { name: /[\w\.-]+/ } do
+ template = TemplateFinder
+ .build(params[:type], user_project, name: params[:name])
+ .execute
+
+ not_found!('Template') unless template.present?
+
+ template.resolve!(
+ project_name: params[:project].presence,
+ fullname: params[:fullname].presence || current_user&.name
+ )
+
+ if template.is_a?(::LicenseTemplate)
+ present template, with: Entities::License
+ else
+ present template, with: Entities::Template
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 00bad49ebdc..ae2d327e45b 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -287,6 +287,12 @@ module API
present_projects forks
end
+ desc 'Check pages access of this project'
+ get ':id/pages_access' do
+ authorize! :read_pages_content, user_project unless user_project.public_pages?
+ status 200
+ end
+
desc 'Update an existing project' do
success Entities::Project
end
diff --git a/lib/api/runners.rb b/lib/api/runners.rb
index 60868821810..ce70460af11 100644
--- a/lib/api/runners.rb
+++ b/lib/api/runners.rb
@@ -113,7 +113,7 @@ module API
optional :status, type: String, desc: 'Status of the job', values: Ci::Build::AVAILABLE_STATUSES
use :pagination
end
- get ':id/jobs' do
+ get ':id/jobs' do
runner = get_runner(params[:id])
authenticate_list_runners_jobs!(runner)
diff --git a/lib/api/templates.rb b/lib/api/templates.rb
index 8ff3b2ac33c..8dab19d50c2 100644
--- a/lib/api/templates.rb
+++ b/lib/api/templates.rb
@@ -35,7 +35,7 @@ module API
popular = declared(params)[:popular]
popular = to_boolean(popular) if popular.present?
- templates = TemplateFinder.build(:licenses, popular: popular).execute
+ templates = TemplateFinder.build(:licenses, nil, popular: popular).execute
present paginate(::Kaminari.paginate_array(templates)), with: ::API::Entities::License
end
@@ -48,8 +48,7 @@ module API
requires :name, type: String, desc: 'The name of the template'
end
get "templates/licenses/:name", requirements: { name: /[\w\.-]+/ } do
- templates = TemplateFinder.build(:licenses).execute
- template = templates.find { |template| template.key == params[:name] }
+ template = TemplateFinder.build(:licenses, nil, name: params[:name]).execute
not_found!('License') unless template.present?
@@ -72,7 +71,7 @@ module API
use :pagination
end
get "templates/#{template_type}" do
- templates = ::Kaminari.paginate_array(TemplateFinder.new(template_type).execute)
+ templates = ::Kaminari.paginate_array(TemplateFinder.build(template_type, nil).execute)
present paginate(templates), with: Entities::TemplatesList
end
@@ -84,7 +83,7 @@ module API
requires :name, type: String, desc: 'The name of the template'
end
get "templates/#{template_type}/:name" do
- finder = TemplateFinder.build(template_type, name: declared(params)[:name])
+ finder = TemplateFinder.build(template_type, nil, name: declared(params)[:name])
new_template = finder.execute
render_response(template_type, new_template)
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 11a7f4ef64d..501c5cf1df3 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -256,7 +256,7 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
- desc 'Get the SSH keys of a specified user. Available only for admins.' do
+ desc 'Get the SSH keys of a specified user.' do
success Entities::SSHKey
end
params do
@@ -265,10 +265,8 @@ module API
end
# rubocop: disable CodeReuse/ActiveRecord
get ':id/keys' do
- authenticated_as_admin!
-
user = User.find_by(id: params[:id])
- not_found!('User') unless user
+ not_found!('User') unless user && can?(current_user, :read_user, user)
present paginate(user.keys), with: Entities::SSHKey
end
diff --git a/lib/backup.rb b/lib/backup.rb
index e2c62af23ae..2712b33b4b4 100644
--- a/lib/backup.rb
+++ b/lib/backup.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Backup
Error = Class.new(StandardError)
end
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index 5d4a7efc456..afdc6f383c1 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -243,6 +243,7 @@ module Backup
backup_created_at: Time.now,
gitlab_version: Gitlab::VERSION,
tar_version: tar_version,
+ installation_type: Gitlab::INSTALLATION_TYPE,
skipped: ENV["SKIP"]
}
end
diff --git a/lib/banzai.rb b/lib/banzai.rb
index 5df98f66f3b..1eb41ff7133 100644
--- a/lib/banzai.rb
+++ b/lib/banzai.rb
@@ -1,4 +1,13 @@
+# frozen_string_literal: true
+
module Banzai
+ # if you need to render markdown, then you probably need to post_process as well,
+ # such as removing references that the current user doesn't have
+ # permission to make
+ def self.render_and_post_process(text, context = {})
+ post_process(render(text, context), context)
+ end
+
def self.render(text, context = {})
Renderer.render(text, context)
end
diff --git a/lib/banzai/color_parser.rb b/lib/banzai/color_parser.rb
index 355c364b07b..6d01d51955c 100644
--- a/lib/banzai/color_parser.rb
+++ b/lib/banzai/color_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ColorParser
ALPHA = /0(?:\.\d+)?|\.\d+|1(?:\.0+)?/ # 0.0..1.0
diff --git a/lib/banzai/commit_renderer.rb b/lib/banzai/commit_renderer.rb
index c351a155ae5..f346151a3c1 100644
--- a/lib/banzai/commit_renderer.rb
+++ b/lib/banzai/commit_renderer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module CommitRenderer
ATTRIBUTES = [:description, :title].freeze
diff --git a/lib/banzai/cross_project_reference.rb b/lib/banzai/cross_project_reference.rb
index 3f1e95d4cc0..b7344808989 100644
--- a/lib/banzai/cross_project_reference.rb
+++ b/lib/banzai/cross_project_reference.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
# Common methods for ReferenceFilters that support an optional cross-project
# reference.
@@ -13,6 +15,7 @@ module Banzai
# Returns a Project, or nil if the reference can't be found
def parent_from_ref(ref)
return context[:project] || context[:group] unless ref
+ return context[:project] if context[:project]&.full_path == ref
Project.find_by_full_path(ref)
end
diff --git a/lib/banzai/filter.rb b/lib/banzai/filter.rb
index 3eb544dfef9..7d9766c906c 100644
--- a/lib/banzai/filter.rb
+++ b/lib/banzai/filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
def self.[](name)
diff --git a/lib/banzai/filter/epic_reference_filter.rb b/lib/banzai/filter/epic_reference_filter.rb
index e06e2fb3870..26bcf5c04b4 100644
--- a/lib/banzai/filter/epic_reference_filter.rb
+++ b/lib/banzai/filter/epic_reference_filter.rb
@@ -9,6 +9,12 @@ module Banzai
def self.object_class
Epic
end
+
+ private
+
+ def group
+ context[:group] || context[:project]&.group
+ end
end
end
end
diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb
index b92e9e55bb9..04ec38209c7 100644
--- a/lib/banzai/filter/label_reference_filter.rb
+++ b/lib/banzai/filter/label_reference_filter.rb
@@ -48,7 +48,7 @@ module Banzai
include_ancestor_groups: true,
only_group_labels: true }
else
- { project_id: parent.id,
+ { project: parent,
include_ancestor_groups: true }
end
diff --git a/lib/banzai/filter/markdown_engines/common_mark.rb b/lib/banzai/filter/markdown_engines/common_mark.rb
index dbb25280849..e52c0d15b31 100644
--- a/lib/banzai/filter/markdown_engines/common_mark.rb
+++ b/lib/banzai/filter/markdown_engines/common_mark.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# `CommonMark` markdown engine for GitLab's Banzai markdown filter.
# This module is used in Banzai::Filter::MarkdownFilter.
# Used gem is `commonmarker` which is a ruby wrapper for libcmark (CommonMark parser)
diff --git a/lib/banzai/filter/markdown_engines/redcarpet.rb b/lib/banzai/filter/markdown_engines/redcarpet.rb
index ac99941fefa..ec150d041ff 100644
--- a/lib/banzai/filter/markdown_engines/redcarpet.rb
+++ b/lib/banzai/filter/markdown_engines/redcarpet.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# `Redcarpet` markdown engine for GitLab's Banzai markdown filter.
# This module is used in Banzai::Filter::MarkdownFilter.
# Used gem is `redcarpet` which is a ruby library for markdown processing.
diff --git a/lib/banzai/filter/wiki_link_filter/rewriter.rb b/lib/banzai/filter/wiki_link_filter/rewriter.rb
index 4bf80aff418..f4cc8beeb52 100644
--- a/lib/banzai/filter/wiki_link_filter/rewriter.rb
+++ b/lib/banzai/filter/wiki_link_filter/rewriter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class WikiLinkFilter < HTML::Pipeline::Filter
diff --git a/lib/banzai/filter_array.rb b/lib/banzai/filter_array.rb
index 77835a14027..818af4643a7 100644
--- a/lib/banzai/filter_array.rb
+++ b/lib/banzai/filter_array.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
class FilterArray < Array
# Insert a value immediately after another value
diff --git a/lib/banzai/issuable_extractor.rb b/lib/banzai/issuable_extractor.rb
index ae7dc71e7eb..0a05d46db4c 100644
--- a/lib/banzai/issuable_extractor.rb
+++ b/lib/banzai/issuable_extractor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
# Extract references to issuables from multiple documents
diff --git a/lib/banzai/object_renderer.rb b/lib/banzai/object_renderer.rb
index a176f1e261b..75661ffa233 100644
--- a/lib/banzai/object_renderer.rb
+++ b/lib/banzai/object_renderer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
# Class for rendering multiple objects (e.g. Note instances) in a single pass,
# using +render_field+ to benefit from caching in the database. Rendering and
@@ -38,6 +40,7 @@ module Banzai
redacted_data = redacted[index]
object.__send__("redacted_#{attribute}_html=", redacted_data[:document].to_html(save_options).html_safe) # rubocop:disable GitlabSecurity/PublicSend
object.user_visible_reference_count = redacted_data[:visible_reference_count] if object.respond_to?(:user_visible_reference_count)
+ object.total_reference_count = redacted_data[:total_reference_count] if object.respond_to?(:total_reference_count)
end
end
diff --git a/lib/banzai/pipeline.rb b/lib/banzai/pipeline.rb
index 142a9962eb1..e8a81bebaa9 100644
--- a/lib/banzai/pipeline.rb
+++ b/lib/banzai/pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
def self.[](name)
diff --git a/lib/banzai/pipeline/ascii_doc_pipeline.rb b/lib/banzai/pipeline/ascii_doc_pipeline.rb
index 1048b927cd3..cc4af280872 100644
--- a/lib/banzai/pipeline/ascii_doc_pipeline.rb
+++ b/lib/banzai/pipeline/ascii_doc_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class AsciiDocPipeline < BasePipeline
diff --git a/lib/banzai/pipeline/atom_pipeline.rb b/lib/banzai/pipeline/atom_pipeline.rb
index 9694e4bc23f..13a342351b6 100644
--- a/lib/banzai/pipeline/atom_pipeline.rb
+++ b/lib/banzai/pipeline/atom_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class AtomPipeline < FullPipeline
diff --git a/lib/banzai/pipeline/base_pipeline.rb b/lib/banzai/pipeline/base_pipeline.rb
index 3ae3bed570d..87d1cf9912f 100644
--- a/lib/banzai/pipeline/base_pipeline.rb
+++ b/lib/banzai/pipeline/base_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class BasePipeline
diff --git a/lib/banzai/pipeline/broadcast_message_pipeline.rb b/lib/banzai/pipeline/broadcast_message_pipeline.rb
index 5dd572de3a1..a3d63e0aaf5 100644
--- a/lib/banzai/pipeline/broadcast_message_pipeline.rb
+++ b/lib/banzai/pipeline/broadcast_message_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class BroadcastMessagePipeline < DescriptionPipeline
diff --git a/lib/banzai/pipeline/combined_pipeline.rb b/lib/banzai/pipeline/combined_pipeline.rb
index 60190f8d9dd..56b424dc8e0 100644
--- a/lib/banzai/pipeline/combined_pipeline.rb
+++ b/lib/banzai/pipeline/combined_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
module CombinedPipeline
diff --git a/lib/banzai/pipeline/commit_description_pipeline.rb b/lib/banzai/pipeline/commit_description_pipeline.rb
index 607c2731ed3..e8ec7453f0f 100644
--- a/lib/banzai/pipeline/commit_description_pipeline.rb
+++ b/lib/banzai/pipeline/commit_description_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class CommitDescriptionPipeline < SingleLinePipeline
diff --git a/lib/banzai/pipeline/description_pipeline.rb b/lib/banzai/pipeline/description_pipeline.rb
index 042fb2e6e14..d5ff9b025cc 100644
--- a/lib/banzai/pipeline/description_pipeline.rb
+++ b/lib/banzai/pipeline/description_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class DescriptionPipeline < FullPipeline
diff --git a/lib/banzai/pipeline/email_pipeline.rb b/lib/banzai/pipeline/email_pipeline.rb
index 8f5f144d582..2c08581ce0d 100644
--- a/lib/banzai/pipeline/email_pipeline.rb
+++ b/lib/banzai/pipeline/email_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class EmailPipeline < FullPipeline
diff --git a/lib/banzai/pipeline/full_pipeline.rb b/lib/banzai/pipeline/full_pipeline.rb
index 3c974f73176..a5b1cbdd030 100644
--- a/lib/banzai/pipeline/full_pipeline.rb
+++ b/lib/banzai/pipeline/full_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class FullPipeline < CombinedPipeline.new(PlainMarkdownPipeline, GfmPipeline)
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index bd34614f149..be75e34a673 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class GfmPipeline < BasePipeline
diff --git a/lib/banzai/pipeline/markup_pipeline.rb b/lib/banzai/pipeline/markup_pipeline.rb
index c56d908009f..db79a22549c 100644
--- a/lib/banzai/pipeline/markup_pipeline.rb
+++ b/lib/banzai/pipeline/markup_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class MarkupPipeline < BasePipeline
diff --git a/lib/banzai/pipeline/note_pipeline.rb b/lib/banzai/pipeline/note_pipeline.rb
index 7890f20f716..4480d7ede05 100644
--- a/lib/banzai/pipeline/note_pipeline.rb
+++ b/lib/banzai/pipeline/note_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class NotePipeline < FullPipeline
diff --git a/lib/banzai/pipeline/plain_markdown_pipeline.rb b/lib/banzai/pipeline/plain_markdown_pipeline.rb
index 3f45db21869..b64f13cde47 100644
--- a/lib/banzai/pipeline/plain_markdown_pipeline.rb
+++ b/lib/banzai/pipeline/plain_markdown_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class PlainMarkdownPipeline < BasePipeline
diff --git a/lib/banzai/pipeline/post_process_pipeline.rb b/lib/banzai/pipeline/post_process_pipeline.rb
index 0b2e584ef16..63a998a2c1f 100644
--- a/lib/banzai/pipeline/post_process_pipeline.rb
+++ b/lib/banzai/pipeline/post_process_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class PostProcessPipeline < BasePipeline
diff --git a/lib/banzai/pipeline/pre_process_pipeline.rb b/lib/banzai/pipeline/pre_process_pipeline.rb
index 6cf219661d3..c937f783180 100644
--- a/lib/banzai/pipeline/pre_process_pipeline.rb
+++ b/lib/banzai/pipeline/pre_process_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class PreProcessPipeline < BasePipeline
diff --git a/lib/banzai/pipeline/relative_link_pipeline.rb b/lib/banzai/pipeline/relative_link_pipeline.rb
index 270990e7ab4..88651892acc 100644
--- a/lib/banzai/pipeline/relative_link_pipeline.rb
+++ b/lib/banzai/pipeline/relative_link_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class RelativeLinkPipeline < BasePipeline
diff --git a/lib/banzai/pipeline/single_line_pipeline.rb b/lib/banzai/pipeline/single_line_pipeline.rb
index cd5a6c8875c..61ff7b0bcce 100644
--- a/lib/banzai/pipeline/single_line_pipeline.rb
+++ b/lib/banzai/pipeline/single_line_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class SingleLinePipeline < GfmPipeline
diff --git a/lib/banzai/pipeline/wiki_pipeline.rb b/lib/banzai/pipeline/wiki_pipeline.rb
index c37b8e71cb0..97a03895ff3 100644
--- a/lib/banzai/pipeline/wiki_pipeline.rb
+++ b/lib/banzai/pipeline/wiki_pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Pipeline
class WikiPipeline < FullPipeline
diff --git a/lib/banzai/querying.rb b/lib/banzai/querying.rb
index a19a05e8c0d..55aa5fa66c3 100644
--- a/lib/banzai/querying.rb
+++ b/lib/banzai/querying.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Querying
module_function
diff --git a/lib/banzai/redactor.rb b/lib/banzai/redactor.rb
index 28928d6f376..7db5f5e1f7d 100644
--- a/lib/banzai/redactor.rb
+++ b/lib/banzai/redactor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
# Class for removing Markdown references a certain user is not allowed to
# view.
@@ -37,7 +39,13 @@ module Banzai
all_document_nodes.each do |entry|
nodes_for_document = entry[:nodes]
- doc_data = { document: entry[:document], visible_reference_count: nodes_for_document.count }
+
+ doc_data = {
+ document: entry[:document],
+ total_reference_count: nodes_for_document.count,
+ visible_reference_count: nodes_for_document.count
+ }
+
metadata << doc_data
nodes_for_document.each do |node|
diff --git a/lib/banzai/reference_extractor.rb b/lib/banzai/reference_extractor.rb
index 78588299c18..3fc3ae02088 100644
--- a/lib/banzai/reference_extractor.rb
+++ b/lib/banzai/reference_extractor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
# Extract possible GFM references from an arbitrary String for further processing.
class ReferenceExtractor
diff --git a/lib/banzai/reference_parser.rb b/lib/banzai/reference_parser.rb
index 557bec4316e..efe15096f08 100644
--- a/lib/banzai/reference_parser.rb
+++ b/lib/banzai/reference_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
# Returns the reference parser class for the given type
diff --git a/lib/banzai/reference_parser/base_parser.rb b/lib/banzai/reference_parser/base_parser.rb
index 3ab154a7b1c..8419769085a 100644
--- a/lib/banzai/reference_parser/base_parser.rb
+++ b/lib/banzai/reference_parser/base_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
# Base class for reference parsing classes.
@@ -215,7 +217,7 @@ module Banzai
#
def projects_for_nodes(nodes)
@projects_for_nodes ||=
- grouped_objects_for_nodes(nodes, Project, 'data-project')
+ grouped_objects_for_nodes(nodes, Project.includes(:project_feature), 'data-project')
end
def can?(user, permission, subject = :global)
diff --git a/lib/banzai/reference_parser/commit_parser.rb b/lib/banzai/reference_parser/commit_parser.rb
index 30dc87248b4..0bfb6a92020 100644
--- a/lib/banzai/reference_parser/commit_parser.rb
+++ b/lib/banzai/reference_parser/commit_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class CommitParser < BaseParser
diff --git a/lib/banzai/reference_parser/commit_range_parser.rb b/lib/banzai/reference_parser/commit_range_parser.rb
index 2920e886938..480eefd5c4d 100644
--- a/lib/banzai/reference_parser/commit_range_parser.rb
+++ b/lib/banzai/reference_parser/commit_range_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class CommitRangeParser < BaseParser
diff --git a/lib/banzai/reference_parser/directly_addressed_user_parser.rb b/lib/banzai/reference_parser/directly_addressed_user_parser.rb
index 77df9bbd024..1f18f82b916 100644
--- a/lib/banzai/reference_parser/directly_addressed_user_parser.rb
+++ b/lib/banzai/reference_parser/directly_addressed_user_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class DirectlyAddressedUserParser < UserParser
diff --git a/lib/banzai/reference_parser/epic_parser.rb b/lib/banzai/reference_parser/epic_parser.rb
index 08b8a4c9a0f..7f366f0f8ab 100644
--- a/lib/banzai/reference_parser/epic_parser.rb
+++ b/lib/banzai/reference_parser/epic_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
# The actual parser is implemented in the EE mixin
diff --git a/lib/banzai/reference_parser/external_issue_parser.rb b/lib/banzai/reference_parser/external_issue_parser.rb
index 1802cd04854..029b09dcd25 100644
--- a/lib/banzai/reference_parser/external_issue_parser.rb
+++ b/lib/banzai/reference_parser/external_issue_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class ExternalIssueParser < BaseParser
diff --git a/lib/banzai/reference_parser/issuable_parser.rb b/lib/banzai/reference_parser/issuable_parser.rb
index fad127d7e5b..f8c26288017 100644
--- a/lib/banzai/reference_parser/issuable_parser.rb
+++ b/lib/banzai/reference_parser/issuable_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class IssuableParser < BaseParser
diff --git a/lib/banzai/reference_parser/issue_parser.rb b/lib/banzai/reference_parser/issue_parser.rb
index 7b5915899cf..97c7173ac0f 100644
--- a/lib/banzai/reference_parser/issue_parser.rb
+++ b/lib/banzai/reference_parser/issue_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class IssueParser < IssuableParser
diff --git a/lib/banzai/reference_parser/label_parser.rb b/lib/banzai/reference_parser/label_parser.rb
index 30e2a012f09..398cc45fea0 100644
--- a/lib/banzai/reference_parser/label_parser.rb
+++ b/lib/banzai/reference_parser/label_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class LabelParser < BaseParser
diff --git a/lib/banzai/reference_parser/merge_request_parser.rb b/lib/banzai/reference_parser/merge_request_parser.rb
index 9e5d55f72bc..e8147ac591a 100644
--- a/lib/banzai/reference_parser/merge_request_parser.rb
+++ b/lib/banzai/reference_parser/merge_request_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class MergeRequestParser < IssuableParser
diff --git a/lib/banzai/reference_parser/milestone_parser.rb b/lib/banzai/reference_parser/milestone_parser.rb
index 68675abe22a..925d736fb9a 100644
--- a/lib/banzai/reference_parser/milestone_parser.rb
+++ b/lib/banzai/reference_parser/milestone_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class MilestoneParser < BaseParser
diff --git a/lib/banzai/reference_parser/snippet_parser.rb b/lib/banzai/reference_parser/snippet_parser.rb
index 3ade168b566..6f6ac08de04 100644
--- a/lib/banzai/reference_parser/snippet_parser.rb
+++ b/lib/banzai/reference_parser/snippet_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class SnippetParser < BaseParser
diff --git a/lib/banzai/reference_parser/user_parser.rb b/lib/banzai/reference_parser/user_parser.rb
index ceb7f1d165c..067b06b7590 100644
--- a/lib/banzai/reference_parser/user_parser.rb
+++ b/lib/banzai/reference_parser/user_parser.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module ReferenceParser
class UserParser < BaseParser
diff --git a/lib/banzai/renderer.rb b/lib/banzai/renderer.rb
index 0050295eeda..c7239a5eaa6 100644
--- a/lib/banzai/renderer.rb
+++ b/lib/banzai/renderer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Renderer
# Convert a Markdown String into an HTML-safe String of HTML
diff --git a/lib/banzai/renderer/common_mark/html.rb b/lib/banzai/renderer/common_mark/html.rb
index 0b27316da1b..837665451a1 100644
--- a/lib/banzai/renderer/common_mark/html.rb
+++ b/lib/banzai/renderer/common_mark/html.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Renderer
module CommonMark
diff --git a/lib/banzai/renderer/redcarpet/html.rb b/lib/banzai/renderer/redcarpet/html.rb
index 30e815f1224..84931fdc784 100644
--- a/lib/banzai/renderer/redcarpet/html.rb
+++ b/lib/banzai/renderer/redcarpet/html.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Renderer
module Redcarpet
diff --git a/lib/banzai/request_store_reference_cache.rb b/lib/banzai/request_store_reference_cache.rb
index 9a9704f9837..91fb489b72d 100644
--- a/lib/banzai/request_store_reference_cache.rb
+++ b/lib/banzai/request_store_reference_cache.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module RequestStoreReferenceCache
def cached_call(request_store_key, cache_key, path: [])
diff --git a/lib/bitbucket/client.rb b/lib/bitbucket/client.rb
index f8ee7e0f9ae..1343f424c51 100644
--- a/lib/bitbucket/client.rb
+++ b/lib/bitbucket/client.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
class Client
attr_reader :connection
diff --git a/lib/bitbucket/collection.rb b/lib/bitbucket/collection.rb
index a78495dbf5e..9c496daccaa 100644
--- a/lib/bitbucket/collection.rb
+++ b/lib/bitbucket/collection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
class Collection < Enumerator
def initialize(paginator)
diff --git a/lib/bitbucket/connection.rb b/lib/bitbucket/connection.rb
index ba5a9e2f04c..0041634f9e3 100644
--- a/lib/bitbucket/connection.rb
+++ b/lib/bitbucket/connection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
class Connection
DEFAULT_API_VERSION = '2.0'.freeze
diff --git a/lib/bitbucket/error/unauthorized.rb b/lib/bitbucket/error/unauthorized.rb
index efe10542f19..3cde11babee 100644
--- a/lib/bitbucket/error/unauthorized.rb
+++ b/lib/bitbucket/error/unauthorized.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
module Error
Unauthorized = Class.new(StandardError)
diff --git a/lib/bitbucket/page.rb b/lib/bitbucket/page.rb
index 2b0a3fe7b1a..7cc1342ad65 100644
--- a/lib/bitbucket/page.rb
+++ b/lib/bitbucket/page.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
class Page
attr_reader :attrs, :items
diff --git a/lib/bitbucket/paginator.rb b/lib/bitbucket/paginator.rb
index 135d0d55674..0d004592e67 100644
--- a/lib/bitbucket/paginator.rb
+++ b/lib/bitbucket/paginator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
class Paginator
PAGE_LENGTH = 50 # The minimum length is 10 and the maximum is 100.
diff --git a/lib/bitbucket/representation/base.rb b/lib/bitbucket/representation/base.rb
index 800d5a075c6..bb8dcd91ad5 100644
--- a/lib/bitbucket/representation/base.rb
+++ b/lib/bitbucket/representation/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
module Representation
class Base
diff --git a/lib/bitbucket/representation/comment.rb b/lib/bitbucket/representation/comment.rb
index 4937aa9728f..1b8dc27793a 100644
--- a/lib/bitbucket/representation/comment.rb
+++ b/lib/bitbucket/representation/comment.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
module Representation
class Comment < Representation::Base
diff --git a/lib/bitbucket/representation/issue.rb b/lib/bitbucket/representation/issue.rb
index 44bcbc250b3..a88797cdab9 100644
--- a/lib/bitbucket/representation/issue.rb
+++ b/lib/bitbucket/representation/issue.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
module Representation
class Issue < Representation::Base
diff --git a/lib/bitbucket/representation/pull_request.rb b/lib/bitbucket/representation/pull_request.rb
index eebf8093380..6a0e8b354bf 100644
--- a/lib/bitbucket/representation/pull_request.rb
+++ b/lib/bitbucket/representation/pull_request.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
module Representation
class PullRequest < Representation::Base
diff --git a/lib/bitbucket/representation/pull_request_comment.rb b/lib/bitbucket/representation/pull_request_comment.rb
index c52acbc3ddc..34dbf9ad22d 100644
--- a/lib/bitbucket/representation/pull_request_comment.rb
+++ b/lib/bitbucket/representation/pull_request_comment.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
module Representation
class PullRequestComment < Comment
diff --git a/lib/bitbucket/representation/repo.rb b/lib/bitbucket/representation/repo.rb
index 59b0fda8e14..c5bfc91e43d 100644
--- a/lib/bitbucket/representation/repo.rb
+++ b/lib/bitbucket/representation/repo.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
module Representation
class Repo < Representation::Base
diff --git a/lib/bitbucket/representation/user.rb b/lib/bitbucket/representation/user.rb
index ba6b7667b49..2b45d751e70 100644
--- a/lib/bitbucket/representation/user.rb
+++ b/lib/bitbucket/representation/user.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Bitbucket
module Representation
class User < Representation::Base
diff --git a/lib/carrier_wave_string_file.rb b/lib/carrier_wave_string_file.rb
index 6c848902e4a..c9a64d9e631 100644
--- a/lib/carrier_wave_string_file.rb
+++ b/lib/carrier_wave_string_file.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CarrierWaveStringFile < StringIO
def original_filename
""
diff --git a/lib/constraints/feature_constrainer.rb b/lib/constraints/feature_constrainer.rb
index 05d48b0f25a..ca4376a9d38 100644
--- a/lib/constraints/feature_constrainer.rb
+++ b/lib/constraints/feature_constrainer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Constraints
class FeatureConstrainer
attr_reader :feature
diff --git a/lib/constraints/group_url_constrainer.rb b/lib/constraints/group_url_constrainer.rb
index 87649c50424..8a3f8d2faaf 100644
--- a/lib/constraints/group_url_constrainer.rb
+++ b/lib/constraints/group_url_constrainer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Constraints
class GroupUrlConstrainer
def matches?(request)
diff --git a/lib/constraints/project_url_constrainer.rb b/lib/constraints/project_url_constrainer.rb
index 32aea98f0f7..eadfbf7bc01 100644
--- a/lib/constraints/project_url_constrainer.rb
+++ b/lib/constraints/project_url_constrainer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Constraints
class ProjectUrlConstrainer
def matches?(request)
diff --git a/lib/constraints/user_url_constrainer.rb b/lib/constraints/user_url_constrainer.rb
index 8afa04d29a4..e763569cb2e 100644
--- a/lib/constraints/user_url_constrainer.rb
+++ b/lib/constraints/user_url_constrainer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Constraints
class UserUrlConstrainer
def matches?(request)
diff --git a/lib/container_registry/blob.rb b/lib/container_registry/blob.rb
index d5f85f9fcad..837b22c3082 100644
--- a/lib/container_registry/blob.rb
+++ b/lib/container_registry/blob.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ContainerRegistry
class Blob
attr_reader :repository, :config
diff --git a/lib/container_registry/client.rb b/lib/container_registry/client.rb
index 010ca1ec27b..c80f49f5ae0 100644
--- a/lib/container_registry/client.rb
+++ b/lib/container_registry/client.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'faraday'
require 'faraday_middleware'
diff --git a/lib/container_registry/config.rb b/lib/container_registry/config.rb
index 589f9f4380a..740c0e13da0 100644
--- a/lib/container_registry/config.rb
+++ b/lib/container_registry/config.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ContainerRegistry
class Config
attr_reader :tag, :blob, :data
diff --git a/lib/container_registry/path.rb b/lib/container_registry/path.rb
index 1ab14c1c155..9b2a61cdedc 100644
--- a/lib/container_registry/path.rb
+++ b/lib/container_registry/path.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ContainerRegistry
##
# Class responsible for extracting project and repository name from
diff --git a/lib/container_registry/registry.rb b/lib/container_registry/registry.rb
index f90d711474a..523364ac7c7 100644
--- a/lib/container_registry/registry.rb
+++ b/lib/container_registry/registry.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ContainerRegistry
class Registry
attr_reader :uri, :client, :path
diff --git a/lib/container_registry/tag.rb b/lib/container_registry/tag.rb
index c785bca4dad..8633e764f90 100644
--- a/lib/container_registry/tag.rb
+++ b/lib/container_registry/tag.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ContainerRegistry
class Tag
attr_reader :repository, :name
diff --git a/lib/declarative_policy.rb b/lib/declarative_policy.rb
index dda6cd38dcd..5e22523e45a 100644
--- a/lib/declarative_policy.rb
+++ b/lib/declarative_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_dependency 'declarative_policy/cache'
require_dependency 'declarative_policy/condition'
require_dependency 'declarative_policy/delegate_dsl'
@@ -10,8 +12,6 @@ require_dependency 'declarative_policy/step'
require_dependency 'declarative_policy/base'
-require 'thread'
-
module DeclarativePolicy
CLASS_CACHE_MUTEX = Mutex.new
CLASS_CACHE_IVAR = :@__DeclarativePolicy_CLASS_CACHE
diff --git a/lib/declarative_policy/base.rb b/lib/declarative_policy/base.rb
index da3fabba39b..cd6e1606f22 100644
--- a/lib/declarative_policy/base.rb
+++ b/lib/declarative_policy/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeclarativePolicy
class Base
# A map of ability => list of rules together with :enable
diff --git a/lib/declarative_policy/cache.rb b/lib/declarative_policy/cache.rb
index 780d8f707bd..13006e56454 100644
--- a/lib/declarative_policy/cache.rb
+++ b/lib/declarative_policy/cache.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeclarativePolicy
module Cache
class << self
diff --git a/lib/declarative_policy/condition.rb b/lib/declarative_policy/condition.rb
index 51c4a8b2bbe..b77f40b1093 100644
--- a/lib/declarative_policy/condition.rb
+++ b/lib/declarative_policy/condition.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeclarativePolicy
# A Condition is the data structure that is created by the
# `condition` declaration on DeclarativePolicy::Base. It is
diff --git a/lib/declarative_policy/delegate_dsl.rb b/lib/declarative_policy/delegate_dsl.rb
index ca2eb98e3e8..67e3429b696 100644
--- a/lib/declarative_policy/delegate_dsl.rb
+++ b/lib/declarative_policy/delegate_dsl.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeclarativePolicy
# Used when the name of a delegate is mentioned in
# the rule DSL.
diff --git a/lib/declarative_policy/policy_dsl.rb b/lib/declarative_policy/policy_dsl.rb
index c96049768a1..96741c0478e 100644
--- a/lib/declarative_policy/policy_dsl.rb
+++ b/lib/declarative_policy/policy_dsl.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeclarativePolicy
# The return value of a rule { ... } declaration.
# Can call back to register rules with the containing
diff --git a/lib/declarative_policy/preferred_scope.rb b/lib/declarative_policy/preferred_scope.rb
index c77784cb49d..239780d8626 100644
--- a/lib/declarative_policy/preferred_scope.rb
+++ b/lib/declarative_policy/preferred_scope.rb
@@ -1,4 +1,7 @@
-module DeclarativePolicy # rubocop:disable Naming/FileName
+# rubocop:disable Naming/FileName
+# frozen_string_literal: true
+
+module DeclarativePolicy
PREFERRED_SCOPE_KEY = :"DeclarativePolicy.preferred_scope"
class << self
diff --git a/lib/declarative_policy/rule.rb b/lib/declarative_policy/rule.rb
index 407398cc770..f38f4f0a50f 100644
--- a/lib/declarative_policy/rule.rb
+++ b/lib/declarative_policy/rule.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeclarativePolicy
module Rule
# A Rule is the object that results from the `rule` declaration,
diff --git a/lib/declarative_policy/rule_dsl.rb b/lib/declarative_policy/rule_dsl.rb
index 7254b08eda5..85da7f261fa 100644
--- a/lib/declarative_policy/rule_dsl.rb
+++ b/lib/declarative_policy/rule_dsl.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeclarativePolicy
# The DSL evaluation context inside rule { ... } blocks.
# Responsible for creating and combining Rule objects.
diff --git a/lib/declarative_policy/runner.rb b/lib/declarative_policy/runner.rb
index fec672f4b8c..f739fe5e16e 100644
--- a/lib/declarative_policy/runner.rb
+++ b/lib/declarative_policy/runner.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeclarativePolicy
class Runner
class State
diff --git a/lib/declarative_policy/step.rb b/lib/declarative_policy/step.rb
index 3469fe9f991..c289c17cc19 100644
--- a/lib/declarative_policy/step.rb
+++ b/lib/declarative_policy/step.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeclarativePolicy
# This object represents one step in the runtime decision of whether
# an ability is allowed. It contains a Rule and a context (instance
diff --git a/lib/expand_variables.rb b/lib/expand_variables.rb
index 7b1533d0d32..c83cec9dc4a 100644
--- a/lib/expand_variables.rb
+++ b/lib/expand_variables.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ExpandVariables
class << self
def expand(value, variables)
diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb
index e02d403f7b1..a340a276640 100644
--- a/lib/extracts_path.rb
+++ b/lib/extracts_path.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Module providing methods for dealing with separating a tree-ish string and a
# file path string when combined in a request parameter
module ExtractsPath
@@ -50,7 +52,9 @@ module ExtractsPath
# branches and tags
# Append a trailing slash if we only get a ref and no file path
- id += '/' unless id.ends_with?('/')
+ unless id.ends_with?('/')
+ id = [id, '/'].join
+ end
valid_refs = ref_names.select { |v| id.start_with?("#{v}/") }
@@ -151,9 +155,9 @@ module ExtractsPath
# overriden in subclasses, do not remove
def get_id
- id = params[:id] || params[:ref]
- id += "/" + params[:path] unless params[:path].blank?
- id
+ id = [params[:id] || params[:ref]]
+ id << "/" + params[:path] unless params[:path].blank?
+ id.join
end
def ref_names
diff --git a/lib/feature.rb b/lib/feature.rb
index 0e90ad9a333..e048a443abc 100644
--- a/lib/feature.rb
+++ b/lib/feature.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'flipper/adapters/active_record'
require 'flipper/adapters/active_support_cache_store'
@@ -72,7 +74,11 @@ class Feature
end
def flipper
- @flipper ||= (Gitlab::SafeRequestStore[:flipper] ||= build_flipper_instance)
+ if Gitlab::SafeRequestStore.active?
+ Gitlab::SafeRequestStore[:flipper] ||= build_flipper_instance
+ else
+ @flipper ||= build_flipper_instance
+ end
end
def build_flipper_instance
diff --git a/lib/file_size_validator.rb b/lib/file_size_validator.rb
index 53aa8d04e5c..70a145cd5bd 100644
--- a/lib/file_size_validator.rb
+++ b/lib/file_size_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class FileSizeValidator < ActiveModel::EachValidator
MESSAGES = { is: :wrong_size, minimum: :size_too_small, maximum: :size_too_big }.freeze
CHECKS = { is: :==, minimum: :>=, maximum: :<= }.freeze
diff --git a/lib/forever.rb b/lib/forever.rb
index 7df17912544..0a37118fe68 100644
--- a/lib/forever.rb
+++ b/lib/forever.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Forever
POSTGRESQL_DATE = DateTime.new(3000, 1, 1)
MYSQL_DATE = DateTime.new(2038, 01, 19)
diff --git a/lib/gitlab.rb b/lib/gitlab.rb
index 7790534d5d7..2bb09684441 100644
--- a/lib/gitlab.rb
+++ b/lib/gitlab.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_dependency 'gitlab/popen'
module Gitlab
diff --git a/lib/gitlab/background_migration/remove_restricted_todos.rb b/lib/gitlab/background_migration/remove_restricted_todos.rb
index 68f3fa62170..9941c2fe6d9 100644
--- a/lib/gitlab/background_migration/remove_restricted_todos.rb
+++ b/lib/gitlab/background_migration/remove_restricted_todos.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
# rubocop:disable Style/Documentation
+# rubocop:disable Metrics/ClassLength
module Gitlab
module BackgroundMigration
@@ -49,11 +50,14 @@ module Gitlab
private
def remove_non_members_todos(project_id)
- Todo.where(project_id: project_id)
- .where('user_id NOT IN (?)', authorized_users(project_id))
- .each_batch(of: 5000) do |batch|
- batch.delete_all
- end
+ if Gitlab::Database.postgresql?
+ batch_remove_todos_cte(project_id)
+ else
+ unauthorized_project_todos(project_id)
+ .each_batch(of: 5000) do |batch|
+ batch.delete_all
+ end
+ end
end
def remove_confidential_issue_todos(project_id)
@@ -86,10 +90,13 @@ module Gitlab
next if target_types.empty?
- Todo.where(project_id: project_id)
- .where('user_id NOT IN (?)', authorized_users(project_id))
- .where(target_type: target_types)
- .delete_all
+ if Gitlab::Database.postgresql?
+ batch_remove_todos_cte(project_id, target_types)
+ else
+ unauthorized_project_todos(project_id)
+ .where(target_type: target_types)
+ .delete_all
+ end
end
end
@@ -100,6 +107,65 @@ module Gitlab
def authorized_users(project_id)
ProjectAuthorization.select(:user_id).where(project_id: project_id)
end
+
+ def unauthorized_project_todos(project_id)
+ Todo.where(project_id: project_id)
+ .where('user_id NOT IN (?)', authorized_users(project_id))
+ end
+
+ def batch_remove_todos_cte(project_id, target_types = nil)
+ loop do
+ count = remove_todos_cte(project_id, target_types)
+
+ break if count == 0
+ end
+ end
+
+ def remove_todos_cte(project_id, target_types = nil)
+ sql = []
+ sql << with_all_todos_sql(project_id, target_types)
+ sql << as_deleted_sql
+ sql << "SELECT count(*) FROM deleted"
+
+ result = Todo.connection.exec_query(sql.join(' '))
+ result.rows[0][0].to_i
+ end
+
+ def with_all_todos_sql(project_id, target_types = nil)
+ if target_types
+ table = Arel::Table.new(:todos)
+ in_target = table[:target_type].in(target_types)
+ target_types_sql = " AND #{in_target.to_sql}"
+ end
+
+ <<-SQL
+ WITH all_todos AS (
+ SELECT id
+ FROM "todos"
+ WHERE "todos"."project_id" = #{project_id}
+ AND (user_id NOT IN (
+ SELECT "project_authorizations"."user_id"
+ FROM "project_authorizations"
+ WHERE "project_authorizations"."project_id" = #{project_id})
+ #{target_types_sql}
+ )
+ ),
+ SQL
+ end
+
+ def as_deleted_sql
+ <<-SQL
+ deleted AS (
+ DELETE FROM todos
+ WHERE id IN (
+ SELECT id
+ FROM all_todos
+ LIMIT 5000
+ )
+ RETURNING id
+ )
+ SQL
+ end
end
end
end
diff --git a/lib/gitlab/ci/build/policy/changes.rb b/lib/gitlab/ci/build/policy/changes.rb
new file mode 100644
index 00000000000..7bf51519752
--- /dev/null
+++ b/lib/gitlab/ci/build/policy/changes.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Build
+ module Policy
+ class Changes < Policy::Specification
+ def initialize(globs)
+ @globs = Array(globs)
+ end
+
+ def satisfied_by?(pipeline, seed)
+ return true unless pipeline.branch_updated?
+
+ pipeline.modified_paths.any? do |path|
+ @globs.any? do |glob|
+ File.fnmatch?(glob, path, File::FNM_PATHNAME | File::FNM_DOTMATCH)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb
index 03971254310..f290ff3a565 100644
--- a/lib/gitlab/ci/config/entry/job.rb
+++ b/lib/gitlab/ci/config/entry/job.rb
@@ -36,7 +36,7 @@ module Gitlab
validates :extends, type: String
end
- validates :start_in, duration: true, if: :delayed?
+ validates :start_in, duration: { limit: '1 day' }, if: :delayed?
validates :start_in, absence: true, unless: :delayed?
end
diff --git a/lib/gitlab/ci/config/entry/legacy_validation_helpers.rb b/lib/gitlab/ci/config/entry/legacy_validation_helpers.rb
index a78a85397bd..a3d4432be82 100644
--- a/lib/gitlab/ci/config/entry/legacy_validation_helpers.rb
+++ b/lib/gitlab/ci/config/entry/legacy_validation_helpers.rb
@@ -11,6 +11,15 @@ module Gitlab
false
end
+ def validate_duration_limit(value, limit)
+ return false unless value.is_a?(String)
+
+ ChronicDuration.parse(value).second.from_now <
+ ChronicDuration.parse(limit).second.from_now
+ rescue ChronicDuration::DurationParseError
+ false
+ end
+
def validate_array_of_strings(values)
values.is_a?(Array) && values.all? { |value| validate_string(value) }
end
diff --git a/lib/gitlab/ci/config/entry/policy.rb b/lib/gitlab/ci/config/entry/policy.rb
index 09e8e52b60f..c92562f8c85 100644
--- a/lib/gitlab/ci/config/entry/policy.rb
+++ b/lib/gitlab/ci/config/entry/policy.rb
@@ -25,17 +25,19 @@ module Gitlab
include Entry::Validatable
include Entry::Attributable
- attributes :refs, :kubernetes, :variables
+ ALLOWED_KEYS = %i[refs kubernetes variables changes].freeze
+ attributes :refs, :kubernetes, :variables, :changes
validations do
validates :config, presence: true
- validates :config, allowed_keys: %i[refs kubernetes variables]
+ validates :config, allowed_keys: ALLOWED_KEYS
validate :variables_expressions_syntax
with_options allow_nil: true do
validates :refs, array_of_strings_or_regexps: true
validates :kubernetes, allowed_values: %w[active]
validates :variables, array_of_strings: true
+ validates :changes, array_of_strings: true
end
def variables_expressions_syntax
diff --git a/lib/gitlab/ci/config/entry/validators.rb b/lib/gitlab/ci/config/entry/validators.rb
index b3c889ee92f..f6b4ba7843e 100644
--- a/lib/gitlab/ci/config/entry/validators.rb
+++ b/lib/gitlab/ci/config/entry/validators.rb
@@ -49,6 +49,12 @@ module Gitlab
unless validate_duration(value)
record.errors.add(attribute, 'should be a duration')
end
+
+ if options[:limit]
+ unless validate_duration_limit(value, options[:limit])
+ record.errors.add(attribute, 'should not exceed the limit')
+ end
+ end
end
end
diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
index a96595b33a5..72547c1b407 100644
--- a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
@@ -25,8 +25,9 @@
# level, or manually added below.
#
# Continuous deployment to production is enabled by default.
-# If you want to deploy to staging first, or enable incremental rollouts,
-# set STAGING_ENABLED or INCREMENTAL_ROLLOUT_ENABLED environment variables.
+# If you want to deploy to staging first, set STAGING_ENABLED environment variable.
+# If you want to enable incremental rollout, either manual or time based,
+# set INCREMENTAL_ROLLOUT_TYPE environment variable to "manual" or "timed".
# If you want to use canary deployments, set CANARY_ENABLED environment variable.
#
# If Auto DevOps fails to detect the proper buildpack, or if you want to
@@ -61,6 +62,10 @@ stages:
- staging
- canary
- production
+ - incremental rollout 10%
+ - incremental rollout 25%
+ - incremental rollout 50%
+ - incremental rollout 100%
- performance
- cleanup
@@ -282,11 +287,6 @@ stop_review:
variables:
- $REVIEW_DISABLED
-# Keys that start with a dot (.) will not be processed by GitLab CI.
-# Staging and canary jobs are disabled by default, to enable them
-# remove the dot (.) before the job name.
-# https://docs.gitlab.com/ee/ci/yaml/README.html#hidden-keys
-
# Staging deploys are disabled by default since
# continuous deployment to production is enabled by default
# If you prefer to automatically deploy to staging and
@@ -368,6 +368,7 @@ production:
- $STAGING_ENABLED
- $CANARY_ENABLED
- $INCREMENTAL_ROLLOUT_ENABLED
+ - $INCREMENTAL_ROLLOUT_MODE
production_manual:
<<: *production_template
@@ -383,11 +384,11 @@ production_manual:
except:
variables:
- $INCREMENTAL_ROLLOUT_ENABLED
+ - $INCREMENTAL_ROLLOUT_MODE
# This job implements incremental rollout on for every push to `master`.
.rollout: &rollout_template
- stage: production
script:
- check_kube_domain
- install_dependencies
@@ -405,52 +406,77 @@ production_manual:
artifacts:
paths: [environment_url.txt]
-rollout 10%:
+.manual_rollout_template: &manual_rollout_template
<<: *rollout_template
- variables:
- ROLLOUT_PERCENTAGE: 10
+ stage: production
when: manual
+ # This selectors are backward compatible mode with $INCREMENTAL_ROLLOUT_ENABLED (before 11.4)
only:
refs:
- master
kubernetes: active
variables:
+ - $INCREMENTAL_ROLLOUT_MODE == "manual"
- $INCREMENTAL_ROLLOUT_ENABLED
+ except:
+ variables:
+ - $INCREMENTAL_ROLLOUT_MODE == "timed"
-rollout 25%:
+.timed_rollout_template: &timed_rollout_template
<<: *rollout_template
- variables:
- ROLLOUT_PERCENTAGE: 25
- when: manual
+ when: delayed
+ start_in: 5 minutes
only:
refs:
- master
kubernetes: active
variables:
- - $INCREMENTAL_ROLLOUT_ENABLED
+ - $INCREMENTAL_ROLLOUT_MODE == "timed"
+
+timed rollout 10%:
+ <<: *timed_rollout_template
+ stage: incremental rollout 10%
+ variables:
+ ROLLOUT_PERCENTAGE: 10
+
+timed rollout 25%:
+ <<: *timed_rollout_template
+ stage: incremental rollout 25%
+ variables:
+ ROLLOUT_PERCENTAGE: 25
+
+timed rollout 50%:
+ <<: *timed_rollout_template
+ stage: incremental rollout 50%
+ variables:
+ ROLLOUT_PERCENTAGE: 50
+
+timed rollout 100%:
+ <<: *timed_rollout_template
+ <<: *production_template
+ stage: incremental rollout 100%
+ variables:
+ ROLLOUT_PERCENTAGE: 100
+
+rollout 10%:
+ <<: *manual_rollout_template
+ variables:
+ ROLLOUT_PERCENTAGE: 10
+
+rollout 25%:
+ <<: *manual_rollout_template
+ variables:
+ ROLLOUT_PERCENTAGE: 25
rollout 50%:
- <<: *rollout_template
+ <<: *manual_rollout_template
variables:
ROLLOUT_PERCENTAGE: 50
- when: manual
- only:
- refs:
- - master
- kubernetes: active
- variables:
- - $INCREMENTAL_ROLLOUT_ENABLED
rollout 100%:
+ <<: *manual_rollout_template
<<: *production_template
- when: manual
allow_failure: false
- only:
- refs:
- - master
- kubernetes: active
- variables:
- - $INCREMENTAL_ROLLOUT_ENABLED
# ---------------------------------------------------------------------------
@@ -689,9 +715,6 @@ rollout 100%:
helm version --client
tiller -version
- helm init --client-only
- helm plugin install https://github.com/adamreese/helm-local
-
curl -L -o /usr/bin/kubectl "https://storage.googleapis.com/kubernetes-release/release/v${KUBERNETES_VERSION}/bin/linux/amd64/kubectl"
chmod +x /usr/bin/kubectl
kubectl version --client
@@ -800,9 +823,9 @@ rollout 100%:
function initialize_tiller() {
echo "Checking Tiller..."
- helm local start
- helm local status
export HELM_HOST=":44134"
+ tiller -listen ${HELM_HOST} -alsologtostderr > /dev/null 2>&1 &
+ echo "Tiller is listening on ${HELM_HOST}"
if ! helm version --debug; then
echo "Failed to init Tiller."
diff --git a/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml b/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml
index 0688f77a1d2..d0cad285572 100644
--- a/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml
@@ -25,9 +25,12 @@ before_script:
# Update packages
- apt-get update -yqq
- # Upgrade to Node 7
- - curl -sL https://deb.nodesource.com/setup_7.x | bash -
-
+ # Prep for Node
+ - apt-get install gnupg -yqq
+
+ # Upgrade to Node 8
+ - curl -sL https://deb.nodesource.com/setup_8.x | bash -
+
# Install dependencies
- apt-get install git nodejs libcurl4-gnutls-dev libicu-dev libmcrypt-dev libvpx-dev libjpeg-dev libpng-dev libxpm-dev zlib1g-dev libfreetype6-dev libxml2-dev libexpat1-dev libbz2-dev libgmp3-dev libldap2-dev unixodbc-dev libpq-dev libsqlite3-dev libaspell-dev libsnmp-dev libpcre3-dev libtidy-dev -yqq
diff --git a/lib/gitlab/ci/templates/Python.gitlab-ci.yml b/lib/gitlab/ci/templates/Python.gitlab-ci.yml
index 2e0589de652..098abe4daf5 100644
--- a/lib/gitlab/ci/templates/Python.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Python.gitlab-ci.yml
@@ -5,7 +5,7 @@ image: python:latest
# Change pip's cache directory to be inside the project directory since we can
# only cache local items.
variables:
- PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache"
+ PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
# Pip's cache doesn't store the python packages
# https://pip.pypa.io/en/stable/reference/pip_install/#caching
diff --git a/lib/gitlab/diff/position.rb b/lib/gitlab/diff/position.rb
index fc280f96ec1..f967494199e 100644
--- a/lib/gitlab/diff/position.rb
+++ b/lib/gitlab/diff/position.rb
@@ -69,6 +69,10 @@ module Gitlab
JSON.generate(formatter.to_h, opts)
end
+ def as_json(opts = nil)
+ to_h.as_json(opts)
+ end
+
def type
formatter.line_age
end
diff --git a/lib/gitlab/git/diff_stats_collection.rb b/lib/gitlab/git/diff_stats_collection.rb
index d4033f56387..998c41497a2 100644
--- a/lib/gitlab/git/diff_stats_collection.rb
+++ b/lib/gitlab/git/diff_stats_collection.rb
@@ -18,6 +18,10 @@ module Gitlab
indexed_by_path[path]
end
+ def paths
+ @collection.map(&:path)
+ end
+
private
def indexed_by_path
diff --git a/lib/gitlab/git/push.rb b/lib/gitlab/git/push.rb
new file mode 100644
index 00000000000..b6577ba17f1
--- /dev/null
+++ b/lib/gitlab/git/push.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Git
+ class Push
+ include Gitlab::Utils::StrongMemoize
+
+ attr_reader :ref, :oldrev, :newrev
+
+ def initialize(project, oldrev, newrev, ref)
+ @project = project
+ @oldrev = oldrev.presence || Gitlab::Git::BLANK_SHA
+ @newrev = newrev.presence || Gitlab::Git::BLANK_SHA
+ @ref = ref
+ end
+
+ def branch_name
+ strong_memoize(:branch_name) do
+ Gitlab::Git.branch_name(@ref)
+ end
+ end
+
+ def branch_added?
+ Gitlab::Git.blank_ref?(@oldrev)
+ end
+
+ def branch_removed?
+ Gitlab::Git.blank_ref?(@newrev)
+ end
+
+ def branch_updated?
+ branch_push? && !branch_added? && !branch_removed?
+ end
+
+ def force_push?
+ Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev)
+ end
+
+ def branch_push?
+ strong_memoize(:branch_push) do
+ Gitlab::Git.branch_ref?(@ref)
+ end
+ end
+
+ def modified_paths
+ unless branch_updated?
+ raise ArgumentError, 'Unable to calculate modified paths!'
+ end
+
+ strong_memoize(:modified_paths) do
+ @project.repository.diff_stats(@oldrev, @newrev).paths
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index 30cd09a0ca7..240a0d7d1b8 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -24,8 +24,8 @@ module Gitlab
cannot_push_to_read_only: "You can't push code to a read-only GitLab instance."
}.freeze
- DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive }.freeze
- PUSH_COMMANDS = %w{ git-receive-pack }.freeze
+ DOWNLOAD_COMMANDS = %w{git-upload-pack git-upload-archive}.freeze
+ PUSH_COMMANDS = %w{git-receive-pack}.freeze
ALL_COMMANDS = DOWNLOAD_COMMANDS + PUSH_COMMANDS
attr_reader :actor, :project, :protocol, :authentication_abilities, :namespace_path, :project_path, :redirected_path, :auth_result_type, :changes
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index 500aabcbbb8..4ec87f6a3e7 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -220,7 +220,7 @@ module Gitlab
result
end
- SERVER_FEATURE_FLAGS = %w[gogit_findcommit git_v2].freeze
+ SERVER_FEATURE_FLAGS = %w[gogit_findcommit].freeze
def self.server_feature_flags
SERVER_FEATURE_FLAGS.map do |f|
diff --git a/lib/gitlab/template/base_template.rb b/lib/gitlab/template/base_template.rb
index 3770f3f250b..4456217017f 100644
--- a/lib/gitlab/template/base_template.rb
+++ b/lib/gitlab/template/base_template.rb
@@ -12,14 +12,21 @@ module Gitlab
def name
File.basename(@path, self.class.extension)
end
- alias_method :id, :name
+ alias_method :key, :name
def content
@finder.read(@path)
end
+ # Present for compatibility with license templates, which can replace text
+ # like `[fullname]` with a user-specified string. This is a no-op for
+ # other templates
+ def resolve!(_placeholders = {})
+ self
+ end
+
def to_json
- { name: name, content: content }
+ { key: key, name: name, content: content }
end
def <=>(other)
diff --git a/lib/gitlab/url_sanitizer.rb b/lib/gitlab/url_sanitizer.rb
index 308a95d2f09..29672d68cad 100644
--- a/lib/gitlab/url_sanitizer.rb
+++ b/lib/gitlab/url_sanitizer.rb
@@ -3,7 +3,7 @@ module Gitlab
ALLOWED_SCHEMES = %w[http https ssh git].freeze
def self.sanitize(content)
- regexp = URI::Parser.new.make_regexp(ALLOWED_SCHEMES)
+ regexp = URI::DEFAULT_PARSER.make_regexp(ALLOWED_SCHEMES)
content.gsub(regexp) { |url| new(url).masked_url }
rescue Addressable::URI::InvalidURIError
diff --git a/lib/gt_one_coercion.rb b/lib/gt_one_coercion.rb
index ef2dc09767c..99be51bc8c6 100644
--- a/lib/gt_one_coercion.rb
+++ b/lib/gt_one_coercion.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GtOneCoercion < Virtus::Attribute
def coerce(value)
[1, value.to_i].max
diff --git a/lib/milestone_array.rb b/lib/milestone_array.rb
index 4ed8485b36a..461e73e9670 100644
--- a/lib/milestone_array.rb
+++ b/lib/milestone_array.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MilestoneArray
class << self
def sort(array, sort_method)
diff --git a/lib/mysql_zero_date.rb b/lib/mysql_zero_date.rb
index 64634f789da..216560148fa 100644
--- a/lib/mysql_zero_date.rb
+++ b/lib/mysql_zero_date.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Disable NO_ZERO_DATE mode for mysql in rails 5.
# We use zero date as a default value
# (config/initializers/active_record_mysql_timestamp.rb), in
diff --git a/lib/static_model.rb b/lib/static_model.rb
index 44673c2b5f6..86bf8d62f9a 100644
--- a/lib/static_model.rb
+++ b/lib/static_model.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Provides an ActiveRecord-like interface to a model whose data is not persisted to a database.
module StaticModel
extend ActiveSupport::Concern
diff --git a/lib/system_check.rb b/lib/system_check.rb
index 466c39904fa..7ffd7c03c5b 100644
--- a/lib/system_check.rb
+++ b/lib/system_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Library to perform System Checks
#
# Every Check is implemented as its own class inherited from SystemCheck::BaseCheck
diff --git a/lib/unfold_form.rb b/lib/unfold_form.rb
index fcd01503d1b..05bb3ed7f1c 100644
--- a/lib/unfold_form.rb
+++ b/lib/unfold_form.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative 'gt_one_coercion'
class UnfoldForm
diff --git a/lib/uploaded_file.rb b/lib/uploaded_file.rb
index 53e5ac02e42..aae542f02ac 100644
--- a/lib/uploaded_file.rb
+++ b/lib/uploaded_file.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "tempfile"
require "tmpdir"
require "fileutils"
diff --git a/lib/version_check.rb b/lib/version_check.rb
index 91ad07feee5..ccf7bb493db 100644
--- a/lib/version_check.rb
+++ b/lib/version_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "base64"
# This class is used to build image URL to
diff --git a/locale/ar_SA/gitlab.po b/locale/ar_SA/gitlab.po
index 1b03fe9ce28..d196fac6c60 100644
--- a/locale/ar_SA/gitlab.po
+++ b/locale/ar_SA/gitlab.po
@@ -480,7 +480,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/bg/gitlab.po b/locale/bg/gitlab.po
index 3a925c27e9b..0d5026c0f4a 100644
--- a/locale/bg/gitlab.po
+++ b/locale/bg/gitlab.po
@@ -360,7 +360,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/ca_ES/gitlab.po b/locale/ca_ES/gitlab.po
index 007b2a4d393..1a052348522 100644
--- a/locale/ca_ES/gitlab.po
+++ b/locale/ca_ES/gitlab.po
@@ -360,7 +360,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/cs_CZ/gitlab.po b/locale/cs_CZ/gitlab.po
index 013152917e6..3a2267c4bf7 100644
--- a/locale/cs_CZ/gitlab.po
+++ b/locale/cs_CZ/gitlab.po
@@ -420,7 +420,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/da_DK/gitlab.po b/locale/da_DK/gitlab.po
index ed25f935b9a..1a6e564ed36 100644
--- a/locale/da_DK/gitlab.po
+++ b/locale/da_DK/gitlab.po
@@ -360,7 +360,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/de/gitlab.po b/locale/de/gitlab.po
index 41848faeb30..c27a0dea04d 100644
--- a/locale/de/gitlab.po
+++ b/locale/de/gitlab.po
@@ -360,7 +360,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "Zugriff auf fehlerhafte Speicher wurde vorübergehend deaktiviert, um die Wiederherstellung zu ermöglichen. Für den zukünftigen Zugriff, behebe bitte das Problem und setze danach die Speicherinformationen zurück."
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/eo/gitlab.po b/locale/eo/gitlab.po
index 8bd42855b44..d0a67a1d089 100644
--- a/locale/eo/gitlab.po
+++ b/locale/eo/gitlab.po
@@ -360,7 +360,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/es/gitlab.po b/locale/es/gitlab.po
index 2a8fb756192..6ce5b6a3aff 100644
--- a/locale/es/gitlab.po
+++ b/locale/es/gitlab.po
@@ -360,7 +360,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/et_EE/gitlab.po b/locale/et_EE/gitlab.po
index a9637d4098e..8e4edc84c83 100644
--- a/locale/et_EE/gitlab.po
+++ b/locale/et_EE/gitlab.po
@@ -360,7 +360,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/fil_PH/gitlab.po b/locale/fil_PH/gitlab.po
index 0929e3dd7cb..73eeb56bea2 100644
--- a/locale/fil_PH/gitlab.po
+++ b/locale/fil_PH/gitlab.po
@@ -360,7 +360,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/fr/gitlab.po b/locale/fr/gitlab.po
index cf00055272c..93b30d0ef31 100644
--- a/locale/fr/gitlab.po
+++ b/locale/fr/gitlab.po
@@ -360,7 +360,7 @@ msgstr "Accès à « %{classification_label} » non autorisé"
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "L’accès aux stockages défaillants a été temporairement désactivé pour permettre la récupération du montage. Réinitialisez les informations de stockage quand le problème sera résolu pour permettre à nouveau l’accès."
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr "Accédez à votre jeton d’exécuteur, personnalisez la configuration de votre pipeline et affichez l’état de votre pipeline et le rapport de couverture."
msgid "Account"
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 0d36b9b1170..8b7d4b0f17e 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -317,9 +317,6 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
-msgstr ""
-
msgid "Account"
msgstr ""
@@ -347,6 +344,9 @@ msgstr ""
msgid "Add Readme"
msgstr ""
+msgid "Add a table"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -1084,6 +1084,9 @@ msgstr ""
msgid "CICD|Continuous deployment to production"
msgstr ""
+msgid "CICD|Continuous deployment to production using timed incremental rollout"
+msgstr ""
+
msgid "CICD|Default to Auto DevOps pipeline"
msgstr ""
@@ -2099,6 +2102,9 @@ msgstr ""
msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
+msgstr ""
+
msgid "Cycle Analytics"
msgstr ""
@@ -2746,6 +2752,9 @@ msgstr ""
msgid "Failed to check related branches."
msgstr ""
+msgid "Failed to load emoji list."
+msgstr ""
+
msgid "Failed to remove issue from board, please try again."
msgstr ""
@@ -3369,6 +3378,9 @@ 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."
+msgstr ""
+
msgid "Jan"
msgstr ""
@@ -3700,6 +3712,9 @@ msgstr ""
msgid "MarkdownToolbar|Add a numbered list"
msgstr ""
+msgid "MarkdownToolbar|Add a table"
+msgstr ""
+
msgid "MarkdownToolbar|Add a task list"
msgstr ""
@@ -3733,6 +3748,9 @@ msgstr ""
msgid "Median"
msgstr ""
+msgid "Member since %{date}"
+msgstr ""
+
msgid "Members"
msgstr ""
@@ -5463,6 +5481,30 @@ msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|Add status emoji"
+msgstr ""
+
+msgid "SetStatusModal|Clear status"
+msgstr ""
+
+msgid "SetStatusModal|Edit status"
+msgstr ""
+
+msgid "SetStatusModal|Remove status"
+msgstr ""
+
+msgid "SetStatusModal|Set a status"
+msgstr ""
+
+msgid "SetStatusModal|Set status"
+msgstr ""
+
+msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
+msgstr ""
+
+msgid "SetStatusModal|What's your status?"
+msgstr ""
+
msgid "Settings"
msgstr ""
@@ -5777,6 +5819,12 @@ msgstr ""
msgid "Subscribe at project level"
msgstr ""
+msgid "Subscribed"
+msgstr ""
+
+msgid "Summary of issues, merge requests, push events, and comments (Timezone: %{utcFormatted})"
+msgstr ""
+
msgid "Switch branch/tag"
msgstr ""
@@ -5974,9 +6022,6 @@ msgstr ""
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr ""
-msgid "The secure token used by the Runner to checkout the project"
-msgstr ""
-
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
msgstr ""
@@ -6614,6 +6659,51 @@ msgstr ""
msgid "User map"
msgstr ""
+msgid "UserProfile|Activity"
+msgstr ""
+
+msgid "UserProfile|Already reported for abuse"
+msgstr ""
+
+msgid "UserProfile|Contributed projects"
+msgstr ""
+
+msgid "UserProfile|Edit profile"
+msgstr ""
+
+msgid "UserProfile|Groups"
+msgstr ""
+
+msgid "UserProfile|Most Recent Activity"
+msgstr ""
+
+msgid "UserProfile|Overview"
+msgstr ""
+
+msgid "UserProfile|Personal projects"
+msgstr ""
+
+msgid "UserProfile|Recent contributions"
+msgstr ""
+
+msgid "UserProfile|Report abuse"
+msgstr ""
+
+msgid "UserProfile|Snippets"
+msgstr ""
+
+msgid "UserProfile|Subscribe"
+msgstr ""
+
+msgid "UserProfile|This user has a private profile"
+msgstr ""
+
+msgid "UserProfile|View all"
+msgstr ""
+
+msgid "UserProfile|View user in admin area"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -6902,9 +6992,6 @@ msgstr ""
msgid "You can only edit files when you are on a branch"
msgstr ""
-msgid "You can reset runners registration token by pressing a button below."
-msgstr ""
-
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
@@ -6914,6 +7001,9 @@ msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
+msgid "You do not have any subscriptions yet"
+msgstr ""
+
msgid "You don't have any applications"
msgstr ""
diff --git a/locale/gl_ES/gitlab.po b/locale/gl_ES/gitlab.po
index 2b6dcc6595e..c77dc236458 100644
--- a/locale/gl_ES/gitlab.po
+++ b/locale/gl_ES/gitlab.po
@@ -360,7 +360,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/he_IL/gitlab.po b/locale/he_IL/gitlab.po
index f34f862b9b1..ab014982a72 100644
--- a/locale/he_IL/gitlab.po
+++ b/locale/he_IL/gitlab.po
@@ -420,7 +420,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/id_ID/gitlab.po b/locale/id_ID/gitlab.po
index ba43d50b726..d5c48520155 100644
--- a/locale/id_ID/gitlab.po
+++ b/locale/id_ID/gitlab.po
@@ -330,7 +330,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/it/gitlab.po b/locale/it/gitlab.po
index 375311ccf72..87bcd939fb1 100644
--- a/locale/it/gitlab.po
+++ b/locale/it/gitlab.po
@@ -360,7 +360,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "L'accesso agli storages è stato temporaneamente disabilitato per consentire il mount di ripristino. Resetta le info d'archiviazione dopo che l'issue è stato risolto per consentire nuovamente l'accesso."
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/ja/gitlab.po b/locale/ja/gitlab.po
index a618fa97381..ee5ea023fb5 100644
--- a/locale/ja/gitlab.po
+++ b/locale/ja/gitlab.po
@@ -330,7 +330,7 @@ msgstr "'%{classification_label}'ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã¯è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "mount ã«ã‚ˆã£ã¦å¾©æ—§ã§ãるよã†ã«ã€å¤±æ•—ãŒç™ºç”Ÿã—ã¦ã„るストレージã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’一時的ã«æŠ‘æ­¢ã—ã¾ã—ãŸã€‚å†åº¦ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ãŸã‚ã«ã¯ã€å•é¡Œã‚’解決ã—ã¦ã‹ã‚‰ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸æƒ…報をリセットã—ã¦ãã ã•ã„。"
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr "Runner トークンã«ã‚¢ã‚¯ã‚»ã‚¹ã—ã€ãƒ‘イプラインã®è¨­å®šã‚’カスタマイズã€ãã—ã¦ãƒ‘イプラインã®çŠ¶æ…‹ã¨ã‚«ãƒãƒ¬ãƒƒã‚¸ãƒ¬ãƒãƒ¼ãƒˆã‚’閲覧ã—ã¾ã™ã€‚"
msgid "Account"
diff --git a/locale/ko/gitlab.po b/locale/ko/gitlab.po
index ce0c069712d..3c3bcf9688a 100644
--- a/locale/ko/gitlab.po
+++ b/locale/ko/gitlab.po
@@ -330,7 +330,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "오ë™ìž‘ì¤‘ì¸ ì €ìž¥ê³µê°„ì— ëŒ€í•œ ì ‘ê·¼ì´ ë³µêµ¬ ìž‘ì—…ì„ ìœ„í•´ 마운트할 수 있ë„ë¡ ìž„ì‹œë¡œ 허용ë˜ì—ˆìŠµë‹ˆë‹¤. 문제가 í•´ê²°ëœ í›„ 다시 ì ‘ê·¼ì„ í—ˆìš©í•  수 있게 저장공간 정보를 리셋 해주세요."
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/nl_NL/gitlab.po b/locale/nl_NL/gitlab.po
index d039b51ce40..f354ca50f32 100644
--- a/locale/nl_NL/gitlab.po
+++ b/locale/nl_NL/gitlab.po
@@ -360,7 +360,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/pl_PL/gitlab.po b/locale/pl_PL/gitlab.po
index 3f30108892f..1d6dc4c4399 100644
--- a/locale/pl_PL/gitlab.po
+++ b/locale/pl_PL/gitlab.po
@@ -420,7 +420,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po
index bcc7659e5a2..c76c639e8db 100644
--- a/locale/pt_BR/gitlab.po
+++ b/locale/pt_BR/gitlab.po
@@ -360,7 +360,7 @@ msgstr "Acesso a '%{classification_label}' não permitido"
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "Os acessos à storages com defeito foram temporariamente desabilitados para permitir a sua remontagem. Redefina as informações de armazenamento depois que o problema foi resolvido para permitir o acesso de novo."
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr "Acesse seu runner token, personalize sua configuração de pipeline e visualize o status do seu pipeline e o relatório de coverage."
msgid "Account"
diff --git a/locale/ro_RO/gitlab.po b/locale/ro_RO/gitlab.po
index 3fb198ae037..bae64f360fc 100644
--- a/locale/ro_RO/gitlab.po
+++ b/locale/ro_RO/gitlab.po
@@ -390,7 +390,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/ru/gitlab.po b/locale/ru/gitlab.po
index dc7a0fc9f51..bc2c26da457 100644
--- a/locale/ru/gitlab.po
+++ b/locale/ru/gitlab.po
@@ -420,7 +420,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "ДоÑтуп к вышедшим из ÑÑ‚Ñ€Ð¾Ñ Ñ…Ñ€Ð°Ð½Ð¸Ð»Ð¸Ñ‰Ð°Ð¼ временно отключен Ð´Ð»Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾Ñти Ð¼Ð¾Ð½Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² целÑÑ… воÑÑтановлениÑ. СброÑьте информацию о хранилищах поÑле уÑÑ‚Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ñ‹, чтобы разрешить доÑтуп."
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/sq_AL/gitlab.po b/locale/sq_AL/gitlab.po
index 681827065da..42eeed11534 100644
--- a/locale/sq_AL/gitlab.po
+++ b/locale/sq_AL/gitlab.po
@@ -360,7 +360,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/tr_TR/gitlab.po b/locale/tr_TR/gitlab.po
index cb77032cc50..d1087ffd29e 100644
--- a/locale/tr_TR/gitlab.po
+++ b/locale/tr_TR/gitlab.po
@@ -360,7 +360,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/uk/gitlab.po b/locale/uk/gitlab.po
index 57a131c4ee6..33019a3e5a8 100644
--- a/locale/uk/gitlab.po
+++ b/locale/uk/gitlab.po
@@ -420,7 +420,7 @@ msgstr "ДоÑтуп до \"%{classification_label}\" заборонено"
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "ДоÑтуп до Ñховищ, що вийшли з ладу, тимчаÑово прибраний Ð·Ð°Ð´Ð»Ñ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¼Ð¾Ð½Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ. ПіÑÐ»Ñ Ð²Ð¸Ñ€Ñ–ÑˆÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð¸ обнуліть інформацію Ñховища Ð´Ð»Ñ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ð¾Ñтупу."
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr "Отримайте доÑтуп до Gitlab Runner токену, налаштуйте конфігурацію конвеєра та переглÑньте його ÑтатуÑ, а також звіт про покриттÑ."
msgid "Account"
diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po
index 9629a63e976..861e459bcac 100644
--- a/locale/zh_CN/gitlab.po
+++ b/locale/zh_CN/gitlab.po
@@ -330,7 +330,7 @@ msgstr "ä¸å…许访问%{classification_label}"
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "为方便修å¤æŒ‚载问题,访问故障存储已被暂时ç¦ç”¨ã€‚在问题解决åŽè¯·é‡ç½®å­˜å‚¨è¿è¡ŒçŠ¶å†µä¿¡æ¯ï¼Œä»¥å…许å†æ¬¡è®¿é—®ã€‚"
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr "访问您的 runner 令牌,自定义æµæ°´çº¿é…置,以åŠæŸ¥çœ‹æµæ°´çº¿çŠ¶æ€å’Œè¦†ç›–率报告。"
msgid "Account"
diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po
index 632da40cd54..3ecd9fc4cd2 100644
--- a/locale/zh_HK/gitlab.po
+++ b/locale/zh_HK/gitlab.po
@@ -330,7 +330,7 @@ msgstr ""
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "å› æ¢å¾©å®‰è£ï¼Œè¨ªå•æ•…障存儲已被暫時ç¦ç”¨ã€‚在å•é¡Œè§£æ±ºå¾Œå°‡é‡ç½®å­˜å„²ä¿¡æ¯ï¼Œä»¥ä¾¿å†æ¬¡è¨ªå•ã€‚"
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Account"
diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po
index 0d6fe4395ef..bb907d9a583 100644
--- a/locale/zh_TW/gitlab.po
+++ b/locale/zh_TW/gitlab.po
@@ -330,7 +330,7 @@ msgstr "ä¸å…許存å–「%{classification_label}ã€"
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr "已暫時åœç”¨å¤±æ•—çš„ Git 儲存空間。當儲存空間æ¢å¾©æ­£å¸¸å¾Œï¼Œè«‹é‡ç½®å„²å­˜ç©ºé–“å¥åº·æŒ‡æ•¸ã€‚"
-msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr "å­˜å–您執行器憑證,自定義您的æµæ°´ç·šé…置,並查看你的æµæ°´ç¾ç‹€æ…‹åŠæ¸¬è©¦æ¶µè“‹çŽ‡å ±å‘Šã€‚"
msgid "Account"
diff --git a/package.json b/package.json
index 35984e6d81f..ac9a73cd2c9 100644
--- a/package.json
+++ b/package.json
@@ -121,6 +121,7 @@
"commander": "^2.18.0",
"eslint": "~5.6.0",
"eslint-config-airbnb-base": "^13.1.0",
+ "eslint-config-prettier": "^3.1.0",
"eslint-import-resolver-webpack": "^0.10.1",
"eslint-plugin-filenames": "^1.3.2",
"eslint-plugin-html": "4.0.5",
@@ -144,7 +145,7 @@
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^4.0.0-beta.0",
"nodemon": "^1.18.4",
- "prettier": "1.12.1",
+ "prettier": "1.14.3",
"webpack-dev-server": "^3.1.8"
}
}
diff --git a/qa/qa.rb b/qa/qa.rb
index 227d4424b09..4499139d5cd 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -120,6 +120,7 @@ module QA
module Main
autoload :Login, 'qa/page/main/login'
+ autoload :Menu, 'qa/page/main/menu'
autoload :OAuth, 'qa/page/main/oauth'
autoload :SignUp, 'qa/page/main/sign_up'
end
@@ -128,13 +129,6 @@ module QA
autoload :Common, 'qa/page/settings/common'
end
- module Menu
- autoload :Main, 'qa/page/menu/main'
- autoload :Side, 'qa/page/menu/side'
- autoload :Admin, 'qa/page/menu/admin'
- autoload :Profile, 'qa/page/menu/profile'
- end
-
module Dashboard
autoload :Projects, 'qa/page/dashboard/projects'
autoload :Groups, 'qa/page/dashboard/groups'
@@ -158,6 +152,7 @@ module QA
autoload :New, 'qa/page/project/new'
autoload :Show, 'qa/page/project/show'
autoload :Activity, 'qa/page/project/activity'
+ autoload :Menu, 'qa/page/project/menu'
module Import
autoload :Github, 'qa/page/project/import/github'
@@ -201,6 +196,11 @@ module QA
end
module Operations
+ module Environments
+ autoload :Index, 'qa/page/project/operations/environments/index'
+ autoload :Show, 'qa/page/project/operations/environments/show'
+ end
+
module Kubernetes
autoload :Index, 'qa/page/project/operations/kubernetes/index'
autoload :Add, 'qa/page/project/operations/kubernetes/add'
@@ -217,6 +217,7 @@ module QA
end
module Profile
+ autoload :Menu, 'qa/page/profile/menu'
autoload :PersonalAccessTokens, 'qa/page/profile/personal_access_tokens'
autoload :SSHKeys, 'qa/page/profile/ssh_keys'
end
@@ -235,6 +236,8 @@ module QA
end
module Admin
+ autoload :Menu, 'qa/page/admin/menu'
+
module Settings
autoload :Repository, 'qa/page/admin/settings/repository'
@@ -257,6 +260,9 @@ module QA
autoload :Dropzone, 'qa/page/component/dropzone'
autoload :GroupsFilter, 'qa/page/component/groups_filter'
autoload :Select2, 'qa/page/component/select2'
+ module Issuable
+ autoload :Common, 'qa/page/component/issuable/common'
+ end
end
end
diff --git a/qa/qa/factory/resource/branch.rb b/qa/qa/factory/resource/branch.rb
index 60539992073..f3b52565d17 100644
--- a/qa/qa/factory/resource/branch.rb
+++ b/qa/qa/factory/resource/branch.rb
@@ -43,7 +43,7 @@ module QA
# to `allow_to_push` variable.
return branch unless @protected
- Page::Menu::Side.act do
+ Page::Project::Menu.act do
click_repository_settings
end
diff --git a/qa/qa/factory/resource/deploy_key.rb b/qa/qa/factory/resource/deploy_key.rb
index ea8a3ad687d..4c53c500c27 100644
--- a/qa/qa/factory/resource/deploy_key.rb
+++ b/qa/qa/factory/resource/deploy_key.rb
@@ -24,7 +24,7 @@ module QA
def fabricate!
project.visit!
- Page::Menu::Side.act do
+ Page::Project::Menu.act do
click_repository_settings
end
diff --git a/qa/qa/factory/resource/fork.rb b/qa/qa/factory/resource/fork.rb
index 1fa47e92983..83dd4000f0a 100644
--- a/qa/qa/factory/resource/fork.rb
+++ b/qa/qa/factory/resource/fork.rb
@@ -32,7 +32,7 @@ module QA
puts "Visited project page"
Capybara::Screenshot.screenshot_and_save_page
- return if Page::Menu::Main.act { has_personal_area?(wait: 0) }
+ return if Page::Main::Menu.act { has_personal_area?(wait: 0) }
puts "Not signed in. Attempting to sign in again."
Capybara::Screenshot.screenshot_and_save_page
diff --git a/qa/qa/factory/resource/kubernetes_cluster.rb b/qa/qa/factory/resource/kubernetes_cluster.rb
index 94d7df7128b..cdee35c54e3 100644
--- a/qa/qa/factory/resource/kubernetes_cluster.rb
+++ b/qa/qa/factory/resource/kubernetes_cluster.rb
@@ -16,7 +16,7 @@ module QA
def fabricate!
@project.visit!
- Page::Menu::Side.act { click_operations_kubernetes }
+ Page::Project::Menu.act { click_operations_kubernetes }
Page::Project::Operations::Kubernetes::Index.perform do |page|
page.add_kubernetes_cluster
@@ -31,6 +31,7 @@ module QA
page.set_api_url(@cluster.api_url)
page.set_ca_certificate(@cluster.ca_certificate)
page.set_token(@cluster.token)
+ page.check_rbac! if @cluster.rbac
page.add_cluster!
end
diff --git a/qa/qa/factory/resource/personal_access_token.rb b/qa/qa/factory/resource/personal_access_token.rb
index 514e3615d18..166054cfcdc 100644
--- a/qa/qa/factory/resource/personal_access_token.rb
+++ b/qa/qa/factory/resource/personal_access_token.rb
@@ -12,8 +12,8 @@ module QA
end
def fabricate!
- Page::Menu::Main.act { go_to_profile_settings }
- Page::Menu::Profile.act { click_access_tokens }
+ Page::Main::Menu.act { go_to_profile_settings }
+ Page::Profile::Menu.act { click_access_tokens }
Page::Profile::PersonalAccessTokens.perform do |page|
page.fill_token_name(name || 'api-test-token')
diff --git a/qa/qa/factory/resource/project_milestone.rb b/qa/qa/factory/resource/project_milestone.rb
index 47a5e74204f..1251ae03135 100644
--- a/qa/qa/factory/resource/project_milestone.rb
+++ b/qa/qa/factory/resource/project_milestone.rb
@@ -17,7 +17,7 @@ module QA
def fabricate!
project.visit!
- Page::Menu::Side.act do
+ Page::Project::Menu.act do
click_issues
click_milestones
end
diff --git a/qa/qa/factory/resource/runner.rb b/qa/qa/factory/resource/runner.rb
index 03b69eb1bdf..7ac65fe6913 100644
--- a/qa/qa/factory/resource/runner.rb
+++ b/qa/qa/factory/resource/runner.rb
@@ -26,7 +26,7 @@ module QA
def fabricate!
project.visit!
- Page::Menu::Side.act { click_ci_cd_settings }
+ Page::Project::Menu.act { click_ci_cd_settings }
Service::Runner.new(name).tap do |runner|
Page::Project::Settings::CICD.perform do |settings|
diff --git a/qa/qa/factory/resource/sandbox.rb b/qa/qa/factory/resource/sandbox.rb
index 4f6039f300f..5249e1755a6 100644
--- a/qa/qa/factory/resource/sandbox.rb
+++ b/qa/qa/factory/resource/sandbox.rb
@@ -11,7 +11,7 @@ module QA
end
def fabricate!
- Page::Menu::Main.act { go_to_groups }
+ Page::Main::Menu.act { go_to_groups }
Page::Dashboard::Groups.perform do |page|
if page.has_group?(@name)
diff --git a/qa/qa/factory/resource/secret_variable.rb b/qa/qa/factory/resource/secret_variable.rb
index 12a830da116..4084a7fc2cd 100644
--- a/qa/qa/factory/resource/secret_variable.rb
+++ b/qa/qa/factory/resource/secret_variable.rb
@@ -12,7 +12,7 @@ module QA
def fabricate!
project.visit!
- Page::Menu::Side.act { click_ci_cd_settings }
+ Page::Project::Menu.act { click_ci_cd_settings }
Page::Project::Settings::CICD.perform do |setting|
setting.expand_secret_variables do |page|
diff --git a/qa/qa/factory/resource/ssh_key.rb b/qa/qa/factory/resource/ssh_key.rb
index 6c872f32d16..45236f69de9 100644
--- a/qa/qa/factory/resource/ssh_key.rb
+++ b/qa/qa/factory/resource/ssh_key.rb
@@ -27,8 +27,8 @@ module QA
end
def fabricate!
- Page::Menu::Main.act { go_to_profile_settings }
- Page::Menu::Profile.act { click_ssh_keys }
+ Page::Main::Menu.act { go_to_profile_settings }
+ Page::Profile::Menu.act { click_ssh_keys }
Page::Profile::SSHKeys.perform do |page|
page.add_key(public_key, title)
diff --git a/qa/qa/factory/resource/user.rb b/qa/qa/factory/resource/user.rb
index 34b52223b2d..e8b9ea2e6b4 100644
--- a/qa/qa/factory/resource/user.rb
+++ b/qa/qa/factory/resource/user.rb
@@ -38,8 +38,8 @@ module QA
def fabricate!
# Don't try to log-out if we're not logged-in
- if Page::Menu::Main.act { has_personal_area?(wait: 0) }
- Page::Menu::Main.perform { |main| main.sign_out }
+ if Page::Main::Menu.act { has_personal_area?(wait: 0) }
+ Page::Main::Menu.perform { |main| main.sign_out }
end
if credentials_given?
diff --git a/qa/qa/factory/resource/wiki.rb b/qa/qa/factory/resource/wiki.rb
index cc200a512d5..acfe143fa61 100644
--- a/qa/qa/factory/resource/wiki.rb
+++ b/qa/qa/factory/resource/wiki.rb
@@ -10,7 +10,7 @@ module QA
end
def fabricate!
- Page::Menu::Side.act { click_wiki }
+ Page::Project::Menu.act { click_wiki }
Page::Project::Wiki::New.perform do |page|
page.go_to_create_first_page
page.set_title(@title)
diff --git a/qa/qa/factory/settings/hashed_storage.rb b/qa/qa/factory/settings/hashed_storage.rb
index c69ebed3c6b..f2e58a3ea38 100644
--- a/qa/qa/factory/settings/hashed_storage.rb
+++ b/qa/qa/factory/settings/hashed_storage.rb
@@ -6,8 +6,8 @@ module QA
raise ArgumentError unless traits.include?(:enabled)
Page::Main::Login.act { sign_in_using_credentials }
- Page::Menu::Main.act { go_to_admin_area }
- Page::Menu::Admin.act { go_to_settings }
+ Page::Main::Menu.act { go_to_admin_area }
+ Page::Admin::Menu.act { go_to_repository_settings }
Page::Admin::Settings::Main.perform do |setting|
setting.expand_repository_storage do |page|
@@ -16,7 +16,7 @@ module QA
end
end
- QA::Page::Menu::Main.act { sign_out }
+ QA::Page::Main::Menu.act { sign_out }
end
end
end
diff --git a/qa/qa/page/README.md b/qa/qa/page/README.md
index 2dbc59846e7..dfad460a9a5 100644
--- a/qa/qa/page/README.md
+++ b/qa/qa/page/README.md
@@ -86,8 +86,12 @@ module Page
end
```
-It is possible to use `element` DSL method without value, with a String value
-or with a Regexp.
+The `view` DSL method declares the filename of the view where an
+`element` is implmented.
+
+The `element` DSL method in turn declares an element and defines a value
+to match to the actual view code. It is possible to use `element` with value,
+with a String value or with a Regexp.
```ruby
view 'app/views/my/view.html.haml' do
diff --git a/qa/qa/page/menu/admin.rb b/qa/qa/page/admin/menu.rb
index bf05a912bc6..e8c7d274966 100644
--- a/qa/qa/page/menu/admin.rb
+++ b/qa/qa/page/admin/menu.rb
@@ -1,7 +1,9 @@
+# frozen_string_literal: true
+
module QA
module Page
- module Menu
- class Admin < Page::Base
+ module Admin
+ class Menu < Page::Base
view 'app/views/layouts/nav/sidebar/_admin.html.haml' do
element :admin_sidebar
element :admin_sidebar_submenu
diff --git a/qa/qa/page/component/issuable/common.rb b/qa/qa/page/component/issuable/common.rb
new file mode 100644
index 00000000000..cfd8ac1e7c8
--- /dev/null
+++ b/qa/qa/page/component/issuable/common.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Component
+ module Issuable
+ module Common
+ def self.included(base)
+ base.view 'app/assets/javascripts/issue_show/components/title.vue' do
+ element :edit_button
+ end
+
+ base.view 'app/assets/javascripts/issue_show/components/fields/title.vue' do
+ element :title_input
+ end
+
+ base.view 'app/assets/javascripts/issue_show/components/fields/description.vue' do
+ element :description_textarea
+ end
+
+ base.view 'app/assets/javascripts/issue_show/components/edit_actions.vue' do
+ element :save_button
+ element :delete_button
+ end
+
+ base.view 'app/assets/javascripts/issue_show/components/edit_actions.vue' do
+ element :save_button
+ element :delete_button
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb
index 9b3183ba328..eab7a85ff04 100644
--- a/qa/qa/page/main/login.rb
+++ b/qa/qa/page/main/login.rb
@@ -37,13 +37,13 @@ module QA
# we are already logged-in so we check both cases here.
wait(max: 500) do
has_css?('.login-page') ||
- Page::Menu::Main.act { has_personal_area?(wait: 0) }
+ Page::Main::Menu.act { has_personal_area?(wait: 0) }
end
end
def sign_in_using_credentials(user = nil)
# Don't try to log-in if we're already logged-in
- return if Page::Menu::Main.act { has_personal_area?(wait: 0) }
+ return if Page::Main::Menu.act { has_personal_area?(wait: 0) }
using_wait_time 0 do
set_initial_password_if_present
@@ -57,7 +57,7 @@ module QA
end
end
- Page::Menu::Main.act { has_personal_area? }
+ Page::Main::Menu.act { has_personal_area? }
end
def sign_in_using_admin_credentials
@@ -72,7 +72,7 @@ module QA
sign_in_using_gitlab_credentials(admin)
end
- Page::Menu::Main.act { has_personal_area? }
+ Page::Main::Menu.act { has_personal_area? }
end
def self.path
diff --git a/qa/qa/page/menu/main.rb b/qa/qa/page/main/menu.rb
index 2ae86bbc7dc..e18b95bde9f 100644
--- a/qa/qa/page/menu/main.rb
+++ b/qa/qa/page/main/menu.rb
@@ -1,7 +1,9 @@
+# frozen_string_literal: true
+
module QA
module Page
- module Menu
- class Main < Page::Base
+ module Main
+ class Menu < Page::Base
view 'app/views/layouts/header/_current_user_dropdown.html.haml' do
element :user_sign_out_link, 'link_to _("Sign out")'
element :settings_link, 'link_to s_("CurrentUser|Settings")'
diff --git a/qa/qa/page/main/sign_up.rb b/qa/qa/page/main/sign_up.rb
index 64cd395de78..dddda4f2bdf 100644
--- a/qa/qa/page/main/sign_up.rb
+++ b/qa/qa/page/main/sign_up.rb
@@ -19,7 +19,7 @@ module QA
fill_in :new_user_password, with: user.password
click_button 'Register'
- Page::Menu::Main.act { assert_has_personal_area }
+ Page::Main::Menu.act { assert_has_personal_area }
end
end
end
diff --git a/qa/qa/page/menu/profile.rb b/qa/qa/page/profile/menu.rb
index 7e24fa85c33..f8a7d64e016 100644
--- a/qa/qa/page/menu/profile.rb
+++ b/qa/qa/page/profile/menu.rb
@@ -1,7 +1,9 @@
+# frozen_string_literal: true
+
module QA
module Page
- module Menu
- class Profile < Page::Base
+ module Profile
+ class Menu < Page::Base
view 'app/views/layouts/nav/sidebar/_profile.html.haml' do
element :access_token_link, 'link_to profile_personal_access_tokens_path'
element :access_token_title, 'Access Tokens'
diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb
index 587a02163b9..9a738f56202 100644
--- a/qa/qa/page/project/issue/show.rb
+++ b/qa/qa/page/project/issue/show.rb
@@ -5,6 +5,8 @@ module QA
module Project
module Issue
class Show < Page::Base
+ include Page::Component::Issuable::Common
+
view 'app/views/projects/issues/show.html.haml' do
element :issue_details, '.issue-details'
element :title, '.title'
diff --git a/qa/qa/page/menu/side.rb b/qa/qa/page/project/menu.rb
index a1eedfea42e..d9f01c50f19 100644
--- a/qa/qa/page/menu/side.rb
+++ b/qa/qa/page/project/menu.rb
@@ -1,7 +1,9 @@
+# frozen_string_literal: true
+
module QA
module Page
- module Menu
- class Side < Page::Base
+ module Project
+ class Menu < Page::Base
view 'app/views/layouts/nav/sidebar/_project.html.haml' do
element :settings_item
element :settings_link, 'link_to edit_project_path'
@@ -9,6 +11,7 @@ module QA
element :link_pipelines
element :pipelines_settings_link, "title: _('CI / CD')"
element :operations_kubernetes_link, "title: _('Kubernetes')"
+ element :operations_environments_link
element :issues_link, /link_to.*shortcuts-issues/
element :issues_link_text, "Issues"
element :merge_requests_link, /link_to.*shortcuts-merge_requests/
@@ -40,6 +43,14 @@ module QA
end
end
+ def click_operations_environments
+ hover_operations do
+ within_submenu do
+ click_element(:operations_environments_link)
+ end
+ end
+ end
+
def click_operations_kubernetes
hover_operations do
within_submenu do
diff --git a/qa/qa/page/project/operations/environments/index.rb b/qa/qa/page/project/operations/environments/index.rb
new file mode 100644
index 00000000000..63965a57edd
--- /dev/null
+++ b/qa/qa/page/project/operations/environments/index.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Project
+ module Operations
+ module Environments
+ class Index < Page::Base
+ view 'app/assets/javascripts/environments/components/environment_item.vue' do
+ element :environment_link
+ end
+
+ def go_to_environment(environment_name)
+ wait(reload: false) do
+ find(element_selector_css(:environment_link), text: environment_name).click
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/operations/environments/show.rb b/qa/qa/page/project/operations/environments/show.rb
new file mode 100644
index 00000000000..aa88c218c89
--- /dev/null
+++ b/qa/qa/page/project/operations/environments/show.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Project
+ module Operations
+ module Environments
+ class Show < Page::Base
+ view 'app/views/projects/environments/_external_url.html.haml' do
+ element :view_deployment
+ end
+
+ def view_deployment(&block)
+ new_window = window_opened_by { click_element(:view_deployment) }
+
+ within_window(new_window, &block) if block
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/operations/kubernetes/add_existing.rb b/qa/qa/page/project/operations/kubernetes/add_existing.rb
index eef82b5f329..38f8527b9b4 100644
--- a/qa/qa/page/project/operations/kubernetes/add_existing.rb
+++ b/qa/qa/page/project/operations/kubernetes/add_existing.rb
@@ -10,6 +10,7 @@ module QA
element :ca_certificate, 'text_area :ca_cert'
element :token, 'text_field :token'
element :add_cluster_button, "submit s_('ClusterIntegration|Add Kubernetes cluster')"
+ element :rbac_checkbox
end
def set_cluster_name(name)
@@ -31,6 +32,10 @@ module QA
def add_cluster!
click_on 'Add Kubernetes cluster'
end
+
+ def check_rbac!
+ check_element :rbac_checkbox
+ end
end
end
end
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index 27ba915961d..5bebb5ccec0 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -5,13 +5,17 @@ module QA
# set to 'false' to have Chrome run visibly instead of headless
def chrome_headless?
- (ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i) != 0
+ enabled?(ENV['CHROME_HEADLESS'])
end
def running_in_ci?
ENV['CI'] || ENV['CI_SERVER']
end
+ def signup_disabled?
+ enabled?(ENV['SIGNUP_DISABLED'], default: false)
+ end
+
# specifies token that can be used for the api
def personal_access_token
ENV['PERSONAL_ACCESS_TOKEN']
@@ -83,6 +87,14 @@ module QA
raise ArgumentError, "Please provide GITHUB_ACCESS_TOKEN"
end
+
+ private
+
+ def enabled?(value, default: true)
+ return default if value.nil?
+
+ (value =~ /^(false|no|0)$/i) != 0
+ end
end
end
end
diff --git a/qa/qa/service/kubernetes_cluster.rb b/qa/qa/service/kubernetes_cluster.rb
index abd9d53554f..c5f12255d72 100644
--- a/qa/qa/service/kubernetes_cluster.rb
+++ b/qa/qa/service/kubernetes_cluster.rb
@@ -1,12 +1,17 @@
require 'securerandom'
require 'mkmf'
+require 'pathname'
module QA
module Service
class KubernetesCluster
include Service::Shellout
- attr_reader :api_url, :ca_certificate, :token
+ attr_reader :api_url, :ca_certificate, :token, :rbac
+
+ def initialize(rbac: false)
+ @rbac = rbac
+ end
def cluster_name
@cluster_name ||= "qa-cluster-#{SecureRandom.hex(4)}-#{Time.now.utc.strftime("%Y%m%d%H%M%S")}"
@@ -19,7 +24,8 @@ module QA
shell <<~CMD.tr("\n", ' ')
gcloud container clusters
create #{cluster_name}
- --enable-legacy-authorization
+ #{auth_options}
+ --enable-basic-auth
--zone #{Runtime::Env.gcloud_zone}
&& gcloud container clusters
get-credentials
@@ -28,8 +34,30 @@ module QA
CMD
@api_url = `kubectl config view --minify -o jsonpath='{.clusters[].cluster.server}'`
- @ca_certificate = Base64.decode64(`kubectl get secrets -o jsonpath="{.items[0].data['ca\\.crt']}"`)
- @token = Base64.decode64(`kubectl get secrets -o jsonpath='{.items[0].data.token}'`)
+
+ @admin_user = "#{cluster_name}-admin"
+ master_auth = JSON.parse(`gcloud container clusters describe #{cluster_name} --zone #{Runtime::Env.gcloud_zone} --format 'json(masterAuth.username, masterAuth.password)'`)
+ shell <<~CMD.tr("\n", ' ')
+ kubectl config set-credentials #{@admin_user}
+ --username #{master_auth['masterAuth']['username']}
+ --password #{master_auth['masterAuth']['password']}
+ CMD
+
+ if rbac
+ create_service_account
+
+ secrets = JSON.parse(`kubectl get secrets -o json`)
+ gitlab_account = secrets['items'].find do |item|
+ item['metadata']['annotations']['kubernetes.io/service-account.name'] == 'gitlab-account'
+ end
+
+ @ca_certificate = Base64.decode64(gitlab_account['data']['ca.crt'])
+ @token = Base64.decode64(gitlab_account['data']['token'])
+ else
+ @ca_certificate = Base64.decode64(`kubectl get secrets -o jsonpath="{.items[0].data['ca\\.crt']}"`)
+ @token = Base64.decode64(`kubectl get secrets -o jsonpath='{.items[0].data.token}'`)
+ end
+
self
end
@@ -44,6 +72,42 @@ module QA
private
+ def create_service_account
+ shell('kubectl create -f -', stdin_data: service_account)
+ shell("kubectl --user #{@admin_user} create -f -", stdin_data: service_account_role_binding)
+ end
+
+ def service_account
+ <<~YAML
+ apiVersion: v1
+ kind: ServiceAccount
+ metadata:
+ name: gitlab-account
+ namespace: default
+ YAML
+ end
+
+ def service_account_role_binding
+ <<~YAML
+ kind: ClusterRoleBinding
+ apiVersion: rbac.authorization.k8s.io/v1
+ metadata:
+ name: gitlab-account-binding
+ subjects:
+ - kind: ServiceAccount
+ name: gitlab-account
+ namespace: default
+ roleRef:
+ kind: ClusterRole
+ name: cluster-admin
+ apiGroup: rbac.authorization.k8s.io
+ YAML
+ end
+
+ def auth_options
+ "--enable-legacy-authorization" unless rbac
+ end
+
def validate_dependencies
find_executable('gcloud') || raise("You must first install `gcloud` executable to run these tests.")
find_executable('kubectl') || raise("You must first install `kubectl` executable to run these tests.")
diff --git a/qa/qa/service/shellout.rb b/qa/qa/service/shellout.rb
index 1ca9504bb33..43dc0851571 100644
--- a/qa/qa/service/shellout.rb
+++ b/qa/qa/service/shellout.rb
@@ -11,10 +11,12 @@ module QA
# TODO, make it possible to use generic QA framework classes
# as a library - gitlab-org/gitlab-qa#94
#
- def shell(command)
+ def shell(command, stdin_data: nil)
puts "Executing `#{command}`"
- Open3.popen2e(*command) do |_in, out, wait|
+ Open3.popen2e(*command) do |stdin, out, wait|
+ stdin.puts(stdin_data) if stdin_data
+ stdin.close if stdin_data
out.each { |line| puts line }
if wait.value.exited? && wait.value.exitstatus.nonzero?
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb
index 1c7da930567..ae196349c6b 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb
@@ -8,7 +8,7 @@ module QA
# TODO, since `Signed in successfully` message was removed
# this is the only way to tell if user is signed in correctly.
#
- Page::Menu::Main.perform do |menu|
+ Page::Main::Menu.perform do |menu|
expect(menu).to have_personal_area
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb
index c296296def6..217870531da 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb
@@ -10,7 +10,7 @@ module QA
# TODO, since `Signed in successfully` message was removed
# this is the only way to tell if user is signed in correctly.
#
- Page::Menu::Main.perform do |menu|
+ Page::Main::Menu.perform do |menu|
expect(menu).to have_personal_area
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
index 478a5cb9c4c..fb6b4937554 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
@@ -10,19 +10,19 @@ module QA
# TODO, since `Signed in successfully` message was removed
# this is the only way to tell if user is signed in correctly.
#
- Page::Menu::Main.perform do |menu|
+ Page::Main::Menu.perform do |menu|
expect(menu).to have_personal_area
end
end
end
- context :manage do
+ context :manage, :skip_signup_disabled do
describe 'standard' do
it_behaves_like 'registration and login'
end
end
- context :manage, :orchestrated, :ldap do
+ context :manage, :orchestrated, :ldap, :skip_signup_disabled do
describe 'while LDAP is enabled' do
it_behaves_like 'registration and login'
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb
index 2ef8de61441..d1cd9865aef 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb
@@ -27,7 +27,7 @@ module QA
imported_project # import the project
- Page::Menu::Main.act { go_to_projects }
+ Page::Main::Menu.act { go_to_projects }
Page::Dashboard::Projects.perform do |dashboard|
dashboard.go_to_project(imported_project.name)
end
@@ -48,7 +48,7 @@ module QA
end
def verify_issues_import
- Page::Menu::Side.act { click_issues }
+ Page::Project::Menu.act { click_issues }
expect(page).to have_content('This is a sample issue')
click_link 'This is a sample issue'
@@ -66,7 +66,7 @@ module QA
end
def verify_merge_requests_import
- Page::Menu::Side.act { click_merge_requests }
+ Page::Project::Menu.act { click_merge_requests }
expect(page).to have_content('Improve README.md')
click_link 'Improve README.md'
@@ -101,7 +101,7 @@ module QA
end
def verify_wiki_import
- Page::Menu::Side.act { click_wiki }
+ Page::Project::Menu.act { click_wiki }
expect(page).to have_content('Welcome to the test-project wiki!')
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb
index 34bb6f1c197..97ac35e8dba 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb
@@ -13,7 +13,7 @@ module QA
push.commit_message = 'Add README.md'
end
- Page::Menu::Side.act { go_to_activity }
+ Page::Project::Menu.act { go_to_activity }
Page::Project::Activity.act { go_to_push_events }
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
index 542f532a629..49d76f31e3a 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
@@ -17,7 +17,7 @@ module QA
it 'user creates an issue' do
create_issue
- Page::Menu::Side.act { click_issues }
+ Page::Project::Menu.act { click_issues }
expect(page).to have_content(issue_title)
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb
index 407a15800ab..922feadb4e1 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb
@@ -11,7 +11,7 @@ module QA
merge_request.fork_branch = 'feature-branch'
end
- Page::Menu::Main.perform { |main| main.sign_out }
+ Page::Main::Menu.perform { |main| main.sign_out }
Page::Main::Login.perform { |login| login.sign_in_using_credentials }
merge_request.visit!
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
index ddcbc94b1b1..984cea8ca10 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
@@ -11,7 +11,7 @@ module QA
project.name = "only-fast-forward"
end
- Page::Menu::Side.act { go_to_settings }
+ Page::Project::Menu.act { go_to_settings }
Page::Project::Settings::MergeRequest.act { enable_ff_only }
merge_request = Factory::Resource::MergeRequest.fabricate! do |merge_request|
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb
index 84f663c4866..b163ca896a7 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb
@@ -16,8 +16,8 @@ module QA
expect(page).to have_content("Title: #{key_title}")
expect(page).to have_content(key.fingerprint)
- Page::Menu::Main.act { go_to_profile_settings }
- Page::Menu::Profile.act { click_ssh_keys }
+ Page::Main::Menu.act { go_to_profile_settings }
+ Page::Profile::Menu.act { click_ssh_keys }
Page::Profile::SSHKeys.perform do |ssh_keys|
ssh_keys.remove_key(key_title)
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/use_ssh_key_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/use_ssh_key_spec.rb
index 7c989bfd8cc..563393b3d07 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/use_ssh_key_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/use_ssh_key_spec.rb
@@ -28,8 +28,8 @@ module QA
expect(page).to have_content('README.md')
expect(page).to have_content('Test Use SSH Key')
- Page::Menu::Main.act { go_to_profile_settings }
- Page::Menu::Profile.act { click_ssh_keys }
+ Page::Main::Menu.act { go_to_profile_settings }
+ Page::Profile::Menu.act { click_ssh_keys }
Page::Profile::SSHKeys.perform do |ssh_keys|
ssh_keys.remove_key(key_title)
diff --git a/qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb b/qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb
index 8009b9e8609..44dd85c1746 100644
--- a/qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb
@@ -40,7 +40,7 @@ module QA
push.file_content = '# My Third Wiki Content'
push.commit_message = 'Update Home.md'
end
- Page::Menu::Side.act { click_wiki }
+ Page::Project::Menu.act { click_wiki }
expect(page).to have_content('My Third Wiki Content')
end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb
index cdfe9b90e15..e901531b1bf 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb
@@ -64,7 +64,7 @@ module QA
expect(page).to have_content('Add .gitlab-ci.yml')
- Page::Menu::Side.act { click_ci_cd_pipelines }
+ Page::Project::Menu.act { click_ci_cd_pipelines }
expect(page).to have_content('All 1')
expect(page).to have_content('Add .gitlab-ci.yml')
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 8352d13b06d..73af24e7f50 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
@@ -28,7 +28,7 @@ module QA
resource.image = 'gitlab/gitlab-runner:ubuntu'
end
- Page::Menu::Main.act { sign_out }
+ Page::Main::Menu.act { sign_out }
end
after(:all) do
@@ -90,7 +90,7 @@ module QA
sha1sum = Digest::SHA1.hexdigest(gitlab_ci)
Page::Project::Show.act { wait_for_push }
- Page::Menu::Side.act { click_ci_cd_pipelines }
+ Page::Project::Menu.act { click_ci_cd_pipelines }
Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
Page::Project::Pipeline::Show.act { go_to_first_job }
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 844cc1236c7..785897f4a97 100644
--- a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
+++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
@@ -9,59 +9,73 @@ module QA
@cluster&.remove!
end
- it 'user creates a new project and runs auto devops' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
+ [true, false].each do |rbac|
+ context "when rbac is #{rbac ? 'enabled' : 'disabled'}" do
+ it 'user creates a new project and runs auto devops' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
- project = Factory::Resource::Project.fabricate! do |p|
- p.name = 'project-with-autodevops'
- p.description = 'Project with Auto Devops'
- end
+ project = Factory::Resource::Project.fabricate! do |p|
+ p.name = 'project-with-autodevops'
+ p.description = 'Project with Auto Devops'
+ end
- # Disable code_quality check in Auto DevOps pipeline as it takes
- # too long and times out the test
- Factory::Resource::SecretVariable.fabricate! do |resource|
- resource.project = project
- resource.key = 'CODE_QUALITY_DISABLED'
- resource.value = '1'
- end
+ # Disable code_quality check in Auto DevOps pipeline as it takes
+ # too long and times out the test
+ Factory::Resource::SecretVariable.fabricate! do |resource|
+ resource.project = project
+ resource.key = 'CODE_QUALITY_DISABLED'
+ resource.value = '1'
+ end
- # Create Auto Devops compatible repo
- Factory::Repository::ProjectPush.fabricate! do |push|
- push.project = project
- push.directory = Pathname
- .new(__dir__)
- .join('../../../../../fixtures/auto_devops_rack')
- push.commit_message = 'Create Auto DevOps compatible rack application'
- end
+ # Create Auto Devops compatible repo
+ Factory::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.directory = Pathname
+ .new(__dir__)
+ .join('../../../../../fixtures/auto_devops_rack')
+ push.commit_message = 'Create Auto DevOps compatible rack application'
+ end
- Page::Project::Show.act { wait_for_push }
+ Page::Project::Show.act { wait_for_push }
- # Create and connect K8s cluster
- @cluster = Service::KubernetesCluster.new.create!
- kubernetes_cluster = Factory::Resource::KubernetesCluster.fabricate! do |cluster|
- cluster.project = project
- cluster.cluster = @cluster
- cluster.install_helm_tiller = true
- cluster.install_ingress = true
- cluster.install_prometheus = true
- cluster.install_runner = true
- end
+ # Create and connect K8s cluster
+ @cluster = Service::KubernetesCluster.new(rbac: rbac).create!
+ kubernetes_cluster = Factory::Resource::KubernetesCluster.fabricate! do |cluster|
+ cluster.project = project
+ cluster.cluster = @cluster
+ cluster.install_helm_tiller = true
+ cluster.install_ingress = true
+ cluster.install_prometheus = true
+ cluster.install_runner = true
+ end
- project.visit!
- Page::Menu::Side.act { click_ci_cd_settings }
- Page::Project::Settings::CICD.perform do |p|
- p.enable_auto_devops_with_domain("#{kubernetes_cluster.ingress_ip}.nip.io")
- end
+ project.visit!
+ Page::Project::Menu.act { click_ci_cd_settings }
+ Page::Project::Settings::CICD.perform do |p|
+ p.enable_auto_devops_with_domain("#{kubernetes_cluster.ingress_ip}.nip.io")
+ end
+
+ project.visit!
+ Page::Project::Menu.act { click_ci_cd_pipelines }
+ Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
- project.visit!
- Page::Menu::Side.act { click_ci_cd_pipelines }
- Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ expect(pipeline).to have_build('build', status: :success, wait: 600)
+ expect(pipeline).to have_build('test', status: :success, wait: 600)
+ expect(pipeline).to have_build('production', status: :success, wait: 1200)
+ end
- Page::Project::Pipeline::Show.perform do |pipeline|
- expect(pipeline).to have_build('build', status: :success, wait: 600)
- expect(pipeline).to have_build('test', status: :success, wait: 600)
- expect(pipeline).to have_build('production', status: :success, wait: 1200)
+ Page::Menu::Side.act { click_operations_environments }
+ Page::Project::Operations::Environments::Index.perform do |index|
+ index.go_to_environment('production')
+ end
+ Page::Project::Operations::Environments::Show.perform do |show|
+ show.view_deployment do
+ expect(page).to have_content('Hello World!')
+ end
+ end
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/7_configure/mattermost/create_group_with_mattermost_team_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/mattermost/create_group_with_mattermost_team_spec.rb
index 6ffdc55538a..af24b36b734 100644
--- a/qa/qa/specs/features/browser_ui/7_configure/mattermost/create_group_with_mattermost_team_spec.rb
+++ b/qa/qa/specs/features/browser_ui/7_configure/mattermost/create_group_with_mattermost_team_spec.rb
@@ -6,7 +6,7 @@ module QA
it 'user creates a group with a mattermost team' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
- Page::Menu::Main.act { go_to_groups }
+ Page::Main::Menu.act { go_to_groups }
Page::Dashboard::Groups.perform do |page|
page.go_to_new_group
diff --git a/qa/qa/specs/runner.rb b/qa/qa/specs/runner.rb
index fea0ef94df3..ad397c13f0c 100644
--- a/qa/qa/specs/runner.rb
+++ b/qa/qa/specs/runner.rb
@@ -23,6 +23,8 @@ module QA
args.push(%w[--tag ~orchestrated]) unless (%w[-t --tag] & options).any?
end
+ args.push(%w[--tag ~skip_signup_disabled]) if QA::Runtime::Env.signup_disabled?
+
args.push(options)
args.push(DEFAULT_TEST_PATH_ARGS) unless options.any? { |opt| opt =~ %r{/features/} }
diff --git a/qa/spec/runtime/env_spec.rb b/qa/spec/runtime/env_spec.rb
index d889d185a45..fda955f6600 100644
--- a/qa/spec/runtime/env_spec.rb
+++ b/qa/spec/runtime/env_spec.rb
@@ -1,39 +1,47 @@
describe QA::Runtime::Env do
include Support::StubENV
- describe '.chrome_headless?' do
+ shared_examples 'boolean method' do |method, env_key, default|
context 'when there is an env variable set' do
it 'returns false when falsey values specified' do
- stub_env('CHROME_HEADLESS', 'false')
- expect(described_class.chrome_headless?).to be_falsey
+ stub_env(env_key, 'false')
+ expect(described_class.public_send(method)).to be_falsey
- stub_env('CHROME_HEADLESS', 'no')
- expect(described_class.chrome_headless?).to be_falsey
+ stub_env(env_key, 'no')
+ expect(described_class.public_send(method)).to be_falsey
- stub_env('CHROME_HEADLESS', '0')
- expect(described_class.chrome_headless?).to be_falsey
+ stub_env(env_key, '0')
+ expect(described_class.public_send(method)).to be_falsey
end
it 'returns true when anything else specified' do
- stub_env('CHROME_HEADLESS', 'true')
- expect(described_class.chrome_headless?).to be_truthy
+ stub_env(env_key, 'true')
+ expect(described_class.public_send(method)).to be_truthy
- stub_env('CHROME_HEADLESS', '1')
- expect(described_class.chrome_headless?).to be_truthy
+ stub_env(env_key, '1')
+ expect(described_class.public_send(method)).to be_truthy
- stub_env('CHROME_HEADLESS', 'anything')
- expect(described_class.chrome_headless?).to be_truthy
+ stub_env(env_key, 'anything')
+ expect(described_class.public_send(method)).to be_truthy
end
end
context 'when there is no env variable set' do
- it 'returns the default, true' do
- stub_env('CHROME_HEADLESS', nil)
- expect(described_class.chrome_headless?).to be_truthy
+ it "returns the default, #{default}" do
+ stub_env(env_key, nil)
+ expect(described_class.public_send(method)).to be(default)
end
end
end
+ describe '.signup_disabled?' do
+ it_behaves_like 'boolean method', :signup_disabled?, 'SIGNUP_DISABLED', false
+ end
+
+ describe '.chrome_headless?' do
+ it_behaves_like 'boolean method', :chrome_headless?, 'CHROME_HEADLESS', true
+ end
+
describe '.running_in_ci?' do
context 'when there is an env variable set' do
it 'returns true if CI' do
diff --git a/qa/spec/specs/runner_spec.rb b/qa/spec/specs/runner_spec.rb
index cf22d1c9395..9ddaf7ab1b3 100644
--- a/qa/spec/specs/runner_spec.rb
+++ b/qa/spec/specs/runner_spec.rb
@@ -62,6 +62,20 @@ describe QA::Specs::Runner do
end
end
+ context 'when SIGNUP_DISABLED is true' do
+ before do
+ allow(QA::Runtime::Env).to receive(:signup_disabled?).and_return(true)
+ end
+
+ subject { described_class.new }
+
+ it 'it includes default args and excludes the skip_signup_disabled tag' do
+ expect_rspec_runner_arguments(['--tag', '~orchestrated', '--tag', '~skip_signup_disabled', *described_class::DEFAULT_TEST_PATH_ARGS])
+
+ subject.perform
+ end
+ end
+
def expect_rspec_runner_arguments(arguments)
expect(RSpec::Core::Runner).to receive(:run)
.with(arguments, $stderr, $stdout)
diff --git a/scripts/trigger-build b/scripts/trigger-build
index 0b5fd5995dd..4534fcadebf 100755
--- a/scripts/trigger-build
+++ b/scripts/trigger-build
@@ -1,4 +1,5 @@
#!/usr/bin/env ruby
+# frozen_string_literal: true
require 'gitlab'
@@ -6,38 +7,27 @@ require 'gitlab'
# Configure credentials to be used with gitlab gem
#
Gitlab.configure do |config|
- config.endpoint = 'https://gitlab.com/api/v4'
- config.private_token = ENV['GITLAB_QA_ACCESS_TOKEN'] # gitlab-qa bot access token
+ config.endpoint = 'https://gitlab.com/api/v4'
end
module Trigger
- TOKEN = ENV['BUILD_TRIGGER_TOKEN']
-
def self.ee?
ENV['CI_PROJECT_NAME'] == 'gitlab-ee' || File.exist?('CHANGELOG-EE.md')
end
class Base
- def initialize(api_token)
- Gitlab.private_token = api_token
- end
-
def invoke!(post_comment: false)
pipeline = Gitlab.run_trigger(
downstream_project_path,
- Trigger::TOKEN,
+ trigger_token,
ref,
variables)
- puts "Triggered #{pipeline.web_url}"
+ puts "Triggered downstream pipeline: #{pipeline.web_url}\n"
puts "Waiting for downstream pipeline status"
- begin
- Trigger::CommitComment.post!(downstream_project_path, pipeline) if post_comment
- rescue Gitlab::Error::Error => error
- puts "Ignoring the following error: #{error}"
- end
- Trigger::Pipeline.new(downstream_project_path, pipeline.id)
+ Trigger::CommitComment.post!(pipeline, access_token) if post_comment
+ Trigger::Pipeline.new(downstream_project_path, pipeline.id, access_token)
end
private
@@ -52,6 +42,16 @@ module Trigger
raise NotImplementedError
end
+ # Must be overriden
+ def trigger_token
+ raise NotImplementedError
+ end
+
+ # Must be overriden
+ def access_token
+ raise NotImplementedError
+ end
+
# Can be overriden
def extra_variables
{}
@@ -68,7 +68,10 @@ module Trigger
def base_variables
{
- 'TRIGGERED_USER' => ENV['GITLAB_USER_NAME'],
+ 'TOP_UPSTREAM_TRIGGER_PROJECT' => ENV['TOP_UPSTREAM_TRIGGER_PROJECT'] || ENV['CI_PROJECT_PATH'],
+ 'UPSTREAM_TRIGGER_PROJECT' => ENV['CI_PROJECT_PATH'],
+ 'UPSTREAM_TRIGGER_SOURCE' => ENV['TRIGGER_SOURCE'],
+ 'TRIGGERED_USER' => ENV['TRIGGERED_USER'] || ENV['GITLAB_USER_NAME'],
'TRIGGER_SOURCE' => ENV['CI_JOB_URL']
}
end
@@ -85,13 +88,21 @@ module Trigger
private
def downstream_project_path
- 'gitlab-org/omnibus-gitlab'.freeze
+ 'gitlab-org/omnibus-gitlab'
end
def ref
ENV['OMNIBUS_BRANCH'] || 'master'
end
+ def trigger_token
+ ENV['BUILD_TRIGGER_TOKEN']
+ end
+
+ def access_token
+ ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
+ end
+
def extra_variables
{
'GITLAB_VERSION' => ENV['CI_COMMIT_SHA'],
@@ -112,6 +123,14 @@ module Trigger
ENV['CNG_BRANCH'] || 'master'
end
+ def trigger_token
+ ENV['BUILD_TRIGGER_TOKEN']
+ end
+
+ def access_token
+ ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
+ end
+
def extra_variables
edition = Trigger.ee? ? 'EE' : 'CE'
@@ -134,11 +153,16 @@ module Trigger
end
class CommitComment
- def self.post!(downstream_project_path, downstream_pipeline)
+ def self.post!(downstream_pipeline, access_token)
+ Gitlab.private_token = access_token
+
Gitlab.create_commit_comment(
ENV['CI_PROJECT_PATH'],
ENV['CI_COMMIT_SHA'],
"The [`#{ENV['CI_JOB_NAME']}`](#{ENV['CI_JOB_URL']}) job from pipeline #{ENV['CI_PIPELINE_URL']} triggered #{downstream_pipeline.web_url} downstream.")
+
+ rescue Gitlab::Error::Error => error
+ puts "Ignoring the following error: #{error}"
end
end
@@ -146,15 +170,16 @@ module Trigger
INTERVAL = 60 # seconds
MAX_DURATION = 3600 * 3 # 3 hours
- attr_reader :project, :id
+ attr_reader :project, :id, :api_token
- def initialize(project, id)
+ def initialize(project, id, api_token)
@project = project
@id = id
+ @api_token = api_token
@start = Time.now.to_i
# gitlab-bot's token "GitLab multi-project pipeline polling"
- Gitlab.private_token = ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
+ Gitlab.private_token = api_token
end
def wait!
@@ -197,9 +222,9 @@ end
case ARGV[0]
when 'omnibus'
- Trigger::Omnibus.new(ENV['GITLAB_QA_ACCESS_TOKEN']).invoke!(post_comment: true).wait!
+ Trigger::Omnibus.new.invoke!(post_comment: true).wait!
when 'cng'
- Trigger::CNG.new(ENV['GITLAB_QA_ACCESS_TOKEN']).invoke!.wait!
+ Trigger::CNG.new.invoke!.wait!
else
puts "Please provide a valid option:
omnibus - Triggers a pipeline that builds the omnibus-gitlab package
diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb
index 10e1bfc30f9..2e0f79cd313 100644
--- a/spec/controllers/admin/application_settings_controller_spec.rb
+++ b/spec/controllers/admin/application_settings_controller_spec.rb
@@ -86,4 +86,22 @@ describe Admin::ApplicationSettingsController do
expect(ApplicationSetting.current.receive_max_input_size).to eq(1024)
end
end
+
+ describe 'PUT #reset_registration_token' do
+ before do
+ sign_in(admin)
+ end
+
+ subject { put :reset_registration_token }
+
+ it 'resets runner registration token' do
+ expect { subject }.to change { ApplicationSetting.current.runners_registration_token }
+ end
+
+ it 'redirects the user to admin runners page' do
+ subject
+
+ expect(response).to redirect_to(admin_runners_path)
+ end
+ end
end
diff --git a/spec/controllers/admin/projects_controller_spec.rb b/spec/controllers/admin/projects_controller_spec.rb
index cc200b9fed9..ee1aff09bdf 100644
--- a/spec/controllers/admin/projects_controller_spec.rb
+++ b/spec/controllers/admin/projects_controller_spec.rb
@@ -42,4 +42,15 @@ describe Admin::ProjectsController do
expect { get :index }.not_to exceed_query_limit(control_count)
end
end
+
+ describe 'GET /projects/:id' do
+ render_views
+
+ it 'renders show page' do
+ get :show, namespace_id: project.namespace.path, id: project.path
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.body).to match(project.name)
+ end
+ end
end
diff --git a/spec/controllers/groups/settings/ci_cd_controller_spec.rb b/spec/controllers/groups/settings/ci_cd_controller_spec.rb
index ea18122e0c3..06ccace8242 100644
--- a/spec/controllers/groups/settings/ci_cd_controller_spec.rb
+++ b/spec/controllers/groups/settings/ci_cd_controller_spec.rb
@@ -17,4 +17,18 @@ describe Groups::Settings::CiCdController do
expect(response).to render_template(:show)
end
end
+
+ describe 'PUT #reset_registration_token' do
+ subject { put :reset_registration_token, group_id: group }
+
+ it 'resets runner registration token' do
+ expect { subject }.to change { group.reload.runners_token }
+ end
+
+ it 'redirects the user to admin runners page' do
+ subject
+
+ expect(response).to redirect_to(group_settings_ci_cd_path)
+ end
+ end
end
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index 65d6cd1a295..a099cdafa58 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -202,8 +202,8 @@ describe GroupsController do
end
describe 'GET #issues' do
- let(:issue_1) { create(:issue, project: project) }
- let(:issue_2) { create(:issue, project: project) }
+ let(:issue_1) { create(:issue, project: project, title: 'foo') }
+ let(:issue_2) { create(:issue, project: project, title: 'bar') }
before do
create_list(:award_emoji, 3, awardable: issue_2)
@@ -224,6 +224,31 @@ describe GroupsController do
expect(assigns(:issues)).to eq [issue_2, issue_1]
end
end
+
+ context 'searching' do
+ # Remove as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/52271
+ before do
+ stub_feature_flags(use_cte_for_group_issues_search: false)
+ end
+
+ it 'works with popularity sort' do
+ get :issues, id: group.to_param, search: 'foo', sort: 'popularity'
+
+ expect(assigns(:issues)).to eq([issue_1])
+ end
+
+ it 'works with priority sort' do
+ get :issues, id: group.to_param, search: 'foo', sort: 'priority'
+
+ expect(assigns(:issues)).to eq([issue_1])
+ end
+
+ it 'works with label priority sort' do
+ get :issues, id: group.to_param, search: 'foo', sort: 'label_priority'
+
+ expect(assigns(:issues)).to eq([issue_1])
+ end
+ end
end
describe 'GET #merge_requests' do
diff --git a/spec/controllers/projects/artifacts_controller_spec.rb b/spec/controllers/projects/artifacts_controller_spec.rb
index 436f4525093..6091185e252 100644
--- a/spec/controllers/projects/artifacts_controller_spec.rb
+++ b/spec/controllers/projects/artifacts_controller_spec.rb
@@ -19,15 +19,17 @@ describe Projects::ArtifactsController do
end
describe 'GET download' do
- subject { get :download, namespace_id: project.namespace, project_id: project, job_id: job, file_type: file_type }
+ def download_artifact(extra_params = {})
+ params = { namespace_id: project.namespace, project_id: project, job_id: job }.merge(extra_params)
- context 'when no file type is supplied' do
- let(:file_type) { nil }
+ get :download, params
+ end
+ context 'when no file type is supplied' do
it 'sends the artifacts file' do
expect(controller).to receive(:send_file).with(job.artifacts_file.path, hash_including(disposition: 'attachment')).and_call_original
- subject
+ download_artifact
end
end
@@ -36,7 +38,7 @@ describe Projects::ArtifactsController do
let(:file_type) { 'invalid' }
it 'returns 404' do
- subject
+ download_artifact(file_type: file_type)
expect(response).to have_gitlab_http_status(404)
end
@@ -52,7 +54,7 @@ describe Projects::ArtifactsController do
it 'sends the codequality report' do
expect(controller).to receive(:send_file).with(job.job_artifacts_codequality.file.path, hash_including(disposition: 'attachment')).and_call_original
- subject
+ download_artifact(file_type: file_type)
end
end
end
diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb
index 8695aa826bb..17883d0fadd 100644
--- a/spec/controllers/projects/compare_controller_spec.rb
+++ b/spec/controllers/projects/compare_controller_spec.rb
@@ -97,6 +97,30 @@ describe Projects::CompareController do
expect(assigns(:commits)).to eq([])
end
end
+
+ context 'when the target ref is invalid' do
+ let(:target_ref) { "master%' AND 2554=4423 AND '%'='" }
+ let(:source_ref) { "improve%2Fawesome" }
+
+ it 'shows a flash message and redirects' do
+ show_request
+
+ expect(flash[:alert]).to eq('Invalid branch name')
+ expect(response).to have_http_status(302)
+ end
+ end
+
+ context 'when the source ref is invalid' do
+ let(:source_ref) { "master%' AND 2554=4423 AND '%'='" }
+ let(:target_ref) { "improve%2Fawesome" }
+
+ it 'shows a flash message and redirects' do
+ show_request
+
+ expect(flash[:alert]).to eq('Invalid branch name')
+ expect(response).to have_http_status(302)
+ end
+ end
end
describe 'GET diff_for_path' do
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 5b347b1109a..9df77560320 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -637,6 +637,18 @@ describe Projects::IssuesController do
project_id: project,
id: id
end
+
+ it 'avoids (most) N+1s loading labels', :request_store do
+ label = create(:label, project: project).to_reference
+ labels = create_list(:label, 10, project: project).map(&:to_reference)
+ issue = create(:issue, project: project, description: 'Test issue')
+
+ control_count = ActiveRecord::QueryRecorder.new { issue.update(description: [issue.description, label].join(' ')) }.count
+
+ # Follow-up to get rid of this `2 * label.count` requirement: https://gitlab.com/gitlab-org/gitlab-ce/issues/52230
+ expect { issue.update(description: [issue.description, labels].join(' ')) }
+ .not_to exceed_query_limit(control_count + 2 * labels.count)
+ end
end
describe 'GET #realtime_changes' do
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index 5b09e4a082c..5c8180baf8a 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -225,7 +225,6 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to match_schema('job/job_details')
expect(json_response['deployment_status']["status"]).to eq 'creating'
- expect(json_response['deployment_status']["icon"]).to eq 'passed'
expect(json_response['deployment_status']["environment"]).not_to be_nil
end
end
@@ -600,35 +599,68 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
before do
project.add_developer(user)
sign_in(user)
-
- post_cancel
end
- context 'when job is cancelable' do
+ context 'when continue url is present' do
let(:job) { create(:ci_build, :cancelable, pipeline: pipeline) }
- it 'redirects to the canceled job page' do
- expect(response).to have_gitlab_http_status(:found)
- expect(response).to redirect_to(namespace_project_job_path(id: job.id))
+ context 'when continue to is a safe url' do
+ let(:url) { '/test' }
+
+ before do
+ post_cancel(continue: { to: url })
+ end
+
+ it 'redirects to the continue url' do
+ expect(response).to have_gitlab_http_status(:found)
+ expect(response).to redirect_to(url)
+ end
+
+ it 'transits to canceled' do
+ expect(job.reload).to be_canceled
+ end
end
- it 'transits to canceled' do
- expect(job.reload).to be_canceled
+ context 'when continue to is not a safe url' do
+ let(:url) { 'http://example.com' }
+
+ it 'raises an error' do
+ expect { cancel_with_redirect(url) }.to raise_error
+ end
end
end
- context 'when job is not cancelable' do
- let(:job) { create(:ci_build, :canceled, pipeline: pipeline) }
+ context 'when continue url is not present' do
+ before do
+ post_cancel
+ end
- it 'returns unprocessable_entity' do
- expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ context 'when job is cancelable' do
+ let(:job) { create(:ci_build, :cancelable, pipeline: pipeline) }
+
+ it 'redirects to the builds page' do
+ expect(response).to have_gitlab_http_status(:found)
+ expect(response).to redirect_to(builds_namespace_project_pipeline_path(id: pipeline.id))
+ end
+
+ it 'transits to canceled' do
+ expect(job.reload).to be_canceled
+ end
+ end
+
+ context 'when job is not cancelable' do
+ let(:job) { create(:ci_build, :canceled, pipeline: pipeline) }
+
+ it 'returns unprocessable_entity' do
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ end
end
end
- def post_cancel
- post :cancel, namespace_id: project.namespace,
- project_id: project,
- id: job.id
+ def post_cancel(additional_params = {})
+ post :cancel, { namespace_id: project.namespace,
+ project_id: project,
+ id: job.id }.merge(additional_params)
end
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 7446e0650f7..73b62dc1151 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -76,28 +76,6 @@ describe Projects::MergeRequestsController do
expect(response).to be_success
end
- context "loads notes" do
- let(:first_contributor) { create(:user) }
- let(:contributor) { create(:user) }
- let(:merge_request) { create(:merge_request, author: first_contributor, target_project: project, source_project: project) }
- let(:contributor_merge_request) { create(:merge_request, :merged, author: contributor, target_project: project, source_project: project) }
- # the order here is important
- # as the controller reloads these from DB, references doesn't correspond after
- let!(:first_contributor_note) { create(:note, author: first_contributor, noteable: merge_request, project: project) }
- let!(:contributor_note) { create(:note, author: contributor, noteable: merge_request, project: project) }
- let!(:owner_note) { create(:note, author: user, noteable: merge_request, project: project) }
-
- it "with special_role FIRST_TIME_CONTRIBUTOR" do
- go(format: :html)
-
- notes = assigns(:notes)
- expect(notes).to match(a_collection_containing_exactly(an_object_having_attributes(special_role: Note::SpecialRole::FIRST_TIME_CONTRIBUTOR),
- an_object_having_attributes(special_role: nil),
- an_object_having_attributes(special_role: nil)
- ))
- end
- end
-
context "that is invalid" do
let(:merge_request) { create(:invalid_merge_request, target_project: project, source_project: project) }
@@ -763,25 +741,35 @@ describe Projects::MergeRequestsController do
describe 'GET ci_environments_status' do
context 'the environment is from a forked project' do
- let!(:forked) { fork_project(project, user, repository: true) }
- let!(:environment) { create(:environment, project: forked) }
- let!(:deployment) { create(:deployment, environment: environment, sha: forked.commit.id, ref: 'master') }
- let(:admin) { create(:admin) }
+ let!(:forked) { fork_project(project, user, repository: true) }
+ let!(:environment) { create(:environment, project: forked) }
+ let!(:deployment) { create(:deployment, environment: environment, sha: forked.commit.id, ref: 'master') }
+ let(:admin) { create(:admin) }
let(:merge_request) do
create(:merge_request, source_project: forked, target_project: project)
end
- before do
+ it 'links to the environment on that project' do
+ get_ci_environments_status
+
+ expect(json_response.first['url']).to match /#{forked.full_path}/
+ end
+
+ # we're trying to reduce the overall number of queries for this method.
+ # set a hard limit for now. https://gitlab.com/gitlab-org/gitlab-ce/issues/52287
+ it 'keeps queries in check' do
+ control_count = ActiveRecord::QueryRecorder.new { get_ci_environments_status }.count
+
+ expect(control_count).to be <= 137
+ end
+
+ def get_ci_environments_status
get :ci_environments_status,
namespace_id: merge_request.project.namespace.to_param,
project_id: merge_request.project,
id: merge_request.iid, format: 'json'
end
-
- it 'links to the environment on that project' do
- expect(json_response.first['url']).to match /#{forked.full_path}/
- end
end
end
diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
index 1f14a0cc381..4629929f9af 100644
--- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb
+++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
@@ -74,6 +74,19 @@ describe Projects::Settings::CiCdController do
end
end
+ describe 'PUT #reset_registration_token' do
+ subject { put :reset_registration_token, namespace_id: project.namespace, project_id: project }
+ it 'resets runner registration token' do
+ expect { subject }.to change { project.reload.runners_token }
+ end
+
+ it 'redirects the user to admin runners page' do
+ subject
+
+ expect(response).to redirect_to(namespace_project_settings_ci_cd_path)
+ end
+ end
+
describe 'PATCH update' do
let(:params) { { ci_config_path: '' } }
diff --git a/spec/factories/broadcast_messages.rb b/spec/factories/broadcast_messages.rb
index 9a65e7f8a3f..1a2be5e9552 100644
--- a/spec/factories/broadcast_messages.rb
+++ b/spec/factories/broadcast_messages.rb
@@ -1,17 +1,17 @@
FactoryBot.define do
factory :broadcast_message do
message "MyText"
- starts_at 1.day.ago
- ends_at 1.day.from_now
+ starts_at { 1.day.ago }
+ ends_at { 1.day.from_now }
trait :expired do
- starts_at 5.days.ago
- ends_at 3.days.ago
+ starts_at { 5.days.ago }
+ ends_at { 3.days.ago }
end
trait :future do
- starts_at 5.days.from_now
- ends_at 6.days.from_now
+ starts_at { 5.days.from_now }
+ ends_at { 6.days.from_now }
end
end
end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 7a4b1dfafac..85ba7d4097d 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -180,12 +180,12 @@ FactoryBot.define do
end
trait :erased do
- erased_at Time.now
+ erased_at { Time.now }
erased_by factory: :user
end
trait :queued do
- queued_at Time.now
+ queued_at { Time.now }
runner factory: :ci_runner
end
@@ -215,7 +215,7 @@ FactoryBot.define do
end
trait :expired do
- artifacts_expire_at 1.minute.ago
+ artifacts_expire_at { 1.minute.ago }
end
trait :with_commit do
diff --git a/spec/factories/ci/runners.rb b/spec/factories/ci/runners.rb
index 347e4f433e2..f564e7bee47 100644
--- a/spec/factories/ci/runners.rb
+++ b/spec/factories/ci/runners.rb
@@ -9,7 +9,7 @@ FactoryBot.define do
runner_type :instance_type
trait :online do
- contacted_at Time.now
+ contacted_at { Time.now }
end
trait :instance do
diff --git a/spec/factories/clusters/applications/helm.rb b/spec/factories/clusters/applications/helm.rb
index 5756486df27..3c9ca22a051 100644
--- a/spec/factories/clusters/applications/helm.rb
+++ b/spec/factories/clusters/applications/helm.rb
@@ -42,7 +42,7 @@ FactoryBot.define do
trait :timeouted do
installing
- updated_at ClusterWaitForAppInstallationWorker::TIMEOUT.ago
+ updated_at { ClusterWaitForAppInstallationWorker::TIMEOUT.ago }
end
factory :clusters_applications_ingress, class: Clusters::Applications::Ingress do
diff --git a/spec/factories/clusters/platforms/kubernetes.rb b/spec/factories/clusters/platforms/kubernetes.rb
index 36ac2372204..4a0d1b181ea 100644
--- a/spec/factories/clusters/platforms/kubernetes.rb
+++ b/spec/factories/clusters/platforms/kubernetes.rb
@@ -3,11 +3,10 @@ FactoryBot.define do
cluster
namespace nil
api_url 'https://kubernetes.example.com'
- token 'a' * 40
+ token { 'a' * 40 }
trait :configured do
api_url 'https://kubernetes.example.com'
- token 'a' * 40
username 'xxxxxx'
password 'xxxxxx'
diff --git a/spec/factories/emails.rb b/spec/factories/emails.rb
index d23ddf9d79b..feacd5ccf15 100644
--- a/spec/factories/emails.rb
+++ b/spec/factories/emails.rb
@@ -3,7 +3,7 @@ FactoryBot.define do
user
email { generate(:email_alias) }
- trait(:confirmed) { confirmed_at Time.now }
+ trait(:confirmed) { confirmed_at { Time.now } }
trait(:skip_validate) { to_create {|instance| instance.save(validate: false) } }
end
end
diff --git a/spec/factories/gpg_keys.rb b/spec/factories/gpg_keys.rb
index 51b8ddc9934..3c0f43cc1b6 100644
--- a/spec/factories/gpg_keys.rb
+++ b/spec/factories/gpg_keys.rb
@@ -2,11 +2,11 @@ require_relative '../support/helpers/gpg_helpers'
FactoryBot.define do
factory :gpg_key do
- key GpgHelpers::User1.public_key
+ key { GpgHelpers::User1.public_key }
user
factory :gpg_key_with_subkeys do
- key GpgHelpers::User1.public_key_with_extra_signing_key
+ key { GpgHelpers::User1.public_key_with_extra_signing_key }
end
end
end
diff --git a/spec/factories/group_members.rb b/spec/factories/group_members.rb
index 47036560b9d..12be63e5d92 100644
--- a/spec/factories/group_members.rb
+++ b/spec/factories/group_members.rb
@@ -9,7 +9,7 @@ FactoryBot.define do
trait(:developer) { access_level GroupMember::DEVELOPER }
trait(:maintainer) { access_level GroupMember::MAINTAINER }
trait(:owner) { access_level GroupMember::OWNER }
- trait(:access_request) { requested_at Time.now }
+ trait(:access_request) { requested_at { Time.now } }
trait(:invited) do
user_id nil
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index b8b089b069b..8094c43b065 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -80,7 +80,7 @@ FactoryBot.define do
trait :merge_when_pipeline_succeeds do
merge_when_pipeline_succeeds true
- merge_user author
+ merge_user { author }
end
trait :remove_source_branch do
diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb
index 6844ed8aa4a..2d1f48bf249 100644
--- a/spec/factories/notes.rb
+++ b/spec/factories/notes.rb
@@ -90,7 +90,7 @@ FactoryBot.define do
noteable nil
noteable_type 'Commit'
noteable_id nil
- commit_id RepoHelpers.sample_commit.id
+ commit_id { RepoHelpers.sample_commit.id }
end
trait :legacy_diff_note do
diff --git a/spec/factories/oauth_access_grants.rb b/spec/factories/oauth_access_grants.rb
index 9e6af24c4eb..02c51cd9899 100644
--- a/spec/factories/oauth_access_grants.rb
+++ b/spec/factories/oauth_access_grants.rb
@@ -3,7 +3,7 @@ FactoryBot.define do
resource_owner_id { create(:user).id }
application
token { Doorkeeper::OAuth::Helpers::UniqueToken.generate }
- expires_in 2.hours
+ expires_in { 2.hours }
redirect_uri { application.redirect_uri }
scopes { application.scopes }
diff --git a/spec/factories/project_auto_devops.rb b/spec/factories/project_auto_devops.rb
index b77f702f9e1..75ac7cc7687 100644
--- a/spec/factories/project_auto_devops.rb
+++ b/spec/factories/project_auto_devops.rb
@@ -5,8 +5,16 @@ FactoryBot.define do
domain "example.com"
deploy_strategy :continuous
- trait :manual do
- deploy_strategy :manual
+ trait :continuous_deployment do
+ deploy_strategy ProjectAutoDevops.deploy_strategies[:continuous] # rubocop:disable FactoryBot/DynamicAttributeDefinedStatically
+ end
+
+ trait :manual_deployment do
+ deploy_strategy ProjectAutoDevops.deploy_strategies[:manual] # rubocop:disable FactoryBot/DynamicAttributeDefinedStatically
+ end
+
+ trait :timed_incremental_deployment do
+ deploy_strategy ProjectAutoDevops.deploy_strategies[:timed_incremental] # rubocop:disable FactoryBot/DynamicAttributeDefinedStatically
end
trait :disabled do
diff --git a/spec/factories/project_members.rb b/spec/factories/project_members.rb
index 22a8085ea45..c72e0487895 100644
--- a/spec/factories/project_members.rb
+++ b/spec/factories/project_members.rb
@@ -8,7 +8,7 @@ FactoryBot.define do
trait(:reporter) { access_level ProjectMember::REPORTER }
trait(:developer) { access_level ProjectMember::DEVELOPER }
trait(:maintainer) { access_level ProjectMember::MAINTAINER }
- trait(:access_request) { requested_at Time.now }
+ trait(:access_request) { requested_at { Time.now } }
trait(:invited) do
user_id nil
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 80801eb1082..e4823a5adf1 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -1,6 +1,8 @@
require_relative '../support/helpers/test_env'
FactoryBot.define do
+ PAGES_ACCESS_LEVEL_SCHEMA_VERSION = 20180423204600
+
# Project without repository
#
# Project does not have bare repository.
@@ -23,6 +25,7 @@ FactoryBot.define do
issues_access_level ProjectFeature::ENABLED
merge_requests_access_level ProjectFeature::ENABLED
repository_access_level ProjectFeature::ENABLED
+ pages_access_level ProjectFeature::ENABLED
# we can't assign the delegated `#ci_cd_settings` attributes directly, as the
# `#ci_cd_settings` relation needs to be created first
@@ -34,13 +37,20 @@ FactoryBot.define do
builds_access_level = [evaluator.builds_access_level, evaluator.repository_access_level].min
merge_requests_access_level = [evaluator.merge_requests_access_level, evaluator.repository_access_level].min
- project.project_feature.update(
+ hash = {
wiki_access_level: evaluator.wiki_access_level,
builds_access_level: builds_access_level,
snippets_access_level: evaluator.snippets_access_level,
issues_access_level: evaluator.issues_access_level,
merge_requests_access_level: merge_requests_access_level,
- repository_access_level: evaluator.repository_access_level)
+ repository_access_level: evaluator.repository_access_level
+ }
+
+ if ActiveRecord::Migrator.current_version >= PAGES_ACCESS_LEVEL_SCHEMA_VERSION
+ hash.store("pages_access_level", evaluator.pages_access_level)
+ end
+
+ project.project_feature.update(hash)
# Normally the class Projects::CreateService is used for creating
# projects, and this class takes care of making sure the owner and current
@@ -244,6 +254,10 @@ FactoryBot.define do
trait(:repository_enabled) { repository_access_level ProjectFeature::ENABLED }
trait(:repository_disabled) { repository_access_level ProjectFeature::DISABLED }
trait(:repository_private) { repository_access_level ProjectFeature::PRIVATE }
+ trait(:pages_public) { pages_access_level ProjectFeature::PUBLIC }
+ trait(:pages_enabled) { pages_access_level ProjectFeature::ENABLED }
+ trait(:pages_disabled) { pages_access_level ProjectFeature::DISABLED }
+ trait(:pages_private) { pages_access_level ProjectFeature::PRIVATE }
trait :auto_devops do
association :auto_devops, factory: :project_auto_devops
diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb
index 14486c80341..ed3d87eb76b 100644
--- a/spec/factories/todos.rb
+++ b/spec/factories/todos.rb
@@ -49,7 +49,7 @@ FactoryBot.define do
author
user
action { Todo::ASSIGNED }
- commit_id RepoHelpers.sample_commit.id
+ commit_id { RepoHelpers.sample_commit.id }
target_type "Commit"
end
end
diff --git a/spec/factories/uploads.rb b/spec/factories/uploads.rb
index 81c485fba1a..7256f785e1f 100644
--- a/spec/factories/uploads.rb
+++ b/spec/factories/uploads.rb
@@ -1,7 +1,7 @@
FactoryBot.define do
factory :upload do
model { build(:project) }
- size 100.kilobytes
+ size { 100.kilobytes }
uploader "AvatarUploader"
mount_point :avatar
secret nil
@@ -19,13 +19,13 @@ FactoryBot.define do
uploader "PersonalFileUploader"
path { File.join(secret, filename) }
model { build(:personal_snippet) }
- secret SecureRandom.hex
+ secret { SecureRandom.hex }
end
trait :issuable_upload do
uploader "FileUploader"
path { File.join(secret, filename) }
- secret SecureRandom.hex
+ secret { SecureRandom.hex }
end
trait :with_file do
@@ -43,14 +43,14 @@ FactoryBot.define do
model { build(:group) }
path { File.join(secret, filename) }
uploader "NamespaceFileUploader"
- secret SecureRandom.hex
+ secret { SecureRandom.hex }
end
trait :favicon_upload do
model { build(:appearance) }
path { File.join(secret, filename) }
uploader "FaviconUploader"
- secret SecureRandom.hex
+ secret { SecureRandom.hex }
end
trait :attachment_upload do
diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb
index f08946b0593..aa3ca8923ff 100644
--- a/spec/features/calendar_spec.rb
+++ b/spec/features/calendar_spec.rb
@@ -64,7 +64,7 @@ describe 'Contributions Calendar', :js do
end
def selected_day_activities(visible: true)
- find('.user-calendar-activities', visible: visible).text
+ find('.tab-pane#activity .user-calendar-activities', visible: visible).text
end
before do
@@ -74,15 +74,16 @@ describe 'Contributions Calendar', :js do
describe 'calendar day selection' do
before do
visit user.username
+ page.find('.js-activity-tab a').click
wait_for_requests
end
it 'displays calendar' do
- expect(page).to have_css('.js-contrib-calendar')
+ expect(find('.tab-pane#activity')).to have_css('.js-contrib-calendar')
end
describe 'select calendar day' do
- let(:cells) { page.all('.user-contrib-cell') }
+ let(:cells) { page.all('.tab-pane#activity .user-contrib-cell') }
before do
cells[0].click
@@ -108,6 +109,7 @@ describe 'Contributions Calendar', :js do
describe 'deselect calendar day' do
before do
cells[0].click
+ page.find('.js-activity-tab a').click
wait_for_requests
end
@@ -122,6 +124,7 @@ describe 'Contributions Calendar', :js do
shared_context 'visit user page' do
before do
visit user.username
+ page.find('.js-activity-tab a').click
wait_for_requests
end
end
@@ -130,12 +133,12 @@ describe 'Contributions Calendar', :js do
include_context 'visit user page'
it 'displays calendar activity square color for 1 contribution' do
- expect(page).to have_selector(get_cell_color_selector(contribution_count), count: 1)
+ expect(find('.tab-pane#activity')).to have_selector(get_cell_color_selector(contribution_count), count: 1)
end
it 'displays calendar activity square on the correct date' do
today = Date.today.strftime(date_format)
- expect(page).to have_selector(get_cell_date_selector(contribution_count, today), count: 1)
+ expect(find('.tab-pane#activity')).to have_selector(get_cell_date_selector(contribution_count, today), count: 1)
end
end
@@ -150,7 +153,7 @@ describe 'Contributions Calendar', :js do
include_context 'visit user page'
it 'displays calendar activity log' do
- expect(find('.content_list .event-note')).to have_content issue_title
+ expect(find('.tab-pane#activity .content_list .event-note')).to have_content issue_title
end
end
end
@@ -182,17 +185,17 @@ describe 'Contributions Calendar', :js do
include_context 'visit user page'
it 'displays calendar activity squares for both days' do
- expect(page).to have_selector(get_cell_color_selector(1), count: 2)
+ expect(find('.tab-pane#activity')).to have_selector(get_cell_color_selector(1), count: 2)
end
it 'displays calendar activity square for yesterday' do
yesterday = Date.yesterday.strftime(date_format)
- expect(page).to have_selector(get_cell_date_selector(1, yesterday), count: 1)
+ expect(find('.tab-pane#activity')).to have_selector(get_cell_date_selector(1, yesterday), count: 1)
end
it 'displays calendar activity square for today' do
today = Date.today.strftime(date_format)
- expect(page).to have_selector(get_cell_date_selector(1, today), count: 1)
+ expect(find('.tab-pane#activity')).to have_selector(get_cell_date_selector(1, today), count: 1)
end
end
end
diff --git a/spec/features/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb
index d7234158fa1..0db8093411b 100644
--- a/spec/features/dashboard/datetime_on_tooltips_spec.rb
+++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb
@@ -14,7 +14,7 @@ describe 'Tooltips on .timeago dates', :js do
updated_at: created_date, created_at: created_date)
sign_in user
- visit user_path(user)
+ visit user_activity_path(user)
wait_for_requests()
page.find('.js-timeago').hover
diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb
index 59254ecc982..08fd9f8af2a 100644
--- a/spec/features/groups/group_settings_spec.rb
+++ b/spec/features/groups/group_settings_spec.rb
@@ -98,6 +98,22 @@ describe 'Edit group settings' do
end
end
+ describe 'edit group path' do
+ it 'has a root URL label for top-level group' do
+ visit edit_group_path(group)
+
+ expect(find(:css, '.group-root-path').text).to eq(root_url)
+ end
+
+ it 'has a parent group URL label for a subgroup group', :postgresql do
+ subgroup = create(:group, parent: group)
+
+ visit edit_group_path(subgroup)
+
+ expect(find(:css, '.group-root-path').text).to eq(group_url(subgroup.parent) + '/')
+ end
+ end
+
def update_path(new_group_path)
visit edit_group_path(group)
diff --git a/spec/features/groups/labels/subscription_spec.rb b/spec/features/groups/labels/subscription_spec.rb
index d9543bfa97f..22b51b297a6 100644
--- a/spec/features/groups/labels/subscription_spec.rb
+++ b/spec/features/groups/labels/subscription_spec.rb
@@ -3,7 +3,8 @@ require 'spec_helper'
describe 'Labels subscription' do
let(:user) { create(:user) }
let(:group) { create(:group) }
- let!(:feature) { create(:group_label, group: group, title: 'feature') }
+ let!(:label1) { create(:group_label, group: group, title: 'foo') }
+ let!(:label2) { create(:group_label, group: group, title: 'bar') }
context 'when signed in' do
before do
@@ -14,9 +15,9 @@ describe 'Labels subscription' do
it 'users can subscribe/unsubscribe to group labels', :js do
visit group_labels_path(group)
- expect(page).to have_content('feature')
+ expect(page).to have_content(label1.title)
- within "#group_label_#{feature.id}" do
+ within "#group_label_#{label1.id}" do
expect(page).not_to have_button 'Unsubscribe'
click_button 'Subscribe'
@@ -30,15 +31,48 @@ describe 'Labels subscription' do
expect(page).not_to have_button 'Unsubscribe'
end
end
+
+ context 'subscription filter' do
+ before do
+ visit group_labels_path(group)
+ end
+
+ it 'shows only subscribed labels' do
+ label1.subscribe(user)
+
+ click_subscribed_tab
+
+ page.within('.labels-container') do
+ expect(page).to have_content label1.title
+ end
+ end
+
+ it 'shows no subscribed labels message' do
+ click_subscribed_tab
+
+ page.within('.labels-container') do
+ expect(page).not_to have_content label1.title
+ expect(page).to have_content('You do not have any subscriptions yet')
+ end
+ end
+ end
end
context 'when not signed in' do
- it 'users can not subscribe/unsubscribe to labels' do
+ before do
visit group_labels_path(group)
+ end
- expect(page).to have_content 'feature'
+ it 'users can not subscribe/unsubscribe to labels' do
+ expect(page).to have_content label1.title
expect(page).not_to have_button('Subscribe')
end
+
+ it 'does not show subscribed tab' do
+ page.within('.nav-tabs') do
+ expect(page).not_to have_link 'Subscribed'
+ end
+ end
end
def click_link_on_dropdown(text)
@@ -48,4 +82,10 @@ describe 'Labels subscription' do
find('a.js-subscribe-button', text: text).click
end
end
+
+ def click_subscribed_tab
+ page.within('.nav-tabs') do
+ click_link 'Subscribed'
+ end
+ end
end
diff --git a/spec/features/groups/settings/ci_cd_spec.rb b/spec/features/groups/settings/ci_cd_spec.rb
new file mode 100644
index 00000000000..d422fd18346
--- /dev/null
+++ b/spec/features/groups/settings/ci_cd_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Group CI/CD settings' do
+ include WaitForRequests
+
+ let(:user) {create(:user)}
+ let(:group) {create(:group)}
+
+ before do
+ group.add_owner(user)
+ sign_in(user)
+ end
+
+ describe 'runners registration token' do
+ let!(:token) { group.runners_token }
+
+ before do
+ visit group_settings_ci_cd_path(group)
+ end
+
+ it 'has a registration token' do
+ expect(page.find('#registration_token')).to have_content(token)
+ end
+
+ describe 'reload registration token' do
+ let(:page_token) { find('#registration_token').text }
+
+ before do
+ click_button 'Reset runners registration token'
+ end
+
+ it 'changes registration token' do
+ expect(page_token).not_to eq token
+ end
+ end
+ end
+end
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index 98e37d8011a..08bf9bc7243 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -5,6 +5,7 @@ describe 'GFM autocomplete', :js do
let(:project) { create(:project) }
let(:label) { create(:label, project: project, title: 'special+') }
let(:issue) { create(:issue, project: project) }
+ let!(:project_snippet) { create(:project_snippet, project: project, title: 'code snippet') }
before do
project.add_maintainer(user)
@@ -301,6 +302,16 @@ describe 'GFM autocomplete', :js do
end
end
+ it 'shows project snippets' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('$')
+ end
+
+ page.within '.atwho-container' do
+ expect(page).to have_content(project_snippet.title)
+ end
+ end
+
private
def expect_to_wrap(should_wrap, item, note, value)
diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb
index 206a3a4fe9a..5e0434c1c2c 100644
--- a/spec/features/profiles/user_edit_profile_spec.rb
+++ b/spec/features/profiles/user_edit_profile_spec.rb
@@ -61,83 +61,231 @@ describe 'User edit profile' do
end
context 'user status', :js do
- def select_emoji(emoji_name)
+ def select_emoji(emoji_name, is_modal = false)
+ emoji_menu_class = is_modal ? '.js-modal-status-emoji-menu' : '.js-status-emoji-menu'
toggle_button = find('.js-toggle-emoji-menu')
toggle_button.click
- emoji_button = find(%Q{.js-status-emoji-menu .js-emoji-btn gl-emoji[data-name="#{emoji_name}"]})
+ emoji_button = find(%Q{#{emoji_menu_class} .js-emoji-btn gl-emoji[data-name="#{emoji_name}"]})
emoji_button.click
end
- it 'shows the user status form' do
- visit(profile_path)
+ context 'profile edit form' do
+ it 'shows the user status form' do
+ visit(profile_path)
- expect(page).to have_content('Current status')
- end
+ expect(page).to have_content('Current status')
+ end
- it 'adds emoji to user status' do
- emoji = 'biohazard'
- visit(profile_path)
- select_emoji(emoji)
- submit_settings
+ it 'adds emoji to user status' do
+ emoji = 'biohazard'
+ visit(profile_path)
+ select_emoji(emoji)
+ submit_settings
- visit user_path(user)
- within('.cover-status') do
- expect(page).to have_emoji(emoji)
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji(emoji)
+ end
end
- end
- it 'adds message to user status' do
- message = 'I have something to say'
- visit(profile_path)
- fill_in 'js-status-message-field', with: message
- submit_settings
+ it 'adds message to user status' do
+ message = 'I have something to say'
+ visit(profile_path)
+ fill_in 'js-status-message-field', with: message
+ submit_settings
- visit user_path(user)
- within('.cover-status') do
- expect(page).to have_emoji('speech_balloon')
- expect(page).to have_content message
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji('speech_balloon')
+ expect(page).to have_content message
+ end
end
- end
- it 'adds message and emoji to user status' do
- emoji = 'tanabata_tree'
- message = 'Playing outside'
- visit(profile_path)
- select_emoji(emoji)
- fill_in 'js-status-message-field', with: message
- submit_settings
+ it 'adds message and emoji to user status' do
+ emoji = 'tanabata_tree'
+ message = 'Playing outside'
+ visit(profile_path)
+ select_emoji(emoji)
+ fill_in 'js-status-message-field', with: message
+ submit_settings
- visit user_path(user)
- within('.cover-status') do
- expect(page).to have_emoji(emoji)
- expect(page).to have_content message
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji(emoji)
+ expect(page).to have_content message
+ end
end
- end
- it 'clears the user status' do
- user_status = create(:user_status, user: user, message: 'Eating bread', emoji: 'stuffed_flatbread')
+ it 'clears the user status' do
+ user_status = create(:user_status, user: user, message: 'Eating bread', emoji: 'stuffed_flatbread')
+
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji(user_status.emoji)
+ expect(page).to have_content user_status.message
+ end
+
+ visit(profile_path)
+ click_button 'js-clear-user-status-button'
+ submit_settings
+
+ wait_for_requests
- visit user_path(user)
- within('.cover-status') do
- expect(page).to have_emoji(user_status.emoji)
- expect(page).to have_content user_status.message
+ visit user_path(user)
+ expect(page).not_to have_selector '.cover-status'
end
- visit(profile_path)
- click_button 'js-clear-user-status-button'
- submit_settings
+ it 'displays a default emoji if only message is entered' do
+ message = 'a status without emoji'
+ visit(profile_path)
+ fill_in 'js-status-message-field', with: message
- visit user_path(user)
- expect(page).not_to have_selector '.cover-status'
+ within('.js-toggle-emoji-menu') do
+ expect(page).to have_emoji('speech_balloon')
+ end
+ end
end
- it 'displays a default emoji if only message is entered' do
- message = 'a status without emoji'
- visit(profile_path)
- fill_in 'js-status-message-field', with: message
+ context 'user menu' do
+ def open_user_status_modal
+ find('.header-user-dropdown-toggle').click
+
+ page.within ".header-user" do
+ click_button 'Set status'
+ end
+ end
+
+ def set_user_status_in_modal
+ page.within "#set-user-status-modal" do
+ click_button 'Set status'
+ end
+ end
+
+ before do
+ visit root_path(user)
+ end
+
+ it 'shows the "Set status" menu item in the user menu' do
+ find('.header-user-dropdown-toggle').click
+
+ page.within ".header-user" do
+ expect(page).to have_content('Set status')
+ end
+ end
+
+ it 'shows the "Edit status" menu item in the user menu' do
+ user_status = create(:user_status, user: user, message: 'Eating bread', emoji: 'stuffed_flatbread')
+ visit root_path(user)
+
+ find('.header-user-dropdown-toggle').click
+
+ page.within ".header-user" do
+ expect(page).to have_emoji(user_status.emoji)
+ expect(page).to have_content user_status.message
+ expect(page).to have_content('Edit status')
+ end
+ end
+
+ it 'shows user status modal' do
+ open_user_status_modal
+
+ expect(page.find('#set-user-status-modal')).to be_visible
+ expect(page).to have_content('Set a status')
+ end
+
+ it 'adds emoji to user status' do
+ emoji = 'biohazard'
+ open_user_status_modal
+ select_emoji(emoji, true)
+ set_user_status_in_modal
+
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji(emoji)
+ end
+ end
+
+ it 'adds message to user status' do
+ message = 'I have something to say'
+ open_user_status_modal
+ find('.js-status-message-field').native.send_keys(message)
+ set_user_status_in_modal
+
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji('speech_balloon')
+ expect(page).to have_content message
+ end
+ end
+
+ it 'adds message and emoji to user status' do
+ emoji = 'tanabata_tree'
+ message = 'Playing outside'
+ open_user_status_modal
+ select_emoji(emoji, true)
+ find('.js-status-message-field').native.send_keys(message)
+ set_user_status_in_modal
+
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji(emoji)
+ expect(page).to have_content message
+ end
+ end
+
+ it 'clears the user status with the "X" button' do
+ user_status = create(:user_status, user: user, message: 'Eating bread', emoji: 'stuffed_flatbread')
+
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji(user_status.emoji)
+ expect(page).to have_content user_status.message
+ end
+
+ find('.header-user-dropdown-toggle').click
+
+ page.within ".header-user" do
+ click_button 'Edit status'
+ end
+
+ find('.js-clear-user-status-button').click
+ set_user_status_in_modal
+
+ visit user_path(user)
+ expect(page).not_to have_selector '.cover-status'
+ end
+
+ it 'clears the user status with the "Remove status" button' do
+ user_status = create(:user_status, user: user, message: 'Eating bread', emoji: 'stuffed_flatbread')
+
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji(user_status.emoji)
+ expect(page).to have_content user_status.message
+ end
+
+ find('.header-user-dropdown-toggle').click
+
+ page.within ".header-user" do
+ click_button 'Edit status'
+ end
+
+ page.within "#set-user-status-modal" do
+ click_button 'Remove status'
+ end
+
+ visit user_path(user)
+ expect(page).not_to have_selector '.cover-status'
+ end
+
+ it 'displays a default emoji if only message is entered' do
+ message = 'a status without emoji'
+ open_user_status_modal
+ find('.js-status-message-field').native.send_keys(message)
- within('.js-toggle-emoji-menu') do
- expect(page).to have_emoji('speech_balloon')
+ within('.js-toggle-emoji-menu') do
+ expect(page).to have_emoji('speech_balloon')
+ end
end
end
end
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index edc763ad0ad..8b92b9fc869 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -84,10 +84,8 @@ describe 'Gcp Cluster', :js do
it_behaves_like 'valid cluster gcp form'
- context 'rbac_clusters feature flag is enabled' do
+ context 'RBAC is enabled for the cluster' do
before do
- stub_feature_flags(rbac_clusters: true)
-
check 'cluster_provider_gcp_attributes_legacy_abac'
end
diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb
index 2b4998ed5ac..9ae1dba60b5 100644
--- a/spec/features/projects/clusters/user_spec.rb
+++ b/spec/features/projects/clusters/user_spec.rb
@@ -44,10 +44,8 @@ describe 'User Cluster', :js do
it_behaves_like 'valid cluster user form'
- context 'rbac_clusters feature flag is enabled' do
+ context 'RBAC is enabled for the cluster' do
before do
- stub_feature_flags(rbac_clusters: true)
-
check 'cluster_platform_kubernetes_attributes_authorization_type'
end
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index 45fc492f23f..2076ce7b4f7 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -542,7 +542,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
visit project_job_path(project, job)
end
- it 'shows manual action empty state' do
+ it 'shows manual action empty state', :js do
expect(page).to have_content(job.detailed_status(user).illustration[:title])
expect(page).to have_content('This job requires a manual action')
expect(page).to have_content('This job depends on a user to trigger its process. Often they are used to deploy code to production environments')
@@ -567,14 +567,13 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
visit project_job_path(project, job)
end
- it 'shows delayed job' do
- expect(page).to have_content(job.detailed_status(user).illustration[:title])
+ it 'shows delayed job', :js do
expect(page).to have_content('This is a scheduled to run in')
expect(page).to have_content("This job will automatically run after it's timer finishes.")
expect(page).to have_link('Unschedule job')
end
- it 'unschedule delayed job and shows manual action', :js do
+ it 'unschedules delayed job and shows manual action', :js do
click_link 'Unschedule job'
wait_for_requests
@@ -591,14 +590,14 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
visit project_job_path(project, job)
end
- it 'shows empty state' do
+ it 'shows empty state', :js do
expect(page).to have_content(job.detailed_status(user).illustration[:title])
expect(page).to have_content('This job has not been triggered yet')
expect(page).to have_content('This job depends on upstream jobs that need to succeed in order for this job to be triggered')
end
end
- context 'Pending job' do
+ context 'Pending job', :js do
let(:job) { create(:ci_build, :pending, pipeline: pipeline) }
before do
@@ -625,7 +624,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
end
- context 'without log' do
+ context 'without log', :js do
let(:job) { create(:ci_build, :canceled, pipeline: pipeline) }
before do
@@ -640,7 +639,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
end
- context 'Skipped job' do
+ context 'Skipped job', :js do
let(:job) { create(:ci_build, :skipped, pipeline: pipeline) }
before do
@@ -654,7 +653,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
end
- context 'when job is failed but has no trace' do
+ context 'when job is failed but has no trace', :js do
let(:job) { create(:ci_build, :failed, pipeline: pipeline) }
it 'renders empty state' do
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index e6cb137b023..491c64fc329 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -84,7 +84,7 @@ describe 'Pipeline', :js do
end
end
- it 'should be possible to cancel the running build' do
+ it 'cancels the running build and shows retry button' do
find('#ci-badge-deploy .ci-action-icon-container').click
page.within('#ci-badge-deploy') do
@@ -112,8 +112,8 @@ describe 'Pipeline', :js do
end
end
- context 'when pipeline has scheduled builds' do
- it 'shows the scheduled icon and a unschedule action for the scheduled build' do
+ context 'when pipeline has a delayed job' do
+ it 'shows the scheduled icon and an unschedule action for the delayed job' do
page.within('#ci-badge-delayed-job') do
expect(page).to have_selector('.js-ci-status-icon-scheduled')
expect(page).to have_content('delayed-job')
@@ -124,10 +124,12 @@ describe 'Pipeline', :js do
end
end
- it 'should be possible to unschedule the scheduled job' do
+ it 'unschedules the delayed job and shows play button as a manual job' do
find('#ci-badge-delayed-job .ci-action-icon-container').click
- expect(page).not_to have_content('Unschedule job')
+ page.within('#ci-badge-delayed-job') do
+ expect(page).to have_css('.js-icon-play')
+ end
end
end
@@ -341,14 +343,16 @@ describe 'Pipeline', :js do
it { expect(build_manual.reload).to be_pending }
end
- context 'unscheduling scheduled job' do
+ context 'when user unschedules a delayed job' do
before do
within '.pipeline-holder' do
click_link('Unschedule')
end
end
- it { expect(build_scheduled.reload).to be_manual }
+ it 'unschedules the delayed job and shows play button as a manual job' do
+ expect(page).to have_content('Trigger this manual action')
+ end
end
context 'failed jobs' do
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 43f9608e8e3..17772a35779 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -232,7 +232,7 @@ describe 'Pipelines', :js do
end
end
- context 'with delayed job' do
+ context 'when there is a delayed job' do
let!(:delayed_job) do
create(:ci_build, :scheduled,
pipeline: pipeline,
@@ -245,26 +245,43 @@ describe 'Pipelines', :js do
visit_project_pipelines
end
- it 'has a dropdown with play button' do
+ it 'has a dropdown for actionable jobs' do
expect(page).to have_selector('.dropdown-new.btn.btn-default .icon-play')
end
- it 'has link to the scheduled action' do
+ it "has link to the delayed job's action" do
find('.js-pipeline-dropdown-manual-actions').click
+ time_diff = [0, delayed_job.scheduled_at - Time.now].max
expect(page).to have_button('delayed job')
+ expect(page).to have_content(Time.at(time_diff).utc.strftime("%H:%M:%S"))
end
- context 'when scheduled action was played' do
+ context 'when delayed job is expired already' do
+ let!(:delayed_job) do
+ create(:ci_build, :expired_scheduled,
+ pipeline: pipeline,
+ name: 'delayed job',
+ stage: 'test',
+ commands: 'test')
+ end
+
+ it "shows 00:00:00 as the remaining time" do
+ find('.js-pipeline-dropdown-manual-actions').click
+
+ expect(page).to have_content("00:00:00")
+ end
+ end
+
+ context 'when user played a delayed job immediately' do
before do
- accept_confirm do
- find('.js-pipeline-dropdown-manual-actions').click
- click_button('delayed job')
- end
+ find('.js-pipeline-dropdown-manual-actions').click
+ page.accept_confirm { click_button('delayed job') }
+ wait_for_requests
end
- it 'enqueues scheduled action job' do
- expect(page).to have_selector('.js-pipeline-dropdown-manual-actions:disabled')
+ it 'enqueues the delayed job', :js do
+ expect(delayed_job.reload).to be_pending
end
end
end
diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb
index 30b0a5578ea..6f8ec0015ad 100644
--- a/spec/features/projects/settings/pipelines_settings_spec.rb
+++ b/spec/features/projects/settings/pipelines_settings_spec.rb
@@ -137,5 +137,29 @@ describe "Projects > Settings > Pipelines settings" do
end
end
end
+
+ describe 'runners registration token' do
+ let!(:token) { project.runners_token }
+
+ before do
+ visit project_settings_ci_cd_path(project)
+ end
+
+ it 'has a registration token' do
+ expect(page.find('#registration_token')).to have_content(token)
+ end
+
+ describe 'reload registration token' do
+ let(:page_token) { find('#registration_token').text }
+
+ before do
+ click_button 'Reset runners registration token'
+ end
+
+ it 'changes registration token' do
+ expect(page_token).not_to eq token
+ end
+ end
+ end
end
end
diff --git a/spec/features/search/user_uses_search_filters_spec.rb b/spec/features/search/user_uses_search_filters_spec.rb
index 66afe163447..0725ff178ac 100644
--- a/spec/features/search/user_uses_search_filters_spec.rb
+++ b/spec/features/search/user_uses_search_filters_spec.rb
@@ -14,7 +14,7 @@ describe 'User uses search filters', :js do
visit(search_path)
end
- context' when filtering by group' do
+ context 'when filtering by group' do
it 'shows group projects' do
find('.js-search-group-dropdown').click
@@ -36,7 +36,7 @@ describe 'User uses search filters', :js do
end
end
- context' when filtering by project' do
+ context 'when filtering by project' do
it 'shows a project' do
page.within('.project-filter') do
find('.js-search-project-dropdown').click
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
index f1192f48b86..ae9b65d1a39 100644
--- a/spec/features/u2f_spec.rb
+++ b/spec/features/u2f_spec.rb
@@ -42,7 +42,7 @@ describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
it 'allows registering a new device with a name' do
visit profile_account_path
manage_two_factor_authentication
- expect(page).to have_content("You've already enabled two-factor authentication using mobile")
+ expect(page).to have_content("You've already enabled two-factor authentication using one time password authenticators")
u2f_device = register_u2f_device
@@ -70,7 +70,7 @@ describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
it 'allows deleting a device' do
visit profile_account_path
manage_two_factor_authentication
- expect(page).to have_content("You've already enabled two-factor authentication using mobile")
+ expect(page).to have_content("You've already enabled two-factor authentication using one time password authenticators")
first_u2f_device = register_u2f_device
second_u2f_device = register_u2f_device(name: 'My other device')
diff --git a/spec/features/users/overview_spec.rb b/spec/features/users/overview_spec.rb
new file mode 100644
index 00000000000..11f357cbaa5
--- /dev/null
+++ b/spec/features/users/overview_spec.rb
@@ -0,0 +1,123 @@
+require 'spec_helper'
+
+describe 'Overview tab on a user profile', :js do
+ let(:user) { create(:user) }
+ let(:contributed_project) { create(:project, :public, :repository) }
+
+ def push_code_contribution
+ event = create(:push_event, project: contributed_project, author: user)
+
+ create(:push_event_payload,
+ event: event,
+ commit_from: '11f9ac0a48b62cef25eedede4c1819964f08d5ce',
+ commit_to: '1cf19a015df3523caf0a1f9d40c98a267d6a2fc2',
+ commit_count: 3,
+ ref: 'master')
+ end
+
+ before do
+ sign_in user
+ end
+
+ describe 'activities section' do
+ shared_context 'visit overview tab' do
+ before do
+ visit user.username
+ page.find('.js-overview-tab a').click
+ wait_for_requests
+ end
+ end
+
+ describe 'user has no activities' do
+ include_context 'visit overview tab'
+
+ it 'does not show any entries in the list of activities' do
+ page.within('.activities-block') do
+ expect(page).not_to have_selector('.event-item')
+ end
+ end
+
+ it 'does not show a link to the activity list' do
+ expect(find('#js-overview .activities-block')).to have_selector('.js-view-all', visible: false)
+ end
+ end
+
+ describe 'user has 3 activities' do
+ before do
+ 3.times { push_code_contribution }
+ end
+
+ include_context 'visit overview tab'
+
+ it 'display 3 entries in the list of activities' do
+ expect(find('#js-overview')).to have_selector('.event-item', count: 3)
+ end
+ end
+
+ describe 'user has 10 activities' do
+ before do
+ 10.times { push_code_contribution }
+ end
+
+ include_context 'visit overview tab'
+
+ it 'displays 5 entries in the list of activities' do
+ expect(find('#js-overview')).to have_selector('.event-item', count: 5)
+ end
+
+ it 'shows a link to the activity list' do
+ expect(find('#js-overview .activities-block')).to have_selector('.js-view-all', visible: true)
+ end
+
+ it 'links to the activity tab' do
+ page.within('.activities-block') do
+ find('.js-view-all').click
+ wait_for_requests
+ expect(URI.parse(current_url).path).to eq("/users/#{user.username}/activity")
+ end
+ end
+ end
+ end
+
+ describe 'projects section' do
+ shared_context 'visit overview tab' do
+ before do
+ visit user.username
+ page.find('.js-overview-tab a').click
+ wait_for_requests
+ end
+ end
+
+ describe 'user has no personal projects' do
+ include_context 'visit overview tab'
+
+ it 'it shows an empty project list with an info message' do
+ page.within('.projects-block') do
+ expect(page).to have_content('No projects found')
+ expect(page).not_to have_selector('.project-row')
+ end
+ end
+
+ it 'does not show a link to the project list' do
+ expect(find('#js-overview .projects-block')).to have_selector('.js-view-all', visible: false)
+ end
+ end
+
+ describe 'user has a personal project' do
+ let(:private_project) { create(:project, :private, namespace: user.namespace, creator: user) { |p| p.add_maintainer(user) } }
+ let!(:private_event) { create(:event, project: private_project, author: user) }
+
+ include_context 'visit overview tab'
+
+ it 'it shows one entry in the list of projects' do
+ page.within('.projects-block') do
+ expect(page).to have_selector('.project-row', count: 1)
+ end
+ end
+
+ it 'shows a link to the project list' do
+ expect(find('#js-overview .projects-block')).to have_selector('.js-view-all', visible: true)
+ end
+ end
+ end
+end
diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb
index bc07ab48c39..86379164cf0 100644
--- a/spec/features/users/show_spec.rb
+++ b/spec/features/users/show_spec.rb
@@ -8,6 +8,7 @@ describe 'User page' do
visit(user_path(user))
page.within '.nav-links' do
+ expect(page).to have_link('Overview')
expect(page).to have_link('Activity')
expect(page).to have_link('Groups')
expect(page).to have_link('Contributed projects')
@@ -44,6 +45,7 @@ describe 'User page' do
visit(user_path(user))
page.within '.nav-links' do
+ expect(page).to have_link('Overview')
expect(page).to have_link('Activity')
expect(page).to have_link('Groups')
expect(page).to have_link('Contributed projects')
diff --git a/spec/finders/group_labels_finder_spec.rb b/spec/finders/group_labels_finder_spec.rb
index ef68fc105e4..7bdd312eff0 100644
--- a/spec/finders/group_labels_finder_spec.rb
+++ b/spec/finders/group_labels_finder_spec.rb
@@ -4,29 +4,38 @@ require 'spec_helper'
describe GroupLabelsFinder, '#execute' do
let!(:group) { create(:group) }
+ let!(:user) { create(:user) }
let!(:label1) { create(:group_label, title: 'Foo', description: 'Lorem ipsum', group: group) }
let!(:label2) { create(:group_label, title: 'Bar', description: 'Fusce consequat', group: group) }
it 'returns all group labels sorted by name if no params' do
- result = described_class.new(group).execute
+ result = described_class.new(user, group).execute
expect(result.to_a).to match_array([label2, label1])
end
it 'returns all group labels sorted by name desc' do
- result = described_class.new(group, sort: 'name_desc').execute
+ result = described_class.new(user, group, sort: 'name_desc').execute
expect(result.to_a).to match_array([label2, label1])
end
- it 'returns group labels that march search' do
- result = described_class.new(group, search: 'Foo').execute
+ it 'returns group labels that match search' do
+ result = described_class.new(user, group, search: 'Foo').execute
expect(result.to_a).to match_array([label1])
end
+ it 'returns group labels user subscribed to' do
+ label2.subscribe(user)
+
+ result = described_class.new(user, group, subscribed: 'true').execute
+
+ expect(result.to_a).to match_array([label2])
+ end
+
it 'returns second page of labels' do
- result = described_class.new(group, page: '2').execute
+ result = described_class.new(user, group, page: '2').execute
expect(result.to_a).to match_array([])
end
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index d78451112ec..0689c843104 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -125,6 +125,14 @@ describe IssuesFinder do
end
end
+ context 'filtering by any milestone' do
+ let(:params) { { milestone_title: Milestone::Any.title } }
+
+ it 'returns issues with any assigned milestone' do
+ expect(issues).to contain_exactly(issue1)
+ end
+ end
+
context 'filtering by upcoming milestone' do
let(:params) { { milestone_title: Milestone::Upcoming.name } }
diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb
index f5cec8e349a..9abc52aa664 100644
--- a/spec/finders/labels_finder_spec.rb
+++ b/spec/finders/labels_finder_spec.rb
@@ -210,5 +210,15 @@ describe LabelsFinder do
expect(finder.execute).to eq [project_label_1]
end
end
+
+ context 'filter by subscription' do
+ it 'returns labels user subscribed to' do
+ project_label_1.subscribe(user)
+
+ finder = described_class.new(user, subscribed: 'true')
+
+ expect(finder.execute).to eq [project_label_1]
+ end
+ end
end
end
diff --git a/spec/finders/license_template_finder_spec.rb b/spec/finders/license_template_finder_spec.rb
index a97903103c9..f6f40bf33cc 100644
--- a/spec/finders/license_template_finder_spec.rb
+++ b/spec/finders/license_template_finder_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe LicenseTemplateFinder do
describe '#execute' do
- subject(:result) { described_class.new(params).execute }
+ subject(:result) { described_class.new(nil, params).execute }
let(:categories) { categorised_licenses.keys }
let(:categorised_licenses) { result.group_by(&:category) }
@@ -31,7 +31,7 @@ describe LicenseTemplateFinder do
it 'returns all licenses known by the Licensee gem' do
from_licensee = Licensee::License.all.map { |l| l.key }
- expect(result.map(&:id)).to match_array(from_licensee)
+ expect(result.map(&:key)).to match_array(from_licensee)
end
it 'correctly copies all attributes' do
diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb
index 33d01697c75..ff4c6b8dd42 100644
--- a/spec/finders/merge_requests_finder_spec.rb
+++ b/spec/finders/merge_requests_finder_spec.rb
@@ -3,21 +3,37 @@ require 'spec_helper'
describe MergeRequestsFinder do
include ProjectForksHelper
+ # We need to explicitly permit Gitaly N+1s because of the specs that use
+ # :request_store. Gitaly N+1 detection is only enabled when :request_store is,
+ # but we don't care about potential N+1s when we're just creating several
+ # projects in the setup phase.
+ def create_project_without_n_plus_1(*args)
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ create(:project, :public, *args)
+ end
+ end
+
let(:user) { create :user }
let(:user2) { create :user }
let(:group) { create(:group) }
let(:subgroup) { create(:group, parent: group) }
- let(:project1) { create(:project, :public, group: group) }
- let(:project2) { fork_project(project1, user) }
+ let(:project1) { create_project_without_n_plus_1(group: group) }
+ let(:project2) do
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ fork_project(project1, user)
+ end
+ end
let(:project3) do
- p = fork_project(project1, user)
- p.update!(archived: true)
- p
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ p = fork_project(project1, user)
+ p.update!(archived: true)
+ p
+ end
end
- let(:project4) { create(:project, :public, group: subgroup) }
- let(:project5) { create(:project, :public, group: subgroup) }
- let(:project6) { create(:project, :public, group: subgroup) }
+ let(:project4) { create_project_without_n_plus_1(group: subgroup) }
+ let(:project5) { create_project_without_n_plus_1(group: subgroup) }
+ let(:project6) { create_project_without_n_plus_1(group: subgroup) }
let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) }
let!(:merge_request2) { create(:merge_request, :conflict, author: user, source_project: project2, target_project: project1, state: 'closed') }
diff --git a/spec/finders/pending_todos_finder_spec.rb b/spec/finders/pending_todos_finder_spec.rb
new file mode 100644
index 00000000000..32fad5e225f
--- /dev/null
+++ b/spec/finders/pending_todos_finder_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe PendingTodosFinder do
+ let(:user) { create(:user) }
+
+ describe '#execute' do
+ it 'returns only pending todos' do
+ create(:todo, :done, user: user)
+
+ todo = create(:todo, :pending, user: user)
+ todos = described_class.new(user).execute
+
+ expect(todos).to eq([todo])
+ end
+
+ it 'supports retrieving of todos for a specific project' do
+ project1 = create(:project)
+ project2 = create(:project)
+
+ create(:todo, :pending, user: user, project: project2)
+
+ todo = create(:todo, :pending, user: user, project: project1)
+ todos = described_class.new(user, project_id: project1.id).execute
+
+ expect(todos).to eq([todo])
+ end
+
+ it 'supports retrieving of todos for a specific todo target' do
+ issue = create(:issue)
+ note = create(:note)
+ todo = create(:todo, :pending, user: user, target: issue)
+
+ create(:todo, :pending, user: user, target: note)
+
+ todos = described_class.new(user, target_id: issue.id).execute
+
+ expect(todos).to eq([todo])
+ end
+
+ it 'supports retrieving of todos for a specific target type' do
+ issue = create(:issue)
+ note = create(:note)
+ todo = create(:todo, :pending, user: user, target: issue)
+
+ create(:todo, :pending, user: user, target: note)
+
+ todos = described_class.new(user, target_type: issue.class).execute
+
+ expect(todos).to eq([todo])
+ end
+
+ it 'supports retrieving of todos for a specific commit ID' do
+ create(:todo, :pending, user: user, commit_id: '456')
+
+ todo = create(:todo, :pending, user: user, commit_id: '123')
+ todos = described_class.new(user, commit_id: '123').execute
+
+ expect(todos).to eq([todo])
+ end
+ end
+end
diff --git a/spec/finders/template_finder_spec.rb b/spec/finders/template_finder_spec.rb
index 1d399e8194f..114af9461e0 100644
--- a/spec/finders/template_finder_spec.rb
+++ b/spec/finders/template_finder_spec.rb
@@ -4,6 +4,8 @@ describe TemplateFinder do
using RSpec::Parameterized::TableSyntax
describe '#build' do
+ let(:project) { build_stubbed(:project) }
+
where(:type, :expected_class) do
:dockerfiles | described_class
:gitignores | described_class
@@ -12,9 +14,10 @@ describe TemplateFinder do
end
with_them do
- subject { described_class.build(type) }
+ subject(:finder) { described_class.build(type, project) }
it { is_expected.to be_a(expected_class) }
+ it { expect(finder.project).to eq(project) }
end
end
@@ -27,19 +30,19 @@ describe TemplateFinder do
with_them do
it 'returns all vendored templates when no name is specified' do
- result = described_class.new(type).execute
+ result = described_class.new(type, nil).execute
expect(result).to include(have_attributes(name: vendored_name))
end
it 'returns only the specified vendored template when a name is specified' do
- result = described_class.new(type, name: vendored_name).execute
+ result = described_class.new(type, nil, name: vendored_name).execute
expect(result).to have_attributes(name: vendored_name)
end
it 'returns nil when an unknown name is specified' do
- result = described_class.new(type, name: 'unknown').execute
+ result = described_class.new(type, nil, name: 'unknown').execute
expect(result).to be_nil
end
diff --git a/spec/finders/todos_finder_spec.rb b/spec/finders/todos_finder_spec.rb
index 7f7cfb2cb98..d4ed41d54f0 100644
--- a/spec/finders/todos_finder_spec.rb
+++ b/spec/finders/todos_finder_spec.rb
@@ -105,9 +105,24 @@ describe TodosFinder do
todos = finder.new(user, { sort: 'priority' }).execute
- puts todos.to_sql
expect(todos).to eq([todo_3, todo_5, todo_4, todo_2, todo_1])
end
end
end
+
+ describe '#any_for_target?' do
+ it 'returns true if there are any todos for the given target' do
+ todo = create(:todo, :pending)
+ finder = described_class.new(todo.user)
+
+ expect(finder.any_for_target?(todo.target)).to eq(true)
+ end
+
+ it 'returns false if there are no todos for the given target' do
+ issue = create(:issue)
+ finder = described_class.new(issue.author)
+
+ expect(finder.any_for_target?(issue)).to eq(false)
+ end
+ end
end
diff --git a/spec/finders/users_with_pending_todos_finder_spec.rb b/spec/finders/users_with_pending_todos_finder_spec.rb
new file mode 100644
index 00000000000..fa15355531c
--- /dev/null
+++ b/spec/finders/users_with_pending_todos_finder_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe UsersWithPendingTodosFinder do
+ describe '#execute' do
+ it 'returns the users for all pending todos of a target' do
+ issue = create(:issue)
+ note = create(:note)
+ todo = create(:todo, :pending, target: issue)
+
+ create(:todo, :pending, target: note)
+
+ users = described_class.new(issue).execute
+
+ expect(users).to eq([todo.user])
+ end
+ end
+end
diff --git a/spec/fixtures/api/schemas/entities/diff_viewer.json b/spec/fixtures/api/schemas/entities/diff_viewer.json
new file mode 100644
index 00000000000..19780f49a88
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/diff_viewer.json
@@ -0,0 +1,8 @@
+{
+ "type": "object",
+ "required": ["name"],
+ "properties": {
+ "name": { "type": ["string"] }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/entities/note_user_entity.json b/spec/fixtures/api/schemas/entities/note_user_entity.json
new file mode 100644
index 00000000000..9b838054563
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/note_user_entity.json
@@ -0,0 +1,21 @@
+{
+ "type": "object",
+ "required": [
+ "id",
+ "state",
+ "avatar_url",
+ "path",
+ "name",
+ "username"
+ ],
+ "properties": {
+ "id": { "type": "integer" },
+ "state": { "type": "string" },
+ "avatar_url": { "type": "string" },
+ "path": { "type": "string" },
+ "name": { "type": "string" },
+ "username": { "type": "string" },
+ "status_tooltip_html": { "$ref": "../types/nullable_string.json" }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/job/deployment_status.json b/spec/fixtures/api/schemas/job/deployment_status.json
index a90b8b35654..83b1899fdf3 100644
--- a/spec/fixtures/api/schemas/job/deployment_status.json
+++ b/spec/fixtures/api/schemas/job/deployment_status.json
@@ -2,7 +2,6 @@
"type": "object",
"required": [
"status",
- "icon",
"environment"
],
"properties": {
@@ -20,7 +19,6 @@
{ "type": "null" }
]
},
- "icon": { "type": "string" },
"environment": { "$ref": "../environment.json" }
},
"additionalProperties": false
diff --git a/spec/fixtures/api/schemas/job/runners.json b/spec/fixtures/api/schemas/job/runners.json
index bebb0c88652..646bfd3a82d 100644
--- a/spec/fixtures/api/schemas/job/runners.json
+++ b/spec/fixtures/api/schemas/job/runners.json
@@ -8,6 +8,5 @@
"online": { "type": "boolean" },
"available": { "type": "boolean" },
"settings_path": { "type": "string" }
- },
- "additionalProperties": false
+ }
}
diff --git a/spec/fixtures/api/schemas/public_api/v4/license.json b/spec/fixtures/api/schemas/public_api/v4/license.json
new file mode 100644
index 00000000000..38c8c3e9192
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/license.json
@@ -0,0 +1,30 @@
+{
+ "type": "object",
+ "required": [
+ "key",
+ "name",
+ "nickname",
+ "popular",
+ "html_url",
+ "source_url",
+ "description",
+ "conditions",
+ "permissions",
+ "limitations",
+ "content"
+ ],
+ "properties": {
+ "key": { "type": "string" },
+ "name": { "type": "string" },
+ "nickname": { "type": ["null", "string"] },
+ "popular": { "type": "boolean" },
+ "html_url": { "type": ["null", "string"] },
+ "source_url": { "type": ["null", "string"] },
+ "description": { "type": ["null", "string"] },
+ "conditions": { "type": "array", "items": { "type": "string" } },
+ "permissions": { "type": "array", "items": { "type": "string" } },
+ "limitations": { "type": "array", "items": { "type": "string" } },
+ "content": { "type": "string" }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/template.json b/spec/fixtures/api/schemas/public_api/v4/template.json
new file mode 100644
index 00000000000..38601aa6b45
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/template.json
@@ -0,0 +1,12 @@
+{
+ "type": "object",
+ "required": [
+ "name",
+ "content"
+ ],
+ "properties": {
+ "name": { "type": "string" },
+ "content": { "type": "string" }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/template_list.json b/spec/fixtures/api/schemas/public_api/v4/template_list.json
new file mode 100644
index 00000000000..2336dafb17b
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/template_list.json
@@ -0,0 +1,15 @@
+{
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "key",
+ "name"
+ ],
+ "properties": {
+ "key": { "type": "string" },
+ "name": { "type": "string" }
+ },
+ "additionalProperties": false
+ }
+}
diff --git a/spec/fixtures/ssh_host_example_key.pub b/spec/fixtures/ssh_host_example_key.pub
index 6bac42b3ad0..d43315ddae8 100644
--- a/spec/fixtures/ssh_host_example_key.pub
+++ b/spec/fixtures/ssh_host_example_key.pub
@@ -1 +1 @@
-random content
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuRkAgwaap/pXThwCpjX8Wd5tR36Tqx3sW2sVVHs3UKB7kd+xNknw7e4qpuEATv56xHrhKm2+ye/JidTuQ/1EwFhjaz7I5wTslfVawQpeH1ZqAGmvdO/xTw+l7fgEFVlGVx9y0HV3m52y2C9yw82qmg+BohbTVgPtjjutpFc+CwLQxLTnTrRhZf5udQgz+YlwLv+Y0kDx6+DWWOl8N9+TWuGyFKBln79CyBgFcK5NFmF48kYn8W+r7rmawfw9XbuF1aa+6JF+6cNR1mCEonyrRLdXP+vWcxpLKYfejB0NmA1y+W9M/K53AcIHA5zlRQ49tFh0P22eh/Gl8JQ6yyuin foo@bar.mynet
diff --git a/spec/graphql/types/permission_types/project_spec.rb b/spec/graphql/types/permission_types/project_spec.rb
index 89eecef096e..927153adc5b 100644
--- a/spec/graphql/types/permission_types/project_spec.rb
+++ b/spec/graphql/types/permission_types/project_spec.rb
@@ -10,7 +10,7 @@ describe Types::PermissionTypes::Project do
:read_commit_status, :request_access, :create_pipeline, :create_pipeline_schedule,
:create_merge_request_from, :create_wiki, :push_code, :create_deployment, :push_to_delete_protected_branch,
:admin_wiki, :admin_project, :update_pages, :admin_remote_mirror, :create_label,
- :update_wiki, :destroy_wiki, :create_pages, :destroy_pages
+ :update_wiki, :destroy_wiki, :create_pages, :destroy_pages, :read_pages_content
]
expect(described_class).to have_graphql_fields(expected_permissions)
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 1238cfbd1e7..4135f31e051 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -174,9 +174,7 @@ describe ApplicationHelper do
it 'returns paths for autocomplete_sources_controller' do
sources = helper.autocomplete_data_sources(project, noteable_type)
-
- expect(sources.keys).to match_array([:members, :issues, :mergeRequests, :labels, :milestones, :commands])
-
+ expect(sources.keys).to match_array([:members, :issues, :mergeRequests, :labels, :milestones, :commands, :snippets])
sources.keys.each do |key|
expect(sources[key]).not_to be_nil
end
diff --git a/spec/javascripts/api_spec.js b/spec/javascripts/api_spec.js
index 54cb6d84109..091edf13cfe 100644
--- a/spec/javascripts/api_spec.js
+++ b/spec/javascripts/api_spec.js
@@ -250,71 +250,45 @@ describe('Api', () => {
});
});
- describe('licenseText', () => {
- it('fetches a license text', done => {
- const licenseKey = "driver's license";
- const data = { unused: 'option' };
- const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/templates/licenses/${licenseKey}`;
+ describe('issueTemplate', () => {
+ it('fetches an issue template', done => {
+ const namespace = 'some namespace';
+ const project = 'some project';
+ const templateKey = ' template #%?.key ';
+ const templateType = 'template type';
+ const expectedUrl = `${dummyUrlRoot}/${namespace}/${project}/templates/${templateType}/${encodeURIComponent(
+ templateKey,
+ )}`;
mock.onGet(expectedUrl).reply(200, 'test');
- Api.licenseText(licenseKey, data, response => {
+ Api.issueTemplate(namespace, project, templateKey, templateType, (error, response) => {
expect(response).toBe('test');
done();
});
});
});
- describe('gitignoreText', () => {
- it('fetches a gitignore text', done => {
- const gitignoreKey = 'ignore git';
- const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/templates/gitignores/${gitignoreKey}`;
- mock.onGet(expectedUrl).reply(200, 'test');
-
- Api.gitignoreText(gitignoreKey, response => {
- expect(response).toBe('test');
- done();
- });
- });
- });
+ describe('projectTemplates', () => {
+ it('fetches a list of templates', done => {
+ const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/projects/gitlab-org%2Fgitlab-ce/templates/licenses`;
- describe('gitlabCiYml', () => {
- it('fetches a .gitlab-ci.yml', done => {
- const gitlabCiYmlKey = 'Y CI ML';
- const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/templates/gitlab_ci_ymls/${gitlabCiYmlKey}`;
mock.onGet(expectedUrl).reply(200, 'test');
- Api.gitlabCiYml(gitlabCiYmlKey, response => {
+ Api.projectTemplates('gitlab-org/gitlab-ce', 'licenses', {}, response => {
expect(response).toBe('test');
done();
});
});
});
- describe('dockerfileYml', () => {
- it('fetches a Dockerfile', done => {
- const dockerfileYmlKey = 'a giant whale';
- const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/templates/dockerfiles/${dockerfileYmlKey}`;
- mock.onGet(expectedUrl).reply(200, 'test');
-
- Api.dockerfileYml(dockerfileYmlKey, response => {
- expect(response).toBe('test');
- done();
- });
- });
- });
+ describe('projectTemplate', () => {
+ it('fetches a single template', done => {
+ const data = { unused: 'option' };
+ const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/projects/gitlab-org%2Fgitlab-ce/templates/licenses/test%20license`;
- describe('issueTemplate', () => {
- it('fetches an issue template', done => {
- const namespace = 'some namespace';
- const project = 'some project';
- const templateKey = ' template #%?.key ';
- const templateType = 'template type';
- const expectedUrl = `${dummyUrlRoot}/${namespace}/${project}/templates/${templateType}/${encodeURIComponent(
- templateKey,
- )}`;
mock.onGet(expectedUrl).reply(200, 'test');
- Api.issueTemplate(namespace, project, templateKey, templateType, (error, response) => {
+ Api.projectTemplate('gitlab-org/gitlab-ce', 'licenses', 'test license', data, response => {
expect(response).toBe('test');
done();
});
diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js
index ada26b37f4a..0c5d68990d5 100644
--- a/spec/javascripts/awards_handler_spec.js
+++ b/spec/javascripts/awards_handler_spec.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-var, one-var, one-var-declaration-per-line, no-unused-expressions, no-unused-vars, prefer-template, max-len */
+/* eslint-disable no-var, one-var, no-unused-expressions, no-unused-vars, prefer-template */
import $ from 'jquery';
import Cookies from 'js-cookie';
diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js
index 1ee6f4cf680..ed43ce9029e 100644
--- a/spec/javascripts/boards/boards_store_spec.js
+++ b/spec/javascripts/boards/boards_store_spec.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, no-unused-vars */
+/* eslint-disable no-unused-vars */
/* global ListIssue */
import Vue from 'vue';
diff --git a/spec/javascripts/boards/issue_spec.js b/spec/javascripts/boards/issue_spec.js
index db68096e3bd..0beb5782283 100644
--- a/spec/javascripts/boards/issue_spec.js
+++ b/spec/javascripts/boards/issue_spec.js
@@ -1,4 +1,3 @@
-/* eslint-disable comma-dangle */
/* global ListIssue */
import Vue from 'vue';
diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js
index ac8bbb8f2a8..4232e0fc221 100644
--- a/spec/javascripts/boards/list_spec.js
+++ b/spec/javascripts/boards/list_spec.js
@@ -1,4 +1,3 @@
-/* eslint-disable comma-dangle */
/* global List */
/* global ListIssue */
diff --git a/spec/javascripts/diff_comments_store_spec.js b/spec/javascripts/diff_comments_store_spec.js
index d6fc6b56b82..c6f2e66cebd 100644
--- a/spec/javascripts/diff_comments_store_spec.js
+++ b/spec/javascripts/diff_comments_store_spec.js
@@ -1,4 +1,4 @@
-/* eslint-disable jasmine/no-global-setup, dot-notation, jasmine/no-expect-in-setup-teardown, max-len */
+/* eslint-disable jasmine/no-global-setup, dot-notation, jasmine/no-expect-in-setup-teardown */
/* global CommentsStore */
import '~/diff_notes/models/discussion';
diff --git a/spec/javascripts/diffs/components/commit_item_spec.js b/spec/javascripts/diffs/components/commit_item_spec.js
index 627fb8c490a..8c3376c0eb3 100644
--- a/spec/javascripts/diffs/components/commit_item_spec.js
+++ b/spec/javascripts/diffs/components/commit_item_spec.js
@@ -9,6 +9,8 @@ import getDiffWithCommit from '../mock_data/diff_with_commit';
const TEST_AUTHOR_NAME = 'test';
const TEST_AUTHOR_EMAIL = 'test+test@gitlab.com';
const TEST_AUTHOR_GRAVATAR = `${TEST_HOST}/avatar/test?s=36`;
+const TEST_SIGNATURE_HTML = '<a>Legit commit</a>';
+const TEST_PIPELINE_STATUS_PATH = `${TEST_HOST}/pipeline/status`;
const getTitleElement = vm => vm.$el.querySelector('.commit-row-message.item-title');
const getDescElement = vm => vm.$el.querySelector('pre.commit-row-description');
@@ -16,6 +18,7 @@ const getDescExpandElement = vm => vm.$el.querySelector('.commit-content .text-e
const getShaElement = vm => vm.$el.querySelector('.commit-sha-group');
const getAvatarElement = vm => vm.$el.querySelector('.user-avatar-link');
const getCommitterElement = vm => vm.$el.querySelector('.commiter');
+const getCommitActionsElement = vm => vm.$el.querySelector('.commit-actions');
describe('diffs/components/commit_widget', () => {
const Component = Vue.extend(CommitItem);
@@ -125,4 +128,36 @@ describe('diffs/components/commit_widget', () => {
expect(nameElement).toHaveText(TEST_AUTHOR_NAME);
});
});
+
+ describe('with signature', () => {
+ beforeEach(done => {
+ vm.commit.signatureHtml = TEST_SIGNATURE_HTML;
+
+ vm.$nextTick()
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('renders signature html', () => {
+ const actionsElement = getCommitActionsElement(vm);
+
+ expect(actionsElement).toContainHtml(TEST_SIGNATURE_HTML);
+ });
+ });
+
+ describe('with pipeline status', () => {
+ beforeEach(done => {
+ vm.commit.pipelineStatusPath = TEST_PIPELINE_STATUS_PATH;
+
+ vm.$nextTick()
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('renders pipeline status', () => {
+ const actionsElement = getCommitActionsElement(vm);
+
+ expect(actionsElement).toContainElement('.ci-status-link');
+ });
+ });
});
diff --git a/spec/javascripts/diffs/components/diff_content_spec.js b/spec/javascripts/diffs/components/diff_content_spec.js
index dea600a783a..67f7b569f47 100644
--- a/spec/javascripts/diffs/components/diff_content_spec.js
+++ b/spec/javascripts/diffs/components/diff_content_spec.js
@@ -8,13 +8,12 @@ import diffFileMockData from '../mock_data/diff_file';
describe('DiffContent', () => {
const Component = Vue.extend(DiffContentComponent);
let vm;
- const getDiffFileMock = () => Object.assign({}, diffFileMockData);
beforeEach(() => {
vm = mountComponentWithStore(Component, {
store,
props: {
- diffFile: getDiffFileMock(),
+ diffFile: JSON.parse(JSON.stringify(diffFileMockData)),
},
});
});
@@ -43,7 +42,7 @@ describe('DiffContent', () => {
describe('Non-Text diffs', () => {
beforeEach(() => {
- vm.diffFile.text = false;
+ vm.diffFile.viewer.name = 'image';
});
describe('image diff', () => {
diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js
index 2a52cd2b179..13859f43e98 100644
--- a/spec/javascripts/diffs/components/diff_file_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_spec.js
@@ -6,11 +6,10 @@ import diffFileMockData from '../mock_data/diff_file';
describe('DiffFile', () => {
let vm;
- const getDiffFileMock = () => Object.assign({}, diffFileMockData);
beforeEach(() => {
vm = createComponentWithStore(Vue.extend(DiffFileComponent), store, {
- file: getDiffFileMock(),
+ file: JSON.parse(JSON.stringify(diffFileMockData)),
canCurrentUserFork: false,
}).$mount();
});
@@ -18,7 +17,7 @@ describe('DiffFile', () => {
describe('template', () => {
it('should render component with file header, file content components', () => {
const el = vm.$el;
- const { fileHash, filePath } = diffFileMockData;
+ const { fileHash, filePath } = vm.file;
expect(el.id).toEqual(fileHash);
expect(el.classList.contains('diff-file')).toEqual(true);
diff --git a/spec/javascripts/diffs/mock_data/diff_discussions.js b/spec/javascripts/diffs/mock_data/diff_discussions.js
index b29a22da7c2..0ad214ea4a4 100644
--- a/spec/javascripts/diffs/mock_data/diff_discussions.js
+++ b/spec/javascripts/diffs/mock_data/diff_discussions.js
@@ -2,15 +2,13 @@ export default {
id: '6b232e05bea388c6b043ccc243ba505faac04ea8',
reply_id: '6b232e05bea388c6b043ccc243ba505faac04ea8',
position: {
- formatter: {
- old_line: null,
- new_line: 2,
- old_path: 'CHANGELOG',
- new_path: 'CHANGELOG',
- base_sha: 'e63f41fe459e62e1228fcef60d7189127aeba95a',
- start_sha: 'd9eaefe5a676b820c57ff18cf5b68316025f7962',
- head_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13',
- },
+ old_line: null,
+ new_line: 2,
+ old_path: 'CHANGELOG',
+ new_path: 'CHANGELOG',
+ base_sha: 'e63f41fe459e62e1228fcef60d7189127aeba95a',
+ start_sha: 'd9eaefe5a676b820c57ff18cf5b68316025f7962',
+ head_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13',
},
line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2',
expanded: true,
diff --git a/spec/javascripts/diffs/mock_data/diff_file.js b/spec/javascripts/diffs/mock_data/diff_file.js
index 2aa2f8f3528..d7bc0dbe431 100644
--- a/spec/javascripts/diffs/mock_data/diff_file.js
+++ b/spec/javascripts/diffs/mock_data/diff_file.js
@@ -23,6 +23,9 @@ export default {
aMode: '100644',
bMode: '100644',
text: true,
+ viewer: {
+ name: 'text',
+ },
addedLines: 2,
removedLines: 0,
diffRefs: {
diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/javascripts/diffs/store/actions_spec.js
index aacad7a479b..85c1926fcb1 100644
--- a/spec/javascripts/diffs/store/actions_spec.js
+++ b/spec/javascripts/diffs/store/actions_spec.js
@@ -148,12 +148,8 @@ describe('DiffsStoreActions', () => {
},
fileHash: 'ABC',
resolvable: true,
- position: {
- formatter: diffPosition,
- },
- original_position: {
- formatter: diffPosition,
- },
+ position: diffPosition,
+ original_position: diffPosition,
};
const discussions = reduceDiscussionsToLineCodes([singleDiscussion]);
@@ -178,6 +174,7 @@ describe('DiffsStoreActions', () => {
oldLine: 5,
oldPath: 'file2',
lineCode: 'ABC_1_1',
+ positionType: 'text',
},
},
},
diff --git a/spec/javascripts/diffs/store/mutations_spec.js b/spec/javascripts/diffs/store/mutations_spec.js
index cc8d5dc4bac..0b712055956 100644
--- a/spec/javascripts/diffs/store/mutations_spec.js
+++ b/spec/javascripts/diffs/store/mutations_spec.js
@@ -194,24 +194,16 @@ describe('DiffsStoreMutations', () => {
line_code: 'ABC_1',
diff_discussion: true,
resolvable: true,
- original_position: {
- formatter: diffPosition,
- },
- position: {
- formatter: diffPosition,
- },
+ original_position: diffPosition,
+ position: diffPosition,
},
{
id: 2,
line_code: 'ABC_1',
diff_discussion: true,
resolvable: true,
- original_position: {
- formatter: diffPosition,
- },
- position: {
- formatter: diffPosition,
- },
+ original_position: diffPosition,
+ position: diffPosition,
},
];
diff --git a/spec/javascripts/diffs/store/utils_spec.js b/spec/javascripts/diffs/store/utils_spec.js
index e660f94c72e..257270a91ec 100644
--- a/spec/javascripts/diffs/store/utils_spec.js
+++ b/spec/javascripts/diffs/store/utils_spec.js
@@ -333,20 +333,12 @@ describe('DiffsStoreUtils', () => {
const discussions = {
upToDateDiscussion1: {
- original_position: {
- formatter: diffPosition,
- },
- position: {
- formatter: wrongDiffPosition,
- },
+ original_position: diffPosition,
+ position: wrongDiffPosition,
},
outDatedDiscussion1: {
- original_position: {
- formatter: wrongDiffPosition,
- },
- position: {
- formatter: wrongDiffPosition,
- },
+ original_position: wrongDiffPosition,
+ position: wrongDiffPosition,
},
};
diff --git a/spec/javascripts/droplab/plugins/input_setter_spec.js b/spec/javascripts/droplab/plugins/input_setter_spec.js
index bd625f4ae80..1988811a305 100644
--- a/spec/javascripts/droplab/plugins/input_setter_spec.js
+++ b/spec/javascripts/droplab/plugins/input_setter_spec.js
@@ -1,5 +1,3 @@
-/* eslint-disable */
-
import InputSetter from '~/droplab/plugins/input_setter';
describe('InputSetter', function () {
diff --git a/spec/javascripts/gfm_auto_complete_spec.js b/spec/javascripts/gfm_auto_complete_spec.js
index 4f9cacf2724..b57c4943c01 100644
--- a/spec/javascripts/gfm_auto_complete_spec.js
+++ b/spec/javascripts/gfm_auto_complete_spec.js
@@ -103,7 +103,7 @@ describe('GfmAutoComplete', function () {
gfmAutoCompleteCallbacks.matcher.call(context, flag, subtext)
);
- const flagsUseDefaultMatcher = ['@', '#', '!', '~', '%'];
+ const flagsUseDefaultMatcher = ['@', '#', '!', '~', '%', '$'];
const otherFlags = ['/', ':'];
const flags = flagsUseDefaultMatcher.concat(otherFlags);
diff --git a/spec/javascripts/gl_dropdown_spec.js b/spec/javascripts/gl_dropdown_spec.js
index af58dff7da7..25b819543da 100644
--- a/spec/javascripts/gl_dropdown_spec.js
+++ b/spec/javascripts/gl_dropdown_spec.js
@@ -1,4 +1,4 @@
-/* eslint-disable comma-dangle, no-param-reassign */
+/* eslint-disable no-param-reassign */
import $ from 'jquery';
import GLDropdown from '~/gl_dropdown';
diff --git a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
index d8a8c8cc260..4a4d6969e86 100644
--- a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
+++ b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
@@ -1,4 +1,5 @@
-/* eslint-disable quotes, jasmine/no-suite-dupes, vars-on-top, no-var */
+/* eslint-disable jasmine/no-suite-dupes, vars-on-top, no-var */
+
import { scaleLinear, scaleTime } from 'd3-scale';
import { timeParse } from 'd3-time-format';
import { ContributorsGraph, ContributorsMasterGraph } from '~/pages/projects/graphs/show/stat_graph_contributors_graph';
diff --git a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
index 22a9afe1a9d..02d1ca1cc3b 100644
--- a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
+++ b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
@@ -1,4 +1,4 @@
-/* eslint-disable quotes, no-var, camelcase, object-property-newline, comma-dangle, max-len, vars-on-top, quote-props */
+/* eslint-disable no-var, camelcase, vars-on-top */
import ContributorsStatGraphUtil from '~/pages/projects/graphs/show/stat_graph_contributors_util';
diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js
index e12419b835d..62c71e00334 100644
--- a/spec/javascripts/issue_spec.js
+++ b/spec/javascripts/issue_spec.js
@@ -1,4 +1,4 @@
-/* eslint-disable one-var, one-var-declaration-per-line, no-use-before-define, comma-dangle */
+/* eslint-disable one-var, no-use-before-define */
import $ from 'jquery';
import MockAdapter from 'axios-mock-adapter';
diff --git a/spec/javascripts/jobs/components/commit_block_spec.js b/spec/javascripts/jobs/components/commit_block_spec.js
index 61ee993f46a..0bcc4ff940f 100644
--- a/spec/javascripts/jobs/components/commit_block_spec.js
+++ b/spec/javascripts/jobs/components/commit_block_spec.js
@@ -56,7 +56,7 @@ describe('Commit block', () => {
props.mergeRequest.path,
);
expect(vm.$el.querySelector('.js-link-commit').textContent.trim()).toEqual(
- props.mergeRequest.iid,
+ `!${props.mergeRequest.iid}`,
);
});
});
diff --git a/spec/javascripts/jobs/components/empty_state_spec.js b/spec/javascripts/jobs/components/empty_state_spec.js
index 872cc1e3864..73488eaab9b 100644
--- a/spec/javascripts/jobs/components/empty_state_spec.js
+++ b/spec/javascripts/jobs/components/empty_state_spec.js
@@ -67,7 +67,7 @@ describe('Empty State', () => {
content,
action: {
path: 'runner',
- title: 'Check runner',
+ button_title: 'Check runner',
method: 'post',
},
});
diff --git a/spec/javascripts/jobs/components/job_app_spec.js b/spec/javascripts/jobs/components/job_app_spec.js
index c31fa6f9887..e02eb9723fe 100644
--- a/spec/javascripts/jobs/components/job_app_spec.js
+++ b/spec/javascripts/jobs/components/job_app_spec.js
@@ -37,6 +37,7 @@ describe('Job App ', () => {
available: false,
},
tags: ['docker'],
+ has_trace: true,
};
const props = {
@@ -182,4 +183,142 @@ describe('Job App ', () => {
expect(vm.$el.querySelector('.js-job-stuck')).toBeNull();
});
});
+
+ describe('environments block', () => {
+ it('renders environment block when job has environment', () => {
+ store.dispatch(
+ 'receiveJobSuccess',
+ Object.assign({}, job, {
+ deployment_status: {
+ environment: {
+ environment_path: '/path',
+ name: 'foo',
+ },
+ },
+ }),
+ );
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ expect(vm.$el.querySelector('.js-job-environment')).not.toBeNull();
+ });
+
+ it('does not render environment block when job has environment', () => {
+ store.dispatch('receiveJobSuccess', job);
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ expect(vm.$el.querySelector('.js-job-environment')).toBeNull();
+ });
+ });
+
+ describe('erased block', () => {
+ it('renders erased block when `erased` is true', () => {
+ store.dispatch(
+ 'receiveJobSuccess',
+ Object.assign({}, job, {
+ erased: true,
+ erased_by: {
+ username: 'root',
+ web_url: 'gitlab.com/root',
+ },
+ erased_at: '2016-11-07T11:11:16.525Z',
+ }),
+ );
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ expect(vm.$el.querySelector('.js-job-erased')).not.toBeNull();
+ });
+
+ it('does not render erased block when `erased` is false', () => {
+ store.dispatch('receiveJobSuccess', Object.assign({}, job, { erased: false }));
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ expect(vm.$el.querySelector('.js-job-erased')).toBeNull();
+ });
+ });
+
+ describe('empty states block', () => {
+ it('renders empty state when job does not have trace and is not running', () => {
+ store.dispatch(
+ 'receiveJobSuccess',
+ Object.assign({}, job, {
+ has_trace: false,
+ status: {
+ group: 'pending',
+ icon: 'status_pending',
+ label: 'pending',
+ text: 'pending',
+ details_path: 'path',
+ illustration: {
+ image: 'path',
+ size: '340',
+ title: 'Empty State',
+ content: 'This is an empty state',
+ },
+ action: {
+ button_title: 'Retry job',
+ method: 'post',
+ path: '/path',
+ },
+ },
+ }),
+ );
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ expect(vm.$el.querySelector('.js-job-empty-state')).not.toBeNull();
+ });
+
+ it('does not render empty state when job does not have trace but it is running', () => {
+ store.dispatch(
+ 'receiveJobSuccess',
+ Object.assign({}, job, {
+ has_trace: false,
+ status: {
+ group: 'running',
+ icon: 'status_running',
+ label: 'running',
+ text: 'running',
+ details_path: 'path',
+ },
+ }),
+ );
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ expect(vm.$el.querySelector('.js-job-empty-state')).toBeNull();
+ });
+
+ it('does not render empty state when job has trace but it is not running', () => {
+ store.dispatch('receiveJobSuccess', Object.assign({}, job, { has_trace: true }));
+
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+
+ expect(vm.$el.querySelector('.js-job-empty-state')).toBeNull();
+ });
+ });
});
diff --git a/spec/javascripts/jobs/store/getters_spec.js b/spec/javascripts/jobs/store/getters_spec.js
index 63ef4135d83..160b2f4b34a 100644
--- a/spec/javascripts/jobs/store/getters_spec.js
+++ b/spec/javascripts/jobs/store/getters_spec.js
@@ -99,12 +99,14 @@ describe('Job Store Getters', () => {
expect(getters.hasEnvironment(localState)).toEqual(false);
});
});
+
describe('with an empty object for `deployment_status`', () => {
it('returns false', () => {
localState.job.deployment_status = {};
expect(getters.hasEnvironment(localState)).toEqual(false);
});
});
+
describe('when `deployment_status` is defined and not empty', () => {
it('returns true', () => {
localState.job.deployment_status = {
@@ -118,4 +120,94 @@ describe('Job Store Getters', () => {
});
});
});
+
+ describe('hasTrace', () => {
+ describe('when has_trace is true', () => {
+ it('returns true', () => {
+ localState.job.has_trace = true;
+ localState.job.status = {};
+
+ expect(getters.hasTrace(localState)).toEqual(true);
+ });
+ });
+
+ describe('when job is running', () => {
+ it('returns true', () => {
+ localState.job.has_trace = false;
+ localState.job.status = { group: 'running' };
+
+ expect(getters.hasTrace(localState)).toEqual(true);
+ });
+ });
+
+ describe('when has_trace is false and job is not running', () => {
+ it('returns false', () => {
+ localState.job.has_trace = false;
+ localState.job.status = { group: 'pending' };
+
+ expect(getters.hasTrace(localState)).toEqual(false);
+ });
+ });
+ });
+
+ describe('emptyStateIllustration', () => {
+ describe('with defined illustration', () => {
+ it('returns the state illustration object', () => {
+ localState.job.status = {
+ illustration: {
+ path: 'foo',
+ },
+ };
+
+ expect(getters.emptyStateIllustration(localState)).toEqual({ path: 'foo' });
+ });
+ });
+
+ describe('when illustration is not defined', () => {
+ it('returns an empty object', () => {
+ expect(getters.emptyStateIllustration(localState)).toEqual({});
+ });
+ });
+ });
+
+ describe('isJobStuck', () => {
+ describe('when job is pending and runners are not available', () => {
+ it('returns true', () => {
+ localState.job.status = {
+ group: 'pending',
+ };
+ localState.job.runners = {
+ available: false,
+ };
+
+ expect(getters.isJobStuck(localState)).toEqual(true);
+ });
+ });
+
+ describe('when job is not pending', () => {
+ it('returns false', () => {
+ localState.job.status = {
+ group: 'running',
+ };
+ localState.job.runners = {
+ available: false,
+ };
+
+ expect(getters.isJobStuck(localState)).toEqual(false);
+ });
+ });
+
+ describe('when runners are available', () => {
+ it('returns false', () => {
+ localState.job.status = {
+ group: 'pending',
+ };
+ localState.job.runners = {
+ available: true,
+ };
+
+ expect(getters.isJobStuck(localState)).toEqual(false);
+ });
+ });
+ });
});
diff --git a/spec/javascripts/line_highlighter_spec.js b/spec/javascripts/line_highlighter_spec.js
index 8cf0017f4d8..c32ecb17e89 100644
--- a/spec/javascripts/line_highlighter_spec.js
+++ b/spec/javascripts/line_highlighter_spec.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-var, quotes, prefer-template, no-else-return, dot-notation, no-return-assign, comma-dangle, no-new, one-var, one-var-declaration-per-line, no-underscore-dangle, max-len */
+/* eslint-disable no-var, prefer-template, no-else-return, dot-notation, no-return-assign, no-new, one-var, no-underscore-dangle */
import $ from 'jquery';
import LineHighlighter from '~/line_highlighter';
diff --git a/spec/javascripts/monitoring/dashboard_spec.js b/spec/javascripts/monitoring/dashboard_spec.js
index f0d53b2d8d7..732c37a24bf 100644
--- a/spec/javascripts/monitoring/dashboard_spec.js
+++ b/spec/javascripts/monitoring/dashboard_spec.js
@@ -4,30 +4,32 @@ import Dashboard from '~/monitoring/components/dashboard.vue';
import axios from '~/lib/utils/axios_utils';
import { metricsGroupsAPIResponse, mockApiEndpoint, environmentData } from './mock_data';
+const propsData = {
+ hasMetrics: false,
+ documentationPath: '/path/to/docs',
+ settingsPath: '/path/to/settings',
+ clustersPath: '/path/to/clusters',
+ tagsPath: '/path/to/tags',
+ projectPath: '/path/to/project',
+ metricsEndpoint: mockApiEndpoint,
+ deploymentEndpoint: null,
+ emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
+ emptyLoadingSvgPath: '/path/to/loading.svg',
+ emptyNoDataSvgPath: '/path/to/no-data.svg',
+ emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
+ environmentsEndpoint: '/root/hello-prometheus/environments/35',
+ currentEnvironmentName: 'production',
+};
+
+export default propsData;
+
describe('Dashboard', () => {
let DashboardComponent;
- const propsData = {
- hasMetrics: false,
- documentationPath: '/path/to/docs',
- settingsPath: '/path/to/settings',
- clustersPath: '/path/to/clusters',
- tagsPath: '/path/to/tags',
- projectPath: '/path/to/project',
- metricsEndpoint: mockApiEndpoint,
- deploymentEndpoint: null,
- emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
- emptyLoadingSvgPath: '/path/to/loading.svg',
- emptyNoDataSvgPath: '/path/to/no-data.svg',
- emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
- environmentsEndpoint: '/root/hello-prometheus/environments/35',
- currentEnvironmentName: 'production',
- };
-
beforeEach(() => {
setFixtures(`
<div class="prometheus-graphs"></div>
- <div class="nav-sidebar"></div>
+ <div class="nav-sidebar"></div>
`);
DashboardComponent = Vue.extend(Dashboard);
});
diff --git a/spec/javascripts/monitoring/graph/legend_spec.js b/spec/javascripts/monitoring/graph/legend_spec.js
index abcc51aa077..9209e77dcf4 100644
--- a/spec/javascripts/monitoring/graph/legend_spec.js
+++ b/spec/javascripts/monitoring/graph/legend_spec.js
@@ -8,7 +8,7 @@ const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeri
const defaultValuesComponent = {};
-const timeSeries = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120);
+const { timeSeries } = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120);
defaultValuesComponent.timeSeries = timeSeries;
diff --git a/spec/javascripts/monitoring/graph/track_info_spec.js b/spec/javascripts/monitoring/graph/track_info_spec.js
index d3121d553f9..ce93ae28842 100644
--- a/spec/javascripts/monitoring/graph/track_info_spec.js
+++ b/spec/javascripts/monitoring/graph/track_info_spec.js
@@ -5,7 +5,7 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from '../mock_data';
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
-const timeSeries = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120);
+const { timeSeries } = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120);
describe('TrackInfo component', () => {
let vm;
diff --git a/spec/javascripts/monitoring/graph/track_line_spec.js b/spec/javascripts/monitoring/graph/track_line_spec.js
index 27602a861eb..2a4f89ddf6e 100644
--- a/spec/javascripts/monitoring/graph/track_line_spec.js
+++ b/spec/javascripts/monitoring/graph/track_line_spec.js
@@ -5,7 +5,7 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from '../mock_data';
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
-const timeSeries = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120);
+const { timeSeries } = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120);
describe('TrackLine component', () => {
let vm;
diff --git a/spec/javascripts/monitoring/graph_path_spec.js b/spec/javascripts/monitoring/graph_path_spec.js
index 2515e2ad897..5f270c5cfe9 100644
--- a/spec/javascripts/monitoring/graph_path_spec.js
+++ b/spec/javascripts/monitoring/graph_path_spec.js
@@ -13,7 +13,7 @@ const createComponent = (propsData) => {
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
-const timeSeries = createTimeSeries(convertedMetrics[0].queries, 428, 272, 120);
+const { timeSeries } = createTimeSeries(convertedMetrics[0].queries, 428, 272, 120);
const firstTimeSeries = timeSeries[0];
describe('Monitoring Paths', () => {
diff --git a/spec/javascripts/monitoring/mock_data.js b/spec/javascripts/monitoring/mock_data.js
index e4c98a3bcb5..6c833b17f98 100644
--- a/spec/javascripts/monitoring/mock_data.js
+++ b/spec/javascripts/monitoring/mock_data.js
@@ -8,6 +8,7 @@ export const metricsGroupsAPIResponse = {
priority: 1,
metrics: [
{
+ id: 5,
title: 'Memory usage',
weight: 1,
queries: [
diff --git a/spec/javascripts/monitoring/utils/multiple_time_series_spec.js b/spec/javascripts/monitoring/utils/multiple_time_series_spec.js
index 99584c75287..8937b7d9680 100644
--- a/spec/javascripts/monitoring/utils/multiple_time_series_spec.js
+++ b/spec/javascripts/monitoring/utils/multiple_time_series_spec.js
@@ -2,7 +2,7 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { convertDatesMultipleSeries, singleRowMetricsMultipleSeries } from '../mock_data';
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
-const timeSeries = createTimeSeries(convertedMetrics[0].queries, 428, 272, 120);
+const { timeSeries } = createTimeSeries(convertedMetrics[0].queries, 428, 272, 120);
const firstTimeSeries = timeSeries[0];
describe('Multiple time series', () => {
diff --git a/spec/javascripts/new_branch_spec.js b/spec/javascripts/new_branch_spec.js
index 122e5bc58b2..e52ac686435 100644
--- a/spec/javascripts/new_branch_spec.js
+++ b/spec/javascripts/new_branch_spec.js
@@ -1,4 +1,4 @@
-/* eslint-disable one-var, no-var, one-var-declaration-per-line, no-return-assign, quotes, max-len */
+/* eslint-disable one-var, no-var, no-return-assign */
import $ from 'jquery';
import NewBranchForm from '~/new_branch_form';
diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js
index 1f030e5af28..9a0e7f34a9c 100644
--- a/spec/javascripts/notes/mock_data.js
+++ b/spec/javascripts/notes/mock_data.js
@@ -1177,10 +1177,8 @@ export const discussion1 = {
file_path: 'about.md',
},
position: {
- formatter: {
- new_line: 50,
- old_line: null,
- },
+ new_line: 50,
+ old_line: null,
},
notes: [
{
@@ -1197,10 +1195,8 @@ export const resolvedDiscussion1 = {
file_path: 'about.md',
},
position: {
- formatter: {
- new_line: 50,
- old_line: null,
- },
+ new_line: 50,
+ old_line: null,
},
notes: [
{
@@ -1217,10 +1213,8 @@ export const discussion2 = {
file_path: 'README.md',
},
position: {
- formatter: {
- new_line: null,
- old_line: 20,
- },
+ new_line: null,
+ old_line: 20,
},
notes: [
{
@@ -1237,10 +1231,8 @@ export const discussion3 = {
file_path: 'README.md',
},
position: {
- formatter: {
- new_line: 21,
- old_line: null,
- },
+ new_line: 21,
+ old_line: null,
},
notes: [
{
diff --git a/spec/javascripts/pipelines/pipelines_actions_spec.js b/spec/javascripts/pipelines/pipelines_actions_spec.js
index 72fb0a8f9ef..0566bc55693 100644
--- a/spec/javascripts/pipelines/pipelines_actions_spec.js
+++ b/spec/javascripts/pipelines/pipelines_actions_spec.js
@@ -1,46 +1,98 @@
import Vue from 'vue';
-import pipelinesActionsComp from '~/pipelines/components/pipelines_actions.vue';
+import eventHub from '~/pipelines/event_hub';
+import PipelinesActions from '~/pipelines/components/pipelines_actions.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { TEST_HOST } from 'spec/test_constants';
describe('Pipelines Actions dropdown', () => {
- let component;
- let actions;
- let ActionsComponent;
+ const Component = Vue.extend(PipelinesActions);
+ let vm;
- beforeEach(() => {
- ActionsComponent = Vue.extend(pipelinesActionsComp);
+ afterEach(() => {
+ vm.$destroy();
+ });
- actions = [
+ describe('manual actions', () => {
+ const actions = [
{
name: 'stop_review',
- path: '/root/review-app/builds/1893/play',
+ path: `${TEST_HOST}/root/review-app/builds/1893/play`,
},
{
name: 'foo',
- path: '#',
+ path: `${TEST_HOST}/disabled/pipeline/action`,
playable: false,
},
];
- component = new ActionsComponent({
- propsData: {
- actions,
- },
- }).$mount();
- });
+ beforeEach(() => {
+ vm = mountComponent(Component, { actions });
+ });
- it('should render a dropdown with the provided actions', () => {
- expect(
- component.$el.querySelectorAll('.dropdown-menu li').length,
- ).toEqual(actions.length);
+ it('renders a dropdown with the provided actions', () => {
+ const dropdownItems = vm.$el.querySelectorAll('.dropdown-menu li');
+ expect(dropdownItems.length).toEqual(actions.length);
+ });
+
+ it("renders a disabled action when it's not playable", () => {
+ const dropdownItem = vm.$el.querySelector('.dropdown-menu li:last-child button');
+ expect(dropdownItem).toBeDisabled();
+ });
});
- it('should render a disabled action when it\'s not playable', () => {
- expect(
- component.$el.querySelector('.dropdown-menu li:last-child button').getAttribute('disabled'),
- ).toEqual('disabled');
+ describe('scheduled jobs', () => {
+ const scheduledJobAction = {
+ name: 'scheduled action',
+ path: `${TEST_HOST}/scheduled/job/action`,
+ playable: true,
+ scheduled_at: '2063-04-05T00:42:00Z',
+ };
+ const expiredJobAction = {
+ name: 'expired action',
+ path: `${TEST_HOST}/expired/job/action`,
+ playable: true,
+ scheduled_at: '2018-10-05T08:23:00Z',
+ };
+ const findDropdownItem = action => {
+ const buttons = vm.$el.querySelectorAll('.dropdown-menu li button');
+ return Array.prototype.find.call(buttons, element =>
+ element.innerText.trim().startsWith(action.name),
+ );
+ };
+
+ beforeEach(() => {
+ spyOn(Date, 'now').and.callFake(() => new Date('2063-04-04T00:42:00Z').getTime());
+ vm = mountComponent(Component, { actions: [scheduledJobAction, expiredJobAction] });
+ });
+
+ it('emits postAction event after confirming', () => {
+ const emitSpy = jasmine.createSpy('emit');
+ eventHub.$on('postAction', emitSpy);
+ spyOn(window, 'confirm').and.callFake(() => true);
+
+ findDropdownItem(scheduledJobAction).click();
+
+ expect(window.confirm).toHaveBeenCalled();
+ expect(emitSpy).toHaveBeenCalledWith(scheduledJobAction.path);
+ });
+
+ it('does not emit postAction event if confirmation is cancelled', () => {
+ const emitSpy = jasmine.createSpy('emit');
+ eventHub.$on('postAction', emitSpy);
+ spyOn(window, 'confirm').and.callFake(() => false);
+
+ findDropdownItem(scheduledJobAction).click();
+
+ expect(window.confirm).toHaveBeenCalled();
+ expect(emitSpy).not.toHaveBeenCalled();
+ });
+
+ it('displays the remaining time in the dropdown', () => {
+ expect(findDropdownItem(scheduledJobAction)).toContainText('24:00:00');
+ });
- expect(
- component.$el.querySelector('.dropdown-menu li:last-child button').classList.contains('disabled'),
- ).toEqual(true);
+ it('displays 00:00:00 for expired jobs in the dropdown', () => {
+ expect(findDropdownItem(expiredJobAction)).toContainText('00:00:00');
+ });
});
});
diff --git a/spec/javascripts/pipelines/pipelines_table_row_spec.js b/spec/javascripts/pipelines/pipelines_table_row_spec.js
index 03ffc122795..42795f5c134 100644
--- a/spec/javascripts/pipelines/pipelines_table_row_spec.js
+++ b/spec/javascripts/pipelines/pipelines_table_row_spec.js
@@ -158,8 +158,13 @@ describe('Pipelines Table Row', () => {
});
describe('actions column', () => {
+ const scheduledJobAction = {
+ name: 'some scheduled job',
+ };
+
beforeEach(() => {
const withActions = Object.assign({}, pipeline);
+ withActions.details.scheduled_actions = [scheduledJobAction];
withActions.flags.cancelable = true;
withActions.flags.retryable = true;
withActions.cancel_path = '/cancel';
@@ -171,6 +176,8 @@ describe('Pipelines Table Row', () => {
it('should render the provided actions', () => {
expect(component.$el.querySelector('.js-pipelines-retry-button')).not.toBeNull();
expect(component.$el.querySelector('.js-pipelines-cancel-button')).not.toBeNull();
+ const dropdownMenu = component.$el.querySelectorAll('.dropdown-menu');
+ expect(dropdownMenu).toContainText(scheduledJobAction.name);
});
it('emits `retryPipeline` event when retry button is clicked and toggles loading', () => {
diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js
index c7190ea9960..f9395eedfea 100644
--- a/spec/javascripts/right_sidebar_spec.js
+++ b/spec/javascripts/right_sidebar_spec.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-var, one-var, one-var-declaration-per-line, no-return-assign, vars-on-top, jasmine/no-unsafe-spy, max-len */
+/* eslint-disable no-var, one-var, no-return-assign, vars-on-top, jasmine/no-unsafe-spy */
import $ from 'jquery';
import MockAdapter from 'axios-mock-adapter';
diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js
index 646d843162c..b96023a33c4 100644
--- a/spec/javascripts/search_autocomplete_spec.js
+++ b/spec/javascripts/search_autocomplete_spec.js
@@ -1,4 +1,4 @@
-/* eslint-disable max-len, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, consistent-return, no-param-reassign, default-case, no-return-assign, object-shorthand, prefer-template, vars-on-top, max-len */
+/* eslint-disable no-var, one-var, no-unused-expressions, consistent-return, no-param-reassign, default-case, no-return-assign, object-shorthand, prefer-template, vars-on-top */
import $ from 'jquery';
import '~/gl_dropdown';
diff --git a/spec/javascripts/syntax_highlight_spec.js b/spec/javascripts/syntax_highlight_spec.js
index 1c3dac3584e..af3a5d58ba7 100644
--- a/spec/javascripts/syntax_highlight_spec.js
+++ b/spec/javascripts/syntax_highlight_spec.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-var, no-return-assign, quotes */
+/* eslint-disable no-var, no-return-assign */
import $ from 'jquery';
import syntaxHighlight from '~/syntax_highlight';
diff --git a/spec/javascripts/u2f/mock_u2f_device.js b/spec/javascripts/u2f/mock_u2f_device.js
index 012a1cefbbf..a8692be3546 100644
--- a/spec/javascripts/u2f/mock_u2f_device.js
+++ b/spec/javascripts/u2f/mock_u2f_device.js
@@ -1,4 +1,4 @@
-/* eslint-disable wrap-iife, no-unused-expressions, no-return-assign, no-param-reassign */
+/* eslint-disable no-unused-expressions, no-return-assign, no-param-reassign */
export default class MockU2FDevice {
constructor() {
diff --git a/spec/javascripts/vue_shared/components/markdown/header_spec.js b/spec/javascripts/vue_shared/components/markdown/header_spec.js
index bc934afe7a4..a4681617e66 100644
--- a/spec/javascripts/vue_shared/components/markdown/header_spec.js
+++ b/spec/javascripts/vue_shared/components/markdown/header_spec.js
@@ -17,8 +17,13 @@ describe('Markdown field header component', () => {
Vue.nextTick(done);
});
- it('renders markdown buttons', () => {
- expect(vm.$el.querySelectorAll('.js-md').length).toBe(8);
+ it('renders markdown header buttons', () => {
+ const buttons = ['Add bold text', 'Add italic text', 'Insert a quote', 'Insert code', 'Add a link', 'Add a bullet list', 'Add a numbered list', 'Add a task list', 'Add a table', 'Go full screen'];
+ const elements = vm.$el.querySelectorAll('.toolbar-btn');
+
+ elements.forEach((buttonEl, index) => {
+ expect(buttonEl.getAttribute('data-original-title')).toBe(buttons[index]);
+ });
});
it('renders `write` link as active when previewMarkdown is false', () => {
@@ -69,4 +74,8 @@ describe('Markdown field header component', () => {
done();
});
});
+
+ it('renders markdown table template', () => {
+ expect(vm.mdTable).toEqual('| header | header |\n| ------ | ------ |\n| cell | cell |\n| cell | cell |');
+ });
});
diff --git a/spec/lib/banzai/cross_project_reference_spec.rb b/spec/lib/banzai/cross_project_reference_spec.rb
index aadfe7637dd..ba995e16be7 100644
--- a/spec/lib/banzai/cross_project_reference_spec.rb
+++ b/spec/lib/banzai/cross_project_reference_spec.rb
@@ -1,16 +1,21 @@
require 'spec_helper'
describe Banzai::CrossProjectReference do
- include described_class
+ let(:including_class) { Class.new.include(described_class).new }
+
+ before do
+ allow(including_class).to receive(:context).and_return({})
+ allow(including_class).to receive(:parent_from_ref).and_call_original
+ end
describe '#parent_from_ref' do
context 'when no project was referenced' do
it 'returns the project from context' do
project = double
- allow(self).to receive(:context).and_return({ project: project })
+ allow(including_class).to receive(:context).and_return({ project: project })
- expect(parent_from_ref(nil)).to eq project
+ expect(including_class.parent_from_ref(nil)).to eq project
end
end
@@ -18,15 +23,15 @@ describe Banzai::CrossProjectReference do
it 'returns the group from context' do
group = double
- allow(self).to receive(:context).and_return({ group: group })
+ allow(including_class).to receive(:context).and_return({ group: group })
- expect(parent_from_ref(nil)).to eq group
+ expect(including_class.parent_from_ref(nil)).to eq group
end
end
context 'when referenced project does not exist' do
it 'returns nil' do
- expect(parent_from_ref('invalid/reference')).to be_nil
+ expect(including_class.parent_from_ref('invalid/reference')).to be_nil
end
end
@@ -37,7 +42,7 @@ describe Banzai::CrossProjectReference do
expect(Project).to receive(:find_by_full_path)
.with('cross/reference').and_return(project2)
- expect(parent_from_ref('cross/reference')).to eq project2
+ expect(including_class.parent_from_ref('cross/reference')).to eq project2
end
end
end
diff --git a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
index e1af5a15371..cbff2fdab14 100644
--- a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
@@ -60,6 +60,7 @@ describe Banzai::Filter::CommitRangeReferenceFilter do
exp = act = "See #{commit1.id.reverse}...#{commit2.id}"
allow(project.repository).to receive(:commit).with(commit1.id.reverse)
+ allow(project.repository).to receive(:commit).with(commit2.id)
expect(reference_filter(act).to_html).to eq exp
end
diff --git a/spec/lib/banzai/reference_parser/commit_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_parser_spec.rb
index cca53a8b9b9..f558dea209f 100644
--- a/spec/lib/banzai/reference_parser/commit_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/commit_parser_spec.rb
@@ -120,4 +120,22 @@ describe Banzai::ReferenceParser::CommitParser do
expect(subject.find_commits(project, %w{123})).to eq([])
end
end
+
+ context 'when checking commits on another projects' do
+ let(:control_links) do
+ [commit_link]
+ end
+
+ let(:actual_links) do
+ control_links + [commit_link, commit_link]
+ end
+
+ def commit_link
+ project = create(:project, :repository, :public)
+
+ Nokogiri::HTML.fragment(%Q{<a data-commit="#{project.commit.id}" data-project="#{project.id}"></a>}).children[0]
+ end
+
+ it_behaves_like 'no project N+1 queries'
+ end
end
diff --git a/spec/lib/gitlab/ci/build/policy/changes_spec.rb b/spec/lib/gitlab/ci/build/policy/changes_spec.rb
new file mode 100644
index 00000000000..ab401108c84
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/policy/changes_spec.rb
@@ -0,0 +1,107 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Build::Policy::Changes do
+ set(:project) { create(:project) }
+
+ describe '#satisfied_by?' do
+ describe 'paths matching matching' do
+ let(:pipeline) do
+ build(:ci_empty_pipeline, project: project,
+ ref: 'master',
+ source: :push,
+ sha: '1234abcd',
+ before_sha: '0123aabb')
+ end
+
+ let(:ci_build) do
+ build(:ci_build, pipeline: pipeline, project: project, ref: 'master')
+ end
+
+ let(:seed) { double('build seed', to_resource: ci_build) }
+
+ before do
+ allow(pipeline).to receive(:modified_paths) do
+ %w[some/modified/ruby/file.rb some/other_file.txt some/.dir/file]
+ end
+ end
+
+ it 'is satisfied by matching literal path' do
+ policy = described_class.new(%w[some/other_file.txt])
+
+ expect(policy).to be_satisfied_by(pipeline, seed)
+ end
+
+ it 'is satisfied by matching simple pattern' do
+ policy = described_class.new(%w[some/*.txt])
+
+ expect(policy).to be_satisfied_by(pipeline, seed)
+ end
+
+ it 'is satisfied by matching recusive pattern' do
+ policy = described_class.new(%w[some/**/*.rb])
+
+ expect(policy).to be_satisfied_by(pipeline, seed)
+ end
+
+ it 'is satisfied by matching a pattern with a dot' do
+ policy = described_class.new(%w[some/*/file])
+
+ expect(policy).to be_satisfied_by(pipeline, seed)
+ end
+
+ it 'is not satisfied when pattern does not match path' do
+ policy = described_class.new(%w[some/*.rb])
+
+ expect(policy).not_to be_satisfied_by(pipeline, seed)
+ end
+
+ it 'is not satisfied when pattern does not match' do
+ policy = described_class.new(%w[invalid/*.md])
+
+ expect(policy).not_to be_satisfied_by(pipeline, seed)
+ end
+
+ context 'when pipelines does not run for a branch update' do
+ before do
+ pipeline.before_sha = Gitlab::Git::BLANK_SHA
+ end
+
+ it 'is always satisfied' do
+ policy = described_class.new(%w[invalid/*])
+
+ expect(policy).to be_satisfied_by(pipeline, seed)
+ end
+ end
+ end
+
+ describe 'gitaly integration' do
+ set(:project) { create(:project, :repository) }
+
+ let(:pipeline) do
+ create(:ci_empty_pipeline, project: project,
+ ref: 'master',
+ source: :push,
+ sha: '498214d',
+ before_sha: '281d3a7')
+ end
+
+ let(:build) do
+ create(:ci_build, pipeline: pipeline, project: project, ref: 'master')
+ end
+
+ let(:seed) { double('build seed', to_resource: build) }
+
+ it 'is satisfied by changes introduced by a push' do
+ policy = described_class.new(['with space/*.md'])
+
+ expect(policy).to be_satisfied_by(pipeline, seed)
+ end
+
+ it 'is not satisfied by changes that are not in the push' do
+ policy = described_class.new(%w[files/js/commit.js])
+
+ expect(policy).not_to be_satisfied_by(pipeline, seed)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb
index d745c4ca2ad..1169938b80c 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -44,9 +44,7 @@ describe Gitlab::Ci::Config::Entry::Job do
context 'when start_in is specified' do
let(:config) { { script: 'echo', when: 'delayed', start_in: '1 day' } }
- it 'returns error about invalid type' do
- expect(entry).to be_valid
- end
+ it { expect(entry).to be_valid }
end
end
end
@@ -158,7 +156,7 @@ describe Gitlab::Ci::Config::Entry::Job do
end
end
- context 'when start_in is not formateed ad a duration' do
+ context 'when start_in is not formatted as a duration' do
let(:config) { { when: 'delayed', start_in: 'test' } }
it 'returns error about invalid type' do
@@ -166,6 +164,15 @@ describe Gitlab::Ci::Config::Entry::Job do
expect(entry.errors).to include 'job start in should be a duration'
end
end
+
+ context 'when start_in is longer than one day' do
+ let(:config) { { when: 'delayed', start_in: '2 days' } }
+
+ it 'returns error about exceeding the limit' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'job start in should not exceed the limit'
+ end
+ end
end
context 'when start_in specified without delayed specification' do
diff --git a/spec/lib/gitlab/ci/config/entry/policy_spec.rb b/spec/lib/gitlab/ci/config/entry/policy_spec.rb
index 83d39b82068..bef93fe7af7 100644
--- a/spec/lib/gitlab/ci/config/entry/policy_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/policy_spec.rb
@@ -1,4 +1,5 @@
-require 'spec_helper'
+require 'fast_spec_helper'
+require_dependency 'active_model'
describe Gitlab::Ci::Config::Entry::Policy do
let(:entry) { described_class.new(config) }
@@ -124,6 +125,23 @@ describe Gitlab::Ci::Config::Entry::Policy do
end
end
+ context 'when specifying a valid changes policy' do
+ let(:config) { { changes: %w[some/* paths/**/*.rb] } }
+
+ it 'is a correct configuraton' do
+ expect(entry).to be_valid
+ expect(entry.value).to eq(config)
+ end
+ end
+
+ context 'when changes policy is invalid' do
+ let(:config) { { changes: [1, 2] } }
+
+ it 'returns errors' do
+ expect(entry.errors).to include /changes should be an array of strings/
+ end
+ end
+
context 'when specifying unknown policy' do
let(:config) { { refs: ['master'], invalid: :something } }
diff --git a/spec/lib/gitlab/ci/status/build/scheduled_spec.rb b/spec/lib/gitlab/ci/status/build/scheduled_spec.rb
index 3098a17c50d..f98183d6d18 100644
--- a/spec/lib/gitlab/ci/status/build/scheduled_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/scheduled_spec.rb
@@ -17,7 +17,7 @@ describe Gitlab::Ci::Status::Build::Scheduled do
let(:build) { create(:ci_build, scheduled_at: 1.minute.since, project: project) }
it 'shows execute_in of the scheduled job' do
- Timecop.freeze do
+ Timecop.freeze(Time.now.change(usec: 0)) do
expect(subject.status_tooltip).to include('00:01:00')
end
end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index d75c473eb66..85b23edce9f 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -1369,7 +1369,7 @@ module Gitlab
end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec dependencies should be an array of strings")
end
- it 'returns errors if pipeline variables expression is invalid' do
+ it 'returns errors if pipeline variables expression policy is invalid' do
config = YAML.dump({ rspec: { script: 'test', only: { variables: ['== null'] } } })
expect { Gitlab::Ci::YamlProcessor.new(config) }
@@ -1377,6 +1377,14 @@ module Gitlab
'jobs:rspec:only variables invalid expression syntax')
end
+ it 'returns errors if pipeline changes policy is invalid' do
+ config = YAML.dump({ rspec: { script: 'test', only: { changes: [1] } } })
+
+ expect { Gitlab::Ci::YamlProcessor.new(config) }
+ .to raise_error(Gitlab::Ci::YamlProcessor::ValidationError,
+ 'jobs:rspec:only changes should be an array of strings')
+ end
+
it 'returns errors if extended hash configuration is invalid' do
config = YAML.dump({ rspec: { extends: 'something', script: 'test' } })
diff --git a/spec/lib/gitlab/diff/position_spec.rb b/spec/lib/gitlab/diff/position_spec.rb
index 677eb373d22..2d94356f386 100644
--- a/spec/lib/gitlab/diff/position_spec.rb
+++ b/spec/lib/gitlab/diff/position_spec.rb
@@ -5,6 +5,34 @@ describe Gitlab::Diff::Position do
let(:project) { create(:project, :repository) }
+ let(:args_for_img) do
+ {
+ old_path: "files/any.img",
+ new_path: "files/any.img",
+ base_sha: nil,
+ head_sha: nil,
+ start_sha: nil,
+ width: 100,
+ height: 100,
+ x: 1,
+ y: 100,
+ position_type: "image"
+ }
+ end
+
+ let(:args_for_text) do
+ {
+ old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: 14,
+ base_sha: nil,
+ head_sha: nil,
+ start_sha: nil,
+ position_type: "text"
+ }
+ end
+
describe "position for an added text file" do
let(:commit) { project.commit("2ea1f3dec713d940208fb5ce4a38765ecb5d3f73") }
@@ -529,53 +557,49 @@ describe Gitlab::Diff::Position do
end
end
+ describe "#as_json" do
+ shared_examples "diff position json" do
+ let(:diff_position) { described_class.new(args) }
+
+ it "returns the position as JSON" do
+ expect(diff_position.as_json).to eq(args.stringify_keys)
+ end
+ end
+
+ context "for text positon" do
+ let(:args) { args_for_text }
+
+ it_behaves_like "diff position json"
+ end
+
+ context "for image positon" do
+ let(:args) { args_for_img }
+
+ it_behaves_like "diff position json"
+ end
+ end
+
describe "#to_json" do
shared_examples "diff position json" do
+ let(:diff_position) { described_class.new(args) }
+
it "returns the position as JSON" do
- expect(JSON.parse(diff_position.to_json)).to eq(hash.stringify_keys)
+ expect(JSON.parse(diff_position.to_json)).to eq(args.stringify_keys)
end
it "works when nested under another hash" do
- expect(JSON.parse(JSON.generate(pos: diff_position))).to eq('pos' => hash.stringify_keys)
+ expect(JSON.parse(JSON.generate(pos: diff_position))).to eq('pos' => args.stringify_keys)
end
end
context "for text positon" do
- let(:hash) do
- {
- old_path: "files/ruby/popen.rb",
- new_path: "files/ruby/popen.rb",
- old_line: nil,
- new_line: 14,
- base_sha: nil,
- head_sha: nil,
- start_sha: nil,
- position_type: "text"
- }
- end
-
- let(:diff_position) { described_class.new(hash) }
+ let(:args) { args_for_text }
it_behaves_like "diff position json"
end
context "for image positon" do
- let(:hash) do
- {
- old_path: "files/any.img",
- new_path: "files/any.img",
- base_sha: nil,
- head_sha: nil,
- start_sha: nil,
- width: 100,
- height: 100,
- x: 1,
- y: 100,
- position_type: "image"
- }
- end
-
- let(:diff_position) { described_class.new(hash) }
+ let(:args) { args_for_img }
it_behaves_like "diff position json"
end
diff --git a/spec/lib/gitlab/git/diff_stats_collection_spec.rb b/spec/lib/gitlab/git/diff_stats_collection_spec.rb
index 89927cbb3a6..b07690ef39c 100644
--- a/spec/lib/gitlab/git/diff_stats_collection_spec.rb
+++ b/spec/lib/gitlab/git/diff_stats_collection_spec.rb
@@ -14,7 +14,7 @@ describe Gitlab::Git::DiffStatsCollection do
let(:diff_stats) { [stats_a, stats_b] }
let(:collection) { described_class.new(diff_stats) }
- describe '.find_by_path' do
+ describe '#find_by_path' do
it 'returns stats by path when found' do
expect(collection.find_by_path('foo')).to eq(stats_a)
end
@@ -23,4 +23,10 @@ describe Gitlab::Git::DiffStatsCollection do
expect(collection.find_by_path('no-file')).to be_nil
end
end
+
+ describe '#paths' do
+ it 'returns only modified paths' do
+ expect(collection.paths).to eq %w[foo bar]
+ end
+ end
end
diff --git a/spec/lib/gitlab/git/push_spec.rb b/spec/lib/gitlab/git/push_spec.rb
new file mode 100644
index 00000000000..566c8209504
--- /dev/null
+++ b/spec/lib/gitlab/git/push_spec.rb
@@ -0,0 +1,166 @@
+require 'spec_helper'
+
+describe Gitlab::Git::Push do
+ set(:project) { create(:project, :repository) }
+
+ let(:oldrev) { project.commit('HEAD~2').id }
+ let(:newrev) { project.commit.id }
+ let(:ref) { 'refs/heads/some-branch' }
+
+ subject { described_class.new(project, oldrev, newrev, ref) }
+
+ describe '#branch_name' do
+ context 'when it is a branch push' do
+ let(:ref) { 'refs/heads/my-branch' }
+
+ it 'returns branch name' do
+ expect(subject.branch_name).to eq 'my-branch'
+ end
+ end
+
+ context 'when it is a tag push' do
+ let(:ref) { 'refs/tags/my-branch' }
+
+ it 'returns nil' do
+ expect(subject.branch_name).to be_nil
+ end
+ end
+ end
+
+ describe '#branch_push?' do
+ context 'when pushing a branch ref' do
+ let(:ref) { 'refs/heads/my-branch' }
+
+ it { is_expected.to be_branch_push }
+ end
+
+ context 'when it is a tag push' do
+ let(:ref) { 'refs/tags/my-tag' }
+
+ it { is_expected.not_to be_branch_push }
+ end
+ end
+
+ describe '#branch_updated?' do
+ context 'when it is a branch push with correct old and new revisions' do
+ it { is_expected.to be_branch_updated }
+ end
+
+ context 'when it is not a branch push' do
+ let(:ref) { 'refs/tags/my-tag' }
+
+ it { is_expected.not_to be_branch_updated }
+ end
+
+ context 'when old revision is blank' do
+ let(:oldrev) { Gitlab::Git::BLANK_SHA }
+
+ it { is_expected.not_to be_branch_updated }
+ end
+
+ context 'when it is not a branch push' do
+ let(:newrev) { Gitlab::Git::BLANK_SHA }
+
+ it { is_expected.not_to be_branch_updated }
+ end
+
+ context 'when oldrev is nil' do
+ let(:oldrev) { nil }
+
+ it { is_expected.not_to be_branch_updated }
+ end
+ end
+
+ describe '#force_push?' do
+ context 'when old revision is an ancestor of the new revision' do
+ let(:oldrev) { 'HEAD~3' }
+ let(:newrev) { 'HEAD~1' }
+
+ it { is_expected.not_to be_force_push }
+ end
+
+ context 'when old revision is not an ancestor of the new revision' do
+ let(:oldrev) { 'HEAD~3' }
+ let(:newrev) { '123456' }
+
+ it { is_expected.to be_force_push }
+ end
+ end
+
+ describe '#branch_added?' do
+ context 'when old revision is defined' do
+ it { is_expected.not_to be_branch_added }
+ end
+
+ context 'when old revision is not defined' do
+ let(:oldrev) { Gitlab::Git::BLANK_SHA }
+
+ it { is_expected.to be_branch_added }
+ end
+ end
+
+ describe '#branch_removed?' do
+ context 'when new revision is defined' do
+ it { is_expected.not_to be_branch_removed }
+ end
+
+ context 'when new revision is not defined' do
+ let(:newrev) { Gitlab::Git::BLANK_SHA }
+
+ it { is_expected.to be_branch_removed }
+ end
+ end
+
+ describe '#modified_paths' do
+ context 'when a push is a branch update' do
+ let(:newrev) { '498214d' }
+ let(:oldrev) { '281d3a7' }
+
+ it 'returns modified paths' do
+ expect(subject.modified_paths).to eq ['bar/branch-test.txt',
+ 'files/js/commit.coffee',
+ 'with space/README.md']
+ end
+ end
+
+ context 'when a push is not a branch update' do
+ let(:oldrev) { Gitlab::Git::BLANK_SHA }
+
+ it 'raises an error' do
+ expect { subject.modified_paths }.to raise_error(ArgumentError)
+ end
+ end
+ end
+
+ describe '#oldrev' do
+ context 'when a valid oldrev is provided' do
+ it 'returns oldrev' do
+ expect(subject.oldrev).to eq oldrev
+ end
+ end
+
+ context 'when a nil valud is provided' do
+ let(:oldrev) { nil }
+
+ it 'returns blank SHA' do
+ expect(subject.oldrev).to eq Gitlab::Git::BLANK_SHA
+ end
+ end
+ end
+
+ describe '#newrev' do
+ context 'when valid newrev is provided' do
+ it 'returns newrev' do
+ expect(subject.newrev).to eq newrev
+ end
+ end
+
+ context 'when a nil valud is provided' do
+ let(:newrev) { nil }
+
+ it 'returns blank SHA' do
+ expect(subject.newrev).to eq Gitlab::Git::BLANK_SHA
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/relation_factory_spec.rb b/spec/lib/gitlab/import_export/relation_factory_spec.rb
index cf9e0f71910..a31f77484d8 100644
--- a/spec/lib/gitlab/import_export/relation_factory_spec.rb
+++ b/spec/lib/gitlab/import_export/relation_factory_spec.rb
@@ -191,9 +191,7 @@ describe Gitlab::ImportExport::RelationFactory do
"author" => {
"name" => "Administrator"
},
- "events" => [
-
- ]
+ "events" => []
}
end
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 1d59cff7ba8..f7935149b23 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -493,6 +493,7 @@ ProjectFeature:
- snippets_access_level
- builds_access_level
- repository_access_level
+- pages_access_level
- created_at
- updated_at
ProtectedBranch::MergeAccessLevel:
diff --git a/spec/lib/gitlab/version_info_spec.rb b/spec/lib/gitlab/version_info_spec.rb
index c8a1e433d59..30035c79e58 100644
--- a/spec/lib/gitlab/version_info_spec.rb
+++ b/spec/lib/gitlab/version_info_spec.rb
@@ -57,6 +57,9 @@ describe 'Gitlab::VersionInfo' do
context 'parse' do
it { expect(Gitlab::VersionInfo.parse("1.0.0")).to eq(@v1_0_0) }
it { expect(Gitlab::VersionInfo.parse("1.0.0.1")).to eq(@v1_0_0) }
+ it { expect(Gitlab::VersionInfo.parse("1.0.0-ee")).to eq(@v1_0_0) }
+ it { expect(Gitlab::VersionInfo.parse("1.0.0-rc1")).to eq(@v1_0_0) }
+ it { expect(Gitlab::VersionInfo.parse("1.0.0-rc1-ee")).to eq(@v1_0_0) }
it { expect(Gitlab::VersionInfo.parse("git 1.0.0b1")).to eq(@v1_0_0) }
it { expect(Gitlab::VersionInfo.parse("git 1.0b1")).not_to be_valid }
end
diff --git a/spec/migrations/add_pages_access_level_to_project_feature_spec.rb b/spec/migrations/add_pages_access_level_to_project_feature_spec.rb
new file mode 100644
index 00000000000..3946602c5be
--- /dev/null
+++ b/spec/migrations/add_pages_access_level_to_project_feature_spec.rb
@@ -0,0 +1,30 @@
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20180423204600_add_pages_access_level_to_project_feature.rb')
+
+describe AddPagesAccessLevelToProjectFeature, :migration do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:features) { table(:project_features) }
+ let!(:namespace) { namespaces.create(name: 'gitlab', path: 'gitlab') }
+ let!(:first_project) { projects.create(name: 'gitlab1', path: 'gitlab1', namespace_id: namespace.id) }
+ let!(:first_project_features) { features.create(project_id: first_project.id) }
+ let!(:second_project) { projects.create(name: 'gitlab2', path: 'gitlab2', namespace_id: namespace.id) }
+ let!(:second_project_features) { features.create(project_id: second_project.id) }
+
+ it 'correctly migrate pages for old projects to be public' do
+ migrate!
+
+ # For old projects pages should be public
+ expect(first_project_features.reload.pages_access_level).to eq ProjectFeature::PUBLIC
+ expect(second_project_features.reload.pages_access_level).to eq ProjectFeature::PUBLIC
+ end
+
+ it 'after migration pages are enabled as default' do
+ migrate!
+
+ # For new project default is enabled
+ third_project = projects.create(name: 'gitlab3', path: 'gitlab3', namespace_id: namespace.id)
+ third_project_features = features.create(project_id: third_project.id)
+ expect(third_project_features.reload.pages_access_level).to eq ProjectFeature::ENABLED
+ end
+end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 31bcfe1c6b1..a046541031e 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -261,7 +261,7 @@ describe Ci::Build do
it 'schedules BuildScheduleWorker at the right time' do
Timecop.freeze do
expect(Ci::BuildScheduleWorker)
- .to receive(:perform_at).with(1.minute.since, build.id)
+ .to receive(:perform_at).with(be_like_time(1.minute.since), build.id)
subject
end
@@ -1852,6 +1852,7 @@ describe Ci::Build do
describe '#variables' do
let(:container_registry_enabled) { false }
+ let(:gitlab_version_info) { Gitlab::VersionInfo.parse(Gitlab::VERSION) }
let(:predefined_variables) do
[
{ key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true },
@@ -1869,6 +1870,9 @@ describe Ci::Build do
{ key: 'GITLAB_FEATURES', value: project.licensed_features.join(','), public: true },
{ key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
{ key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true },
+ { key: 'CI_SERVER_VERSION_MAJOR', value: gitlab_version_info.major.to_s, public: true },
+ { key: 'CI_SERVER_VERSION_MINOR', value: gitlab_version_info.minor.to_s, public: true },
+ { key: 'CI_SERVER_VERSION_PATCH', value: gitlab_version_info.patch.to_s, public: true },
{ key: 'CI_SERVER_REVISION', value: Gitlab.revision, public: true },
{ key: 'CI_JOB_NAME', value: 'test', public: true },
{ key: 'CI_JOB_STAGE', value: 'test', public: true },
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index b19e75a956d..3b01b39ecab 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -837,6 +837,57 @@ describe Ci::Pipeline, :mailer do
end
end
+ describe '#branch_updated?' do
+ context 'when pipeline has before SHA' do
+ before do
+ pipeline.update_column(:before_sha, 'a1b2c3d4')
+ end
+
+ it 'runs on a branch update push' do
+ expect(pipeline.before_sha).not_to be Gitlab::Git::BLANK_SHA
+ expect(pipeline.branch_updated?).to be true
+ end
+ end
+
+ context 'when pipeline does not have before SHA' do
+ before do
+ pipeline.update_column(:before_sha, Gitlab::Git::BLANK_SHA)
+ end
+
+ it 'does not run on a branch updating push' do
+ expect(pipeline.branch_updated?).to be false
+ end
+ end
+ end
+
+ describe '#modified_paths' do
+ context 'when old and new revisions are set' do
+ let(:project) { create(:project, :repository) }
+
+ before do
+ pipeline.update(before_sha: '1234abcd', sha: '2345bcde')
+ end
+
+ it 'fetches stats for changes between commits' do
+ expect(project.repository)
+ .to receive(:diff_stats).with('1234abcd', '2345bcde')
+ .and_call_original
+
+ pipeline.modified_paths
+ end
+ end
+
+ context 'when either old or new revision is missing' do
+ before do
+ pipeline.update_column(:before_sha, Gitlab::Git::BLANK_SHA)
+ end
+
+ it 'raises an error' do
+ expect { pipeline.modified_paths }.to raise_error(ArgumentError)
+ end
+ end
+ end
+
describe '#has_kubernetes_active?' do
context 'when kubernetes is active' do
shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
@@ -1993,4 +2044,34 @@ describe Ci::Pipeline, :mailer do
end
end
end
+
+ describe '#default_branch?' do
+ let(:default_branch) { 'master'}
+
+ subject { pipeline.default_branch? }
+
+ before do
+ allow(project).to receive(:default_branch).and_return(default_branch)
+ end
+
+ context 'when pipeline ref is the default branch of the project' do
+ let(:pipeline) do
+ build(:ci_empty_pipeline, status: :created, project: project, ref: default_branch)
+ end
+
+ it "returns true" do
+ expect(subject).to be_truthy
+ end
+ end
+
+ context 'when pipeline ref is not the default branch of the project' do
+ let(:pipeline) do
+ build(:ci_empty_pipeline, status: :created, project: project, ref: 'another_branch')
+ end
+
+ it "returns false" do
+ expect(subject).to be_falsey
+ end
+ end
+ end
end
diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb
index 060a1d95293..5076f7faeac 100644
--- a/spec/models/ci/stage_spec.rb
+++ b/spec/models/ci/stage_spec.rb
@@ -200,8 +200,8 @@ describe Ci::Stage, :models do
end
end
- describe '#schedule' do
- subject { stage.schedule }
+ describe '#delay' do
+ subject { stage.delay }
let(:stage) { create(:ci_stage_entity, status: :created) }
diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb
index da26d802688..f8d50e89d40 100644
--- a/spec/models/concerns/cache_markdown_field_spec.rb
+++ b/spec/models/concerns/cache_markdown_field_spec.rb
@@ -331,11 +331,12 @@ describe CacheMarkdownField do
end
context 'with a project' do
- let(:thing) { thing_subclass(:project).new(foo: markdown, foo_html: html, project: :project_value) }
+ let(:project) { create(:project, group: create(:group)) }
+ let(:thing) { thing_subclass(:project).new(foo: markdown, foo_html: html, project: project) }
it 'sets the project in the context' do
is_expected.to have_key(:project)
- expect(context[:project]).to eq(:project_value)
+ expect(context[:project]).to eq(project)
end
it 'invalidates the cache when project changes' do
diff --git a/spec/models/instance_configuration_spec.rb b/spec/models/instance_configuration_spec.rb
index 34db94920f3..cb3d6c7cda2 100644
--- a/spec/models/instance_configuration_spec.rb
+++ b/spec/models/instance_configuration_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-RSpec.describe InstanceConfiguration do
+describe InstanceConfiguration do
context 'without cache' do
describe '#settings' do
describe '#ssh_algorithms_hashes' do
- let(:md5) { '54:e0:f8:70:d6:4f:4c:b1:b3:02:44:77:cf:cd:0d:fc' }
- let(:sha256) { '9327f0d15a48c4d9f6a3aee65a1825baf9a3412001c98169c5fd022ac27762fc' }
+ let(:md5) { '5a:65:6c:4d:d4:4c:6d:e6:59:25:b8:cf:ba:34:e7:64' }
+ let(:sha256) { 'SHA256:2KJDT7xf2i68mBgJ3TVsjISntg4droLbXYLfQj0VvSY' }
it 'does not return anything if file does not exist' do
stub_pub_file(exist: false)
diff --git a/spec/models/internal_id_spec.rb b/spec/models/internal_id_spec.rb
index f2aad455d5f..52c00a74b4b 100644
--- a/spec/models/internal_id_spec.rb
+++ b/spec/models/internal_id_spec.rb
@@ -65,7 +65,8 @@ describe InternalId do
context 'with an insufficient schema version' do
before do
described_class.reset_column_information
- expect(ActiveRecord::Migrator).to receive(:current_version).and_return(InternalId::REQUIRED_SCHEMA_VERSION - 1)
+ # Project factory will also call the current_version
+ expect(ActiveRecord::Migrator).to receive(:current_version).twice.and_return(InternalId::REQUIRED_SCHEMA_VERSION - 1)
end
let(:init) { double('block') }
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 19bc2713ef5..6f900a60213 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -268,45 +268,6 @@ describe Issue do
end
end
- describe '#related_branches' do
- let(:user) { create(:admin) }
-
- before do
- allow(subject.project.repository).to receive(:branch_names)
- .and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name, "#{subject.iid}-branch"])
-
- # Without this stub, the `create(:merge_request)` above fails because it can't find
- # the source branch. This seems like a reasonable compromise, in comparison with
- # setting up a full repo here.
- allow_any_instance_of(MergeRequest).to receive(:create_merge_request_diff)
- end
-
- it "selects the right branches when there are no referenced merge requests" do
- expect(subject.related_branches(user)).to eq([subject.to_branch_name, "#{subject.iid}-branch"])
- end
-
- it "selects the right branches when there is a referenced merge request" do
- merge_request = create(:merge_request, { description: "Closes ##{subject.iid}",
- source_project: subject.project,
- source_branch: "#{subject.iid}-branch" })
- merge_request.create_cross_references!(user)
-
- referenced_merge_requests = Issues::ReferencedMergeRequestsService
- .new(subject.project, user)
- .referenced_merge_requests(subject)
-
- expect(referenced_merge_requests).not_to be_empty
- expect(subject.related_branches(user)).to eq([subject.to_branch_name])
- end
-
- it 'excludes stable branches from the related branches' do
- allow(subject.project.repository).to receive(:branch_names)
- .and_return(["#{subject.iid}-0-stable"])
-
- expect(subject.related_branches(user)).to eq []
- end
- end
-
describe '#suggested_branch_name' do
let(:repository) { double }
diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb
index 99670af786a..3fc6c06b7fa 100644
--- a/spec/models/label_spec.rb
+++ b/spec/models/label_spec.rb
@@ -155,4 +155,40 @@ describe Label do
expect(described_class.search('feature')).to be_empty
end
end
+
+ describe '.subscribed_by' do
+ let!(:user) { create(:user) }
+ let!(:label) { create(:label) }
+ let!(:label2) { create(:label) }
+
+ before do
+ label.subscribe(user)
+ end
+
+ it 'returns subscribed labels' do
+ expect(described_class.subscribed_by(user.id)).to eq([label])
+ end
+
+ it 'returns nothing' do
+ expect(described_class.subscribed_by(0)).to be_empty
+ end
+ end
+
+ describe '.optionally_subscribed_by' do
+ let!(:user) { create(:user) }
+ let!(:label) { create(:label) }
+ let!(:label2) { create(:label) }
+
+ before do
+ label.subscribe(user)
+ end
+
+ it 'returns subscribed labels' do
+ expect(described_class.optionally_subscribed_by(user.id)).to eq([label])
+ end
+
+ it 'returns all labels if user_id is nil' do
+ expect(described_class.optionally_subscribed_by(nil)).to match_array([label, label2])
+ end
+ end
end
diff --git a/spec/models/license_template_spec.rb b/spec/models/license_template_spec.rb
index c633e1908d4..dd912eefac1 100644
--- a/spec/models/license_template_spec.rb
+++ b/spec/models/license_template_spec.rb
@@ -54,6 +54,6 @@ describe LicenseTemplate do
end
def build_template(content)
- described_class.new(id: 'foo', name: 'foo', category: :Other, content: content)
+ described_class.new(key: 'foo', name: 'foo', category: :Other, content: content)
end
end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 947be44c903..1783dd3206b 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -231,33 +231,60 @@ describe Note do
let(:ext_proj) { create(:project, :public) }
let(:ext_issue) { create(:issue, project: ext_proj) }
- let(:note) do
- create :note,
- noteable: ext_issue, project: ext_proj,
- note: "mentioned in issue #{private_issue.to_reference(ext_proj)}",
- system: true
- end
+ shared_examples "checks references" do
+ it "returns true" do
+ expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
+ end
- it "returns true" do
- expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
- end
+ it "returns false" do
+ expect(note.cross_reference_not_visible_for?(private_user)).to be_falsy
+ end
- it "returns false" do
- expect(note.cross_reference_not_visible_for?(private_user)).to be_falsy
+ it "returns false if user visible reference count set" do
+ note.user_visible_reference_count = 1
+ note.total_reference_count = 1
+
+ expect(note).not_to receive(:reference_mentionables)
+ expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_falsy
+ end
+
+ it "returns true if ref count is 0" do
+ note.user_visible_reference_count = 0
+
+ expect(note).not_to receive(:reference_mentionables)
+ expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
+ end
end
- it "returns false if user visible reference count set" do
- note.user_visible_reference_count = 1
+ context "when there is one reference in note" do
+ let(:note) do
+ create :note,
+ noteable: ext_issue, project: ext_proj,
+ note: "mentioned in issue #{private_issue.to_reference(ext_proj)}",
+ system: true
+ end
- expect(note).not_to receive(:reference_mentionables)
- expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_falsy
+ it_behaves_like "checks references"
end
- it "returns true if ref count is 0" do
- note.user_visible_reference_count = 0
+ context "when there are two references in note" do
+ let(:note) do
+ create :note,
+ noteable: ext_issue, project: ext_proj,
+ note: "mentioned in issue #{private_issue.to_reference(ext_proj)} and " \
+ "public issue #{ext_issue.to_reference(ext_proj)}",
+ system: true
+ end
+
+ it_behaves_like "checks references"
- expect(note).not_to receive(:reference_mentionables)
- expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
+ it "returns true if user visible reference count set and there is a private reference" do
+ note.user_visible_reference_count = 1
+ note.total_reference_count = 2
+
+ expect(note).not_to receive(:reference_mentionables)
+ expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
+ end
end
end
@@ -269,7 +296,7 @@ describe Note do
end
context 'when the note might contain cross references' do
- SystemNoteMetadata::TYPES_WITH_CROSS_REFERENCES.each do |type|
+ SystemNoteMetadata.new.cross_reference_types.each do |type|
let(:note) { create(:note, :system) }
let!(:metadata) { create(:system_note_metadata, note: note, action: type) }
diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb
index 797d767465a..342798f730b 100644
--- a/spec/models/project_auto_devops_spec.rb
+++ b/spec/models/project_auto_devops_spec.rb
@@ -70,24 +70,31 @@ describe ProjectAutoDevops do
end
context 'when deploy_strategy is manual' do
- let(:domain) { 'example.com' }
-
- before do
- auto_devops.deploy_strategy = 'manual'
+ let(:auto_devops) { build_stubbed(:project_auto_devops, :manual_deployment, project: project) }
+ let(:expected_variables) do
+ [
+ { key: 'INCREMENTAL_ROLLOUT_MODE', value: 'manual' },
+ { key: 'STAGING_ENABLED', value: '1' },
+ { key: 'INCREMENTAL_ROLLOUT_ENABLED', value: '1' }
+ ]
end
+ it { expect(auto_devops.predefined_variables).to include(*expected_variables) }
+ end
+
+ context 'when deploy_strategy is continuous' do
+ let(:auto_devops) { build_stubbed(:project_auto_devops, :continuous_deployment, project: project) }
+
it do
expect(auto_devops.predefined_variables.map { |var| var[:key] })
- .to include("STAGING_ENABLED", "INCREMENTAL_ROLLOUT_ENABLED")
+ .not_to include("STAGING_ENABLED", "INCREMENTAL_ROLLOUT_ENABLED")
end
end
- context 'when deploy_strategy is continuous' do
- let(:domain) { 'example.com' }
+ context 'when deploy_strategy is timed_incremental' do
+ let(:auto_devops) { build_stubbed(:project_auto_devops, :timed_incremental_deployment, project: project) }
- before do
- auto_devops.deploy_strategy = 'continuous'
- end
+ it { expect(auto_devops.predefined_variables).to include(key: 'INCREMENTAL_ROLLOUT_MODE', value: 'timed') }
it do
expect(auto_devops.predefined_variables.map { |var| var[:key] })
diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb
index cd7f77024da..fee7d65c217 100644
--- a/spec/models/project_feature_spec.rb
+++ b/spec/models/project_feature_spec.rb
@@ -17,7 +17,7 @@ describe ProjectFeature do
end
describe '#feature_available?' do
- let(:features) { %w(issues wiki builds merge_requests snippets repository) }
+ let(:features) { %w(issues wiki builds merge_requests snippets repository pages) }
context 'when features are disabled' do
it "returns false" do
@@ -73,6 +73,22 @@ describe ProjectFeature do
end
end
end
+
+ context 'when feature is disabled by a feature flag' do
+ it 'returns false' do
+ stub_feature_flags(issues: false)
+
+ expect(project.feature_available?(:issues, user)).to eq(false)
+ end
+ end
+
+ context 'when feature is enabled by a feature flag' do
+ it 'returns true' do
+ stub_feature_flags(issues: true)
+
+ expect(project.feature_available?(:issues, user)).to eq(true)
+ end
+ end
end
context 'repository related features' do
@@ -96,6 +112,19 @@ describe ProjectFeature do
end
end
+ context 'public features' do
+ it "does not allow public for other than pages" do
+ features = %w(issues wiki builds merge_requests snippets repository)
+ project_feature = project.project_feature
+
+ features.each do |feature|
+ field = "#{feature}_access_level".to_sym
+ project_feature.update_attribute(field, ProjectFeature::PUBLIC)
+ expect(project_feature.valid?).to be_falsy
+ end
+ end
+ end
+
describe '#*_enabled?' do
let(:features) { %w(wiki builds merge_requests) }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 8b71919544e..3fecddefff2 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -4039,6 +4039,63 @@ describe Project do
end
end
+ describe "#find_or_initialize_services" do
+ subject { build(:project) }
+
+ it 'returns only enabled services' do
+ allow(Service).to receive(:available_services_names).and_return(%w(prometheus pushover))
+ allow(subject).to receive(:disabled_services).and_return(%w(prometheus))
+
+ services = subject.find_or_initialize_services
+
+ expect(services.count).to eq 1
+ expect(services).to include(PushoverService)
+ end
+ end
+
+ describe "#find_or_initialize_service" do
+ subject { build(:project) }
+
+ it 'avoids N+1 database queries' do
+ allow(Service).to receive(:available_services_names).and_return(%w(prometheus pushover))
+
+ control_count = ActiveRecord::QueryRecorder.new { subject.find_or_initialize_service('prometheus') }.count
+
+ allow(Service).to receive(:available_services_names).and_call_original
+
+ expect { subject.find_or_initialize_service('prometheus') }.not_to exceed_query_limit(control_count)
+ end
+
+ it 'returns nil if service is disabled' do
+ allow(subject).to receive(:disabled_services).and_return(%w(prometheus))
+
+ expect(subject.find_or_initialize_service('prometheus')).to be_nil
+ end
+ end
+
+ describe '.find_without_deleted' do
+ it 'returns nil if the project is about to be removed' do
+ project = create(:project, pending_delete: true)
+
+ expect(described_class.find_without_deleted(project.id)).to be_nil
+ end
+
+ it 'returns a project when it is not about to be removed' do
+ project = create(:project)
+
+ expect(described_class.find_without_deleted(project.id)).to eq(project)
+ end
+ end
+
+ describe '.for_group' do
+ it 'returns the projects for a given group' do
+ group = create(:group)
+ project = create(:project, namespace: group)
+
+ expect(described_class.for_group(group)).to eq([project])
+ end
+ end
+
def rugged_config
rugged_repo(project.repository).config
end
diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb
index f29abcf536e..2c01578aaca 100644
--- a/spec/models/todo_spec.rb
+++ b/spec/models/todo_spec.rb
@@ -173,4 +173,129 @@ describe Todo do
expect(subject).not_to be_self_assigned
end
end
+
+ describe '.for_action' do
+ it 'returns the todos for a given action' do
+ create(:todo, action: Todo::MENTIONED)
+
+ todo = create(:todo, action: Todo::ASSIGNED)
+
+ expect(described_class.for_action(Todo::ASSIGNED)).to eq([todo])
+ end
+ end
+
+ describe '.for_author' do
+ it 'returns the todos for a given author' do
+ user1 = create(:user)
+ user2 = create(:user)
+ todo = create(:todo, author: user1)
+
+ create(:todo, author: user2)
+
+ expect(described_class.for_author(user1)).to eq([todo])
+ end
+ end
+
+ describe '.for_project' do
+ it 'returns the todos for a given project' do
+ project1 = create(:project)
+ project2 = create(:project)
+ todo = create(:todo, project: project1)
+
+ create(:todo, project: project2)
+
+ expect(described_class.for_project(project1)).to eq([todo])
+ end
+ end
+
+ describe '.for_group' do
+ it 'returns the todos for a given group' do
+ group1 = create(:group)
+ group2 = create(:group)
+ todo = create(:todo, group: group1)
+
+ create(:todo, group: group2)
+
+ expect(described_class.for_group(group1)).to eq([todo])
+ end
+ end
+
+ describe '.for_type' do
+ it 'returns the todos for a given target type' do
+ todo = create(:todo, target: create(:issue))
+
+ create(:todo, target: create(:merge_request))
+
+ expect(described_class.for_type(Issue)).to eq([todo])
+ end
+ end
+
+ describe '.for_target' do
+ it 'returns the todos for a given target' do
+ todo = create(:todo, target: create(:issue))
+
+ create(:todo, target: create(:merge_request))
+
+ expect(described_class.for_target(todo.target)).to eq([todo])
+ end
+ end
+
+ describe '.for_commit' do
+ it 'returns the todos for a commit ID' do
+ todo = create(:todo, commit_id: '123')
+
+ create(:todo, commit_id: '456')
+
+ expect(described_class.for_commit('123')).to eq([todo])
+ end
+ end
+
+ describe '.for_group_and_descendants' do
+ it 'returns the todos for a group and its descendants' do
+ parent_group = create(:group)
+ child_group = create(:group, parent: parent_group)
+
+ todo1 = create(:todo, group: parent_group)
+ todo2 = create(:todo, group: child_group)
+ todos = described_class.for_group_and_descendants(parent_group)
+
+ expect(todos).to include(todo1)
+
+ # Nested groups only work on PostgreSQL, so on MySQL todo2 won't be
+ # present.
+ expect(todos).to include(todo2) if Gitlab::Database.postgresql?
+ end
+ end
+
+ describe '.any_for_target?' do
+ it 'returns true if there are todos for a given target' do
+ todo = create(:todo)
+
+ expect(described_class.any_for_target?(todo.target)).to eq(true)
+ end
+
+ it 'returns false if there are no todos for a given target' do
+ issue = create(:issue)
+
+ expect(described_class.any_for_target?(issue)).to eq(false)
+ end
+ end
+
+ describe '.update_state' do
+ it 'updates the state of todos' do
+ todo = create(:todo, :pending)
+ ids = described_class.update_state(:done)
+
+ todo.reload
+
+ expect(ids).to eq([todo.id])
+ expect(todo.state).to eq('done')
+ end
+
+ it 'does not update todos that already have the given state' do
+ create(:todo, :pending)
+
+ expect(described_class.update_state(:pending)).to be_empty
+ end
+ end
end
diff --git a/spec/presenters/ci/build_presenter_spec.rb b/spec/presenters/ci/build_presenter_spec.rb
index b2fe10bb0b0..d7992f0a4a9 100644
--- a/spec/presenters/ci/build_presenter_spec.rb
+++ b/spec/presenters/ci/build_presenter_spec.rb
@@ -227,7 +227,7 @@ describe Ci::BuildPresenter do
it 'returns execution time' do
Timecop.freeze do
- is_expected.to eq(60.0)
+ is_expected.to be_like_time(60.0)
end
end
end
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 06ccf383362..98399471f9a 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -572,6 +572,20 @@ describe API::Commits do
expect(json_response['title']).to eq(message)
end
+ it 'includes the commit stats' do
+ post api(url, user), valid_mo_params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response).to include 'stats'
+ end
+
+ it "doesn't include the commit stats when stats is false" do
+ post api(url, user), valid_mo_params.merge(stats: false)
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response).not_to include 'stats'
+ end
+
it 'return a 400 bad request if there are any issues' do
post api(url, user), invalid_mo_params
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 1e2e13a723c..9f6cf12f9a7 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -56,6 +56,7 @@ describe API::Issues do
let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) }
let(:no_milestone_title) { URI.escape(Milestone::None.title) }
+ let(:any_milestone_title) { URI.escape(Milestone::Any.title) }
before(:all) do
project.add_reporter(user)
@@ -811,6 +812,15 @@ describe API::Issues do
expect(json_response.first['id']).to eq(confidential_issue.id)
end
+ it 'returns an array of issues with any milestone' do
+ get api("#{base_url}/issues?milestone=#{any_milestone_title}", user)
+
+ response_ids = json_response.map { |issue| issue['id'] }
+
+ expect_paginated_array_response(size: 2)
+ expect(response_ids).to contain_exactly(closed_issue.id, issue.id)
+ end
+
it 'sorts by created_at descending by default' do
get api("#{base_url}/issues", user)
diff --git a/spec/requests/api/markdown_spec.rb b/spec/requests/api/markdown_spec.rb
index a55796cf343..e369c1435f0 100644
--- a/spec/requests/api/markdown_spec.rb
+++ b/spec/requests/api/markdown_spec.rb
@@ -106,6 +106,52 @@ describe API::Markdown do
.and include("#1</a>")
end
end
+
+ context 'with a public project and confidential issue' do
+ let(:public_project) { create(:project, :public) }
+ let(:confidential_issue) { create(:issue, :confidential, project: public_project, title: 'Confidential title') }
+
+ let(:text) { ":tada: Hello world! :100: #{confidential_issue.to_reference}" }
+ let(:params) { { text: text, gfm: true, project: public_project.full_path } }
+
+ shared_examples 'user without proper access' do
+ it 'does not render the title or link' do
+ expect(response).to have_http_status(201)
+ expect(json_response["html"]).not_to include('Confidential title')
+ expect(json_response["html"]).not_to include('<a href=')
+ expect(json_response["html"]).to include('Hello world!')
+ .and include('data-name="tada"')
+ .and include('data-name="100"')
+ .and include('#1</p>')
+ end
+ end
+
+ context 'when not logged in' do
+ let(:user) { }
+
+ it_behaves_like 'user without proper access'
+ end
+
+ context 'when logged in as user without access' do
+ let(:user) { create(:user) }
+
+ it_behaves_like 'user without proper access'
+ end
+
+ context 'when logged in as author' do
+ let(:user) { confidential_issue.author }
+
+ it 'renders the title or link' do
+ expect(response).to have_http_status(201)
+ expect(json_response["html"]).to include('Confidential title')
+ expect(json_response["html"]).to include('Hello world!')
+ .and include('data-name="tada"')
+ .and include('data-name="100"')
+ .and include("<a href=\"#{IssuesHelper.url_for_issue(confidential_issue.iid, public_project)}\"")
+ .and include("#1</a>")
+ end
+ end
+ end
end
end
end
diff --git a/spec/requests/api/pages/internal_access_spec.rb b/spec/requests/api/pages/internal_access_spec.rb
new file mode 100644
index 00000000000..c41eabe0a48
--- /dev/null
+++ b/spec/requests/api/pages/internal_access_spec.rb
@@ -0,0 +1,88 @@
+require 'spec_helper'
+
+describe "Internal Project Pages Access" do
+ using RSpec::Parameterized::TableSyntax
+ include AccessMatchers
+
+ set(:group) { create(:group) }
+ set(:project) { create(:project, :internal, pages_access_level: ProjectFeature::ENABLED, namespace: group) }
+
+ set(:admin) { create(:admin) }
+ set(:owner) { create(:user) }
+ set(:master) { create(:user) }
+ set(:developer) { create(:user) }
+ set(:reporter) { create(:user) }
+ set(:guest) { create(:user) }
+ set(:user) { create(:user) }
+
+ before do
+ allow(Gitlab.config.pages).to receive(:access_control).and_return(true)
+ group.add_owner(owner)
+ project.add_master(master)
+ project.add_developer(developer)
+ project.add_reporter(reporter)
+ project.add_guest(guest)
+ end
+
+ describe "Project should be internal" do
+ describe '#internal?' do
+ subject { project.internal? }
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ describe "GET /projects/:id/pages_access" do
+ context 'access depends on the level' do
+ where(:pages_access_level, :with_user, :expected_result) do
+ ProjectFeature::DISABLED | "admin" | 403
+ ProjectFeature::DISABLED | "owner" | 403
+ ProjectFeature::DISABLED | "master" | 403
+ ProjectFeature::DISABLED | "developer" | 403
+ ProjectFeature::DISABLED | "reporter" | 403
+ ProjectFeature::DISABLED | "guest" | 403
+ ProjectFeature::DISABLED | "user" | 403
+ ProjectFeature::DISABLED | nil | 404
+ ProjectFeature::PUBLIC | "admin" | 200
+ ProjectFeature::PUBLIC | "owner" | 200
+ ProjectFeature::PUBLIC | "master" | 200
+ ProjectFeature::PUBLIC | "developer" | 200
+ ProjectFeature::PUBLIC | "reporter" | 200
+ ProjectFeature::PUBLIC | "guest" | 200
+ ProjectFeature::PUBLIC | "user" | 200
+ ProjectFeature::PUBLIC | nil | 404
+ ProjectFeature::ENABLED | "admin" | 200
+ ProjectFeature::ENABLED | "owner" | 200
+ ProjectFeature::ENABLED | "master" | 200
+ ProjectFeature::ENABLED | "developer" | 200
+ ProjectFeature::ENABLED | "reporter" | 200
+ ProjectFeature::ENABLED | "guest" | 200
+ ProjectFeature::ENABLED | "user" | 200
+ ProjectFeature::ENABLED | nil | 404
+ ProjectFeature::PRIVATE | "admin" | 200
+ ProjectFeature::PRIVATE | "owner" | 200
+ ProjectFeature::PRIVATE | "master" | 200
+ ProjectFeature::PRIVATE | "developer" | 200
+ ProjectFeature::PRIVATE | "reporter" | 200
+ ProjectFeature::PRIVATE | "guest" | 200
+ ProjectFeature::PRIVATE | "user" | 403
+ ProjectFeature::PRIVATE | nil | 404
+ end
+
+ with_them do
+ before do
+ project.project_feature.update(pages_access_level: pages_access_level)
+ end
+ it "correct return value" do
+ if !with_user.nil?
+ user = public_send(with_user)
+ get api("/projects/#{project.id}/pages_access", user)
+ else
+ get api("/projects/#{project.id}/pages_access")
+ end
+
+ expect(response).to have_gitlab_http_status(expected_result)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/pages/private_access_spec.rb b/spec/requests/api/pages/private_access_spec.rb
new file mode 100644
index 00000000000..d69c15b0477
--- /dev/null
+++ b/spec/requests/api/pages/private_access_spec.rb
@@ -0,0 +1,88 @@
+require 'spec_helper'
+
+describe "Private Project Pages Access" do
+ using RSpec::Parameterized::TableSyntax
+ include AccessMatchers
+
+ set(:group) { create(:group) }
+ set(:project) { create(:project, :private, pages_access_level: ProjectFeature::ENABLED, namespace: group) }
+
+ set(:admin) { create(:admin) }
+ set(:owner) { create(:user) }
+ set(:master) { create(:user) }
+ set(:developer) { create(:user) }
+ set(:reporter) { create(:user) }
+ set(:guest) { create(:user) }
+ set(:user) { create(:user) }
+
+ before do
+ allow(Gitlab.config.pages).to receive(:access_control).and_return(true)
+ group.add_owner(owner)
+ project.add_master(master)
+ project.add_developer(developer)
+ project.add_reporter(reporter)
+ project.add_guest(guest)
+ end
+
+ describe "Project should be private" do
+ describe '#private?' do
+ subject { project.private? }
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ describe "GET /projects/:id/pages_access" do
+ context 'access depends on the level' do
+ where(:pages_access_level, :with_user, :expected_result) do
+ ProjectFeature::DISABLED | "admin" | 403
+ ProjectFeature::DISABLED | "owner" | 403
+ ProjectFeature::DISABLED | "master" | 403
+ ProjectFeature::DISABLED | "developer" | 403
+ ProjectFeature::DISABLED | "reporter" | 403
+ ProjectFeature::DISABLED | "guest" | 403
+ ProjectFeature::DISABLED | "user" | 404
+ ProjectFeature::DISABLED | nil | 404
+ ProjectFeature::PUBLIC | "admin" | 200
+ ProjectFeature::PUBLIC | "owner" | 200
+ ProjectFeature::PUBLIC | "master" | 200
+ ProjectFeature::PUBLIC | "developer" | 200
+ ProjectFeature::PUBLIC | "reporter" | 200
+ ProjectFeature::PUBLIC | "guest" | 200
+ ProjectFeature::PUBLIC | "user" | 404
+ ProjectFeature::PUBLIC | nil | 404
+ ProjectFeature::ENABLED | "admin" | 200
+ ProjectFeature::ENABLED | "owner" | 200
+ ProjectFeature::ENABLED | "master" | 200
+ ProjectFeature::ENABLED | "developer" | 200
+ ProjectFeature::ENABLED | "reporter" | 200
+ ProjectFeature::ENABLED | "guest" | 200
+ ProjectFeature::ENABLED | "user" | 404
+ ProjectFeature::ENABLED | nil | 404
+ ProjectFeature::PRIVATE | "admin" | 200
+ ProjectFeature::PRIVATE | "owner" | 200
+ ProjectFeature::PRIVATE | "master" | 200
+ ProjectFeature::PRIVATE | "developer" | 200
+ ProjectFeature::PRIVATE | "reporter" | 200
+ ProjectFeature::PRIVATE | "guest" | 200
+ ProjectFeature::PRIVATE | "user" | 404
+ ProjectFeature::PRIVATE | nil | 404
+ end
+
+ with_them do
+ before do
+ project.project_feature.update(pages_access_level: pages_access_level)
+ end
+ it "correct return value" do
+ if !with_user.nil?
+ user = public_send(with_user)
+ get api("/projects/#{project.id}/pages_access", user)
+ else
+ get api("/projects/#{project.id}/pages_access")
+ end
+
+ expect(response).to have_gitlab_http_status(expected_result)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/pages/public_access_spec.rb b/spec/requests/api/pages/public_access_spec.rb
new file mode 100644
index 00000000000..882ca26ac51
--- /dev/null
+++ b/spec/requests/api/pages/public_access_spec.rb
@@ -0,0 +1,88 @@
+require 'spec_helper'
+
+describe "Public Project Pages Access" do
+ using RSpec::Parameterized::TableSyntax
+ include AccessMatchers
+
+ set(:group) { create(:group) }
+ set(:project) { create(:project, :public, pages_access_level: ProjectFeature::ENABLED, namespace: group) }
+
+ set(:admin) { create(:admin) }
+ set(:owner) { create(:user) }
+ set(:master) { create(:user) }
+ set(:developer) { create(:user) }
+ set(:reporter) { create(:user) }
+ set(:guest) { create(:user) }
+ set(:user) { create(:user) }
+
+ before do
+ allow(Gitlab.config.pages).to receive(:access_control).and_return(true)
+ group.add_owner(owner)
+ project.add_master(master)
+ project.add_developer(developer)
+ project.add_reporter(reporter)
+ project.add_guest(guest)
+ end
+
+ describe "Project should be public" do
+ describe '#public?' do
+ subject { project.public? }
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ describe "GET /projects/:id/pages_access" do
+ context 'access depends on the level' do
+ where(:pages_access_level, :with_user, :expected_result) do
+ ProjectFeature::DISABLED | "admin" | 403
+ ProjectFeature::DISABLED | "owner" | 403
+ ProjectFeature::DISABLED | "master" | 403
+ ProjectFeature::DISABLED | "developer" | 403
+ ProjectFeature::DISABLED | "reporter" | 403
+ ProjectFeature::DISABLED | "guest" | 403
+ ProjectFeature::DISABLED | "user" | 403
+ ProjectFeature::DISABLED | nil | 403
+ ProjectFeature::PUBLIC | "admin" | 200
+ ProjectFeature::PUBLIC | "owner" | 200
+ ProjectFeature::PUBLIC | "master" | 200
+ ProjectFeature::PUBLIC | "developer" | 200
+ ProjectFeature::PUBLIC | "reporter" | 200
+ ProjectFeature::PUBLIC | "guest" | 200
+ ProjectFeature::PUBLIC | "user" | 200
+ ProjectFeature::PUBLIC | nil | 200
+ ProjectFeature::ENABLED | "admin" | 200
+ ProjectFeature::ENABLED | "owner" | 200
+ ProjectFeature::ENABLED | "master" | 200
+ ProjectFeature::ENABLED | "developer" | 200
+ ProjectFeature::ENABLED | "reporter" | 200
+ ProjectFeature::ENABLED | "guest" | 200
+ ProjectFeature::ENABLED | "user" | 200
+ ProjectFeature::ENABLED | nil | 200
+ ProjectFeature::PRIVATE | "admin" | 200
+ ProjectFeature::PRIVATE | "owner" | 200
+ ProjectFeature::PRIVATE | "master" | 200
+ ProjectFeature::PRIVATE | "developer" | 200
+ ProjectFeature::PRIVATE | "reporter" | 200
+ ProjectFeature::PRIVATE | "guest" | 200
+ ProjectFeature::PRIVATE | "user" | 403
+ ProjectFeature::PRIVATE | nil | 403
+ end
+
+ with_them do
+ before do
+ project.project_feature.update(pages_access_level: pages_access_level)
+ end
+ it "correct return value" do
+ if !with_user.nil?
+ user = public_send(with_user)
+ get api("/projects/#{project.id}/pages_access", user)
+ else
+ get api("/projects/#{project.id}/pages_access")
+ end
+
+ expect(response).to have_gitlab_http_status(expected_result)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/project_templates_spec.rb b/spec/requests/api/project_templates_spec.rb
new file mode 100644
index 00000000000..86e33f23951
--- /dev/null
+++ b/spec/requests/api/project_templates_spec.rb
@@ -0,0 +1,145 @@
+require 'spec_helper'
+
+describe API::ProjectTemplates do
+ let(:public_project) { create(:project, :public) }
+ let(:private_project) { create(:project, :private) }
+ let(:developer) { create(:user) }
+
+ before do
+ private_project.add_developer(developer)
+ end
+
+ describe 'GET /projects/:id/templates/:type' do
+ it 'returns dockerfiles' do
+ get api("/projects/#{public_project.id}/templates/dockerfiles")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(response).to match_response_schema('public_api/v4/template_list')
+ expect(json_response).to satisfy_one { |template| template['key'] == 'Binary' }
+ end
+
+ it 'returns gitignores' do
+ get api("/projects/#{public_project.id}/templates/gitignores")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(response).to match_response_schema('public_api/v4/template_list')
+ expect(json_response).to satisfy_one { |template| template['key'] == 'Actionscript' }
+ end
+
+ it 'returns gitlab_ci_ymls' do
+ get api("/projects/#{public_project.id}/templates/gitlab_ci_ymls")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(response).to match_response_schema('public_api/v4/template_list')
+ expect(json_response).to satisfy_one { |template| template['key'] == 'Android' }
+ end
+
+ it 'returns licenses' do
+ get api("/projects/#{public_project.id}/templates/licenses")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(response).to match_response_schema('public_api/v4/template_list')
+ expect(json_response).to satisfy_one { |template| template['key'] == 'mit' }
+ end
+
+ it 'returns 400 for an unknown template type' do
+ get api("/projects/#{public_project.id}/templates/unknown")
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it 'denies access to an anonymous user on a private project' do
+ get api("/projects/#{private_project.id}/templates/licenses")
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'permits access to a developer on a private project' do
+ get api("/projects/#{private_project.id}/templates/licenses", developer)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/template_list')
+ end
+ end
+
+ describe 'GET /projects/:id/templates/licenses' do
+ it 'returns key and name for the listed licenses' do
+ get api("/projects/#{public_project.id}/templates/licenses")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/template_list')
+ end
+ end
+
+ describe 'GET /projects/:id/templates/:type/:key' do
+ it 'returns a specific dockerfile' do
+ get api("/projects/#{public_project.id}/templates/dockerfiles/Binary")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/template')
+ expect(json_response['name']).to eq('Binary')
+ end
+
+ it 'returns a specific gitignore' do
+ get api("/projects/#{public_project.id}/templates/gitignores/Actionscript")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/template')
+ expect(json_response['name']).to eq('Actionscript')
+ end
+
+ it 'returns a specific gitlab_ci_yml' do
+ get api("/projects/#{public_project.id}/templates/gitlab_ci_ymls/Android")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/template')
+ expect(json_response['name']).to eq('Android')
+ end
+
+ it 'returns a specific license' do
+ get api("/projects/#{public_project.id}/templates/licenses/mit")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/license')
+ end
+
+ it 'returns 404 for an unknown specific template' do
+ get api("/projects/#{public_project.id}/templates/licenses/unknown")
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'denies access to an anonymous user on a private project' do
+ get api("/projects/#{private_project.id}/templates/licenses/mit")
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'permits access to a developer on a private project' do
+ get api("/projects/#{private_project.id}/templates/licenses/mit", developer)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/license')
+ end
+ end
+
+ describe 'GET /projects/:id/templates/licenses/:key' do
+ it 'fills placeholders in the license' do
+ get api("/projects/#{public_project.id}/templates/licenses/agpl-3.0"),
+ project: 'Project Placeholder',
+ fullname: 'Fullname Placeholder'
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/license')
+
+ content = json_response['content']
+
+ expect(content).to include('Project Placeholder')
+ expect(content).to include("Copyright (C) #{Time.now.year} Fullname Placeholder")
+ end
+ end
+end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index b7d62df0663..09c1d016081 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -785,35 +785,25 @@ describe API::Users do
end
describe 'GET /user/:id/keys' do
- before do
- admin
- end
+ it 'returns 404 for non-existing user' do
+ user_id = not_existing_user_id
- context 'when unauthenticated' do
- it 'returns authentication error' do
- get api("/users/#{user.id}/keys")
- expect(response).to have_gitlab_http_status(401)
- end
- end
+ get api("/users/#{user_id}/keys")
- context 'when authenticated' do
- it 'returns 404 for non-existing user' do
- get api('/users/999999/keys', admin)
- expect(response).to have_gitlab_http_status(404)
- expect(json_response['message']).to eq('404 User Not Found')
- end
+ expect(response).to have_gitlab_http_status(404)
+ expect(json_response['message']).to eq('404 User Not Found')
+ end
- it 'returns array of ssh keys' do
- user.keys << key
- user.save
+ it 'returns array of ssh keys' do
+ user.keys << key
+ user.save
- get api("/users/#{user.id}/keys", admin)
+ get api("/users/#{user.id}/keys")
- expect(response).to have_gitlab_http_status(200)
- expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
- expect(json_response.first['title']).to eq(key.title)
- end
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.first['title']).to eq(key.title)
end
end
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 5abc6d81958..bdfb12dc5df 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -133,8 +133,9 @@ describe 'project routing' do
# labels_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/labels(.:format) projects/autocomplete_sources#labels
# milestones_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/milestones(.:format) projects/autocomplete_sources#milestones
# commands_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/commands(.:format) projects/autocomplete_sources#commands
+ # snippets_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/snippets(.:format) projects/autocomplete_sources#snippets
describe Projects::AutocompleteSourcesController, 'routing' do
- [:members, :issues, :merge_requests, :labels, :milestones, :commands].each do |action|
+ [:members, :issues, :merge_requests, :labels, :milestones, :commands, :snippets].each do |action|
it "to ##{action}" do
expect(get("/gitlab/gitlabhq/autocomplete_sources/#{action}")).to route_to("projects/autocomplete_sources##{action}", namespace_id: 'gitlab', project_id: 'gitlabhq')
end
@@ -258,10 +259,10 @@ describe 'project routing' do
end
it 'to #logs_tree' do
- expect(get('/gitlab/gitlabhq/refs/stable/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable')
- expect(get('/gitlab/gitlabhq/refs/feature%2345/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45')
- expect(get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45')
- expect(get('/gitlab/gitlabhq/refs/feature@45/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45')
+ expect(get('/gitlab/gitlabhq/refs/stable/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable')
+ expect(get('/gitlab/gitlabhq/refs/feature%2345/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45')
+ expect(get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45')
+ expect(get('/gitlab/gitlabhq/refs/feature@45/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45')
expect(get('/gitlab/gitlabhq/refs/stable/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable', path: 'foo/bar/baz')
expect(get('/gitlab/gitlabhq/refs/feature%2345/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45', path: 'foo/bar/baz')
expect(get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45', path: 'foo/bar/baz')
diff --git a/spec/serializers/commit_entity_spec.rb b/spec/serializers/commit_entity_spec.rb
index 35215e06f5f..b9995818e98 100644
--- a/spec/serializers/commit_entity_spec.rb
+++ b/spec/serializers/commit_entity_spec.rb
@@ -1,10 +1,11 @@
require 'spec_helper'
describe CommitEntity do
+ SIGNATURE_HTML = 'TEST'.freeze
+
let(:entity) do
described_class.new(commit, request: request)
end
-
let(:request) { double('request') }
let(:project) { create(:project, :repository) }
let(:commit) { project.commit }
@@ -12,7 +13,11 @@ describe CommitEntity do
subject { entity.as_json }
before do
+ render = double('render')
+ allow(render).to receive(:call).and_return(SIGNATURE_HTML)
+
allow(request).to receive(:project).and_return(project)
+ allow(request).to receive(:render).and_return(render)
end
context 'when commit author is a user' do
@@ -61,7 +66,7 @@ describe CommitEntity do
context 'when type is "full"' do
let(:entity) do
- described_class.new(commit, request: request, type: :full)
+ described_class.new(commit, request: request, type: :full, pipeline_ref: project.default_branch, pipeline_project: project)
end
it 'exposes extra properties' do
@@ -70,6 +75,25 @@ describe CommitEntity do
expect(subject.fetch(:description_html)).not_to be_nil
expect(subject.fetch(:title_html)).not_to be_nil
end
+
+ context 'when commit has signature' do
+ let(:commit) { project.commit(TestEnv::BRANCH_SHA['signed-commits']) }
+
+ it 'exposes "signature_html"' do
+ expect(request.render).to receive(:call)
+ expect(subject.fetch(:signature_html)).to be SIGNATURE_HTML
+ end
+ end
+
+ context 'when commit has pipeline' do
+ before do
+ create(:ci_pipeline, project: project, sha: commit.id)
+ end
+
+ it 'exposes "pipeline_status_path"' do
+ expect(subject.fetch(:pipeline_status_path)).not_to be_nil
+ end
+ end
end
context 'when commit_url_params is set' do
diff --git a/spec/serializers/diff_file_entity_spec.rb b/spec/serializers/diff_file_entity_spec.rb
index 3d90ce44dfb..7497b8f27bd 100644
--- a/spec/serializers/diff_file_entity_spec.rb
+++ b/spec/serializers/diff_file_entity_spec.rb
@@ -26,6 +26,11 @@ describe DiffFileEntity do
)
end
+ it 'includes viewer' do
+ expect(subject[:viewer].with_indifferent_access)
+ .to match_schema('entities/diff_viewer')
+ end
+
# Converted diff files from GitHub import does not contain blob file
# and content sha.
context 'when diff file does not have a blob and content sha' do
diff --git a/spec/serializers/diff_viewer_entity_spec.rb b/spec/serializers/diff_viewer_entity_spec.rb
new file mode 100644
index 00000000000..66ac6ef2adc
--- /dev/null
+++ b/spec/serializers/diff_viewer_entity_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe DiffViewerEntity do
+ include RepoHelpers
+
+ let(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
+ let(:commit) { project.commit(sample_commit.id) }
+ let(:diff_refs) { commit.diff_refs }
+ let(:diff) { commit.raw_diffs.first }
+ let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs: diff_refs, repository: repository) }
+ let(:viewer) { diff_file.simple_viewer }
+
+ subject { described_class.new(viewer).as_json }
+
+ it 'serializes diff file viewer' do
+ expect(subject.with_indifferent_access).to match_schema('entities/diff_viewer')
+ end
+end
diff --git a/spec/serializers/discussion_entity_spec.rb b/spec/serializers/discussion_entity_spec.rb
index 378540a35b6..0590304e832 100644
--- a/spec/serializers/discussion_entity_spec.rb
+++ b/spec/serializers/discussion_entity_spec.rb
@@ -36,6 +36,13 @@ describe DiscussionEntity do
)
end
+ it 'resolved_by matches note_user_entity schema' do
+ Notes::ResolveService.new(note.project, user).execute(note)
+
+ expect(subject[:resolved_by].with_indifferent_access)
+ .to match_schema('entities/note_user_entity')
+ end
+
context 'when is LegacyDiffDiscussion' do
let(:project) { create(:project) }
let(:merge_request) { create(:merge_request, source_project: project) }
diff --git a/spec/services/issues/related_branches_service_spec.rb b/spec/services/issues/related_branches_service_spec.rb
new file mode 100644
index 00000000000..c2e1eba6a63
--- /dev/null
+++ b/spec/services/issues/related_branches_service_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe Issues::RelatedBranchesService do
+ let(:user) { create(:admin) }
+ let(:issue) { create(:issue) }
+
+ subject { described_class.new(issue.project, user) }
+
+ describe '#execute' do
+ before do
+ allow(issue.project.repository).to receive(:branch_names).and_return(["mpempe", "#{issue.iid}mepmep", issue.to_branch_name, "#{issue.iid}-branch"])
+ end
+
+ it "selects the right branches when there are no referenced merge requests" do
+ expect(subject.execute(issue)).to eq([issue.to_branch_name, "#{issue.iid}-branch"])
+ end
+
+ it "selects the right branches when there is a referenced merge request" do
+ merge_request = create(:merge_request, { description: "Closes ##{issue.iid}",
+ source_project: issue.project,
+ source_branch: "#{issue.iid}-branch" })
+ merge_request.create_cross_references!(user)
+
+ referenced_merge_requests = Issues::ReferencedMergeRequestsService
+ .new(issue.project, user)
+ .referenced_merge_requests(issue)
+
+ expect(referenced_merge_requests).not_to be_empty
+ expect(subject.execute(issue)).to eq([issue.to_branch_name])
+ end
+
+ it 'excludes stable branches from the related branches' do
+ allow(issue.project.repository).to receive(:branch_names)
+ .and_return(["#{issue.iid}-0-stable"])
+
+ expect(subject.execute(issue)).to eq []
+ end
+ end
+end
diff --git a/spec/services/notification_recipient_service_spec.rb b/spec/services/notification_recipient_service_spec.rb
index 14ba6b7bed2..cea5ea125b9 100644
--- a/spec/services/notification_recipient_service_spec.rb
+++ b/spec/services/notification_recipient_service_spec.rb
@@ -10,27 +10,50 @@ describe NotificationRecipientService do
let(:issue) { create(:issue, project: project, assignees: [assignee]) }
let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id) }
- def create_watcher
- watcher = create(:user)
- create(:notification_setting, source: project, user: watcher, level: :watch)
+ shared_examples 'no N+1 queries' do
+ it 'avoids N+1 queries', :request_store do
+ create_user
- other_projects.each do |other_project|
- create(:notification_setting, source: other_project, user: watcher, level: :watch)
+ service.build_new_note_recipients(note)
+
+ control_count = ActiveRecord::QueryRecorder.new do
+ service.build_new_note_recipients(note)
+ end
+
+ create_user
+
+ expect { service.build_new_note_recipients(note) }.not_to exceed_query_limit(control_count)
end
end
- it 'avoids N+1 queries', :request_store do
- create_watcher
+ context 'when there are multiple watchers' do
+ def create_user
+ watcher = create(:user)
+ create(:notification_setting, source: project, user: watcher, level: :watch)
+
+ other_projects.each do |other_project|
+ create(:notification_setting, source: other_project, user: watcher, level: :watch)
+ end
+ end
- service.build_new_note_recipients(note)
+ include_examples 'no N+1 queries'
+ end
- control_count = ActiveRecord::QueryRecorder.new do
- service.build_new_note_recipients(note)
+ context 'when there are multiple subscribers' do
+ def create_user
+ subscriber = create(:user)
+ issue.subscriptions.create(user: subscriber, project: project, subscribed: true)
end
- create_watcher
+ include_examples 'no N+1 queries'
- expect { service.build_new_note_recipients(note) }.not_to exceed_query_limit(control_count)
+ context 'when the project is private' do
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ include_examples 'no N+1 queries'
+ end
end
end
end
diff --git a/spec/services/projects/autocomplete_service_spec.rb b/spec/services/projects/autocomplete_service_spec.rb
index e98df375d48..373fe7cb7dd 100644
--- a/spec/services/projects/autocomplete_service_spec.rb
+++ b/spec/services/projects/autocomplete_service_spec.rb
@@ -148,7 +148,7 @@ describe Projects::AutocompleteService do
let!(:label1) { create(:label, project: project) }
let!(:label2) { create(:label, project: project) }
let!(:sub_group_label) { create(:group_label, group: sub_group) }
- let!(:parent_group_label) { create(:group_label, group: group.parent) }
+ let!(:parent_group_label) { create(:group_label, group: group.parent, group_id: group.id) }
before do
create(:group_member, group: group, user: user)
@@ -156,7 +156,7 @@ describe Projects::AutocompleteService do
it 'returns labels from project and ancestor groups' do
service = described_class.new(project, user)
- results = service.labels_as_hash
+ results = service.labels_as_hash(nil)
expected_labels = [label1, label2, parent_group_label]
expect_labels_to_equal(results, expected_labels)
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index 695b9980548..d58ff2cedc0 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -340,6 +340,27 @@ describe Projects::UpdateService do
call_service
end
end
+
+ context 'when updating #pages_access_level' do
+ subject(:call_service) do
+ update_project(project, admin, project_feature_attributes: { pages_access_level: ProjectFeature::PRIVATE })
+ end
+
+ it 'updates the attribute' do
+ expect { call_service }
+ .to change { project.project_feature.pages_access_level }
+ .to(ProjectFeature::PRIVATE)
+ end
+
+ it 'calls Projects::UpdatePagesConfigurationService' do
+ expect(Projects::UpdatePagesConfigurationService)
+ .to receive(:new)
+ .with(project)
+ .and_call_original
+
+ call_service
+ end
+ end
end
describe '#run_auto_devops_pipeline?' do
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index f4b7cb8c90a..a18126ee339 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -324,7 +324,7 @@ describe SystemNoteService do
end
it "posts the 'merge when pipeline succeeds' system note" do
- expect(subject.note).to eq "canceled the automatic merge"
+ expect(subject.note).to eq "canceled the automatic merge"
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index d1337325973..cd69160be10 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -34,6 +34,11 @@ Rainbow.enabled = false
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
# Requires helpers, and shared contexts/examples first since they're used in other support files
+
+# Load these first since they may be required by other helpers
+require Rails.root.join("spec/support/helpers/git_helpers.rb")
+
+# Then the rest
Dir[Rails.root.join("spec/support/helpers/*.rb")].each { |f| require f }
Dir[Rails.root.join("spec/support/shared_contexts/*.rb")].each { |f| require f }
Dir[Rails.root.join("spec/support/shared_examples/*.rb")].each { |f| require f }
diff --git a/spec/support/helpers/reference_parser_helpers.rb b/spec/support/helpers/reference_parser_helpers.rb
index c01897ed1a1..9f27502aa52 100644
--- a/spec/support/helpers/reference_parser_helpers.rb
+++ b/spec/support/helpers/reference_parser_helpers.rb
@@ -3,7 +3,7 @@ module ReferenceParserHelpers
Nokogiri::HTML.fragment('<a></a>').children[0]
end
- shared_examples 'no N+1 queries' do
+ shared_examples 'no project N+1 queries' do
it 'avoids N+1 queries in #nodes_visible_to_user', :request_store do
context = Banzai::RenderContext.new(project, user)
@@ -19,6 +19,10 @@ module ReferenceParserHelpers
expect(actual.count).to be <= control.count
expect(actual.cached_count).to be <= control.cached_count
end
+ end
+
+ shared_examples 'no N+1 queries' do
+ it_behaves_like 'no project N+1 queries'
it 'avoids N+1 queries in #records_for_nodes', :request_store do
context = Banzai::RenderContext.new(project, user)
diff --git a/spec/support/services/clusters/create_service_shared.rb b/spec/support/services/clusters/create_service_shared.rb
index 22f712f3fcf..b0bf942aa09 100644
--- a/spec/support/services/clusters/create_service_shared.rb
+++ b/spec/support/services/clusters/create_service_shared.rb
@@ -30,10 +30,6 @@ shared_context 'invalid cluster create params' do
end
shared_examples 'create cluster service success' do
- before do
- stub_feature_flags(rbac_clusters: false)
- end
-
it 'creates a cluster object and performs a worker' do
expect(ClusterProvisionWorker).to receive(:perform_async)
diff --git a/spec/workers/prune_old_events_worker_spec.rb b/spec/workers/prune_old_events_worker_spec.rb
index ea974355050..b999a6fd5b6 100644
--- a/spec/workers/prune_old_events_worker_spec.rb
+++ b/spec/workers/prune_old_events_worker_spec.rb
@@ -4,23 +4,29 @@ describe PruneOldEventsWorker do
describe '#perform' do
let(:user) { create(:user) }
- let!(:expired_event) { create(:event, :closed, author: user, created_at: 13.months.ago) }
- let!(:not_expired_event) { create(:event, :closed, author: user, created_at: 1.day.ago) }
- let!(:exactly_12_months_event) { create(:event, :closed, author: user, created_at: 12.months.ago) }
+ let!(:expired_event) { create(:event, :closed, author: user, created_at: 25.months.ago) }
+ let!(:not_expired_1_day_event) { create(:event, :closed, author: user, created_at: 1.day.ago) }
+ let!(:not_expired_13_month_event) { create(:event, :closed, author: user, created_at: 13.months.ago) }
+ let!(:not_expired_2_years_event) { create(:event, :closed, author: user, created_at: 2.years.ago) }
- it 'prunes events older than 12 months' do
+ it 'prunes events older than 2 years' do
expect { subject.perform }.to change { Event.count }.by(-1)
expect(Event.find_by(id: expired_event.id)).to be_nil
end
it 'leaves fresh events' do
subject.perform
- expect(not_expired_event.reload).to be_present
+ expect(not_expired_1_day_event.reload).to be_present
end
- it 'leaves events from exactly 12 months ago' do
+ it 'leaves events from 13 months ago' do
subject.perform
- expect(exactly_12_months_event).to be_present
+ expect(not_expired_13_month_event.reload).to be_present
+ end
+
+ it 'leaves events from 2 years ago' do
+ subject.perform
+ expect(not_expired_2_years_event).to be_present
end
end
end
diff --git a/vendor/gitignore/Global/JetBrains.gitignore b/vendor/gitignore/Global/JetBrains.gitignore
index 343be1a3b8e..72f4d988a19 100644
--- a/vendor/gitignore/Global/JetBrains.gitignore
+++ b/vendor/gitignore/Global/JetBrains.gitignore
@@ -61,3 +61,6 @@ fabric.properties
# Editor-based Rest Client
.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
diff --git a/vendor/gitignore/Node.gitignore b/vendor/gitignore/Node.gitignore
index 3a4c8581b3a..c221276ebae 100644
--- a/vendor/gitignore/Node.gitignore
+++ b/vendor/gitignore/Node.gitignore
@@ -20,7 +20,7 @@ coverage
# nyc test coverage
.nyc_output
-# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
diff --git a/vendor/gitignore/TeX.gitignore b/vendor/gitignore/TeX.gitignore
index ff87d483645..753f2b954ff 100644
--- a/vendor/gitignore/TeX.gitignore
+++ b/vendor/gitignore/TeX.gitignore
@@ -64,6 +64,9 @@ acs-*.bib
# changes
*.soc
+# comment
+*.cut
+
# cprotect
*.cpt
@@ -205,6 +208,9 @@ pythontex-files-*/
# easy-todo
*.lod
+# xcolor
+*.xcp
+
# xmpincl
*.xmpi
diff --git a/vendor/gitignore/VisualStudio.gitignore b/vendor/gitignore/VisualStudio.gitignore
index 94b41b913fb..4d13c54854e 100644
--- a/vendor/gitignore/VisualStudio.gitignore
+++ b/vendor/gitignore/VisualStudio.gitignore
@@ -4,6 +4,7 @@
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
+*.rsuser
*.suo
*.user
*.userosscache
@@ -76,6 +77,7 @@ StyleCopReport.xml
*.tlh
*.tmp
*.tmp_proj
+*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
@@ -290,8 +292,8 @@ paket-files/
.idea/
*.sln.iml
-# CodeRush
-.cr/
+# CodeRush personal settings
+.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
diff --git a/vendor/jupyter/values.yaml b/vendor/jupyter/values.yaml
index 4ea5b44c59c..049ffcc3407 100644
--- a/vendor/jupyter/values.yaml
+++ b/vendor/jupyter/values.yaml
@@ -13,6 +13,10 @@ auth:
singleuser:
defaultUrl: "/lab"
+ lifecycleHooks:
+ postStart:
+ exec:
+ command: ["git", "clone", "https://gitlab.com/gitlab-org/nurtch-demo.git", "DevOps-Runbook-Demo"]
ingress:
enabled: true
diff --git a/vendor/licenses.csv b/vendor/licenses.csv
index 6adcb28f736..9083fe076c3 100644
--- a/vendor/licenses.csv
+++ b/vendor/licenses.csv
@@ -1,31 +1,102 @@
-@gitlab-org/gitlab-svgs,1.29.0,MIT
-@gitlab-org/gitlab-ui,1.1.0,MIT
+@babel/code-frame,7.0.0,MIT
+@babel/core,7.1.2,MIT
+@babel/generator,7.1.2,MIT
+@babel/helper-annotate-as-pure,7.0.0,MIT
+@babel/helper-builder-binary-assignment-operator-visitor,7.1.0,MIT
+@babel/helper-call-delegate,7.1.0,MIT
+@babel/helper-define-map,7.1.0,MIT
+@babel/helper-explode-assignable-expression,7.1.0,MIT
+@babel/helper-function-name,7.1.0,MIT
+@babel/helper-get-function-arity,7.0.0,MIT
+@babel/helper-hoist-variables,7.0.0,MIT
+@babel/helper-member-expression-to-functions,7.0.0,MIT
+@babel/helper-module-imports,7.0.0,MIT
+@babel/helper-module-transforms,7.1.0,MIT
+@babel/helper-optimise-call-expression,7.0.0,MIT
+@babel/helper-plugin-utils,7.0.0,MIT
+@babel/helper-regex,7.0.0,MIT
+@babel/helper-remap-async-to-generator,7.1.0,MIT
+@babel/helper-replace-supers,7.1.0,MIT
+@babel/helper-simple-access,7.1.0,MIT
+@babel/helper-split-export-declaration,7.0.0,MIT
+@babel/helper-wrap-function,7.1.0,MIT
+@babel/helpers,7.1.2,MIT
+@babel/highlight,7.0.0,MIT
+@babel/parser,7.1.2,MIT
+@babel/plugin-proposal-async-generator-functions,7.1.0,MIT
+@babel/plugin-proposal-class-properties,7.1.0,MIT
+@babel/plugin-proposal-json-strings,7.0.0,MIT
+@babel/plugin-proposal-object-rest-spread,7.0.0,MIT
+@babel/plugin-proposal-optional-catch-binding,7.0.0,MIT
+@babel/plugin-proposal-unicode-property-regex,7.0.0,MIT
+@babel/plugin-syntax-async-generators,7.0.0,MIT
+@babel/plugin-syntax-class-properties,7.0.0,MIT
+@babel/plugin-syntax-dynamic-import,7.0.0,MIT
+@babel/plugin-syntax-import-meta,7.0.0,MIT
+@babel/plugin-syntax-json-strings,7.0.0,MIT
+@babel/plugin-syntax-object-rest-spread,7.0.0,MIT
+@babel/plugin-syntax-optional-catch-binding,7.0.0,MIT
+@babel/plugin-transform-arrow-functions,7.0.0,MIT
+@babel/plugin-transform-async-to-generator,7.1.0,MIT
+@babel/plugin-transform-block-scoped-functions,7.0.0,MIT
+@babel/plugin-transform-block-scoping,7.0.0,MIT
+@babel/plugin-transform-classes,7.1.0,MIT
+@babel/plugin-transform-computed-properties,7.0.0,MIT
+@babel/plugin-transform-destructuring,7.1.2,MIT
+@babel/plugin-transform-dotall-regex,7.0.0,MIT
+@babel/plugin-transform-duplicate-keys,7.0.0,MIT
+@babel/plugin-transform-exponentiation-operator,7.1.0,MIT
+@babel/plugin-transform-for-of,7.0.0,MIT
+@babel/plugin-transform-function-name,7.1.0,MIT
+@babel/plugin-transform-literals,7.0.0,MIT
+@babel/plugin-transform-modules-amd,7.1.0,MIT
+@babel/plugin-transform-modules-commonjs,7.1.0,MIT
+@babel/plugin-transform-modules-systemjs,7.0.0,MIT
+@babel/plugin-transform-modules-umd,7.1.0,MIT
+@babel/plugin-transform-new-target,7.0.0,MIT
+@babel/plugin-transform-object-super,7.1.0,MIT
+@babel/plugin-transform-parameters,7.1.0,MIT
+@babel/plugin-transform-regenerator,7.0.0,MIT
+@babel/plugin-transform-shorthand-properties,7.0.0,MIT
+@babel/plugin-transform-spread,7.0.0,MIT
+@babel/plugin-transform-sticky-regex,7.0.0,MIT
+@babel/plugin-transform-template-literals,7.0.0,MIT
+@babel/plugin-transform-typeof-symbol,7.0.0,MIT
+@babel/plugin-transform-unicode-regex,7.0.0,MIT
+@babel/preset-env,7.1.0,MIT
+@babel/template,7.1.2,MIT
+@babel/traverse,7.1.0,MIT
+@babel/types,7.1.2,MIT
+@gitlab-org/gitlab-svgs,1.31.0,MIT
+@gitlab-org/gitlab-ui,1.8.0,MIT
@sindresorhus/is,0.7.0,MIT
@types/jquery,2.0.48,MIT
-@vue/component-compiler-utils,1.2.1,MIT
-@webassemblyjs/ast,1.5.13,MIT
-@webassemblyjs/floating-point-hex-parser,1.5.13,MIT
-@webassemblyjs/helper-api-error,1.5.13,MIT
-@webassemblyjs/helper-buffer,1.5.13,MIT
-@webassemblyjs/helper-code-frame,1.5.13,MIT
-@webassemblyjs/helper-fsm,1.5.13,ISC
-@webassemblyjs/helper-module-context,1.5.13,MIT
-@webassemblyjs/helper-wasm-bytecode,1.5.13,MIT
-@webassemblyjs/helper-wasm-section,1.5.13,MIT
-@webassemblyjs/ieee754,1.5.13,MIT
-@webassemblyjs/leb128,1.5.13,MIT
-@webassemblyjs/utf8,1.5.13,MIT
-@webassemblyjs/wasm-edit,1.5.13,MIT
-@webassemblyjs/wasm-gen,1.5.13,MIT
-@webassemblyjs/wasm-opt,1.5.13,MIT
-@webassemblyjs/wasm-parser,1.5.13,MIT
-@webassemblyjs/wast-parser,1.5.13,MIT
-@webassemblyjs/wast-printer,1.5.13,MIT
+@vue/component-compiler-utils,2.2.0,MIT
+@webassemblyjs/ast,1.7.6,MIT
+@webassemblyjs/floating-point-hex-parser,1.7.6,MIT
+@webassemblyjs/helper-api-error,1.7.6,MIT
+@webassemblyjs/helper-buffer,1.7.6,MIT
+@webassemblyjs/helper-code-frame,1.7.6,MIT
+@webassemblyjs/helper-fsm,1.7.6,ISC
+@webassemblyjs/helper-module-context,1.7.6,MIT
+@webassemblyjs/helper-wasm-bytecode,1.7.6,MIT
+@webassemblyjs/helper-wasm-section,1.7.6,MIT
+@webassemblyjs/ieee754,1.7.6,MIT
+@webassemblyjs/leb128,1.7.6,MIT
+@webassemblyjs/utf8,1.7.6,MIT
+@webassemblyjs/wasm-edit,1.7.6,MIT
+@webassemblyjs/wasm-gen,1.7.6,MIT
+@webassemblyjs/wasm-opt,1.7.6,MIT
+@webassemblyjs/wasm-parser,1.7.6,MIT
+@webassemblyjs/wast-parser,1.7.6,MIT
+@webassemblyjs/wast-printer,1.7.6,MIT
+@xtuc/ieee754,1.2.0,New BSD
+@xtuc/long,4.2.1,Apache 2.0
RedCloth,4.3.2,MIT
-abbrev,1.1.1,ISC
-accepts,1.3.4,MIT
+abbrev,1.0.9,ISC
+accepts,1.3.5,MIT
ace-rails-ap,4.1.2,MIT
-acorn,5.7.1,MIT
+acorn,5.7.3,MIT
acorn-dynamic-import,3.0.0,MIT
actionmailer,4.2.10,MIT
actionpack,4.2.10,MIT
@@ -37,8 +108,9 @@ activesupport,4.2.10,MIT
acts-as-taggable-on,5.0.0,MIT
addressable,2.5.2,Apache 2.0
aes_key_wrap,1.0.1,MIT
-ajv,6.1.1,MIT
-ajv-keywords,3.1.0,MIT
+ajv,6.5.3,MIT
+ajv-errors,1.0.0,MIT
+ajv-keywords,3.2.0,MIT
akismet,2.0.0,MIT
ansi-escapes,1.4.0,MIT
ansi-escapes,3.0.0,MIT
@@ -73,78 +145,10 @@ autosize,4.0.0,MIT
axiom-types,0.1.1,MIT
axios,0.17.1,MIT
babel-code-frame,6.26.0,MIT
-babel-core,6.26.3,MIT
-babel-generator,6.26.0,MIT
-babel-helper-bindify-decorators,6.24.1,MIT
-babel-helper-builder-binary-assignment-operator-visitor,6.24.1,MIT
-babel-helper-call-delegate,6.24.1,MIT
-babel-helper-define-map,6.26.0,MIT
-babel-helper-explode-assignable-expression,6.24.1,MIT
-babel-helper-explode-class,6.24.1,MIT
-babel-helper-function-name,6.24.1,MIT
-babel-helper-get-function-arity,6.24.1,MIT
-babel-helper-hoist-variables,6.24.1,MIT
-babel-helper-optimise-call-expression,6.24.1,MIT
-babel-helper-regex,6.26.0,MIT
-babel-helper-remap-async-to-generator,6.24.1,MIT
-babel-helper-replace-supers,6.24.1,MIT
-babel-helpers,6.24.1,MIT
-babel-loader,7.1.5,MIT
-babel-messages,6.23.0,MIT
-babel-plugin-check-es2015-constants,6.22.0,MIT
-babel-plugin-syntax-async-functions,6.13.0,MIT
-babel-plugin-syntax-async-generators,6.13.0,MIT
-babel-plugin-syntax-class-properties,6.13.0,MIT
-babel-plugin-syntax-decorators,6.13.0,MIT
-babel-plugin-syntax-dynamic-import,6.18.0,MIT
-babel-plugin-syntax-exponentiation-operator,6.13.0,MIT
-babel-plugin-syntax-object-rest-spread,6.13.0,MIT
-babel-plugin-syntax-trailing-function-commas,6.22.0,MIT
-babel-plugin-transform-async-generator-functions,6.24.1,MIT
-babel-plugin-transform-async-to-generator,6.24.1,MIT
-babel-plugin-transform-class-properties,6.24.1,MIT
-babel-plugin-transform-decorators,6.24.1,MIT
-babel-plugin-transform-define,1.3.0,MIT
-babel-plugin-transform-es2015-arrow-functions,6.22.0,MIT
-babel-plugin-transform-es2015-block-scoped-functions,6.22.0,MIT
-babel-plugin-transform-es2015-block-scoping,6.26.0,MIT
-babel-plugin-transform-es2015-classes,6.24.1,MIT
-babel-plugin-transform-es2015-computed-properties,6.24.1,MIT
-babel-plugin-transform-es2015-destructuring,6.23.0,MIT
-babel-plugin-transform-es2015-duplicate-keys,6.24.1,MIT
-babel-plugin-transform-es2015-for-of,6.23.0,MIT
-babel-plugin-transform-es2015-function-name,6.24.1,MIT
-babel-plugin-transform-es2015-literals,6.22.0,MIT
-babel-plugin-transform-es2015-modules-amd,6.24.1,MIT
-babel-plugin-transform-es2015-modules-commonjs,6.26.0,MIT
-babel-plugin-transform-es2015-modules-systemjs,6.24.1,MIT
-babel-plugin-transform-es2015-modules-umd,6.24.1,MIT
-babel-plugin-transform-es2015-object-super,6.24.1,MIT
-babel-plugin-transform-es2015-parameters,6.24.1,MIT
-babel-plugin-transform-es2015-shorthand-properties,6.24.1,MIT
-babel-plugin-transform-es2015-spread,6.22.0,MIT
-babel-plugin-transform-es2015-sticky-regex,6.24.1,MIT
-babel-plugin-transform-es2015-template-literals,6.22.0,MIT
-babel-plugin-transform-es2015-typeof-symbol,6.23.0,MIT
-babel-plugin-transform-es2015-unicode-regex,6.24.1,MIT
-babel-plugin-transform-exponentiation-operator,6.24.1,MIT
-babel-plugin-transform-object-rest-spread,6.23.0,MIT
-babel-plugin-transform-regenerator,6.26.0,MIT
-babel-plugin-transform-strict-mode,6.24.1,MIT
+babel-loader,8.0.4,MIT
babel-polyfill,6.23.0,MIT
-babel-preset-es2015,6.24.1,MIT
-babel-preset-es2016,6.24.1,MIT
-babel-preset-es2017,6.24.1,MIT
-babel-preset-latest,6.24.1,MIT
-babel-preset-stage-2,6.24.1,MIT
-babel-preset-stage-3,6.24.1,MIT
-babel-register,6.26.0,MIT
babel-runtime,6.26.0,MIT
-babel-template,6.26.0,MIT
-babel-traverse,6.26.0,MIT
-babel-types,6.26.0,MIT
babosa,1.0.2,MIT
-babylon,6.18.0,MIT
balanced-match,1.0.0,MIT
base,0.11.2,MIT
base32,0.3.2,MIT
@@ -152,8 +156,8 @@ base64-js,1.2.3,MIT
batch-loader,1.2.1,MIT
bcrypt,3.1.12,MIT
bcrypt_pbkdf,1.0.0,MIT
-bfj-node4,5.2.1,MIT
-big.js,3.1.3,MIT
+bfj,6.1.1,MIT
+big.js,3.2.0,MIT
binary-extensions,1.11.0,MIT
binaryextensions,2.1.1,MIT
bindata,2.4.3,ruby
@@ -161,7 +165,7 @@ blackst0ne-mermaid,7.1.0-fixed,MIT
bluebird,3.5.1,MIT
bn.js,4.11.8,MIT
body-parser,1.18.2,MIT
-bootstrap,4.1.2,MIT
+bootstrap,4.1.1,MIT
bootstrap-vue,2.0.0-rc.11,MIT
bootstrap_form,2.7.0,MIT
brace-expansion,1.1.11,MIT
@@ -174,6 +178,7 @@ browserify-des,1.0.0,MIT
browserify-rsa,4.0.1,MIT
browserify-sign,4.0.4,ISC
browserify-zlib,0.2.0,MIT
+browserslist,4.1.1,MIT
buffer,4.9.1,MIT
buffer-from,1.0.0,MIT
buffer-xor,1.0.3,MIT
@@ -181,10 +186,12 @@ builder,3.2.3,MIT
builtin-status-codes,3.0.0,MIT
bytes,3.0.0,MIT
cacache,10.0.4,ISC
+cacache,11.2.0,ISC
cache-base,1.0.1,MIT
cache-loader,1.2.2,MIT
cacheable-request,2.1.4,MIT
camelcase,4.1.0,MIT
+caniuse-lite,1.0.30000888,CC-BY-4.0
carrierwave,1.2.3,MIT
cause,0.1,MIT
chalk,1.1.3,MIT
@@ -216,31 +223,31 @@ codesandbox-import-util-types,1.2.11,LGPL
codesandbox-import-utils,1.2.11,LGPL
coercible,1.0.0,MIT
collection-visit,1.0.0,MIT
-color-convert,1.9.1,MIT
-color-name,1.1.2,MIT
+color-convert,1.9.3,MIT
+color-name,1.1.3,MIT
commander,2.13.0,MIT
-commander,2.15.1,MIT
+commander,2.18.0,MIT
commondir,1.0.1,MIT
-commonmarker,0.17.8,MIT
+commonmarker,0.17.13,MIT
component-emitter,1.2.1,MIT
-compression-webpack-plugin,1.1.11,MIT
+compression-webpack-plugin,2.0.0,MIT
concat-map,0.0.1,MIT
concat-stream,1.6.2,MIT
concurrent-ruby-ext,1.0.5,MIT
-connection_pool,2.2.1,MIT
+connection_pool,2.2.2,MIT
console-browserify,1.1.0,MIT
console-control-strings,1.1.0,ISC
consolidate,0.15.1,MIT
constants-browserify,1.0.0,MIT
content-disposition,0.5.2,MIT
content-type,1.0.4,MIT
-convert-source-map,1.5.1,MIT
+convert-source-map,1.6.0,MIT
cookie,0.3.1,MIT
cookie-signature,1.0.6,MIT
copy-concurrently,1.0.5,ISC
copy-descriptor,0.1.1,MIT
core-js,2.3.0,MIT
-core-js,2.5.3,MIT
+core-js,2.5.7,MIT
core-util-is,1.0.2,MIT
crack,0.4.3,MIT
crass,1.0.4,MIT
@@ -249,7 +256,6 @@ create-hash,1.1.3,MIT
create-hmac,1.1.6,MIT
creole,0.5.0,ruby
cropper,2.3.0,MIT
-cross-spawn,5.1.0,MIT
cross-spawn,6.0.5,MIT
crypt,0.0.2,New BSD
crypto-browserify,3.12.0,MIT
@@ -296,9 +302,9 @@ date-now,0.1.4,MIT
dateformat,3.0.3,MIT
de-indent,1.0.2,MIT
debug,2.6.9,MIT
-debug,3.1.0,MIT
+debug,3.2.5,MIT
debugger-ruby_core_source,1.3.8,MIT
-decamelize,1.2.0,MIT
+decamelize,2.0.0,MIT
deckar01-task_list,2.0.0,MIT
declarative,0.0.10,MIT
declarative-option,0.1.0,MIT
@@ -306,16 +312,17 @@ decode-uri-component,0.2.0,MIT
decompress-response,3.3.0,MIT
deep-extend,0.4.2,MIT
default_value_for,3.0.2,MIT
+define-properties,1.1.3,MIT
define-property,0.2.5,MIT
define-property,1.0.0,MIT
define-property,2.0.2,MIT
delegate,3.1.2,MIT
delegates,1.0.0,MIT
depd,1.1.1,MIT
+depd,1.1.2,MIT
des.js,1.0.0,MIT
descendants_tracker,0.0.4,MIT
destroy,1.0.4,MIT
-detect-indent,4.0.0,MIT
detect-libc,1.0.3,Apache 2.0
device_detector,1.0.0,LGPL
devise,4.4.3,MIT
@@ -342,7 +349,8 @@ duplexify,3.5.3,MIT
ed25519,1.2.4,MIT
editions,1.3.4,MIT
ee-first,1.1.1,MIT
-ejs,2.5.9,Apache 2.0
+ejs,2.6.1,Apache 2.0
+electron-to-chromium,1.3.73,ISC
elliptic,6.4.0,MIT
email_reply_trimmer,0.1.6,MIT
emoji-unicode-version,0.2.1,MIT
@@ -356,11 +364,13 @@ entities,1.1.1,Simplified BSD
equalizer,0.0.11,MIT
errno,0.1.7,MIT
erubis,2.7.0,MIT
+es-abstract,1.12.0,MIT
+es-to-primitive,1.1.1,MIT
es6-promise,3.0.2,MIT
escape-html,1.0.3,MIT
escape-string-regexp,1.0.5,MIT
escape_utils,1.1.1,MIT
-eslint-scope,3.7.1,Simplified BSD
+eslint-scope,4.0.0,Simplified BSD
esrecurse,4.2.1,Simplified BSD
estraverse,4.2.0,Simplified BSD
esutils,2.0.2,Simplified BSD
@@ -370,11 +380,11 @@ eve-raphael,0.5.0,Apache 2.0
events,1.1.1,MIT
evp_bytestokey,1.0.3,MIT
excon,0.62.0,MIT
-execa,0.7.0,MIT
+execa,0.10.0,MIT
execjs,2.6.0,MIT
expand-brackets,2.1.4,MIT
exports-loader,0.7.0,MIT
-express,4.16.2,MIT
+express,4.16.3,MIT
expression_parser,0.9.0,MIT
extend-shallow,2.0.1,MIT
extend-shallow,3.0.2,MIT
@@ -384,19 +394,22 @@ extglob,2.0.4,MIT
faraday,0.12.2,MIT
faraday_middleware,0.12.2,MIT
faraday_middleware-multi_json,0.0.6,MIT
-fast-deep-equal,1.0.0,MIT
+fast-deep-equal,2.0.1,MIT
fast-json-stable-stringify,2.0.0,MIT
fast_blank,1.0.0,MIT
fast_gettext,1.6.0,"MIT,ruby"
fastparse,1.1.1,MIT
ffi,1.9.25,New BSD
+figgy-pudding,3.5.1,ISC
figures,2.0.0,MIT
-file-loader,1.1.11,MIT
-filesize,3.6.0,New BSD
+file-loader,2.0.0,MIT
+filesize,3.6.1,New BSD
fill-range,4.0.0,MIT
-finalhandler,1.1.0,MIT
+finalhandler,1.1.1,MIT
find-cache-dir,1.0.0,MIT
+find-cache-dir,2.0.0,MIT
find-up,2.1.0,MIT
+find-up,3.0.0,MIT
flipper,0.13.0,MIT
flipper-active_record,0.13.0,MIT
flipper-active_support_cache_store,0.13.0,MIT
@@ -424,6 +437,7 @@ fs-minipass,1.2.5,ISC
fs-write-stream-atomic,1.0.10,ISC
fs.realpath,1.0.0,ISC
fsevents,1.2.4,MIT
+function-bind,1.1.1,MIT
fuzzaldrin-plus,0.5.0,MIT
gauge,2.7.4,ISC
gemojione,3.3.0,MIT
@@ -433,19 +447,20 @@ get-value,2.0.6,MIT
get_process_mem,0.2.0,MIT
gettext_i18n_rails,1.8.0,MIT
gettext_i18n_rails_js,1.3.0,MIT
-gitaly-proto,0.117.0,MIT
+gitaly-proto,0.118.1,MIT
github-linguist,5.3.3,MIT
github-markup,1.7.0,MIT
gitlab-flowdock-git-hook,1.0.1,MIT
gitlab-gollum-lib,4.2.7.5,MIT
gitlab-grit,2.8.2,MIT
gitlab-markup,1.6.4,MIT
+gitlab-sidekiq-fetcher,0.3.0,LGPL
gitlab_omniauth-ldap,2.0.4,MIT
-glob,7.1.2,ISC
+glob,7.1.3,ISC
glob-parent,3.1.0,ISC
global-modules-path,2.1.0,Apache 2.0
globalid,0.4.1,MIT
-globals,9.18.0,MIT
+globals,11.7.0,MIT
gollum-grit_adapter,1.0.1,MIT
gon,6.2.0,MIT
good-listener,1.2.2,MIT
@@ -456,7 +471,7 @@ googleauth,0.6.2,Apache 2.0
got,8.3.0,MIT
gpgme,2.0.13,LGPL-2.1+
graceful-fs,4.1.11,ISC
-grape,1.0.3,MIT
+grape,1.1.0,MIT
grape-entity,0.7.1,MIT
grape-path-helpers,1.0.6,MIT
grape_logging,1.7.0,MIT
@@ -464,12 +479,14 @@ graphiql-rails,1.4.10,MIT
graphlib,2.1.1,MIT
graphql,1.8.1,MIT
grpc,1.11.0,Apache 2.0
-gzip-size,4.1.0,MIT
+gzip-size,5.0.0,MIT
hamlit,2.8.8,MIT
hangouts-chat,0.0.5,MIT
+has,1.0.1,MIT
has-ansi,2.0.0,MIT
has-flag,3.0.0,MIT
has-symbol-support-x,1.3.0,MIT
+has-symbols,1.0.0,MIT
has-to-string-tag-x,1.3.0,MIT
has-unicode,2.0.1,ISC
has-value,0.3.1,MIT
@@ -486,7 +503,7 @@ he,1.1.1,MIT
health_check,2.6.0,MIT
hipchat,1.5.2,MIT
hmac-drbg,1.0.1,MIT
-home-or-tmp,2.0.0,MIT
+hoopy,0.1.4,MIT
html-pipeline,2.8.4,MIT
html2text,0.2.0,MIT
htmlentities,4.3.4,MIT
@@ -522,25 +539,26 @@ inherits,2.0.1,ISC
inherits,2.0.3,ISC
ini,1.3.5,ISC
inquirer,3.0.6,MIT
-inquirer,6.0.0,MIT
+inquirer,6.2.0,MIT
interpret,1.1.0,MIT
into-stream,3.1.0,MIT
-invariant,2.2.2,New BSD
-invert-kv,1.0.0,MIT
-ipaddr.js,1.6.0,MIT
+invariant,2.2.4,MIT
+invert-kv,2.0.0,MIT
+ipaddr.js,1.8.0,MIT
ipaddress,0.8.3,MIT
is-accessor-descriptor,0.1.6,MIT
is-accessor-descriptor,1.0.0,MIT
is-binary-path,1.0.1,MIT
is-buffer,1.1.6,MIT
+is-callable,1.1.4,MIT
is-data-descriptor,0.1.4,MIT
is-data-descriptor,1.0.0,MIT
+is-date-object,1.0.1,MIT
is-descriptor,0.1.6,MIT
is-descriptor,1.0.2,MIT
is-extendable,0.1.1,MIT
is-extendable,1.0.1,MIT
is-extglob,2.1.1,MIT
-is-finite,1.0.2,MIT
is-fullwidth-code-point,1.0.0,MIT
is-fullwidth-code-point,2.0.0,MIT
is-glob,3.1.0,MIT
@@ -553,8 +571,10 @@ is-odd,2.0.0,MIT
is-plain-obj,1.1.0,MIT
is-plain-object,2.0.4,MIT
is-promise,2.1.0,MIT
+is-regex,1.0.4,MIT
is-retry-allowed,1.1.0,MIT
is-stream,1.1.0,MIT
+is-symbol,1.0.2,MIT
is-windows,1.0.2,MIT
isarray,1.0.0,MIT
isexe,2.0.0,ISC
@@ -569,15 +589,17 @@ jquery-atwho-rails,1.3.2,MIT
jquery-ujs,1.2.2,MIT
jquery.waitforimages,2.2.0,MIT
js-cookie,2.1.3,MIT
+js-levenshtein,1.1.4,MIT
js-tokens,3.0.2,MIT
+js-tokens,4.0.0,MIT
js_regex,2.2.1,MIT
jsesc,0.5.0,MIT
-jsesc,1.3.0,MIT
+jsesc,2.5.1,MIT
json,1.8.6,ruby
json-buffer,3.0.0,MIT
json-jwt,1.9.4,MIT
json-parse-better-errors,1.0.2,MIT
-json-schema-traverse,0.3.1,MIT
+json-schema-traverse,0.4.1,MIT
json5,0.5.1,MIT
jszip,3.1.3,(MIT OR GPL-3.0)
jszip-utils,0.0.2,MIT or GPLv3
@@ -586,7 +608,7 @@ kaminari,1.0.1,MIT
kaminari-actionview,1.0.1,MIT
kaminari-activerecord,1.0.1,MIT
kaminari-core,1.0.1,MIT
-katex,0.8.3,MIT
+katex,0.9.0,MIT
keyv,3.0.0,MIT
kgio,2.10.0,LGPL-2.1+
kind-of,3.2.2,MIT
@@ -595,7 +617,7 @@ kind-of,5.1.0,MIT
kind-of,6.0.2,MIT
kubeclient,3.1.0,MIT
lazy-cache,2.0.2,MIT
-lcid,1.0.0,MIT
+lcid,2.0.0,MIT
licensee,8.9.2,MIT
lie,3.1.1,MIT
little-plugger,1.1.4,MIT
@@ -603,8 +625,8 @@ loader-runner,2.3.0,MIT
loader-utils,1.1.0,MIT
locale,2.1.2,"ruby,LGPLv3+"
locate-path,2.0.0,MIT
-lodash,4.17.10,MIT
-lodash,4.17.4,MIT
+locate-path,3.0.0,MIT
+lodash,4.17.11,MIT
lodash.camelcase,4.3.0,MIT
lodash.clonedeep,4.5.0,MIT
lodash.debounce,4.0.8,MIT
@@ -615,24 +637,23 @@ lodash.mergewith,4.6.0,MIT
lodash.startcase,4.4.0,MIT
logging,2.2.2,MIT
lograge,0.10.0,MIT
-long,3.2.0,Apache 2.0
-long,4.0.0,Apache 2.0
loofah,2.2.2,MIT
-loose-envify,1.3.1,MIT
+loose-envify,1.4.0,MIT
lowercase-keys,1.0.0,MIT
lru-cache,4.1.3,ISC
lz-string,1.4.4,WTFPL
mail,2.7.0,MIT
mail_room,0.9.1,MIT
-make-dir,1.2.0,MIT
+make-dir,1.3.0,MIT
mamacro,0.0.3,MIT
+map-age-cleaner,0.1.2,MIT
map-cache,0.2.2,MIT
map-visit,1.0.0,MIT
marked,0.3.12,MIT
match-at,0.1.1,MIT
md5.js,1.3.4,MIT
media-typer,0.3.0,MIT
-mem,1.1.0,MIT
+mem,4.0.0,MIT
memoist,0.16.0,MIT
memory-fs,0.4.1,MIT
merge-descriptors,1.0.1,MIT
@@ -651,7 +672,7 @@ mimemagic,0.3.0,MIT
mimic-fn,1.1.0,MIT
mimic-response,1.0.0,MIT
mini_magick,4.8.0,MIT
-mini_mime,1.0.0,MIT
+mini_mime,1.0.1,MIT
mini_portile2,2.3.0,MIT
minimalistic-assert,1.0.0,ISC
minimalistic-crypto-utils,1.0.1,MIT
@@ -661,20 +682,22 @@ minimist,1.2.0,MIT
minipass,2.3.3,ISC
minizlib,1.1.0,MIT
mississippi,2.0.0,Simplified BSD
+mississippi,3.0.0,Simplified BSD
mixin-deep,1.3.1,MIT
mkdirp,0.5.1,MIT
moment,2.19.2,MIT
monaco-editor,0.14.3,MIT
-monaco-editor-webpack-plugin,1.5.2,MIT
+monaco-editor-webpack-plugin,1.5.4,MIT
mousetrap,1.4.6,Apache 2.0
mousetrap-rails,1.4.6,"MIT,Apache"
move-concurrently,1.0.1,ISC
ms,2.0.0,MIT
+ms,2.1.1,MIT
msgpack,1.2.4,Apache 2.0
multi_json,1.13.1,MIT
multi_xml,0.6.0,MIT
multipart-post,2.0.0,MIT
-mustermann,1.0.2,MIT
+mustermann,1.0.3,MIT
mustermann-grape,1.0.0,MIT
mute-stream,0.0.7,ISC
mysql2,0.4.10,MIT
@@ -690,6 +713,7 @@ nice-try,1.0.4,MIT
node-fetch,1.6.3,MIT
node-libs-browser,2.1.0,MIT
node-pre-gyp,0.10.0,New BSD
+node-releases,1.0.0-alpha.12,CC-BY-4.0
nokogiri,1.8.4,MIT
nokogumbo,1.5.0,Apache 2.0
nopt,4.0.1,ISC
@@ -705,7 +729,9 @@ oauth,0.5.4,MIT
oauth2,1.4.0,MIT
object-assign,4.1.1,MIT
object-copy,0.1.0,MIT
+object-keys,1.0.12,MIT
object-visit,1.0.1,MIT
+object.getownpropertydescriptors,2.0.3,MIT
object.pick,1.3.0,MIT
octokit,4.9.0,MIT
omniauth,1.8.1,MIT
@@ -730,23 +756,27 @@ on-finished,2.3.0,MIT
once,1.4.0,ISC
onetime,2.0.1,MIT
opencollective,1.0.3,MIT
-opener,1.4.3,(WTFPL OR MIT)
+opener,1.5.1,(WTFPL OR MIT)
opn,4.0.2,MIT
org-ruby,0.9.12,MIT
orm_adapter,0.5.0,MIT
os,0.9.6,MIT
os-browserify,0.3.0,MIT
os-homedir,1.0.2,MIT
-os-locale,2.1.0,MIT
+os-locale,3.0.1,MIT
os-tmpdir,1.0.2,MIT
osenv,0.1.5,ISC
p-cancelable,0.4.1,MIT
+p-defer,1.0.0,MIT
p-finally,1.0.0,MIT
p-is-promise,1.1.0,MIT
p-limit,1.2.0,MIT
+p-limit,2.0.0,MIT
p-locate,2.0.0,MIT
+p-locate,3.0.0,MIT
p-timeout,2.0.1,MIT
p-try,1.0.0,MIT
+p-try,2.0.0,MIT
pako,1.0.6,(MIT AND Zlib)
parallel-transform,1.1.0,MIT
parse-asn1,5.1.0,ISC
@@ -757,6 +787,7 @@ path-dirname,1.0.2,MIT
path-exists,3.0.0,MIT
path-is-absolute,1.0.1,MIT
path-key,2.0.1,MIT
+path-parse,1.0.6,MIT
path-to-regexp,0.1.7,MIT
pbkdf2,3.0.14,MIT
peek,1.0.1,MIT
@@ -765,13 +796,13 @@ peek-mysql2,1.1.0,MIT
peek-pg,1.3.0,MIT
peek-rblineprof,0.2.0,MIT
peek-redis,1.2.0,MIT
-peek-sidekiq,1.0.3,MIT
pg,0.18.4,"BSD,ruby,GPL"
pify,3.0.0,MIT
pikaday,1.6.1,MIT
pinkie,2.0.4,MIT
pinkie-promise,2.0.1,MIT
pkg-dir,2.0.0,MIT
+pkg-dir,3.0.0,MIT
po_to_json,1.0.1,MIT
popper.js,1.14.3,MIT
posix-character-classes,0.1.1,MIT
@@ -786,7 +817,7 @@ postcss-value-parser,3.3.0,MIT
premailer,1.10.4,New BSD
premailer-rails,1.9.7,MIT
prepend-http,2.0.0,MIT
-prettier,1.12.1,MIT
+prettier,1.13.7,MIT
prismjs,1.6.0,MIT
private,0.1.8,MIT
process,0.11.10,MIT
@@ -794,15 +825,16 @@ process-nextick-args,1.0.7,MIT
process-nextick-args,2.0.0,MIT
prometheus-client-mmap,0.9.4,Apache 2.0
promise-inflight,1.0.1,ISC
-proxy-addr,2.0.3,MIT
+proxy-addr,2.0.4,MIT
prr,1.0.1,MIT
pseudomap,1.0.2,ISC
public-encrypt,4.0.0,MIT
-public_suffix,3.0.2,MIT
+public_suffix,3.0.3,MIT
pump,2.0.1,MIT
+pump,3.0.0,MIT
pumpify,1.4.0,MIT
punycode,1.3.2,MIT
-punycode,1.4.1,MIT
+punycode,2.1.1,MIT
pyu-ruby-sasl,0.0.3.3,MIT
qs,6.5.1,New BSD
query-string,5.1.1,MIT
@@ -813,7 +845,7 @@ rack-accept,0.4.5,MIT
rack-attack,4.4.1,MIT
rack-cors,1.0.2,MIT
rack-oauth2,1.2.3,MIT
-rack-protection,2.0.1,MIT
+rack-protection,2.0.3,MIT
rack-proxy,0.6.0,MIT
rack-test,0.6.3,MIT
rails,4.2.10,MIT
@@ -851,24 +883,27 @@ redis-namespace,1.6.0,MIT
redis-rack,2.0.4,MIT
redis-rails,5.0.2,MIT
redis-store,1.4.1,MIT
-regenerate,1.3.2,MIT
+regenerate,1.4.0,MIT
+regenerate-unicode-properties,7.0.0,MIT
regenerator-runtime,0.10.5,MIT
-regenerator-runtime,0.11.0,MIT
-regenerator-transform,0.10.1,BSD
+regenerator-runtime,0.11.1,MIT
+regenerator-transform,0.13.3,MIT
regex-not,1.0.2,MIT
regexp_parser,0.5.0,MIT
regexpu-core,1.0.0,MIT
-regexpu-core,2.0.0,MIT
+regexpu-core,4.2.0,MIT
regjsgen,0.2.0,MIT
+regjsgen,0.4.0,MIT
regjsparser,0.1.5,Simplified BSD
+regjsparser,0.3.0,Simplified BSD
remove-trailing-separator,1.1.0,ISC
repeat-element,1.1.2,MIT
repeat-string,1.6.1,MIT
-repeating,2.0.1,MIT
representable,3.0.4,MIT
request_store,1.3.1,MIT
require-directory,2.1.1,MIT
require-main-filename,1.0.1,ISC
+resolve,1.8.1,MIT
resolve-cwd,2.0.0,MIT
resolve-from,3.0.0,MIT
resolve-url,0.2.1,MIT
@@ -882,7 +917,7 @@ rimraf,2.6.2,ISC
rinku,2.0.0,ISC
ripemd160,2.0.1,MIT
rotp,2.1.2,MIT
-rouge,3.2.1,MIT
+rouge,3.3.0,MIT
rqrcode,0.7.0,MIT
rqrcode-rails3,0.1.7,MIT
ruby-enum,0.7.2,MIT
@@ -913,15 +948,16 @@ sass-rails,5.0.6,MIT
sawyer,0.8.1,MIT
sax,1.2.4,ISC
schema-utils,0.4.5,MIT
+schema-utils,1.0.0,MIT
seed-fu,2.3.7,MIT
select,1.1.2,MIT
select2,3.5.2-browserify,Apache*
select2-rails,3.5.9.3,MIT
-semver,5.5.0,ISC
-send,0.16.1,MIT
+semver,5.5.1,ISC
+send,0.16.2,MIT
sentry-raven,2.7.2,Apache 2.0
serialize-javascript,1.4.0,New BSD
-serve-static,1.13.1,MIT
+serve-static,1.13.2,MIT
set-blocking,2.0.0,ISC
set-getter,0.1.0,MIT
set-immediate-shim,1.0.1,MIT
@@ -936,13 +972,11 @@ sha.js,2.4.10,MIT
sha1,1.1.1,New BSD
shebang-command,1.2.0,MIT
shebang-regex,1.0.0,MIT
-sidekiq,5.1.3,LGPL
+sidekiq,5.2.1,LGPL
sidekiq-cron,0.6.0,MIT
-sidekiq-limit_fetch,3.4.0,MIT
signal-exit,3.0.2,ISC
signet,0.8.1,Apache 2.0
slack-notifier,1.5.1,MIT
-slash,1.0.0,MIT
smooshpack,0.0.48,LGPL
snapdragon,0.8.1,MIT
snapdragon-node,2.1.1,MIT
@@ -954,7 +988,6 @@ source-map,0.5.0,New BSD
source-map,0.5.7,New BSD
source-map,0.6.1,New BSD
source-map-resolve,0.5.1,MIT
-source-map-support,0.4.18,MIT
source-map-url,0.4.0,MIT
split-string,3.1.0,MIT
sprockets,3.7.2,MIT
@@ -963,12 +996,12 @@ sql.js,0.4.0,MIT
srcset,1.0.0,MIT
sshkey,1.9.0,MIT
ssri,5.2.4,ISC
+ssri,6.0.1,ISC
state_machines,0.5.0,MIT
state_machines-activemodel,0.5.1,MIT
state_machines-activerecord,0.5.1,MIT
static-extend,0.1.2,MIT
-statuses,1.3.1,MIT
-statuses,1.5.0,MIT
+statuses,1.4.0,MIT
stickyfilljs,2.0.5,MIT
stream-browserify,2.0.1,MIT
stream-each,1.2.2,MIT
@@ -984,12 +1017,12 @@ strip-ansi,3.0.1,MIT
strip-ansi,4.0.0,MIT
strip-eof,1.0.0,MIT
strip-json-comments,2.0.1,MIT
-style-loader,0.21.0,MIT
+style-loader,0.23.0,MIT
supports-color,2.0.0,MIT
-supports-color,5.4.0,MIT
+supports-color,5.5.0,MIT
svg4everybody,2.1.9,CC0-1.0
sys-filesystem,1.1.6,Artistic 2.0
-tapable,1.0.0,MIT
+tapable,1.1.0,MIT
tar,4.4.4,ISC
temple,0.8.0,MIT
text,1.3.1,MIT
@@ -1009,12 +1042,11 @@ timfel-krb5-auth,0.8.3,LGPL
tiny-emitter,2.0.2,MIT
tmp,0.0.33,MIT
to-arraybuffer,1.0.1,MIT
-to-fast-properties,1.0.3,MIT
+to-fast-properties,2.0.0,MIT
to-object-path,0.3.0,MIT
to-regex,3.0.2,MIT
to-regex-range,2.1.1,MIT
toml-rb,1.0.0,MIT
-traverse,0.6.6,MIT
trim-right,1.0.1,MIT
trollop,2.1.3,MIT
truncato,0.7.10,MIT
@@ -1029,10 +1061,13 @@ uber,0.1.0,MIT
uglifier,2.7.2,MIT
uglify-es,3.3.9,Simplified BSD
uglifyjs-webpack-plugin,1.2.5,MIT
-ultron,1.1.1,MIT
underscore,1.9.0,MIT
unf,0.1.4,BSD
unf_ext,0.0.7.5,MIT
+unicode-canonical-property-names-ecmascript,1.0.4,MIT
+unicode-match-property-ecmascript,1.0.4,MIT
+unicode-match-property-value-ecmascript,1.0.2,MIT
+unicode-property-aliases-ecmascript,1.0.4,MIT
unicorn,5.1.0,ruby
unicorn-worker-killer,0.4.4,ruby
union-value,1.0.0,MIT
@@ -1042,14 +1077,16 @@ unique-slug,2.0.0,ISC
unpipe,1.0.0,MIT
unset-value,1.0.0,MIT
upath,1.1.0,MIT
+uri-js,4.2.2,Simplified BSD
urix,0.1.0,MIT
url,0.11.0,MIT
-url-loader,1.0.1,MIT
+url-loader,1.1.1,MIT
url-parse-lax,3.0.0,MIT
url-to-options,1.0.1,MIT
use,2.0.2,MIT
util,0.10.3,MIT
util-deprecate,1.0.2,MIT
+util.promisify,1.0.0,MIT
utils-merge,1.0.1,MIT
v8-compile-cache,2.0.0,MIT
validates_hostname,1.0.6,MIT
@@ -1059,24 +1096,24 @@ virtus,1.0.5,MIT
visibilityjs,1.2.4,MIT
vm-browserify,0.0.4,MIT
vmstat,2.3.0,MIT
-vue,2.5.16,MIT
+vue,2.5.17,MIT
vue-functional-data-merge,2.0.6,MIT
vue-hot-reload-api,2.3.0,MIT
-vue-loader,15.2.4,MIT
+vue-loader,15.4.2,MIT
vue-resource,1.5.0,MIT
vue-router,3.0.1,MIT
vue-style-loader,4.1.0,MIT
-vue-template-compiler,2.5.16,MIT
+vue-template-compiler,2.5.17,MIT
vue-template-es2015-compiler,1.6.0,MIT
vue-virtual-scroll-list,1.2.5,MIT
vuex,3.0.1,MIT
warden,1.2.7,MIT
watchpack,1.5.0,MIT
-webpack,4.16.0,MIT
-webpack-bundle-analyzer,2.13.1,MIT
-webpack-cli,3.0.8,MIT
-webpack-rails,0.9.10,MIT
-webpack-sources,1.1.0,MIT
+webpack,4.19.1,MIT
+webpack-bundle-analyzer,3.0.2,MIT
+webpack-cli,3.1.0,MIT
+webpack-rails,0.9.11,MIT
+webpack-sources,1.3.0,MIT
webpack-stats-plugin,0.2.1,MIT
which,1.3.0,ISC
which-module,2.0.0,ISC
@@ -1086,14 +1123,14 @@ worker-farm,1.5.2,MIT
worker-loader,2.0.0,MIT
wrap-ansi,2.1.0,MIT
wrappy,1.0.2,ISC
-ws,4.0.0,MIT
+ws,6.0.0,MIT
xml-simple,1.1.5,ruby
xmlhttprequest,1.8.0,MIT
+xregexp,4.0.0,MIT
xtend,4.0.1,MIT
xterm,3.5.0,MIT
-y18n,3.2.1,ISC
y18n,4.0.0,ISC
yallist,2.1.2,ISC
yallist,3.0.2,ISC
-yargs,11.1.0,MIT
-yargs-parser,9.0.2,ISC
+yargs,12.0.2,MIT
+yargs-parser,10.1.0,ISC
diff --git a/yarn.lock b/yarn.lock
index 579b9408986..25ea8d7557c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -29,18 +29,7 @@
semver "^5.4.1"
source-map "^0.5.0"
-"@babel/generator@^7.0.0":
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0.tgz#1efd58bffa951dc846449e58ce3a1d7f02d393aa"
- integrity sha512-/BM2vupkpbZXq22l1ALO7MqXJZH2k8bKVv8Y+pABFnzWdztDB/ZLveP5At21vLz5c2YtSE6p7j2FZEsqafMz5Q==
- dependencies:
- "@babel/types" "^7.0.0"
- jsesc "^2.5.1"
- lodash "^4.17.10"
- source-map "^0.5.0"
- trim-right "^1.0.1"
-
-"@babel/generator@^7.1.2":
+"@babel/generator@^7.0.0", "@babel/generator@^7.1.2":
version "7.1.2"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.1.2.tgz#fde75c072575ce7abbd97322e8fef5bae67e4630"
integrity sha512-70A9HWLS/1RHk3Ck8tNHKxOoKQuSKocYgwDN85Pyl/RBduss6AKxUR7RIZ/lzduQMSYfWEM4DDBu6A+XGbkFig==
@@ -224,12 +213,7 @@
esutils "^2.0.2"
js-tokens "^4.0.0"
-"@babel/parser@^7.0.0", "@babel/parser@^7.1.0":
- version "7.1.0"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.1.0.tgz#a7cd42cb3c12aec52e24375189a47b39759b783e"
- integrity sha512-SmjnXCuPAlai75AFtzv+KCBcJ3sDDWbIn+WytKw1k+wAtEy6phqI2RqKh/zAnw53i1NR8su3Ep/UoqaKcimuLg==
-
-"@babel/parser@^7.1.2":
+"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.2":
version "7.1.2"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.1.2.tgz#85c5c47af6d244fab77bce6b9bd830e38c978409"
integrity sha512-x5HFsW+E/nQalGMw7hu+fvPqnBeBaIr0lWJ2SG0PPL2j+Pm9lYvCrsZJGIgauPIENx0v10INIyFjmSNUD/gSqQ==
@@ -599,7 +583,7 @@
js-levenshtein "^1.1.3"
semver "^5.3.0"
-"@babel/template@^7.0.0", "@babel/template@^7.1.2":
+"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.1.2":
version "7.1.2"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.1.2.tgz#090484a574fef5a2d2d7726a674eceda5c5b5644"
integrity sha512-SY1MmplssORfFiLDcOETrW7fCLl+PavlwMh92rrGcikQaRq4iWPVH0MpwPpY3etVMx6RnDjXtr6VZYr/IbP/Ag==
@@ -608,15 +592,6 @@
"@babel/parser" "^7.1.2"
"@babel/types" "^7.1.2"
-"@babel/template@^7.1.0":
- version "7.1.0"
- resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.1.0.tgz#58cc9572e1bfe24fe1537fdf99d839d53e517e22"
- integrity sha512-yZ948B/pJrwWGY6VxG6XRFsVTee3IQ7bihq9zFpM00Vydu6z5Xwg0C3J644kxI9WOTzd+62xcIsQ+AT1MGhqhA==
- dependencies:
- "@babel/code-frame" "^7.0.0"
- "@babel/parser" "^7.1.0"
- "@babel/types" "^7.0.0"
-
"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0":
version "7.1.0"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.1.0.tgz#503ec6669387efd182c3888c4eec07bcc45d91b2"
@@ -632,16 +607,7 @@
globals "^11.1.0"
lodash "^4.17.10"
-"@babel/types@^7.0.0":
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0.tgz#6e191793d3c854d19c6749989e3bc55f0e962118"
- integrity sha512-5tPDap4bGKTLPtci2SUl/B7Gv8RnuJFuQoWx26RJobS0fFrz4reUA3JnwIM+HVHEmWE0C1mzKhDtTp8NsWY02Q==
- dependencies:
- esutils "^2.0.2"
- lodash "^4.17.10"
- to-fast-properties "^2.0.0"
-
-"@babel/types@^7.1.2":
+"@babel/types@^7.0.0", "@babel/types@^7.1.2":
version "7.1.2"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.1.2.tgz#183e7952cf6691628afdc2e2b90d03240bac80c0"
integrity sha512-pb1I05sZEKiSlMUV9UReaqsCPUpgbHHHu2n1piRm7JkuBkm6QxcaIzKu6FMnMtCbih/cEYTR+RGYYC96Yk9HAg==
@@ -650,14 +616,10 @@
lodash "^4.17.10"
to-fast-properties "^2.0.0"
-"@gitlab-org/gitlab-svgs@^1.23.0":
- version "1.29.0"
- resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.29.0.tgz#03b65b513f9099bbda6ecf94d673a2952f8c6c70"
- integrity sha512-sCl6nP3ph36+8P3nrw9VanAR648rgOUEBlEoLPHkhKm79xB1dUkXGBtI0uaSJVgbJx40M1/Ts8HSdMv+PF3EIg==
-
-"@gitlab-org/gitlab-svgs@^1.29.0":
+"@gitlab-org/gitlab-svgs@^1.23.0", "@gitlab-org/gitlab-svgs@^1.29.0":
version "1.31.0"
resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.31.0.tgz#495b074669f93af40e34f9978ce887773dea470a"
+ integrity sha512-tJbf99XX/ddFkXCXxQr9a0GJD9rPVoW3qMbU14dkxwG4WBmPEoVg+e7sLvm9OWTD1uUqiVW3qWKp++SGhhcRlw==
"@gitlab-org/gitlab-ui@^1.8.0":
version "1.8.0"
@@ -1198,10 +1160,9 @@ babel-loader@^8.0.4:
babel-messages@^6.23.0:
version "6.23.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.23.0.tgz#875d6bc9be761c58a2ae3feee5dc4895d8c7f921"
+ resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e"
integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=
dependencies:
- babel-plugin-syntax-object-rest-spread "^6.8.0"
babel-runtime "^6.22.0"
babel-plugin-istanbul@^5.1.0:
@@ -1218,11 +1179,6 @@ babel-plugin-rewire@^1.2.0:
resolved "https://registry.yarnpkg.com/babel-plugin-rewire/-/babel-plugin-rewire-1.2.0.tgz#822562d72ed2c84e47c0f95ee232c920853e9d89"
integrity sha512-JBZxczHw3tScS+djy6JPLMjblchGhLI89ep15H3SyjujIzlxo5nr6Yjo7AXotdeVczeBmWs0tF8PgJWDdgzAkQ==
-babel-plugin-syntax-object-rest-spread@^6.8.0:
- version "6.13.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5"
- integrity sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=
-
babel-polyfill@6.23.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177466c3b03499d"
@@ -2684,12 +2640,12 @@ delegates@^1.0.0:
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
-depd@1.1.1, depd@~1.1.1:
+depd@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359"
integrity sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=
-depd@~1.1.2:
+depd@~1.1.1, depd@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
@@ -3056,6 +3012,13 @@ eslint-config-airbnb-base@^13.1.0:
object.assign "^4.1.0"
object.entries "^1.0.4"
+eslint-config-prettier@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-3.1.0.tgz#2c26d2cdcfa3a05f0642cd7e6e4ef3316cdabfa2"
+ integrity sha512-QYGfmzuc4q4J6XIhlp8vRKdI/fI0tQfQPy1dME3UOLprE+v4ssH/3W9LM2Q7h5qBcy5m0ehCrBDU2YF8q6OY8w==
+ dependencies:
+ get-stdin "^6.0.0"
+
eslint-import-resolver-node@^0.3.1:
version "0.3.2"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a"
@@ -3752,6 +3715,11 @@ get-caller-file@^1.0.1:
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
integrity sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=
+get-stdin@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
+ integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==
+
get-stream@3.0.0, get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
@@ -5219,14 +5187,7 @@ lz-string@^1.4.4:
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=
-make-dir@^1.0.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.2.0.tgz#6d6a49eead4aae296c53bbf3a1a008bd6c89469b"
- integrity sha512-aNUAa4UMg/UougV25bbrU4ZaaKNjJ/3/xnvg/twpmKROPdKZPZ9wGgI0opdZzO8q/zUFawoUuixuOv33eZ61Iw==
- dependencies:
- pify "^3.0.0"
-
-make-dir@^1.3.0:
+make-dir@^1.0.0, make-dir@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==
@@ -6322,16 +6283,16 @@ prepend-http@^2.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
-prettier@1.12.1:
- version "1.12.1"
- resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325"
- integrity sha1-wa0g6APndJ+vkFpAnSNn4Gu+cyU=
-
prettier@1.13.7:
version "1.13.7"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.13.7.tgz#850f3b8af784a49a6ea2d2eaa7ed1428a34b7281"
integrity sha512-KIU72UmYPGk4MujZGYMFwinB7lOf2LsDNGSOC8ufevsrPLISrZbNJlWstRi3m0AMuszbH+EFSQ/r6w56RSPK6w==
+prettier@1.14.3:
+ version "1.14.3"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.3.tgz#90238dd4c0684b7edce5f83b0fb7328e48bd0895"
+ integrity sha512-qZDVnCrnpsRJJq5nSsiHCE3BYMED2OtsI+cmzIzF1QIfqm5ALf8tEJcO27zV1gKNKRPdhjO0dNWnrzssDQ1tFg==
+
prismjs@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.6.0.tgz#118d95fb7a66dba2272e343b345f5236659db365"
@@ -6619,12 +6580,7 @@ regenerate-unicode-properties@^7.0.0:
dependencies:
regenerate "^1.4.0"
-regenerate@^1.2.1:
- version "1.3.2"
- resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
- integrity sha1-0ZQcZ7rUN+G+dkM63Vs4X5WxkmA=
-
-regenerate@^1.4.0:
+regenerate@^1.2.1, regenerate@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==
@@ -6789,20 +6745,13 @@ resolve@1.1.x:
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
-resolve@^1.3.2:
+resolve@^1.3.2, resolve@^1.4.0, resolve@^1.5.0, resolve@^1.6.0:
version "1.8.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26"
integrity sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==
dependencies:
path-parse "^1.0.5"
-resolve@^1.4.0, resolve@^1.5.0, resolve@^1.6.0:
- version "1.7.1"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.7.1.tgz#aadd656374fd298aee895bc026b8297418677fd3"
- integrity sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==
- dependencies:
- path-parse "^1.0.5"
-
responselike@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7"