summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2017-07-24 14:36:31 +0100
committerPhil Hughes <me@iamphill.com>2017-07-24 14:36:31 +0100
commit4d2be5bbec25d60a8d478bda5bc83159f2c845b1 (patch)
treec3c28a833ccd2a6628d928acee44e62ccfcc6fd8
parent1a2d180e3dfd8bdad94766a2e8b1195288c2b146 (diff)
parentd4c4dec80dc3abd39116440a3c291c19b27258e1 (diff)
downloadgitlab-ce-4d2be5bbec25d60a8d478bda5bc83159f2c845b1.tar.gz
Merge branch 'master' into sidebar-fly-out-sub-nav
-rw-r--r--.gitlab-ci.yml96
-rw-r--r--CHANGELOG.md235
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--Gemfile11
-rw-r--r--Gemfile.lock28
-rw-r--r--VERSION2
-rw-r--r--app/assets/javascripts/dispatcher.js13
-rw-r--r--app/assets/javascripts/due_date_select.js11
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_hint.js5
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_non_user.js5
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_user.js5
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown.js2
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js26
-rw-r--r--app/assets/javascripts/group_name.js15
-rw-r--r--app/assets/javascripts/issuable_bulk_update_sidebar.js13
-rw-r--r--app/assets/javascripts/layout_nav.js10
-rw-r--r--app/assets/javascripts/lib/utils/datefix.js8
-rw-r--r--app/assets/javascripts/new_sidebar.js23
-rw-r--r--app/assets/javascripts/project_select.js8
-rw-r--r--app/assets/javascripts/protected_branches/index.js9
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js53
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_create.js106
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_dropdown.js39
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_edit.js114
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_edit_list.js28
-rw-r--r--app/assets/javascripts/protected_branches/protected_branches_bundle.js5
-rw-r--r--app/assets/javascripts/protected_tags/index.js11
-rw-r--r--app/assets/stylesheets/framework/nav.scss8
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss10
-rw-r--r--app/assets/stylesheets/framework/typography.scss5
-rw-r--r--app/assets/stylesheets/new_nav.scss34
-rw-r--r--app/assets/stylesheets/new_sidebar.scss122
-rw-r--r--app/assets/stylesheets/pages/commits.scss6
-rw-r--r--app/assets/stylesheets/pages/profile.scss15
-rw-r--r--app/assets/stylesheets/pages/projects.scss3
-rw-r--r--app/controllers/admin/hook_logs_controller.rb4
-rw-r--r--app/controllers/admin/hooks_controller.rb15
-rw-r--r--app/controllers/concerns/hooks_execution.rb13
-rw-r--r--app/controllers/concerns/issuable_collections.rb8
-rw-r--r--app/controllers/dashboard/todos_controller.rb10
-rw-r--r--app/controllers/projects/badges_controller.rb6
-rw-r--r--app/controllers/projects/hook_logs_controller.rb4
-rw-r--r--app/controllers/projects/hooks_controller.rb12
-rw-r--r--app/controllers/projects/issues_controller.rb2
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb2
-rw-r--r--app/finders/issuable_finder.rb25
-rw-r--r--app/finders/issues_finder.rb12
-rw-r--r--app/helpers/breadcrumbs_helper.rb25
-rw-r--r--app/helpers/hooks_helper.rb17
-rw-r--r--app/helpers/issuables_helper.rb2
-rw-r--r--app/helpers/page_layout_helper.rb4
-rw-r--r--app/helpers/projects_helper.rb2
-rw-r--r--app/helpers/triggers_helper.rb2
-rw-r--r--app/models/ci/build.rb16
-rw-r--r--app/models/concerns/editable.rb4
-rw-r--r--app/models/hooks/project_hook.rb25
-rw-r--r--app/models/hooks/service_hook.rb1
-rw-r--r--app/models/hooks/system_hook.rb10
-rw-r--r--app/models/hooks/web_hook.rb14
-rw-r--r--app/models/project_services/gitlab_issue_tracker_service.rb2
-rw-r--r--app/models/project_services/issue_tracker_service.rb4
-rw-r--r--app/models/project_services/kubernetes_service.rb20
-rw-r--r--app/models/user.rb4
-rw-r--r--app/services/boards/issues/list_service.rb5
-rw-r--r--app/services/ci/create_pipeline_service.rb2
-rw-r--r--app/services/issuable_base_service.rb23
-rw-r--r--app/services/issues/close_service.rb2
-rw-r--r--app/services/issues/reopen_service.rb2
-rw-r--r--app/services/merge_requests/close_service.rb2
-rw-r--r--app/services/merge_requests/post_merge_service.rb2
-rw-r--r--app/services/merge_requests/reopen_service.rb2
-rw-r--r--app/services/metrics_service.rb2
-rw-r--r--app/services/system_hooks_service.rb2
-rw-r--r--app/services/test_hook_service.rb6
-rw-r--r--app/services/test_hooks/base_service.rb41
-rw-r--r--app/services/test_hooks/project_service.rb63
-rw-r--r--app/services/test_hooks/system_service.rb48
-rw-r--r--app/services/users/migrate_to_ghost_user_service.rb2
-rw-r--r--app/services/web_hook_service.rb11
-rw-r--r--app/services/wiki_pages/base_service.rb16
-rw-r--r--app/uploaders/gitlab_uploader.rb2
-rw-r--r--app/uploaders/personal_file_uploader.rb4
-rw-r--r--app/views/admin/application_settings/_form.html.haml15
-rw-r--r--app/views/admin/applications/edit.html.haml1
-rw-r--r--app/views/admin/applications/new.html.haml2
-rw-r--r--app/views/admin/broadcast_messages/edit.html.haml1
-rw-r--r--app/views/admin/broadcast_messages/index.html.haml1
-rw-r--r--app/views/admin/hooks/edit.html.haml2
-rw-r--r--app/views/admin/hooks/index.html.haml8
-rw-r--r--app/views/admin/runners/index.html.haml33
-rw-r--r--app/views/ci/runner/_how_to_setup_runner.html.haml16
-rw-r--r--app/views/dashboard/_groups_head.html.haml9
-rw-r--r--app/views/dashboard/_projects_head.html.haml10
-rw-r--r--app/views/dashboard/_snippets_head.html.haml9
-rw-r--r--app/views/dashboard/issues.html.haml9
-rw-r--r--app/views/dashboard/merge_requests.html.haml7
-rw-r--r--app/views/dashboard/milestones/index.html.haml6
-rw-r--r--app/views/dashboard/projects/index.html.haml1
-rw-r--r--app/views/dashboard/projects/starred.html.haml3
-rw-r--r--app/views/dashboard/todos/index.html.haml1
-rw-r--r--app/views/devise/shared/_omniauth_box.html.haml4
-rw-r--r--app/views/explore/groups/index.html.haml1
-rw-r--r--app/views/explore/projects/index.html.haml1
-rw-r--r--app/views/explore/projects/starred.html.haml1
-rw-r--r--app/views/explore/projects/trending.html.haml1
-rw-r--r--app/views/explore/snippets/index.html.haml1
-rw-r--r--app/views/groups/issues.html.haml11
-rw-r--r--app/views/groups/labels/index.html.haml10
-rw-r--r--app/views/groups/labels/new.html.haml1
-rw-r--r--app/views/groups/merge_requests.html.haml6
-rw-r--r--app/views/groups/milestones/index.html.haml9
-rw-r--r--app/views/groups/milestones/new.html.haml1
-rw-r--r--app/views/groups/new.html.haml3
-rw-r--r--app/views/groups/show.html.haml1
-rw-r--r--app/views/layouts/_page.html.haml5
-rw-r--r--app/views/layouts/header/_new.html.haml4
-rw-r--r--app/views/layouts/nav/_breadcrumbs.html.haml16
-rw-r--r--app/views/layouts/nav/_new_admin_sidebar.html.haml14
-rw-r--r--app/views/layouts/nav/_new_dashboard.html.haml2
-rw-r--r--app/views/layouts/nav/_new_group_sidebar.html.haml14
-rw-r--r--app/views/layouts/nav/_new_profile_sidebar.html.haml12
-rw-r--r--app/views/layouts/nav/_new_project_sidebar.html.haml14
-rw-r--r--app/views/profiles/preferences/show.html.haml6
-rw-r--r--app/views/profiles/show.html.haml1
-rw-r--r--app/views/profiles/two_factor_auths/show.html.haml6
-rw-r--r--app/views/projects/activity.html.haml3
-rw-r--r--app/views/projects/blob/edit.html.haml1
-rw-r--r--app/views/projects/blob/new.html.haml1
-rw-r--r--app/views/projects/blob/show.html.haml1
-rw-r--r--app/views/projects/boards/_show.html.haml3
-rw-r--r--app/views/projects/branches/index.html.haml10
-rw-r--r--app/views/projects/commits/show.html.haml4
-rw-r--r--app/views/projects/compare/index.html.haml2
-rw-r--r--app/views/projects/compare/show.html.haml3
-rw-r--r--app/views/projects/cycle_analytics/show.html.haml2
-rw-r--r--app/views/projects/environments/index.html.haml3
-rw-r--r--app/views/projects/environments/new.html.haml1
-rw-r--r--app/views/projects/graphs/charts.html.haml2
-rw-r--r--app/views/projects/graphs/show.html.haml4
-rw-r--r--app/views/projects/hooks/edit.html.haml3
-rw-r--r--app/views/projects/issues/new.html.haml1
-rw-r--r--app/views/projects/jobs/_header.html.haml2
-rw-r--r--app/views/projects/jobs/index.html.haml3
-rw-r--r--app/views/projects/labels/index.html.haml7
-rw-r--r--app/views/projects/labels/new.html.haml1
-rw-r--r--app/views/projects/merge_requests/creations/new.html.haml1
-rw-r--r--app/views/projects/milestones/index.html.haml5
-rw-r--r--app/views/projects/milestones/new.html.haml1
-rw-r--r--app/views/projects/network/show.html.haml3
-rw-r--r--app/views/projects/new.html.haml3
-rw-r--r--app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml2
-rw-r--r--app/views/projects/pipeline_schedules/index.html.haml11
-rw-r--r--app/views/projects/pipeline_schedules/new.html.haml5
-rw-r--r--app/views/projects/pipelines/charts.html.haml2
-rw-r--r--app/views/projects/pipelines/new.html.haml1
-rw-r--r--app/views/projects/project_members/index.html.haml3
-rw-r--r--app/views/projects/protected_branches/shared/_branches_list.html.haml4
-rw-r--r--app/views/projects/protected_branches/shared/_create_protected_branch.html.haml2
-rw-r--r--app/views/projects/protected_tags/shared/_tags_list.html.haml2
-rw-r--r--app/views/projects/runners/_specific_runners.html.haml19
-rw-r--r--app/views/projects/services/edit.html.haml5
-rw-r--r--app/views/projects/settings/ci_cd/show.html.haml4
-rw-r--r--app/views/projects/settings/integrations/_project_hook.html.haml14
-rw-r--r--app/views/projects/settings/integrations/show.html.haml2
-rw-r--r--app/views/projects/settings/repository/show.html.haml4
-rw-r--r--app/views/projects/show.html.haml1
-rw-r--r--app/views/projects/snippets/index.html.haml15
-rw-r--r--app/views/projects/tags/index.html.haml3
-rw-r--r--app/views/projects/tree/show.html.haml1
-rw-r--r--app/views/projects/wikis/show.html.haml1
-rw-r--r--app/views/search/show.html.haml2
-rw-r--r--app/views/shared/_logo_type.svg1
-rw-r--r--app/views/shared/_new_project_item_select.html.haml15
-rw-r--r--app/views/shared/empty_states/_issues.html.haml3
-rw-r--r--app/views/shared/issuable/_bulk_update_sidebar.html.haml4
-rw-r--r--app/views/shared/web_hooks/_test_button.html.haml12
-rw-r--r--app/views/snippets/new.html.haml2
-rw-r--r--app/views/snippets/show.html.haml1
-rw-r--r--app/views/users/show.html.haml2
-rw-r--r--changelogs/unreleased/12151-add-since-and-until-params-to-issuables.yml4
-rw-r--r--changelogs/unreleased/12200-add-french-translation.yml4
-rw-r--r--changelogs/unreleased/13336-multiple-broadcast-messages.yml4
-rw-r--r--changelogs/unreleased/18000-remember-me-for-oauth-login.yml4
-rw-r--r--changelogs/unreleased/20628-add-oauth-implicit-grant.yml4
-rw-r--r--changelogs/unreleased/20817-please-add-coordinator-url-to-admin-area-runner-page.yml4
-rw-r--r--changelogs/unreleased/23036-replace-dashboard-event-filters-spinach.yml4
-rw-r--r--changelogs/unreleased/23036-replace-dashboard-mr-spinach.yml4
-rw-r--r--changelogs/unreleased/23036-replace-dashboard-new-project-spinach.yml4
-rw-r--r--changelogs/unreleased/23036-replace-dashboard-spinach.yml4
-rw-r--r--changelogs/unreleased/23036-replace-dashboard-todo-spinach.yml4
-rw-r--r--changelogs/unreleased/23036-replace-snippets-spinach.yml4
-rw-r--r--changelogs/unreleased/23162-allow-creation-of-files-and-dirs-with-spaces-in-web-ui.yml4
-rw-r--r--changelogs/unreleased/23998-blame-age-map.yml4
-rw-r--r--changelogs/unreleased/2501-ce-port-update-welcome-page.yml4
-rw-r--r--changelogs/unreleased/25102-files-view-button.yml4
-rw-r--r--changelogs/unreleased/25103-mobile-members-page-user-avatar-is-misaligned.yml4
-rw-r--r--changelogs/unreleased/25164-disable-fork-on-project-limit.yml4
-rw-r--r--changelogs/unreleased/26125-match-username-on-search.yml5
-rw-r--r--changelogs/unreleased/26212-upload-user-avatar-trough-api.yml4
-rw-r--r--changelogs/unreleased/27070-rename-slash-commands-to-quick-actions.yml5
-rw-r--r--changelogs/unreleased/27586-center-dropdown.yml4
-rw-r--r--changelogs/unreleased/27645-html-email-brackets-bug.yml4
-rw-r--r--changelogs/unreleased/27697-make-arrow-icons-consistent-in-dropdown.yml4
-rw-r--r--changelogs/unreleased/28139-use-color-input-broadcast-messages.yml4
-rw-r--r--changelogs/unreleased/28717-support-additional-prometheus-metrics.yml4
-rw-r--r--changelogs/unreleased/29893-change-menu-locations.yml3
-rw-r--r--changelogs/unreleased/30213-project-transfer-move-rollback.yml4
-rw-r--r--changelogs/unreleased/30708-stop-using-deleted-at-to-filter-namespaces.yml4
-rw-r--r--changelogs/unreleased/30725-reset-user-limits-when-unchecking-external-user.yml4
-rw-r--r--changelogs/unreleased/31397-job-detail-real-time.yml4
-rw-r--r--changelogs/unreleased/31415-responsive-pipelines-table-2.yml4
-rw-r--r--changelogs/unreleased/31982-liberation-mono-linux.yml4
-rw-r--r--changelogs/unreleased/32048-shared-runners-admin-buttons-have-odd-spacing.yml4
-rw-r--r--changelogs/unreleased/32054-rails-should-use-timestamptz-database-type-for-postgresql.yml4
-rw-r--r--changelogs/unreleased/32301-filter-archive-project-on-param-present.yml4
-rw-r--r--changelogs/unreleased/32408-enable-disable-all-restricted-visibility-levels.yml4
-rw-r--r--changelogs/unreleased/32470-pag-links.yml4
-rw-r--r--changelogs/unreleased/32517-disable-hover-state.yml5
-rw-r--r--changelogs/unreleased/32815--Add-Custom-CI-Config-Path.yml4
-rw-r--r--changelogs/unreleased/32834-task-note-only.yml4
-rw-r--r--changelogs/unreleased/32838-admin-panel-spacing.yml4
-rw-r--r--changelogs/unreleased/33003-avatar-in-project-api.yml4
-rw-r--r--changelogs/unreleased/33082-use-update_pipeline_schedule-for-edit-and-take_ownership-in-pipelineschedulescontroller.yml4
-rw-r--r--changelogs/unreleased/33130-remove-group-modal.yml4
-rw-r--r--changelogs/unreleased/33132-change-icon-color.yml4
-rw-r--r--changelogs/unreleased/33208-singup-active-state-underline.yml4
-rw-r--r--changelogs/unreleased/33360-generate-kubeconfig.yml4
-rw-r--r--changelogs/unreleased/33381-display-issue-state-in-mr-widget-issue-links.yml4
-rw-r--r--changelogs/unreleased/33441-supplement_simplified_chinese_translation_of_i18n.yml4
-rw-r--r--changelogs/unreleased/33442-supplement_traditional_chinese_in_hong_kong_translation_of_i18n.yml4
-rw-r--r--changelogs/unreleased/33443-supplement_traditional_chinese_in_taiwan_translation_of_i18n.yml4
-rw-r--r--changelogs/unreleased/33445-document-delete-merge-branches-won-t-touch-protected-branches-docs.yml4
-rw-r--r--changelogs/unreleased/33461-display-user-id.yml4
-rw-r--r--changelogs/unreleased/33538-update-ci-dockerfile-now-that-chrome-headless-no-longer-in-beta.yml4
-rw-r--r--changelogs/unreleased/33561-supplement_bulgarian_translation_of_i18n.yml4
-rw-r--r--changelogs/unreleased/33657-user-projects-api.yml4
-rw-r--r--changelogs/unreleased/33672-supplement_portuguese_brazil_translation_of_i18n.yml4
-rw-r--r--changelogs/unreleased/33741-clarify-k8s-service-keys.yml5
-rw-r--r--changelogs/unreleased/33748-fix-n-plus-1-query-in-the-projects-api.yml4
-rw-r--r--changelogs/unreleased/33770-respect-blockquote-line-breaks.yml4
-rw-r--r--changelogs/unreleased/33772-readonly-gitlab-ci-cache.yml4
-rw-r--r--changelogs/unreleased/33837-remove-trash-on-registry-image.yml4
-rw-r--r--changelogs/unreleased/33846-no-runner-for-admin.yml4
-rw-r--r--changelogs/unreleased/33929-allow-to-enable-perf-bar-for-a-group.yml4
-rw-r--r--changelogs/unreleased/33949-deprecate-healthcheck-access-token.yml4
-rw-r--r--changelogs/unreleased/34052-store-mr-ref-fetched-in-database.yml4
-rw-r--r--changelogs/unreleased/34078-allow-to-enable-feature-flags-with-more-granularity.yml4
-rw-r--r--changelogs/unreleased/34116-milestone-filtering-on-group-issues.yml4
-rw-r--r--changelogs/unreleased/34141-allow-unauthenticated-access-to-the-users-api.yml4
-rw-r--r--changelogs/unreleased/34169-add-simplified-chinese-translations-of-commits-page.yml4
-rw-r--r--changelogs/unreleased/34171-add-traditional-chinese-in-hongkong-translations-of-commits-page.yml4
-rw-r--r--changelogs/unreleased/34172-add-traditional-chinese-in-taiwan-translations-of-commits-page.yml4
-rw-r--r--changelogs/unreleased/34175-add-esperanto-translations-of-commits-page.yml4
-rw-r--r--changelogs/unreleased/34176-add-bulgarian-translations-of-commits-page.yml4
-rw-r--r--changelogs/unreleased/34207-remove-bin-ci-upgrade-rb.yml4
-rw-r--r--changelogs/unreleased/34286-add-esperanto-translations-for-cycle-analytics-and-project-and-repository-pages.yml4
-rw-r--r--changelogs/unreleased/34289-drop-gfm-on-milestone-issuable-title.yml4
-rw-r--r--changelogs/unreleased/34309-drop-gfm-mr-ms.yml4
-rw-r--r--changelogs/unreleased/34403-issue-dropdown-persists-when-adding-issue-number-to-issue-description.yml4
-rw-r--r--changelogs/unreleased/34468-remove-extra-blank-on-admin-on-mobile.yml4
-rw-r--r--changelogs/unreleased/34531-remove-scroll.yml4
-rw-r--r--changelogs/unreleased/34544-add-italian-translation-of-cycle-analytics-page-&-project-page-&-repository-page.yml4
-rw-r--r--changelogs/unreleased/34549-extract-devise-mappings-into-helper.yml4
-rw-r--r--changelogs/unreleased/34563-usage-ping-github.yml4
-rw-r--r--changelogs/unreleased/34578-sidebar-padding.yml4
-rw-r--r--changelogs/unreleased/34590-fix-dashboard-labels-dropdown.yml4
-rw-r--r--changelogs/unreleased/34653-minor-ux-cleanups-for-performance-dashboard.yml4
-rw-r--r--changelogs/unreleased/34655-label-field-for-setting-a-chart-s-legend-text-is-not-working.yml4
-rw-r--r--changelogs/unreleased/34688-add-italian-translations-of-commits-page.yml4
-rw-r--r--changelogs/unreleased/34727-simplified-member-settings.yml4
-rw-r--r--changelogs/unreleased/34736-n-1-problem-on-milestone-page.yml4
-rw-r--r--changelogs/unreleased/34907-dont-show-pipeline-schedule-button-for-non-member.yml4
-rw-r--r--changelogs/unreleased/35035-sidebar-job-spaces.yml4
-rw-r--r--changelogs/unreleased/35087-mr-status-misaligned.yml4
-rw-r--r--changelogs/unreleased/35253-desc-protected-branches-for-non-member.yml4
-rw-r--r--changelogs/unreleased/35399-mini-graph-commits-box.yml4
-rw-r--r--changelogs/unreleased/5971-webhook-testing.yml4
-rw-r--r--changelogs/unreleased/adam-external-issue-references-spike.yml4
-rw-r--r--changelogs/unreleased/add-ci_variables-environment_scope-mysql.yml6
-rw-r--r--changelogs/unreleased/add-group-members-counting-and-plan-related-data-on-namespaces-api.yml4
-rw-r--r--changelogs/unreleased/bvl-rename-all-reserved-paths.yml4
-rw-r--r--changelogs/unreleased/commit-comments-limited-width.yml4
-rw-r--r--changelogs/unreleased/dashboard-milestone-tabs-loading-async.yml4
-rw-r--r--changelogs/unreleased/dm-blob-binaryness-change.yml5
-rw-r--r--changelogs/unreleased/dm-commit-row-browse-button.yml4
-rw-r--r--changelogs/unreleased/dm-diff-viewers.yml4
-rw-r--r--changelogs/unreleased/dm-empty-state-new-merge-request.yml5
-rw-r--r--changelogs/unreleased/dm-group-page-name.yml4
-rw-r--r--changelogs/unreleased/dm-mail-room-check-without-omnibus.yml4
-rw-r--r--changelogs/unreleased/dm-page-image-size.yml4
-rw-r--r--changelogs/unreleased/dm-readme-auxiliary-blob-viewer-without-wiki.yml4
-rw-r--r--changelogs/unreleased/dm-relative-submodule-url-trailing-whitespace.yml4
-rw-r--r--changelogs/unreleased/dm-target-branch-slash-command-desc.yml4
-rw-r--r--changelogs/unreleased/dm-unnecessary-top-padding.yml4
-rw-r--r--changelogs/unreleased/doc-gitaly-network.yml4
-rw-r--r--changelogs/unreleased/dt-printing-to-api.yml4
-rw-r--r--changelogs/unreleased/enable-polling-env.yml4
-rw-r--r--changelogs/unreleased/enable-webpack-code-splitting.yml5
-rw-r--r--changelogs/unreleased/feature-add-support-for-services-configuration.yml4
-rw-r--r--changelogs/unreleased/feature-gb-auto-retry-failed-ci-job.yml4
-rw-r--r--changelogs/unreleased/feature-intermediate-12729-group-secret-variables.yml4
-rw-r--r--changelogs/unreleased/feature-intermediate-32568-adding-variables-to-pipelines-schedules.yml4
-rw-r--r--changelogs/unreleased/feature-no-hypen-at-end-of-commit-ref-slug.yml4
-rw-r--r--changelogs/unreleased/feature-unify-email-layouts.yml4
-rw-r--r--changelogs/unreleased/feature-user-agent-details-api.yml4
-rw-r--r--changelogs/unreleased/feature-user-datetime-search-api-mysql.yml4
-rw-r--r--changelogs/unreleased/fix-33991.yml4
-rw-r--r--changelogs/unreleased/fix-assigned-issuable-lists.yml5
-rw-r--r--changelogs/unreleased/fix-exact-matches-of-username-and-email-on-top-of-the-user-search.yml4
-rw-r--r--changelogs/unreleased/fix-gb-fix-skipped-pipeline-with-allowed-to-fail-jobs.yml4
-rw-r--r--changelogs/unreleased/fix-gb-recover-from-renaming-project-with-container-images.yml4
-rw-r--r--changelogs/unreleased/fix-mrs-merged-immediately.yml4
-rw-r--r--changelogs/unreleased/fix-n-plus-one-in-url-builder.yml4
-rw-r--r--changelogs/unreleased/fix-overflow-slash-commands.yml4
-rw-r--r--changelogs/unreleased/fix-runner_online_check.yml4
-rw-r--r--changelogs/unreleased/fix-sidebar-showing-mobile-merge-requests.yml4
-rw-r--r--changelogs/unreleased/fix-sm-32790-pipeline_schedules-pages-throwing-error-500.yml4
-rw-r--r--changelogs/unreleased/fix-u2f-for-opera.yml4
-rw-r--r--changelogs/unreleased/fix_docs_commits_multiple_files.yml5
-rw-r--r--changelogs/unreleased/foreign-keys-for-project-model.yml4
-rw-r--r--changelogs/unreleased/gitaly-mandatory.yml4
-rw-r--r--changelogs/unreleased/hb-fix-abuse-report-on-stale-user-profile.yml4
-rw-r--r--changelogs/unreleased/hb-hide-archived-labels-from-group-issue-tracker.yml4
-rw-r--r--changelogs/unreleased/help-landing-page-customizations.yml4
-rw-r--r--changelogs/unreleased/issuable-sidebar-edit-button-field-focus.yml4
-rw-r--r--changelogs/unreleased/issue_20900.yml4
-rw-r--r--changelogs/unreleased/issue_30126_be.yml4
-rw-r--r--changelogs/unreleased/issue_33205.yml4
-rw-r--r--changelogs/unreleased/issueable-list-cleanup.yml4
-rw-r--r--changelogs/unreleased/karma-headless-chrome.yml4
-rw-r--r--changelogs/unreleased/monitoring-dashboard-fine-tuning-ux.yml4
-rw-r--r--changelogs/unreleased/monitoring-dashboard-fix-y-label.yml4
-rw-r--r--changelogs/unreleased/moved-submodules.yml4
-rw-r--r--changelogs/unreleased/mr-widget-memory-usage-tech-debt-fix.yml4
-rw-r--r--changelogs/unreleased/pat-alert-when-signin-disabled.yml4
-rw-r--r--changelogs/unreleased/pat-msg-on-auth-failure.yml4
-rw-r--r--changelogs/unreleased/polish-sidebar-toggle.yml4
-rw-r--r--changelogs/unreleased/project-readme-limited-width.yml4
-rw-r--r--changelogs/unreleased/replace_spinach_spec_profile_notifications-feature.yml4
-rw-r--r--changelogs/unreleased/replase_spinach_spec_create-feature.yml4
-rw-r--r--changelogs/unreleased/sh-add-mr-simple-mode.yml4
-rw-r--r--changelogs/unreleased/sh-allow-force-repo-create.yml4
-rw-r--r--changelogs/unreleased/sh-bump-oauth2-gem.yml4
-rw-r--r--changelogs/unreleased/sh-fix-project-destroy-in-namespace.yml4
-rw-r--r--changelogs/unreleased/sh-log-application-controller-exceptions-sentry.yml4
-rw-r--r--changelogs/unreleased/sh-optimize-mr-api-emojis-and-labels.yml4
-rw-r--r--changelogs/unreleased/sh-optimize-project-commit-api.yml4
-rw-r--r--changelogs/unreleased/speed-up-graphs.yml4
-rw-r--r--changelogs/unreleased/speed-up-issue-counting-for-a-project.yml5
-rw-r--r--changelogs/unreleased/speed-up-merge-request-all-commits-shas.yml4
-rw-r--r--changelogs/unreleased/stop-notification-recipient-service-modifying-participants.yml5
-rw-r--r--changelogs/unreleased/tc-follow-up-mia.yml4
-rw-r--r--changelogs/unreleased/tc-link-to-commit-on-help-page.yml4
-rw-r--r--changelogs/unreleased/tc-refactor-projects-finder-init-collection.yml4
-rw-r--r--changelogs/unreleased/workhorse-2-3-0.yml4
-rw-r--r--changelogs/unreleased/zj-commit-status-sortable-name.yml4
-rw-r--r--changelogs/unreleased/zj-faster-charts-page.yml4
-rw-r--r--changelogs/unreleased/zj-pipeline-badge-improvements.yml4
-rw-r--r--changelogs/unreleased/zj-review-apps-usage-data.yml4
-rw-r--r--changelogs/unreleased/zj-usage-ping-only-gl-pipelines.yml4
-rw-r--r--config/boot.rb5
-rw-r--r--config/environments/test.rb2
-rw-r--r--config/gitlab.yml.example6
-rw-r--r--config/initializers/7_prometheus_metrics.rb12
-rw-r--r--config/initializers/lograge.rb2
-rw-r--r--config/prometheus/additional_metrics.yml23
-rw-r--r--config/routes/project.rb6
-rw-r--r--config/routes/uploads.rb8
-rw-r--r--config/webpack.config.js2
-rw-r--r--db/migrate/20170710083355_clean_stage_id_reference_migration.rb18
-rw-r--r--db/migrate/20170717074009_move_system_upload_folder.rb60
-rw-r--r--db/post_migrate/20170406111121_clean_upload_symlinks.rb2
-rw-r--r--db/post_migrate/20170612071012_move_personal_snippets_files.rb91
-rw-r--r--db/post_migrate/20170613111224_clean_appearance_symlinks.rb52
-rw-r--r--db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb40
-rw-r--r--db/post_migrate/20170717150329_enqueue_migrate_system_uploads_to_new_folder.rb20
-rw-r--r--db/schema.rb2
-rw-r--r--doc/README.md1
-rw-r--r--doc/administration/auth/authentiq.md9
-rw-r--r--doc/administration/auth/ldap.md2
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md53
-rw-r--r--doc/api/projects.md8
-rw-r--r--doc/ci/yaml/README.md26
-rw-r--r--doc/development/background_migrations.md36
-rw-r--r--doc/development/gotchas.md29
-rw-r--r--doc/development/sidekiq_style_guide.md7
-rw-r--r--doc/development/testing.md1
-rw-r--r--doc/install/installation.md2
-rw-r--r--doc/integration/img/enable_trello_powerup.pngbin0 -> 17905 bytes
-rw-r--r--doc/integration/img/trello_card_with_gitlab_powerup.pngbin0 -> 18667 bytes
-rw-r--r--doc/integration/trello_power_up.md42
-rw-r--r--doc/user/project/integrations/img/webhook_testing.pngbin0 -> 191267 bytes
-rw-r--r--doc/user/project/integrations/kubernetes.md4
-rw-r--r--doc/user/project/integrations/prometheus_library/haproxy.md20
-rw-r--r--doc/user/project/integrations/prometheus_library/metrics.md1
-rw-r--r--doc/user/project/integrations/prometheus_library/nginx.md2
-rw-r--r--doc/user/project/integrations/webhooks.md9
-rw-r--r--features/dashboard/dashboard.feature70
-rw-r--r--features/dashboard/event_filters.feature58
-rw-r--r--features/project/badges/build.feature27
-rw-r--r--features/steps/dashboard/dashboard.rb83
-rw-r--r--features/steps/dashboard/event_filters.rb92
-rw-r--r--features/steps/groups.rb2
-rw-r--r--features/steps/profile/profile.rb2
-rw-r--r--features/steps/project/badges/build.rb32
-rw-r--r--features/steps/project/project.rb2
-rw-r--r--features/steps/shared/project.rb5
-rw-r--r--lib/api/access_requests.rb1
-rw-r--r--lib/api/award_emoji.rb1
-rw-r--r--lib/api/broadcast_messages.rb1
-rw-r--r--lib/api/deploy_keys.rb1
-rw-r--r--lib/api/environments.rb1
-rw-r--r--lib/api/groups.rb2
-rw-r--r--lib/api/issues.rb1
-rw-r--r--lib/api/labels.rb1
-rw-r--r--lib/api/members.rb1
-rw-r--r--lib/api/merge_requests.rb1
-rw-r--r--lib/api/notes.rb1
-rw-r--r--lib/api/project_hooks.rb1
-rw-r--r--lib/api/project_snippets.rb1
-rw-r--r--lib/api/projects.rb2
-rw-r--r--lib/api/runner.rb1
-rw-r--r--lib/api/runners.rb2
-rw-r--r--lib/api/snippets.rb1
-rw-r--r--lib/api/system_hooks.rb1
-rw-r--r--lib/api/triggers.rb1
-rw-r--r--lib/api/users.rb5
-rw-r--r--lib/api/variables.rb1
-rw-r--r--lib/ci/api/entities.rb2
-rw-r--r--lib/ci/gitlab_ci_yaml_processor.rb3
-rw-r--r--lib/gitlab/background_migration.rb2
-rw-r--r--lib/gitlab/background_migration/migrate_system_uploads_to_new_folder.rb26
-rw-r--r--lib/gitlab/badge/pipeline/metadata.rb (renamed from lib/gitlab/badge/build/metadata.rb)8
-rw-r--r--lib/gitlab/badge/pipeline/status.rb (renamed from lib/gitlab/badge/build/status.rb)10
-rw-r--r--lib/gitlab/badge/pipeline/template.rb (renamed from lib/gitlab/badge/build/template.rb)9
-rw-r--r--lib/gitlab/ci/config/entry/job.rb10
-rw-r--r--lib/gitlab/ci/trace/stream.rb7
-rw-r--r--lib/gitlab/data_builder/push.rb2
-rw-r--r--lib/gitlab/data_builder/wiki_page.rb22
-rw-r--r--lib/gitlab/ee_compat_check.rb13
-rw-r--r--lib/gitlab/git/commit.rb38
-rw-r--r--lib/gitlab/git/diff.rb2
-rw-r--r--lib/gitlab/git/diff_collection.rb74
-rw-r--r--lib/gitlab/git/tree.rb59
-rw-r--r--lib/gitlab/gitaly_client.rb4
-rw-r--r--lib/gitlab/gitaly_client/commit.rb14
-rw-r--r--lib/gitlab/gitaly_client/commit_service.rb51
-rw-r--r--lib/gitlab/gitaly_client/diff.rb2
-rw-r--r--lib/gitlab/i18n.rb11
-rw-r--r--lib/gitlab/metrics/prometheus.rb8
-rw-r--r--lib/gitlab/path_regex.rb1
-rw-r--r--lib/gitlab/regex.rb16
-rw-r--r--lib/gitlab/route_map.rb8
-rw-r--r--lib/gitlab/untrusted_regexp.rb73
-rw-r--r--lib/gitlab/usage_data.rb1
-rw-r--r--lib/gitlab/visibility_level.rb4
-rw-r--r--lib/tasks/gitlab/task_helpers.rb9
-rw-r--r--locale/bg/gitlab.po25
-rw-r--r--locale/eo/gitlab.po25
-rw-r--r--locale/es/gitlab.po5
-rw-r--r--locale/fr/gitlab.po1123
-rw-r--r--locale/gitlab.pot7
-rw-r--r--locale/ja/gitlab.po1204
-rw-r--r--locale/ja/gitlab.po.time_stamp0
-rw-r--r--locale/pt_BR/gitlab.po109
-rw-r--r--locale/ru/gitlab.po1233
-rw-r--r--locale/ru/gitlab.po.time_stamp0
-rw-r--r--locale/uk/gitlab.po1234
-rw-r--r--locale/uk/gitlab.po.time_stamp0
-rw-r--r--locale/zh_CN/gitlab.po26
-rw-r--r--locale/zh_HK/gitlab.po28
-rw-r--r--locale/zh_TW/gitlab.po33
-rw-r--r--qa/qa.rb9
-rw-r--r--qa/qa/page/dashboard/groups.rb (renamed from qa/qa/page/main/groups.rb)6
-rw-r--r--qa/qa/page/group/show.rb11
-rw-r--r--qa/qa/page/main/menu.rb7
-rw-r--r--qa/qa/scenario/gitlab/project/create.rb4
-rw-r--r--spec/controllers/dashboard/todos_controller_spec.rb30
-rw-r--r--spec/controllers/metrics_controller_spec.rb2
-rw-r--r--spec/controllers/profiles/accounts_controller_spec.rb2
-rw-r--r--spec/controllers/projects/badges_controller_spec.rb28
-rw-r--r--spec/controllers/projects/hooks_controller_spec.rb21
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb46
-rw-r--r--spec/controllers/projects/registry/tags_controller_spec.rb48
-rw-r--r--spec/controllers/sessions_controller_spec.rb8
-rw-r--r--spec/controllers/snippets_controller_spec.rb8
-rw-r--r--spec/controllers/uploads_controller_spec.rb4
-rw-r--r--spec/factories/ci/builds.rb6
-rw-r--r--spec/factories/project_hooks.rb1
-rw-r--r--spec/factories/uploads.rb2
-rw-r--r--spec/features/admin/admin_appearance_spec.rb4
-rw-r--r--spec/features/admin/admin_hooks_spec.rb6
-rw-r--r--spec/features/admin/admin_runners_spec.rb9
-rw-r--r--spec/features/dashboard/activity_spec.rb157
-rw-r--r--spec/features/dashboard/groups_list_spec.rb4
-rw-r--r--spec/features/dashboard/issues_filter_spec.rb (renamed from spec/features/dashboard_issues_spec.rb)60
-rw-r--r--spec/features/dashboard/issues_spec.rb2
-rw-r--r--spec/features/dashboard/merge_requests_spec.rb17
-rw-r--r--spec/features/dashboard/milestones_spec.rb (renamed from spec/features/dashboard_milestones_spec.rb)0
-rw-r--r--spec/features/dashboard/projects_spec.rb47
-rw-r--r--spec/features/issues/issue_detail_spec.rb43
-rw-r--r--spec/features/issues/user_uses_slash_commands_spec.rb36
-rw-r--r--spec/features/merge_requests/filter_merge_requests_spec.rb12
-rw-r--r--spec/features/merge_requests/user_uses_slash_commands_spec.rb8
-rw-r--r--spec/features/oauth_login_spec.rb4
-rw-r--r--spec/features/projects/badges/list_spec.rb12
-rw-r--r--spec/features/projects/badges/pipeline_badge_spec.rb70
-rw-r--r--spec/features/projects/branches_spec.rb9
-rw-r--r--spec/features/projects/issuable_counts_caching_spec.rb132
-rw-r--r--spec/features/projects/merge_request_button_spec.rb14
-rw-r--r--spec/features/projects/pipeline_schedules_spec.rb24
-rw-r--r--spec/features/projects/services/mattermost_slash_command_spec.rb2
-rw-r--r--spec/features/projects/services/slack_slash_command_spec.rb2
-rw-r--r--spec/features/projects/settings/integration_settings_spec.rb25
-rw-r--r--spec/features/snippets/user_creates_snippet_spec.rb6
-rw-r--r--spec/features/snippets/user_edits_snippet_spec.rb2
-rw-r--r--spec/features/uploads/user_uploads_avatar_to_group_spec.rb2
-rw-r--r--spec/features/uploads/user_uploads_avatar_to_profile_spec.rb2
-rw-r--r--spec/helpers/application_helper_spec.rb16
-rw-r--r--spec/helpers/auth_helper_spec.rb2
-rw-r--r--spec/helpers/emails_helper_spec.rb2
-rw-r--r--spec/helpers/groups_helper_spec.rb2
-rw-r--r--spec/helpers/hooks_helper_spec.rb20
-rw-r--r--spec/helpers/issuables_helper_spec.rb20
-rw-r--r--spec/helpers/page_layout_helper_spec.rb2
-rw-r--r--spec/javascripts/filtered_search/dropdown_user_spec.js9
-rw-r--r--spec/javascripts/fixtures/u2f.rb4
-rw-r--r--spec/javascripts/vue_shared/components/commit_spec.js4
-rw-r--r--spec/lib/ci/gitlab_ci_yaml_processor_spec.rb22
-rw-r--r--spec/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder_spec.rb19
-rw-r--r--spec/lib/gitlab/background_migration_spec.rb6
-rw-r--r--spec/lib/gitlab/badge/pipeline/metadata_spec.rb (renamed from spec/lib/gitlab/badge/build/metadata_spec.rb)6
-rw-r--r--spec/lib/gitlab/badge/pipeline/status_spec.rb (renamed from spec/lib/gitlab/badge/build/status_spec.rb)37
-rw-r--r--spec/lib/gitlab/badge/pipeline/template_spec.rb (renamed from spec/lib/gitlab/badge/build/template_spec.rb)18
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb39
-rw-r--r--spec/lib/gitlab/ci/trace/stream_spec.rb36
-rw-r--r--spec/lib/gitlab/data_builder/wiki_page_spec.rb21
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb116
-rw-r--r--spec/lib/gitlab/git/diff_collection_spec.rb2
-rw-r--r--spec/lib/gitlab/git/tree_spec.rb23
-rw-r--r--spec/lib/gitlab/gitaly_client/commit_service_spec.rb44
-rw-r--r--spec/lib/gitlab/regex_spec.rb11
-rw-r--r--spec/lib/gitlab/route_map_spec.rb13
-rw-r--r--spec/lib/gitlab/untrusted_regexp_spec.rb98
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb1
-rw-r--r--spec/migrations/clean_appearance_symlinks_spec.rb46
-rw-r--r--spec/migrations/clean_stage_id_reference_migration_spec.rb34
-rw-r--r--spec/migrations/cleanup_move_system_upload_folder_symlink_spec.rb35
-rw-r--r--spec/migrations/move_personal_snippets_files_spec.rb180
-rw-r--r--spec/migrations/move_system_upload_folder_spec.rb62
-rw-r--r--spec/models/ci/build_spec.rb74
-rw-r--r--spec/models/group_spec.rb2
-rw-r--r--spec/models/hooks/project_hook_spec.rb6
-rw-r--r--spec/models/hooks/service_hook_spec.rb4
-rw-r--r--spec/models/hooks/system_hook_spec.rb3
-rw-r--r--spec/models/namespace_spec.rb2
-rw-r--r--spec/models/project_services/gitlab_issue_tracker_service_spec.rb2
-rw-r--r--spec/models/project_services/microsoft_teams_service_spec.rb2
-rw-r--r--spec/models/project_spec.rb14
-rw-r--r--spec/models/user_spec.rb2
-rw-r--r--spec/requests/api/projects_spec.rb2
-rw-r--r--spec/requests/ci/api/builds_spec.rb66
-rw-r--r--spec/requests/openid_connect_spec.rb2
-rw-r--r--spec/routing/project_routing_spec.rb22
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb16
-rw-r--r--spec/services/ci/process_pipeline_service_spec.rb29
-rw-r--r--spec/services/projects/participants_service_spec.rb4
-rw-r--r--spec/services/test_hook_service_spec.rb14
-rw-r--r--spec/services/test_hooks/project_service_spec.rb188
-rw-r--r--spec/services/test_hooks/system_service_spec.rb82
-rw-r--r--spec/services/users/migrate_to_ghost_user_service_spec.rb31
-rw-r--r--spec/services/web_hook_service_spec.rb6
-rw-r--r--spec/spec_helper.rb1
-rw-r--r--spec/support/devise_helpers.rb14
-rw-r--r--spec/support/login_helpers.rb6
-rw-r--r--spec/support/malicious_regexp_shared_examples.rb8
-rw-r--r--spec/support/services/migrate_to_ghost_user_service_shared_examples.rb21
-rw-r--r--spec/support/shared_examples/features/protected_branches_access_control_ce.rb4
-rw-r--r--spec/support/slack_mattermost_notifications_shared_examples.rb2
-rw-r--r--spec/support/sorting_helper.rb18
-rw-r--r--spec/tasks/gitlab/task_helpers_spec.rb15
-rw-r--r--spec/uploaders/attachment_uploader_spec.rb2
-rw-r--r--spec/uploaders/avatar_uploader_spec.rb2
-rw-r--r--spec/uploaders/file_mover_spec.rb14
-rw-r--r--spec/uploaders/personal_file_uploader_spec.rb4
586 files changed, 9742 insertions, 2209 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index fd3a9ce0986..adde3400107 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,10 +1,20 @@
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-phantomjs-2.1-node-7.1-postgresql-9.6"
-cache:
+.default-cache: &default-cache
key: "ruby-233-with-yarn"
paths:
- - vendor/ruby
- - .yarn-cache/
+ - vendor/ruby
+ - .yarn-cache/
+
+.push-cache: &push-cache
+ cache:
+ <<: *default-cache
+ policy: push
+
+.pull-cache: &pull-cache
+ cache:
+ <<: *default-cache
+ policy: pull
variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "1"
@@ -24,11 +34,11 @@ before_script:
- source scripts/prepare_build.sh
stages:
-- build
-- prepare
-- test
-- post-test
-- pages
+ - build
+ - prepare
+ - test
+ - post-test
+ - pages
# Predefined scopes
.dedicated-runner: &dedicated-runner
@@ -41,10 +51,6 @@ stages:
SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false"
KNAPSACK_S3_BUCKET: "gitlab-ce-cache"
- cache:
- key: "knapsack"
- paths:
- - knapsack/
artifacts:
expire_in: 31d
paths:
@@ -79,8 +85,9 @@ stages:
- /(^docs[\/-].*|.*-docs$)/
.rspec-knapsack: &rspec-knapsack
- stage: test
<<: *dedicated-runner
+ <<: *pull-cache
+ stage: test
script:
- JOB_NAME=( $CI_JOB_NAME )
- export CI_NODE_INDEX=${JOB_NAME[-2]}
@@ -110,8 +117,9 @@ stages:
<<: *except-docs
.spinach-knapsack: &spinach-knapsack
- stage: test
<<: *dedicated-runner
+ <<: *pull-cache
+ stage: test
script:
- JOB_NAME=( $CI_JOB_NAME )
- export CI_NODE_INDEX=${JOB_NAME[-2]}
@@ -157,9 +165,13 @@ build-package:
SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false"
stage: build
+ cache: {}
when: manual
script:
- scripts/trigger-build
+ only:
+ - //@gitlab-org/gitlab-ce
+ - //@gitlab-org/gitlab-ee
# Prepare and merge knapsack tests
knapsack:
@@ -167,6 +179,11 @@ knapsack:
<<: *dedicated-runner
<<: *except-docs
stage: prepare
+ cache:
+ key: knapsack
+ paths:
+ - knapsack/
+ policy: pull
script:
- mkdir -p knapsack/${CI_PROJECT_NAME}/
- wget -O $KNAPSACK_RSPEC_SUITE_REPORT_PATH http://${KNAPSACK_S3_BUCKET}.s3.amazonaws.com/$KNAPSACK_RSPEC_SUITE_REPORT_PATH || rm $KNAPSACK_RSPEC_SUITE_REPORT_PATH
@@ -179,6 +196,11 @@ update-knapsack:
<<: *dedicated-runner
<<: *only-canonical-masters
stage: post-test
+ cache:
+ key: knapsack
+ paths:
+ - knapsack/
+ policy: push
script:
- retry gem install fog-aws mime-types
- scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*.json
@@ -191,6 +213,8 @@ setup-test-env:
<<: *dedicated-runner
<<: *except-docs
stage: prepare
+ cache:
+ <<: *default-cache
script:
- node --version
- yarn install --pure-lockfile --cache-folder .yarn-cache
@@ -270,6 +294,7 @@ spinach-mysql 4 5: *spinach-knapsack-mysql
# Static analysis jobs
.ruby-static-analysis: &ruby-static-analysis
+ <<: *pull-cache
variables:
SIMPLECOV: "false"
SETUP_DB: "false"
@@ -278,6 +303,7 @@ spinach-mysql 4 5: *spinach-knapsack-mysql
<<: *ruby-static-analysis
<<: *dedicated-runner
<<: *except-docs
+ <<: *pull-cache
stage: test
script:
- bundle exec rake $CI_JOB_NAME
@@ -294,9 +320,9 @@ static-analysis:
# - Check validity of relative links
# - Make sure cURL examples in API docs use the full switches
docs lint:
+ <<: *dedicated-runner
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:nanoc-bootstrap-ruby-2.4-alpine"
stage: test
- <<: *dedicated-runner
cache: {}
dependencies: []
before_script: []
@@ -339,9 +365,10 @@ ee_compat_check:
# DB migration, rollback, and seed jobs
.db-migrate-reset: &db-migrate-reset
- stage: test
<<: *dedicated-runner
<<: *except-docs
+ <<: *pull-cache
+ stage: test
script:
- bundle exec rake db:migrate:reset
@@ -354,11 +381,12 @@ db:migrate:reset-mysql:
<<: *use-mysql
.migration-paths: &migration-paths
- stage: test
<<: *dedicated-runner
+ <<: *only-canonical-masters
+ <<: *pull-cache
+ stage: test
variables:
SETUP_DB: "false"
- <<: *only-canonical-masters
script:
- git fetch origin v8.14.10
- git checkout -f FETCH_HEAD
@@ -379,9 +407,10 @@ migration:path-mysql:
<<: *use-mysql
.db-rollback: &db-rollback
- stage: test
<<: *dedicated-runner
<<: *except-docs
+ <<: *pull-cache
+ stage: test
script:
- bundle exec rake db:rollback STEP=120
- bundle exec rake db:migrate
@@ -395,9 +424,10 @@ db:rollback-mysql:
<<: *use-mysql
.db-seed_fu: &db-seed_fu
- stage: test
<<: *dedicated-runner
<<: *except-docs
+ <<: *pull-cache
+ stage: test
variables:
SIZE: "1"
SETUP_DB: "false"
@@ -422,9 +452,10 @@ db:seed_fu-mysql:
# Frontend-related jobs
gitlab:assets:compile:
- stage: test
<<: *dedicated-runner
<<: *except-docs
+ <<: *pull-cache
+ stage: test
dependencies: []
variables:
NODE_ENV: "production"
@@ -442,14 +473,15 @@ gitlab:assets:compile:
name: webpack-report
expire_in: 31d
paths:
- - webpack-report/
+ - webpack-report/
karma:
- image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-chrome-59.0-node-7.1-postgresql-9.6"
- stage: test
<<: *use-pg
<<: *dedicated-runner
<<: *except-docs
+ <<: *pull-cache
+ image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-chrome-59.0-node-7.1-postgresql-9.6"
+ stage: test
variables:
BABEL_ENV: "coverage"
CHROME_LOG_FILE: "chrome_debug.log"
@@ -467,6 +499,7 @@ karma:
codeclimate:
<<: *except-docs
+ <<: *pull-cache
before_script: []
image: docker:latest
stage: test
@@ -482,10 +515,11 @@ codeclimate:
paths: [codeclimate.json]
coverage:
- stage: post-test
- services: []
<<: *dedicated-runner
<<: *except-docs
+ <<: *pull-cache
+ stage: post-test
+ services: []
variables:
SETUP_DB: "false"
USE_BUNDLE_INSTALL: "true"
@@ -502,7 +536,10 @@ coverage:
lint:javascript:report:
<<: *dedicated-runner
<<: *except-docs
+ <<: *pull-cache
stage: post-test
+ dependencies:
+ - setup-test-env
before_script: []
script:
- find app/ spec/ -name '*.js' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files
@@ -514,9 +551,10 @@ lint:javascript:report:
- eslint-report.html
pages:
+ <<: *dedicated-runner
+ <<: *pull-cache
before_script: []
stage: pages
- <<: *dedicated-runner
dependencies:
- coverage
- karma
@@ -540,6 +578,7 @@ pages:
# rubygems.org in the future.
cache gems:
<<: *dedicated-runner
+ <<: *pull-cache
only:
- tags
variables:
@@ -554,8 +593,9 @@ cache gems:
- master@gitlab-org/gitlab-ee
gitlab_git_test:
+ <<: *pull-cache
+ <<: *except-docs
variables:
SETUP_DB: "false"
script:
- spec/support/prepare-gitlab-git-test-for-commit --check-for-changes
- <<: *except-docs
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b6f955553e1..daf154eeb07 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,204 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 9.4.0 (2017-07-22)
+
+- Add blame view age mapping. !7198 (Jeff Stubler)
+- Add support for image and services configuration in .gitlab-ci.yml. !8578
+- Fix an email parsing bug where brackets would be inserted in emails from some Outlook clients. !9045 (jneen)
+- Use fa-chevron-down on dropdown arrows for consistency. !9659 (TM Lee)
+- Update the devise mail templates to match the design of the pipeline emails. !10483 (Alexis Reigel)
+- Handle renamed submodules in repository browser. !10798 (David Turner)
+- Display all current broadcast messages, not just the last one. !11113 (rickettm)
+- Fix CI/CD status in case there are only allowed to failed jobs in the pipeline. !11166
+- Omit trailing / leading hyphens in CI_COMMIT_REF_SLUG variable to make it usable as a hostname. !11218 (Stefan Hanreich)
+- Moved "Members in a project" menu entry and path locations. !11560
+- Additional Prometheus metrics support. !11712
+- Rename all reserved paths that could have been created. !11713
+- Move uploads from `uploads/system` to `uploads/-/system` to free up `system` as a group name. !11713
+- Fix offline runner detection. !11751 (Alessio Caiazza)
+- Use authorize_update_pipeline_schedule in PipelineSchedulesController. !11846
+- Rollback project repo move if there is an error in Projects::TransferService. !11877
+- Help landing page customizations. !11878 (Robin Bobbitt)
+- Fixes "sign in / Register" active state underline misalignment. !11890 (Frank Sierra)
+- Honor the "Remember me" parameter for OAuth-based login. !11963
+- Instruct user to use personal access token for Git over HTTP. !11986 (Robin Bobbitt)
+- Accept image for avatar in project API. !11988 (Ivan Chernov)
+- Supplement Simplified Chinese translation of Project Page & Repository Page. !11994 (Huang Tao)
+- Supplement Traditional Chinese in Hong Kong translation of Project Page & Repository Page. !11995 (Huang Tao)
+- Make the revision on the `/help` page clickable. !12016
+- Display issue state in issue links section of merge request widget. !12021
+- Enable support for webpack code-splitting by dynamically setting publicPath at runtime. !12032
+- Replace PhantomJS with headless Chrome for karma test suite. !12036
+- Prevent description change notes when toggling tasks. !12057 (Jared Deckard <jared.deckard@gmail.com>)
+- Update QA Dockerfile to lock Chrome browser version. !12071
+- Fix FIDO U2F for Opera browser. !12082 (Jakub Kramarz and Jonas Kalderstam)
+- Supplement Bulgarian translation of Project Page & Repository Page. !12083 (Lyubomir Vasilev)
+- Removes deleted_at and pending_delete occurrences in Project related queries. !12091
+- Provide hint to create a personal access token for Git over HTTP. !12105 (Robin Bobbitt)
+- Display own user id in account settings page. !12141 (Riccardo Padovani)
+- Accept image for avatar in user API. !12143 (Ivan Chernov)
+- Disable fork button on project limit. !12145 (Ivan Chernov)
+- Added "created_after" and "created_before" params to issuables. !12151 (Kyle Bishop @kybishop)
+- Supplement Portuguese Brazil translation of Project Page & Repository Page. !12156 (Huang Tao)
+- Add review apps to usage metrics. !12185
+- Adding French translations. !12200 (Erwan "Dremor" Georget)
+- Ensures default user limits when external user is unchecked. !12218
+- Provide KUBECONFIG from KubernetesService for runners. !12223
+- Filter archived project in API v3 only if param present. !12245 (Ivan Chernov)
+- Add explicit message when no runners on admin. !12266 (Takuya Noguchi)
+- Split pipelines as internal and external in the usage data. !12277
+- Fix API Scoping. !12300
+- Remove registry image delete button if user cant delete it. !12317 (Ivan Chernov)
+- Allow the feature flags to be enabled/disabled with more granularity. !12357
+- Allow to enable the performance bar per user or Feature group. !12362
+- Rename duplicated variables with the same key for projects. Add environment_scope column to variables and add unique constraint to make sure that no variables could be created with the same key within a project. !12363
+- Add variables to pipelines schedules. !12372
+- Add User#full_private_access? to check if user has access to all private groups & projects. !12373
+- Change milestone endpoint for groups. !12374 (Takuya Noguchi)
+- Improve performance of the pipeline charts page. !12378
+- Add option to run Gitaly on a remote server. !12381
+- #20628 Enable implicit grant in GitLab as OAuth Provider. !12384 (Mateusz Pytel)
+- Replace 'snippets/snippets.feature' spinach with rspec. !12385 (Alexander Randa @randaalex)
+- Add Simplified Chinese translations of Commits Page. !12405 (Huang Tao)
+- Add Traditional Chinese in HongKong translations of Commits Page. !12406 (Huang Tao)
+- Add Traditional Chinese in Taiwan translations of Commits Page. !12407 (Huang Tao)
+- Add Portuguese Brazil translations of Commits Page. !12408 (Huang Tao)
+- Add French translations of Commits Page. !12409 (Huang Tao)
+- Add Esperanto translations of Commits Page. !12410 (Huang Tao)
+- Add Bulgarian translations of Commits Page. !12411 (Huang Tao)
+- Remove bin/ci/upgrade.rb as not working all. !12414 (Takuya Noguchi)
+- Store merge request ref_fetched status in the database. !12424
+- Replace 'dashboard/merge_requests' spinach with rspec. !12440 (Alexander Randa (@randaalex))
+- Add Esperanto translations for Cycle Analytics, Project, and Repository pages. !12442 (Huang Tao)
+- Allow unauthenticated access to the /api/v4/users API. !12445
+- Drop GFM support for the title of Milestone/MergeRequest in template. !12451 (Takuya Noguchi)
+- Replace 'dashboard/todos' spinach with rspec. !12453 (Alexander Randa (@randaalex))
+- Cache open issue and merge request counts for project tabs to speed up project pages. !12457
+- Introduce cache policies for CI jobs. !12483
+- Improve support for external issue references. !12485
+- Fix errors caused by attempts to report already blocked or deleted users. !12502 (Horacio Bertorello)
+- Allow customize CI config path. !12509 (Keith Pope)
+- Supplement Traditional Chinese in Taiwan translation of Project Page & Repository Page. !12514 (Huang Tao)
+- Closes any open Autocomplete of the markdown editor when the form is closed. !12521
+- Inserts exact matches of name, username and email to the top of the search list. !12525
+- Use smaller min-width for dropdown-menu-nav only on mobile. !12528 (Takuya Noguchi)
+- Hide archived project labels from group issue tracker. !12547 (Horacio Bertorello)
+- Replace 'dashboard/new-project.feature' spinach with rspec. !12550 (Alexander Randa (@randaalex))
+- Remove group modal like remove project modal (requires typing + confirmation). !12569 (Diego Souza)
+- Add Italian translation of Cycle Analytics Page & Project Page & Repository Page. !12578 (Huang Tao)
+- Add Group secret variables. !12582
+- Update jobs page output to have a scrollable page. !12587
+- Add user projects API. !12596 (Ivan Chernov)
+- Allow creation of files and directories with spaces through Web UI. !12608
+- Improve members view on mobile. !12619
+- Fixed the chart legend not being set correctly. !12628
+- Add Italian translations of Commits Page. !12645 (Huang Tao)
+- Allow admins to disable all restricted visibility levels. !12649
+- Allow admins to retrieve user agent details for an issue or snippet. !12655
+- Update welcome page UX for new users. !12662
+- N+1 problems on milestone page. !12670 (Takuya Noguchi)
+- Upgrade GitLab Workhorse to v2.3.0. !12676
+- Remove option to disable Gitaly. !12677
+- Improve the performance of the project list API. !12679
+- Add creation time filters to user search API for admins. !12682
+- Add Japanese translations for Cycle Analytics & Project pages & Repository pages & Commits pages & Pipeline Charts. !12693 (Huang Tao)
+- Undo adding the /reassign quick action. !12701
+- Fix dashboard labels dropdown. !12708
+- Username and password are no longer stripped from import url on mirror update. !12725
+- Add Russian translations for Cycle Analytics & Project pages & Repository pages & Commits pages & Pipeline Charts. !12743 (Huang Tao)
+- Add Ukrainian translations for Cycle Analytics & Project pages & Repository pages & Commits pages & Pipeline Charts. !12744 (Huang Tao)
+- Prevent bad data being added to application settings when Redis is unavailable. !12750
+- Do not show pipeline schedule button for non-member. !12757 (Takuya Noguchi)
+- Return `is_admin` attribute in the GET /user endpoint for admins. !12811
+- Recover from renaming project that has container images. !12840
+- Exact matches of username and email are now on top of the user search. !12868
+- Use Ghost user for last_edited_by and merge_user when original user is deleted. !12933
+- Fix docker tag reference routing constraints. !12961
+- Optimize creation of commit API by using Repository#commit instead of Repository#commits.
+- Speed up used languages calculation on charts page.
+- Make loading new merge requests (those created after the 9.4 upgrade) faster.
+- Ensure participants for issues, merge requests, etc. are calculated correctly when sending notifications.
+- Handle nameless legacy jobs.
+- Bump Faraday and dependent OAuth2 gem version to support no_proxy variable.
+- Renders 404 if given project is not readable by the user on Todos dashboard.
+- Render CI statuses with warnings in orange.
+- Document the Delete Merged Branches functionality.
+- Add wells to admin dashboard overview to fix spacing problems.
+- Removes hover style for nodes that are either links or buttons in the pipeline graph.
+- more visual contrast in pagination widget.
+- Deprecate Healthcheck Access Token in favor of IP whitelist.
+- Drop GFM support for issuable title on milestone for consistency and performance. (Takuya Noguchi)
+- fix left & right padding on sidebar.
+- Cleanup minor UX issues in the performance dashboard.
+- Remove two columned layout from project member settings.
+- Make font size of contextual sub menu items 14px.
+- Fix vertical space in job details sidebar.
+- Fix alignment of controls in mr issuable list.
+- Add wip message to new navigation preference section.
+- Add group members counting and plan related data on namespaces API.
+- Fix spacing on runner buttons.
+- Remove uploads/appearance symlink. A leftover from a previous migration.
+- Change order of monospace fonts to fix bug on some linux distros.
+- Limit commit & snippets comments width.
+- Fixed dashboard milestone tabs not loading.
+- Detect if file that appears to be text in the first 1024 bytes is actually binary afer loading all data.
+- Fix inconsistent display of the "Browse files" button in the commit list.
+- Implement diff viewers.
+- Fix 'New merge request' button for users who don't have push access to canonical project.
+- Fix issues with non-UTF8 filenames by always fixing the encoding of tree and blob paths.
+- Show group name instead of path on group page.
+- Don't check if MailRoom is running on Omnibus.
+- Limit OpenGraph image size to 64x64.
+- Don't show auxiliary blob viewer for README when there is no wiki.
+- Strip trailing whitespace in relative submodule URL.
+- Update /target_branch slash command description to be more consistent.
+- Remove unnecessary top padding on group MR index.
+- Added printing_merge_requst_link_enabled to the API. (David Turner <dturner@twosigma.com>)
+- Re-enable realtime for environments table.
+- Create responsive mobile view for pipelines table.
+- Adds realtime feature to job show view header and sidebar info. Updates UX.
+- Use color inputs for broadcast messages.
+- Center dropdown for mini graph.
+- Users can subscribe to group labels on the group labels page.
+- Add issuable-list class to shared mr/issue lists to fix new responsive layout design.
+- Rename "Slash commands" to "Quick actions" and deprecate "chat commands" in favor of "slash commands".
+- Don't mark empty MRs as merged on push to the target branch.
+- Improve issue rendering performance with lots of notes from other users.
+- Fixed overflow on mobile screens for the slash commands.
+- Fix an infinite loop when handling user-supplied regular expressions.
+- Fixed sidebar not collapsing on merge requests in mobile screens.
+- Speed up project removals by adding foreign keys with cascading deletes to various tables.
+- Fix mobile view of files view buttons.
+- Fixed dropdown filter input not focusing after transition.
+- Fixed GFM references not being included when updating issues inline.
+- Remove issues/merge requests drag n drop and sorting from milestone view.
+- Add native group milestones.
+- Fix API bug accepting wrong parameter to create merge request.
+- Clean up UI of issuable lists and make more responsive.
+- Improve the overall UX for the new monitoring dashboard.
+- Fixed the y_label not setting correctly for each graph on the monitoring dashboard.
+- Changed utilities imports from ~ to relative paths.
+- Remove unused space in sidebar todo toggle when not signed in.
+- Limit the width of the projects README text.
+- Add a simple mode to merge request API.
+- Make Project#ensure_repository force create a repo.
+- Use uploads/system directory for personal snippets.
+- Defer project destroys within a namespace in Groups::DestroyService#async_execute.
+- Log rescued exceptions to Sentry.
+- Remove remaining N+1 queries in merge requests API with emojis and labels.
+
+## 9.3.9 (2017-07-20)
+
+- Fix an infinite loop when handling user-supplied regular expressions.
+
+## 9.3.8 (2017-07-19)
+
+- Improve support for external issue references. !12485
+- Renders 404 if given project is not readable by the user on Todos dashboard.
+- Use uploads/system directory for personal snippets.
+- Remove uploads/appearance symlink. A leftover from a previous migration.
+
## 9.3.7 (2017-07-18)
- Prevent bad data being added to application settings when Redis is unavailable. !12750
@@ -263,6 +461,17 @@ entry.
- Remove foreigh key on ci_trigger_schedules only if it exists.
- Allow translation of Pipeline Schedules.
+## 9.2.9 (2017-07-20)
+
+- Fix an infinite loop when handling user-supplied regular expressions.
+
+## 9.2.8 (2017-07-19)
+
+- Improve support for external issue references. !12485
+- Renders 404 if given project is not readable by the user on Todos dashboard.
+- Fix incorrect project authorizations.
+- Remove uploads/appearance symlink. A leftover from a previous migration.
+
## 9.2.7 (2017-06-21)
- Reinstate is_admin flag in users api when authenticated user is an admin. !12211 (rickettm)
@@ -507,6 +716,17 @@ entry.
- Fix preemptive scroll bar on user activity calendar.
- Pipeline chat notifications convert seconds to minutes and hours.
+## 9.1.9 (2017-07-20)
+
+- Fix an infinite loop when handling user-supplied regular expressions.
+
+## 9.1.8 (2017-07-19)
+
+- Improve support for external issue references. !12485
+- Renders 404 if given project is not readable by the user on Todos dashboard.
+- Fix incorrect project authorizations.
+- Remove uploads/appearance symlink. A leftover from a previous migration.
+
## 9.1.7 (2017-06-07)
- No changes.
@@ -819,6 +1039,16 @@ entry.
- Only send chat notifications for the default branch.
- Don't fill in the default kubernetes namespace.
+## 9.0.12 (2017-07-20)
+
+- Fix an infinite loop when handling user-supplied regular expressions.
+
+## 9.0.11 (2017-07-19)
+
+- Renders 404 if given project is not readable by the user on Todos dashboard.
+- Fix incorrect project authorizations.
+- Remove uploads/appearance symlink. A leftover from a previous migration.
+
## 9.0.10 (2017-06-07)
- No changes.
@@ -1189,6 +1419,11 @@ entry.
- Change development tanuki favicon colors to match logo color order.
- API issues - support filtering by iids.
+## 8.17.7 (2017-07-19)
+
+- Renders 404 if given project is not readable by the user on Todos dashboard.
+- Fix incorrect project authorizations.
+
## 8.17.6 (2017-05-05)
- Enforce project features when searching blobs and wikis.
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 59dad104b0b..ca222b7cf39 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.21.2
+0.23.0
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index c7cb1311a64..8a30e8f94a3 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-5.3.1
+5.4.0
diff --git a/Gemfile b/Gemfile
index b9c37e2b3fa..1ee44680774 100644
--- a/Gemfile
+++ b/Gemfile
@@ -37,7 +37,7 @@ gem 'omniauth-saml', '~> 1.7.0'
gem 'omniauth-shibboleth', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth_crowd', '~> 2.2.0'
-gem 'omniauth-authentiq', '~> 0.3.0'
+gem 'omniauth-authentiq', '~> 0.3.1'
gem 'rack-oauth2', '~> 1.2.1'
gem 'jwt', '~> 1.5.6'
@@ -71,7 +71,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.4', require: false
gem 'github-linguist', '~> 4.7.0', require: 'linguist'
# API
-gem 'grape', '~> 0.19.0'
+gem 'grape', '~> 0.19.2'
gem 'grape-entity', '~> 0.6.0'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
@@ -163,6 +163,9 @@ gem 'rainbow', '~> 2.2'
# GitLab settings
gem 'settingslogic', '~> 2.0.9'
+# Linear-time regex library for untrusted regular expressions
+gem 're2', '~> 1.0.0'
+
# Misc
gem 'version_sorter', '~> 2.1.0'
@@ -281,7 +284,7 @@ group :metrics do
gem 'influxdb', '~> 0.2', require: false
# Prometheus
- gem 'prometheus-client-mmap', '~>0.7.0.beta5'
+ gem 'prometheus-client-mmap', '~>0.7.0.beta9'
gem 'raindrops', '~> 0.18'
end
@@ -383,7 +386,7 @@ gem 'vmstat', '~> 2.3.0'
gem 'sys-filesystem', '~> 1.1.6'
# Gitaly GRPC client
-gem 'gitaly', '~> 0.14.0'
+gem 'gitaly', '~> 0.18.0'
gem 'toml-rb', '~> 0.3.15', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index 63eb0712ef3..b0b437ae342 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -269,7 +269,7 @@ GEM
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gherkin-ruby (0.3.2)
- gitaly (0.14.0)
+ gitaly (0.18.0)
google-protobuf (~> 3.1)
grpc (~> 1.0)
github-linguist (4.7.6)
@@ -332,13 +332,13 @@ GEM
multi_json (~> 1.11)
os (~> 0.9)
signet (~> 0.7)
- grape (0.19.1)
+ grape (0.19.2)
activesupport
builder
hashie (>= 2.1.0)
multi_json (>= 1.3.2)
multi_xml (>= 0.5.2)
- mustermann-grape (~> 0.4.0)
+ mustermann-grape (~> 1.0.0)
rack (>= 1.3.0)
rack-accept
virtus (>= 1.0.0)
@@ -463,10 +463,9 @@ GEM
multi_json (1.12.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
- mustermann (0.4.0)
- tool (~> 0.2)
- mustermann-grape (0.4.0)
- mustermann (= 0.4.0)
+ mustermann (1.0.0)
+ mustermann-grape (1.0.0)
+ mustermann (~> 1.0.0)
mysql2 (0.4.5)
net-ldap (0.12.1)
netrc (0.11.0)
@@ -488,7 +487,7 @@ GEM
rack (>= 1.0, < 3)
omniauth-auth0 (1.4.1)
omniauth-oauth2 (~> 1.1)
- omniauth-authentiq (0.3.0)
+ omniauth-authentiq (0.3.1)
omniauth-oauth2 (~> 1.3, >= 1.3.1)
omniauth-azure-oauth2 (0.0.6)
jwt (~> 1.0)
@@ -592,7 +591,7 @@ GEM
premailer-rails (1.9.7)
actionmailer (>= 3, < 6)
premailer (~> 1.7, >= 1.7.9)
- prometheus-client-mmap (0.7.0.beta8)
+ prometheus-client-mmap (0.7.0.beta9)
mmap2 (~> 2.2, >= 2.2.7)
pry (0.10.4)
coderay (~> 1.1.0)
@@ -657,6 +656,7 @@ GEM
debugger-ruby_core_source (~> 1.3)
rdoc (4.2.2)
json (~> 1.4)
+ re2 (1.0.0)
recaptcha (3.0.0)
json
recursive-open-struct (1.0.0)
@@ -849,7 +849,6 @@ GEM
timfel-krb5-auth (0.8.3)
toml-rb (0.3.15)
citrus (~> 3.0, > 3.0)
- tool (0.2.3)
truncato (0.7.8)
htmlentities (~> 4.3.1)
nokogiri (~> 1.6.1)
@@ -971,7 +970,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0)
- gitaly (~> 0.14.0)
+ gitaly (~> 0.18.0)
github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.5.1)
@@ -980,7 +979,7 @@ DEPENDENCIES
gollum-rugged_adapter (~> 0.4.4)
gon (~> 6.1.0)
google-api-client (~> 0.8.6)
- grape (~> 0.19.0)
+ grape (~> 0.19.2)
grape-entity (~> 0.6.0)
haml_lint (~> 0.21.0)
hamlit (~> 2.6.1)
@@ -1015,7 +1014,7 @@ DEPENDENCIES
oj (~> 2.17.4)
omniauth (~> 1.4.2)
omniauth-auth0 (~> 1.4.1)
- omniauth-authentiq (~> 0.3.0)
+ omniauth-authentiq (~> 0.3.1)
omniauth-azure-oauth2 (~> 0.0.6)
omniauth-cas3 (~> 1.1.2)
omniauth-facebook (~> 4.0.0)
@@ -1042,7 +1041,7 @@ DEPENDENCIES
pg (~> 0.18.2)
poltergeist (~> 1.9.0)
premailer-rails (~> 1.9.7)
- prometheus-client-mmap (~> 0.7.0.beta5)
+ prometheus-client-mmap (~> 0.7.0.beta9)
pry-byebug (~> 3.4.1)
pry-rails (~> 0.3.4)
rack-attack (~> 4.4.1)
@@ -1056,6 +1055,7 @@ DEPENDENCIES
raindrops (~> 0.18)
rblineprof (~> 0.3.6)
rdoc (~> 4.2)
+ re2 (~> 1.0.0)
recaptcha (~> 3.0)
redcarpet (~> 3.4)
redis (~> 3.2)
diff --git a/VERSION b/VERSION
index be3d36737cc..027fe8dd2cf 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-9.4.0-pre
+9.5.0-pre
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index ae19592ecbe..ffe97c071ba 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -1,4 +1,5 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */
+/* global ProjectSelect */
/* global ShortcutsNavigation */
/* global IssuableIndex */
/* global ShortcutsIssuable */
@@ -40,7 +41,6 @@ import BlobLinePermalinkUpdater from './blob/blob_line_permalink_updater';
import Landing from './landing';
import BlobForkSuggestion from './blob/blob_fork_suggestion';
import UserCallout from './user_callout';
-import { ProtectedTagCreate, ProtectedTagEditList } from './protected_tags';
import ShortcutsWiki from './shortcuts_wiki';
import Pipelines from './pipelines';
import BlobViewer from './blob/viewer/index';
@@ -157,6 +157,9 @@ import PerformanceBar from './performance_bar';
shortcut_handler = new ShortcutsIssuable();
new ZenMode();
break;
+ case 'dashboard:milestones:index':
+ new ProjectSelect();
+ break;
case 'projects:milestones:show':
case 'groups:milestones:show':
case 'dashboard:milestones:show':
@@ -166,6 +169,7 @@ import PerformanceBar from './performance_bar';
case 'groups:issues':
case 'groups:merge_requests':
new UsersSelect();
+ new ProjectSelect();
break;
case 'dashboard:todos:index':
new Todos();
@@ -259,6 +263,7 @@ import PerformanceBar from './performance_bar';
break;
case 'dashboard:issues':
case 'dashboard:merge_requests':
+ new ProjectSelect();
new UsersSelect();
break;
case 'projects:commit:show':
@@ -390,12 +395,6 @@ import PerformanceBar from './performance_bar';
new Search();
break;
case 'projects:settings:repository:show':
- // Initialize Protected Branch Settings
- new gl.ProtectedBranchCreate();
- new gl.ProtectedBranchEditList();
- // Initialize Protected Tag Settings
- new ProtectedTagCreate();
- new ProtectedTagEditList();
// Initialize expandable settings panels
initSettingsPanels();
break;
diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js
index a8fc5b41fb4..2856c8e2862 100644
--- a/app/assets/javascripts/due_date_select.js
+++ b/app/assets/javascripts/due_date_select.js
@@ -2,6 +2,8 @@
/* global dateFormat */
/* global Pikaday */
+import DateFix from './lib/utils/datefix';
+
class DueDateSelect {
constructor({ $dropdown, $loading } = {}) {
const $dropdownParent = $dropdown.closest('.dropdown');
@@ -43,14 +45,13 @@ class DueDateSelect {
initDatePicker() {
const $dueDateInput = $(`input[name='${this.fieldName}']`);
-
+ const dateFix = DateFix.dashedFix($dueDateInput.val());
const calendar = new Pikaday({
field: $dueDateInput.get(0),
theme: 'gitlab-theme',
format: 'yyyy-mm-dd',
onSelect: (dateText) => {
const formattedDate = dateFormat(new Date(dateText), 'yyyy-mm-dd');
-
$dueDateInput.val(formattedDate);
if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
@@ -62,7 +63,7 @@ class DueDateSelect {
}
});
- calendar.setDate(new Date($dueDateInput.val()));
+ calendar.setDate(dateFix);
this.$datePicker.append(calendar.el);
this.$datePicker.data('pikaday', calendar);
}
@@ -168,6 +169,7 @@ class DueDateSelectors {
initMilestoneDatePicker() {
$('.datepicker').each(function() {
const $datePicker = $(this);
+ const dateFix = DateFix.dashedFix($datePicker.val());
const calendar = new Pikaday({
field: $datePicker.get(0),
theme: 'gitlab-theme animate-picker',
@@ -177,7 +179,8 @@ class DueDateSelectors {
$datePicker.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
}
});
- calendar.setDate(new Date($datePicker.val()));
+
+ calendar.setDate(dateFix);
$datePicker.data('pikaday', calendar);
});
diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js
index 5838b1bdbb7..a81389ab088 100644
--- a/app/assets/javascripts/filtered_search/dropdown_hint.js
+++ b/app/assets/javascripts/filtered_search/dropdown_hint.js
@@ -2,8 +2,9 @@ import Filter from '~/droplab/plugins/filter';
import './filtered_search_dropdown';
class DropdownHint extends gl.FilteredSearchDropdown {
- constructor(droplab, dropdown, input, tokenKeys, filter) {
- super(droplab, dropdown, input, filter);
+ constructor(options = {}) {
+ const { input, tokenKeys } = options;
+ super(options);
this.config = {
Filter: {
template: 'hint',
diff --git a/app/assets/javascripts/filtered_search/dropdown_non_user.js b/app/assets/javascripts/filtered_search/dropdown_non_user.js
index 34a9e34070c..2615d626c4c 100644
--- a/app/assets/javascripts/filtered_search/dropdown_non_user.js
+++ b/app/assets/javascripts/filtered_search/dropdown_non_user.js
@@ -5,8 +5,9 @@ import Filter from '~/droplab/plugins/filter';
import './filtered_search_dropdown';
class DropdownNonUser extends gl.FilteredSearchDropdown {
- constructor(droplab, dropdown, input, tokenKeys, filter, endpoint, symbol) {
- super(droplab, dropdown, input, filter);
+ constructor(options = {}) {
+ const { input, endpoint, symbol } = options;
+ super(options);
this.symbol = symbol;
this.config = {
Ajax: {
diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js b/app/assets/javascripts/filtered_search/dropdown_user.js
index 19fed771197..7246ccbb281 100644
--- a/app/assets/javascripts/filtered_search/dropdown_user.js
+++ b/app/assets/javascripts/filtered_search/dropdown_user.js
@@ -5,8 +5,9 @@ import './filtered_search_dropdown';
import { addClassIfElementExists } from '../lib/utils/dom_utils';
class DropdownUser extends gl.FilteredSearchDropdown {
- constructor(droplab, dropdown, input, tokenKeys, filter) {
- super(droplab, dropdown, input, filter);
+ constructor(options = {}) {
+ const { tokenKeys } = options;
+ super(options);
this.config = {
AjaxFilter: {
endpoint: `${gon.relative_url_root || ''}/autocomplete/users.json`,
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
index 4209ca0d6e2..9e9a9ef74be 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
@@ -1,7 +1,7 @@
const DATA_DROPDOWN_TRIGGER = 'data-dropdown-trigger';
class FilteredSearchDropdown {
- constructor(droplab, dropdown, input, filter) {
+ constructor({ droplab, dropdown, input, filter }) {
this.droplab = droplab;
this.hookId = input && input.id;
this.input = input;
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
index 6bc6bc43f51..61cef435209 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
@@ -42,13 +42,19 @@ class FilteredSearchDropdownManager {
milestone: {
reference: null,
gl: 'DropdownNonUser',
- extraArguments: [`${this.baseEndpoint}/milestones.json`, '%'],
+ extraArguments: {
+ endpoint: `${this.baseEndpoint}/milestones.json`,
+ symbol: '%',
+ },
element: this.container.querySelector('#js-dropdown-milestone'),
},
label: {
reference: null,
gl: 'DropdownNonUser',
- extraArguments: [`${this.baseEndpoint}/labels.json`, '~'],
+ extraArguments: {
+ endpoint: `${this.baseEndpoint}/labels.json`,
+ symbol: '~',
+ },
element: this.container.querySelector('#js-dropdown-label'),
},
hint: {
@@ -97,13 +103,19 @@ class FilteredSearchDropdownManager {
let forceShowList = false;
if (!mappingKey.reference) {
- const dl = this.droplab;
- const defaultArguments =
- [null, dl, element, this.filteredSearchInput, this.filteredSearchTokenKeys, key];
- const glArguments = defaultArguments.concat(mappingKey.extraArguments || []);
+ const defaultArguments = {
+ droplab: this.droplab,
+ dropdown: element,
+ input: this.filteredSearchInput,
+ tokenKeys: this.filteredSearchTokenKeys,
+ filter: key,
+ };
+ const extraArguments = mappingKey.extraArguments || {};
+ const glArguments = Object.assign({}, defaultArguments, extraArguments);
// Passing glArguments to `new gl[glClass](<arguments>)`
- mappingKey.reference = new (Function.prototype.bind.apply(gl[glClass], glArguments))();
+ mappingKey.reference =
+ new (Function.prototype.bind.apply(gl[glClass], [null, glArguments]))();
}
if (firstLoad) {
diff --git a/app/assets/javascripts/group_name.js b/app/assets/javascripts/group_name.js
index 37c6765d942..3e483b69fd2 100644
--- a/app/assets/javascripts/group_name.js
+++ b/app/assets/javascripts/group_name.js
@@ -5,12 +5,15 @@ export default class GroupName {
constructor() {
this.titleContainer = document.querySelector('.js-title-container');
this.title = this.titleContainer.querySelector('.title');
- this.titleWidth = this.title.offsetWidth;
- this.groupTitle = this.titleContainer.querySelector('.group-title');
- this.groups = this.titleContainer.querySelectorAll('.group-path');
- this.toggle = null;
- this.isHidden = false;
- this.init();
+
+ if (this.title) {
+ this.titleWidth = this.title.offsetWidth;
+ this.groupTitle = this.titleContainer.querySelector('.group-title');
+ this.groups = this.titleContainer.querySelectorAll('.group-path');
+ this.toggle = null;
+ this.isHidden = false;
+ this.init();
+ }
}
init() {
diff --git a/app/assets/javascripts/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable_bulk_update_sidebar.js
index 4f376599ba9..d314f3c4d43 100644
--- a/app/assets/javascripts/issuable_bulk_update_sidebar.js
+++ b/app/assets/javascripts/issuable_bulk_update_sidebar.js
@@ -86,10 +86,23 @@ export default class IssuableBulkUpdateSidebar {
this.toggleCheckboxDisplay(enable);
if (enable) {
+ this.initAffix();
SidebarHeightManager.init();
}
}
+ initAffix() {
+ if (!this.$sidebar.hasClass('affix-top')) {
+ const offsetTop = $('.scrolling-tabs-container').outerHeight() + $('.sub-nav-scroll').outerHeight();
+
+ this.$sidebar.affix({
+ offset: {
+ top: offsetTop,
+ },
+ });
+ }
+ }
+
updateSelectedIssuableIds() {
this.$issuableIdsInput.val(IssuableBulkUpdateSidebar.getCheckedIssueIds());
}
diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js
index e0363e091a7..5c1ba416a03 100644
--- a/app/assets/javascripts/layout_nav.js
+++ b/app/assets/javascripts/layout_nav.js
@@ -1,6 +1,7 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, no-unused-vars, one-var, one-var-declaration-per-line, vars-on-top, max-len */
-import Cookies from 'js-cookie';
import _ from 'underscore';
+import Cookies from 'js-cookie';
+import NewNavSidebar from './new_sidebar';
import initFlyOutNav from './fly_out_nav';
(function() {
@@ -55,10 +56,13 @@ import initFlyOutNav from './fly_out_nav';
}
$(() => {
- $(window).on('scroll', _.throttle(applyScrollNavClass, 100));
-
if (Cookies.get('new_nav') === 'true') {
+ const newNavSidebar = new NewNavSidebar();
+ newNavSidebar.bindEvents();
+
initFlyOutNav();
}
+
+ $(window).on('scroll', _.throttle(applyScrollNavClass, 100));
});
}).call(window);
diff --git a/app/assets/javascripts/lib/utils/datefix.js b/app/assets/javascripts/lib/utils/datefix.js
new file mode 100644
index 00000000000..990dc3f6d1a
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/datefix.js
@@ -0,0 +1,8 @@
+const DateFix = {
+ dashedFix(val) {
+ const [y, m, d] = val.split('-');
+ return new Date(y, m - 1, d);
+ },
+};
+
+export default DateFix;
diff --git a/app/assets/javascripts/new_sidebar.js b/app/assets/javascripts/new_sidebar.js
new file mode 100644
index 00000000000..5f98aff8ced
--- /dev/null
+++ b/app/assets/javascripts/new_sidebar.js
@@ -0,0 +1,23 @@
+export default class NewNavSidebar {
+ constructor() {
+ this.initDomElements();
+ }
+
+ initDomElements() {
+ this.$sidebar = $('.nav-sidebar');
+ this.$overlay = $('.mobile-overlay');
+ this.$openSidebar = $('.toggle-mobile-nav');
+ this.$closeSidebar = $('.close-nav-button');
+ }
+
+ bindEvents() {
+ this.$openSidebar.on('click', () => this.toggleSidebarNav(true));
+ this.$closeSidebar.on('click', () => this.toggleSidebarNav(false));
+ this.$overlay.on('click', () => this.toggleSidebarNav(false));
+ }
+
+ toggleSidebarNav(show) {
+ this.$sidebar.toggleClass('nav-sidebar-expanded', show);
+ this.$overlay.toggleClass('mobile-nav-open', show);
+ }
+}
diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js
index 9896b88d487..ebcefc819f5 100644
--- a/app/assets/javascripts/project_select.js
+++ b/app/assets/javascripts/project_select.js
@@ -104,6 +104,14 @@ import Api from './api';
dropdownCssClass: "ajax-project-dropdown"
});
});
+
+ $('.new-project-item-select-button').on('click', function() {
+ $('.project-item-select', this.parentNode).select2('open');
+ });
+
+ $('.project-item-select').on('click', function() {
+ window.location = `${$(this).val()}/${this.dataset.relativePath}`;
+ });
}
return ProjectSelect;
diff --git a/app/assets/javascripts/protected_branches/index.js b/app/assets/javascripts/protected_branches/index.js
new file mode 100644
index 00000000000..c9e7af127d2
--- /dev/null
+++ b/app/assets/javascripts/protected_branches/index.js
@@ -0,0 +1,9 @@
+/* eslint-disable no-unused-vars */
+
+import ProtectedBranchCreate from './protected_branch_create';
+import ProtectedBranchEditList from './protected_branch_edit_list';
+
+$(() => {
+ const protectedBranchCreate = new ProtectedBranchCreate();
+ const protectedBranchEditList = new ProtectedBranchEditList();
+});
diff --git a/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js
index 42993a252c3..38b1406a99f 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js
+++ b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js
@@ -1,31 +1,26 @@
-/* eslint-disable arrow-parens, no-param-reassign, object-shorthand, no-else-return, comma-dangle, max-len */
+export default class ProtectedBranchAccessDropdown {
+ constructor(options) {
+ this.options = options;
+ this.initDropdown();
+ }
-(global => {
- global.gl = global.gl || {};
-
- gl.ProtectedBranchAccessDropdown = class {
- constructor(options) {
- const { $dropdown, data, onSelect } = options;
-
- $dropdown.glDropdown({
- data: data,
- selectable: true,
- inputId: $dropdown.data('input-id'),
- fieldName: $dropdown.data('field-name'),
- toggleLabel(item, el) {
- if (el.is('.is-active')) {
- return item.text;
- } else {
- return 'Select';
- }
- },
- clicked(opts) {
- const { e } = opts;
-
- e.preventDefault();
- onSelect();
+ initDropdown() {
+ const { $dropdown, data, onSelect } = this.options;
+ $dropdown.glDropdown({
+ data,
+ selectable: true,
+ inputId: $dropdown.data('input-id'),
+ fieldName: $dropdown.data('field-name'),
+ toggleLabel(item, $el) {
+ if ($el.is('.is-active')) {
+ return item.text;
}
- });
- }
- };
-})(window);
+ return 'Select';
+ },
+ clicked(options) {
+ options.e.preventDefault();
+ onSelect();
+ },
+ });
+ }
+}
diff --git a/app/assets/javascripts/protected_branches/protected_branch_create.js b/app/assets/javascripts/protected_branches/protected_branch_create.js
index 57ea2f52814..10da3783123 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_create.js
+++ b/app/assets/javascripts/protected_branches/protected_branch_create.js
@@ -1,55 +1,51 @@
-/* eslint-disable no-new, arrow-parens, no-param-reassign, comma-dangle, max-len */
-/* global ProtectedBranchDropdown */
-
-(global => {
- global.gl = global.gl || {};
-
- gl.ProtectedBranchCreate = class {
- constructor() {
- this.$wrap = this.$form = $('#new_protected_branch');
- this.buildDropdowns();
- }
-
- buildDropdowns() {
- const $allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge');
- const $allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push');
-
- // Cache callback
- this.onSelectCallback = this.onSelect.bind(this);
-
- // Allowed to Merge dropdown
- new gl.ProtectedBranchAccessDropdown({
- $dropdown: $allowedToMergeDropdown,
- data: gon.merge_access_levels,
- onSelect: this.onSelectCallback
- });
-
- // Allowed to Push dropdown
- new gl.ProtectedBranchAccessDropdown({
- $dropdown: $allowedToPushDropdown,
- data: gon.push_access_levels,
- onSelect: this.onSelectCallback
- });
-
- // Select default
- $allowedToPushDropdown.data('glDropdown').selectRowAtIndex(0);
- $allowedToMergeDropdown.data('glDropdown').selectRowAtIndex(0);
-
- // Protected branch dropdown
- new ProtectedBranchDropdown({
- $dropdown: this.$wrap.find('.js-protected-branch-select'),
- onSelect: this.onSelectCallback
- });
- }
-
- // This will run after clicked callback
- onSelect() {
- // Enable submit button
- const $branchInput = this.$wrap.find('input[name="protected_branch[name]"]');
- const $allowedToMergeInput = this.$wrap.find('input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]');
- const $allowedToPushInput = this.$wrap.find('input[name="protected_branch[push_access_levels_attributes][0][access_level]"]');
-
- this.$form.find('input[type="submit"]').attr('disabled', !($branchInput.val() && $allowedToMergeInput.length && $allowedToPushInput.length));
- }
- };
-})(window);
+import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown';
+import ProtectedBranchDropdown from './protected_branch_dropdown';
+
+export default class ProtectedBranchCreate {
+ constructor() {
+ this.$form = $('.js-new-protected-branch');
+ this.buildDropdowns();
+ }
+
+ buildDropdowns() {
+ const $allowedToMergeDropdown = this.$form.find('.js-allowed-to-merge');
+ const $allowedToPushDropdown = this.$form.find('.js-allowed-to-push');
+
+ // Cache callback
+ this.onSelectCallback = this.onSelect.bind(this);
+
+ // Allowed to Merge dropdown
+ this.protectedBranchMergeAccessDropdown = new ProtectedBranchAccessDropdown({
+ $dropdown: $allowedToMergeDropdown,
+ data: gon.merge_access_levels,
+ onSelect: this.onSelectCallback,
+ });
+
+ // Allowed to Push dropdown
+ this.protectedBranchPushAccessDropdown = new ProtectedBranchAccessDropdown({
+ $dropdown: $allowedToPushDropdown,
+ data: gon.push_access_levels,
+ onSelect: this.onSelectCallback,
+ });
+
+ // Select default
+ $allowedToPushDropdown.data('glDropdown').selectRowAtIndex(0);
+ $allowedToMergeDropdown.data('glDropdown').selectRowAtIndex(0);
+
+ // Protected branch dropdown
+ this.protectedBranchDropdown = new ProtectedBranchDropdown({
+ $dropdown: this.$form.find('.js-protected-branch-select'),
+ onSelect: this.onSelectCallback,
+ });
+ }
+
+ // This will run after clicked callback
+ onSelect() {
+ // Enable submit button
+ const $branchInput = this.$form.find('input[name="protected_branch[name]"]');
+ const $allowedToMergeInput = this.$form.find('input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]');
+ const $allowedToPushInput = this.$form.find('input[name="protected_branch[push_access_levels_attributes][0][access_level]"]');
+
+ this.$form.find('input[type="submit"]').attr('disabled', !($branchInput.val() && $allowedToMergeInput.length && $allowedToPushInput.length));
+ }
+}
diff --git a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js
index bc6110fcd4e..cc0b2ebe071 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js
+++ b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js
@@ -1,6 +1,10 @@
-/* eslint-disable comma-dangle, no-unused-vars */
-
-class ProtectedBranchDropdown {
+export default class ProtectedBranchDropdown {
+ /**
+ * @param {Object} options containing
+ * `$dropdown` target element
+ * `onSelect` event callback
+ * $dropdown must be an element created using `dropdown_branch()` rails helper
+ */
constructor(options) {
this.onSelect = options.onSelect;
this.$dropdown = options.$dropdown;
@@ -12,7 +16,7 @@ class ProtectedBranchDropdown {
this.bindEvents();
// Hide footer
- this.$dropdownFooter.addClass('hidden');
+ this.toggleFooter(true);
}
buildDropdown() {
@@ -21,7 +25,7 @@ class ProtectedBranchDropdown {
filterable: true,
remote: false,
search: {
- fields: ['title']
+ fields: ['title'],
},
selectable: true,
toggleLabel(selected) {
@@ -36,10 +40,9 @@ class ProtectedBranchDropdown {
},
onFilter: this.toggleCreateNewButton.bind(this),
clicked: (options) => {
- const { $el, e } = options;
- e.preventDefault();
+ options.e.preventDefault();
this.onSelect();
- }
+ },
});
}
@@ -64,20 +67,22 @@ class ProtectedBranchDropdown {
}
toggleCreateNewButton(branchName) {
- this.selectedBranch = {
- title: branchName,
- id: branchName,
- text: branchName
- };
-
if (branchName) {
+ this.selectedBranch = {
+ title: branchName,
+ id: branchName,
+ text: branchName,
+ };
+
this.$dropdownContainer
.find('.js-create-new-protected-branch code')
.text(branchName);
}
- this.$dropdownFooter.toggleClass('hidden', !branchName);
+ this.toggleFooter(!branchName);
}
-}
-window.ProtectedBranchDropdown = ProtectedBranchDropdown;
+ toggleFooter(toggleState) {
+ this.$dropdownFooter.toggleClass('hidden', toggleState);
+ }
+}
diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit.js b/app/assets/javascripts/protected_branches/protected_branch_edit.js
index 6ef59e94384..3b920942a3f 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_edit.js
+++ b/app/assets/javascripts/protected_branches/protected_branch_edit.js
@@ -1,69 +1,67 @@
-/* eslint-disable no-new, arrow-parens, no-param-reassign, comma-dangle, max-len */
+/* eslint-disable no-new */
/* global Flash */
-(global => {
- global.gl = global.gl || {};
+import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown';
- gl.ProtectedBranchEdit = class {
- constructor(options) {
- this.$wrap = options.$wrap;
- this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge');
- this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push');
+export default class ProtectedBranchEdit {
+ constructor(options) {
+ this.$wrap = options.$wrap;
+ this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge');
+ this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push');
+ this.onSelectCallback = this.onSelect.bind(this);
- this.buildDropdowns();
- }
+ this.buildDropdowns();
+ }
- buildDropdowns() {
- // Allowed to merge dropdown
- new gl.ProtectedBranchAccessDropdown({
- $dropdown: this.$allowedToMergeDropdown,
- data: gon.merge_access_levels,
- onSelect: this.onSelect.bind(this)
- });
+ buildDropdowns() {
+ // Allowed to merge dropdown
+ this.protectedBranchAccessDropdown = new ProtectedBranchAccessDropdown({
+ $dropdown: this.$allowedToMergeDropdown,
+ data: gon.merge_access_levels,
+ onSelect: this.onSelectCallback,
+ });
- // Allowed to push dropdown
- new gl.ProtectedBranchAccessDropdown({
- $dropdown: this.$allowedToPushDropdown,
- data: gon.push_access_levels,
- onSelect: this.onSelect.bind(this)
- });
- }
+ // Allowed to push dropdown
+ this.protectedBranchAccessDropdown = new ProtectedBranchAccessDropdown({
+ $dropdown: this.$allowedToPushDropdown,
+ data: gon.push_access_levels,
+ onSelect: this.onSelectCallback,
+ });
+ }
- onSelect() {
- const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`);
- const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`);
+ onSelect() {
+ const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`);
+ const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`);
- // Do not update if one dropdown has not selected any option
- if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return;
+ // Do not update if one dropdown has not selected any option
+ if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return;
- this.$allowedToMergeDropdown.disable();
- this.$allowedToPushDropdown.disable();
+ this.$allowedToMergeDropdown.disable();
+ this.$allowedToPushDropdown.disable();
- $.ajax({
- type: 'POST',
- url: this.$wrap.data('url'),
- dataType: 'json',
- data: {
- _method: 'PATCH',
- protected_branch: {
- merge_access_levels_attributes: [{
- id: this.$allowedToMergeDropdown.data('access-level-id'),
- access_level: $allowedToMergeInput.val()
- }],
- push_access_levels_attributes: [{
- id: this.$allowedToPushDropdown.data('access-level-id'),
- access_level: $allowedToPushInput.val()
- }]
- }
+ $.ajax({
+ type: 'POST',
+ url: this.$wrap.data('url'),
+ dataType: 'json',
+ data: {
+ _method: 'PATCH',
+ protected_branch: {
+ merge_access_levels_attributes: [{
+ id: this.$allowedToMergeDropdown.data('access-level-id'),
+ access_level: $allowedToMergeInput.val(),
+ }],
+ push_access_levels_attributes: [{
+ id: this.$allowedToPushDropdown.data('access-level-id'),
+ access_level: $allowedToPushInput.val(),
+ }],
},
- error() {
- $.scrollTo(0);
- new Flash('Failed to update branch!');
- }
- }).always(() => {
- this.$allowedToMergeDropdown.enable();
- this.$allowedToPushDropdown.enable();
- });
- }
- };
-})(window);
+ },
+ error() {
+ new Flash('Failed to update branch!', null, $('.js-protected-branches-list'));
+ },
+ }).always(() => {
+ this.$allowedToMergeDropdown.enable();
+ this.$allowedToPushDropdown.enable();
+ });
+ }
+}
diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit_list.js b/app/assets/javascripts/protected_branches/protected_branch_edit_list.js
index 336fa6c57a7..b40d3827c30 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_edit_list.js
+++ b/app/assets/javascripts/protected_branches/protected_branch_edit_list.js
@@ -1,18 +1,18 @@
-/* eslint-disable arrow-parens, no-param-reassign, no-new, comma-dangle */
+/* eslint-disable no-new */
-(global => {
- global.gl = global.gl || {};
+import ProtectedBranchEdit from './protected_branch_edit';
- gl.ProtectedBranchEditList = class {
- constructor() {
- this.$wrap = $('.protected-branches-list');
+export default class ProtectedBranchEditList {
+ constructor() {
+ this.$wrap = $('.protected-branches-list');
+ this.initEditForm();
+ }
- // Build edit forms
- this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => {
- new gl.ProtectedBranchEdit({
- $wrap: $(el)
- });
+ initEditForm() {
+ this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => {
+ new ProtectedBranchEdit({
+ $wrap: $(el),
});
- }
- };
-})(window);
+ });
+ }
+}
diff --git a/app/assets/javascripts/protected_branches/protected_branches_bundle.js b/app/assets/javascripts/protected_branches/protected_branches_bundle.js
deleted file mode 100644
index 874d70a1431..00000000000
--- a/app/assets/javascripts/protected_branches/protected_branches_bundle.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import './protected_branch_access_dropdown';
-import './protected_branch_create';
-import './protected_branch_dropdown';
-import './protected_branch_edit';
-import './protected_branch_edit_list';
diff --git a/app/assets/javascripts/protected_tags/index.js b/app/assets/javascripts/protected_tags/index.js
index 61e7ba53862..b1618e24e49 100644
--- a/app/assets/javascripts/protected_tags/index.js
+++ b/app/assets/javascripts/protected_tags/index.js
@@ -1,2 +1,9 @@
-export { default as ProtectedTagCreate } from './protected_tag_create';
-export { default as ProtectedTagEditList } from './protected_tag_edit_list';
+/* eslint-disable no-unused-vars */
+
+import ProtectedTagCreate from './protected_tag_create';
+import ProtectedTagEditList from './protected_tag_edit_list';
+
+$(() => {
+ const protectedtTagCreate = new ProtectedTagCreate();
+ const protectedtTagEditList = new ProtectedTagEditList();
+});
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index e71bf04aec7..99eea97377c 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -190,14 +190,6 @@
display: none;
}
- .btn,
- .dropdown,
- .dropdown-toggle,
- input,
- form {
- height: 35px;
- }
-
input {
display: inline-block;
position: relative;
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 542b641e3dd..49b2f0e43a4 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -92,7 +92,6 @@
@mixin maintain-sidebar-dimensions {
display: block;
width: $gutter-width;
- padding: 10px 0;
}
.issues-bulk-update.right-sidebar {
@@ -104,6 +103,15 @@
&.right-sidebar-expanded {
@include maintain-sidebar-dimensions;
width: $gutter-width;
+
+ .issuable-sidebar-header {
+ // matches `.top-area .nav-controls` for issuable index pages
+ padding: 11px 0;
+ }
+
+ .block:last-of-type {
+ border: none;
+ }
}
&.right-sidebar-collapsed {
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 77b7d901f9a..8a58c1ed567 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -116,9 +116,12 @@
blockquote p {
color: $gl-grayish-blue !important;
- margin: 0;
font-size: inherit;
line-height: 1.5;
+
+ &:last-child {
+ margin: 0;
+ }
}
p {
diff --git a/app/assets/stylesheets/new_nav.scss b/app/assets/stylesheets/new_nav.scss
index 393d5006e24..9f3e278ebfc 100644
--- a/app/assets/stylesheets/new_nav.scss
+++ b/app/assets/stylesheets/new_nav.scss
@@ -41,10 +41,22 @@ header.navbar-gitlab-new {
}
}
+ .logo-text {
+ line-height: initial;
+
+ svg {
+ width: 55px;
+ height: 15px;
+ margin: 0;
+ fill: $white-light;
+ }
+ }
+
&:hover,
&:focus {
- color: $tanuki-yellow;
- text-decoration: none;
+ .logo-text svg {
+ fill: $tanuki-yellow;
+ }
}
}
}
@@ -274,9 +286,7 @@ header.navbar-gitlab-new {
.breadcrumbs {
display: flex;
- min-height: 60px;
- padding-top: $gl-padding-top;
- padding-bottom: $gl-padding-top;
+ min-height: 61px;
color: $gl-text-color;
border-bottom: 1px solid $border-color;
@@ -300,6 +310,7 @@ header.navbar-gitlab-new {
display: flex;
width: 100%;
position: relative;
+ align-items: center;
.dropdown-menu-projects {
margin-top: -$gl-padding;
@@ -330,7 +341,7 @@ header.navbar-gitlab-new {
white-space: nowrap;
> a {
- &:last-of-type {
+ &:last-of-type:not(:first-child) {
font-weight: 600;
}
}
@@ -384,6 +395,7 @@ header.navbar-gitlab-new {
&::after {
content: "/";
margin: 0 2px 0 5px;
+ color: rgba($black, .65);
}
}
@@ -396,3 +408,13 @@ header.navbar-gitlab-new {
color: $gl-text-color;
}
}
+
+.top-area {
+ .nav-controls-new-nav {
+ .dropdown {
+ @media (min-width: $screen-sm-min) {
+ margin-right: 0;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss
index 43c4d52d1e2..78f278c1669 100644
--- a/app/assets/stylesheets/new_sidebar.scss
+++ b/app/assets/stylesheets/new_sidebar.scss
@@ -23,44 +23,82 @@ $new-sidebar-width: 220px;
position: fixed;
height: 100%;
}
+
+ .issues-bulk-update.right-sidebar.right-sidebar-expanded .issuable-sidebar-header {
+ padding: 10px 0 15px;
+ }
}
.context-header {
- border-bottom: 1px solid $border-color;
- font-weight: 600;
- display: flex;
- align-items: center;
- padding: 10px 16px 10px 10px;
- color: $gl-text-color;
-
- .avatar-container {
- flex: 0 0 40px;
- background-color: $white-light;
- }
+ position: relative;
- &:hover {
- background-color: $hover-background;
- color: $hover-color;
- border-color: $hover-background;
+ a {
+ border-bottom: 1px solid $border-color;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ padding: 10px 16px 10px 10px;
+ color: $gl-text-color;
- .avatar-container {
- border-color: transparent;
+ @media (max-width: $screen-xs-max) {
+ padding-right: 30px;
}
- .settings-avatar {
- background-color: $indigo-500;
+ &:hover {
+ background-color: $hover-background;
+ color: $hover-color;
+ border-color: $hover-background;
- i {
- color: $hover-color;
+ .avatar-container {
+ border-color: transparent;
+ }
+
+ .settings-avatar {
+ background-color: $indigo-500;
+
+ i {
+ color: $hover-color;
+ }
}
}
}
+ .avatar-container {
+ flex: 0 0 40px;
+ background-color: $white-light;
+ }
+
.project-title,
.group-title {
overflow: hidden;
text-overflow: ellipsis;
}
+
+
+ &:hover {
+ .close-nav-button {
+ color: $white-light;
+ }
+ }
+
+ .close-nav-button {
+ display: none;
+ position: absolute;
+ top: 0;
+ right: 0;
+ height: 100%;
+ background-color: transparent;
+ border: 0;
+ padding: 0 10px;
+
+ @media (max-width: $screen-xs-max) {
+ display: block;
+ }
+
+ &:hover {
+ color: $gl-text-color;
+ }
+ }
}
.settings-avatar {
@@ -79,7 +117,7 @@ $new-sidebar-width: 220px;
position: fixed;
z-index: 400;
width: $new-sidebar-width;
- transition: width $sidebar-transition-duration;
+ transition: left $sidebar-transition-duration;
top: 50px;
bottom: 0;
left: 0;
@@ -87,6 +125,10 @@ $new-sidebar-width: 220px;
background-color: $gray-normal;
box-shadow: inset -2px 0 0 $border-color;
+ &.nav-sidebar-expanded {
+ left: 0;
+ }
+
a {
transition: none;
text-decoration: none;
@@ -117,7 +159,7 @@ $new-sidebar-width: 220px;
}
@media (max-width: $screen-xs-max) {
- width: 0;
+ left: (-$new-sidebar-width);
}
}
@@ -223,6 +265,38 @@ $new-sidebar-width: 220px;
}
}
+.toggle-mobile-nav {
+ display: none;
+ background-color: transparent;
+ border: 0;
+ padding: 6px 16px;
+ margin: 0 16px 0 -15px;
+ height: 46px;
+ border-right: 1px solid $gl-text-color-quaternary;
+
+ i {
+ font-size: 20px;
+ color: $gl-text-color-secondary;
+ }
+
+ @media (max-width: $screen-xs-max) {
+ display: inline-block;
+ }
+}
+
+.mobile-overlay {
+ display: none;
+
+ &.mobile-nav-open {
+ display: block;
+ position: fixed;
+ background-color: $black-transparent;
+ height: 100%;
+ width: 100%;
+ z-index: 300;
+ }
+}
+
// Make issue boards full-height now that sub-nav is gone
@@ -232,7 +306,7 @@ $new-sidebar-width: 220px;
@media (min-width: $screen-sm-min) {
height: 475px; // Needed for PhantomJS
// scss-lint:disable DuplicateProperty
- height: calc(100vh - 120px);
+ height: calc(100vh - 180px);
// scss-lint:enable DuplicateProperty
}
}
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index a5e4c3311f8..fd0871ec0b8 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -54,7 +54,11 @@
.mr-widget-pipeline-graph {
display: inline-block;
vertical-align: middle;
- margin: 0 -6px 0 0;
+ margin-right: 4px;
+
+ .stage-cell .stage-container {
+ margin: 3px 3px 3px 0;
+ }
.dropdown-menu {
margin-top: 11px;
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index 235c475ff26..22672614e0d 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -376,3 +376,18 @@ table.u2f-registrations {
}
}
}
+
+.nav-wip {
+ border: 1px solid $blue-500;
+ background: $blue-25;
+ padding: $gl-padding;
+ margin-bottom: $gl-padding;
+
+ a {
+ color: $blue-500;
+ }
+
+ p:last-child {
+ margin-bottom: 0;
+ }
+}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index c1423965d0a..a3e07a36c33 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -742,7 +742,8 @@ pre.light-well {
}
}
-.protected-tags-list {
+.protected-tags-list,
+.protected-branches-list {
.dropdown-menu-toggle {
width: 100%;
max-width: 300px;
diff --git a/app/controllers/admin/hook_logs_controller.rb b/app/controllers/admin/hook_logs_controller.rb
index aa069b89563..3017f96c26f 100644
--- a/app/controllers/admin/hook_logs_controller.rb
+++ b/app/controllers/admin/hook_logs_controller.rb
@@ -10,9 +10,9 @@ class Admin::HookLogsController < Admin::ApplicationController
end
def retry
- status, message = hook.execute(hook_log.request_data, hook_log.trigger)
+ result = hook.execute(hook_log.request_data, hook_log.trigger)
- set_hook_execution_notice(status, message)
+ set_hook_execution_notice(result)
redirect_to edit_admin_hook_path(@hook)
end
diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb
index 054c3500b35..77e3c95d197 100644
--- a/app/controllers/admin/hooks_controller.rb
+++ b/app/controllers/admin/hooks_controller.rb
@@ -38,9 +38,9 @@ class Admin::HooksController < Admin::ApplicationController
end
def test
- status, message = hook.execute(sample_hook_data, 'system_hooks')
+ result = TestHooks::SystemService.new(hook, current_user, params[:trigger]).execute
- set_hook_execution_notice(status, message)
+ set_hook_execution_notice(result)
redirect_back_or_default
end
@@ -66,15 +66,4 @@ class Admin::HooksController < Admin::ApplicationController
:url
)
end
-
- def sample_hook_data
- {
- event_name: "project_create",
- name: "Ruby",
- path: "ruby",
- project_id: 1,
- owner_name: "Someone",
- owner_email: "example@gitlabhq.com"
- }
- end
end
diff --git a/app/controllers/concerns/hooks_execution.rb b/app/controllers/concerns/hooks_execution.rb
index 846cd60518f..a22e46b4860 100644
--- a/app/controllers/concerns/hooks_execution.rb
+++ b/app/controllers/concerns/hooks_execution.rb
@@ -3,11 +3,14 @@ module HooksExecution
private
- def set_hook_execution_notice(status, message)
- if status && status >= 200 && status < 400
- flash[:notice] = "Hook executed successfully: HTTP #{status}"
- elsif status
- flash[:alert] = "Hook executed successfully but returned HTTP #{status} #{message}"
+ def set_hook_execution_notice(result)
+ http_status = result[:http_status]
+ message = result[:message]
+
+ if http_status && http_status >= 200 && http_status < 400
+ flash[:notice] = "Hook executed successfully: HTTP #{http_status}"
+ elsif http_status
+ flash[:alert] = "Hook executed successfully but returned HTTP #{http_status} #{message}"
else
flash[:alert] = "Hook execution failed: #{message}"
end
diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb
index e18778cdf80..b43b2c5621f 100644
--- a/app/controllers/concerns/issuable_collections.rb
+++ b/app/controllers/concerns/issuable_collections.rb
@@ -32,10 +32,10 @@ module IssuableCollections
def filter_params
set_sort_order_from_cookie
- set_default_scope
set_default_state
- @filter_params = params.dup
+ # Skip irrelevant Rails routing params
+ @filter_params = params.dup.except(:controller, :action, :namespace_id)
@filter_params[:sort] ||= default_sort_order
@sort = @filter_params[:sort]
@@ -55,10 +55,6 @@ module IssuableCollections
@filter_params
end
- def set_default_scope
- params[:scope] = 'all' if params[:scope].blank?
- end
-
def set_default_state
params[:state] = 'opened' if params[:state].blank?
end
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index 28c90548cc1..59e5b5e4775 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -1,6 +1,7 @@
class Dashboard::TodosController < Dashboard::ApplicationController
include ActionView::Helpers::NumberHelper
+ before_action :authorize_read_project!, only: :index
before_action :find_todos, only: [:index, :destroy_all]
def index
@@ -49,6 +50,15 @@ class Dashboard::TodosController < Dashboard::ApplicationController
private
+ def authorize_read_project!
+ project_id = params[:project_id]
+
+ if project_id.present?
+ project = Project.find(project_id)
+ render_404 unless can?(current_user, :read_project, project)
+ end
+ end
+
def find_todos
@todos ||= TodosFinder.new(current_user, params).execute
end
diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb
index 6c25cd83a24..06ba73d8e8d 100644
--- a/app/controllers/projects/badges_controller.rb
+++ b/app/controllers/projects/badges_controller.rb
@@ -3,11 +3,11 @@ class Projects::BadgesController < Projects::ApplicationController
before_action :authorize_admin_project!, only: [:index]
before_action :no_cache_headers, except: [:index]
- def build
- build_status = Gitlab::Badge::Build::Status
+ def pipeline
+ pipeline_status = Gitlab::Badge::Pipeline::Status
.new(project, params[:ref])
- render_badge build_status
+ render_badge pipeline_status
end
def coverage
diff --git a/app/controllers/projects/hook_logs_controller.rb b/app/controllers/projects/hook_logs_controller.rb
index b9c4b29580a..745e89fc843 100644
--- a/app/controllers/projects/hook_logs_controller.rb
+++ b/app/controllers/projects/hook_logs_controller.rb
@@ -14,9 +14,9 @@ class Projects::HookLogsController < Projects::ApplicationController
end
def retry
- status, message = hook.execute(hook_log.request_data, hook_log.trigger)
+ result = hook.execute(hook_log.request_data, hook_log.trigger)
- set_hook_execution_notice(status, message)
+ set_hook_execution_notice(result)
redirect_to edit_project_hook_path(@project, @hook)
end
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index 18895c3f0f3..85d35900c71 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -9,6 +9,10 @@ class Projects::HooksController < Projects::ApplicationController
layout "project_settings"
+ def index
+ redirect_to project_settings_integrations_path(@project)
+ end
+
def create
@hook = @project.hooks.new(hook_params)
@hook.save
@@ -33,13 +37,9 @@ class Projects::HooksController < Projects::ApplicationController
end
def test
- if !@project.empty_repo?
- status, message = TestHookService.new.execute(hook, current_user)
+ result = TestHooks::ProjectService.new(hook, current_user, params[:trigger]).execute
- set_hook_execution_notice(status, message)
- else
- flash[:alert] = 'Hook execution failed. Ensure the project has commits.'
- end
+ set_hook_execution_notice(result)
redirect_back_or_default(default: { action: 'index' })
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 13f03e7e63e..0ac9da2ff0f 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -266,7 +266,7 @@ class Projects::IssuesController < Projects::ApplicationController
if action_name == 'new'
redirect_to external.new_issue_path
else
- redirect_to external.project_path
+ redirect_to external.issue_tracker_path
end
end
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index ea7ceb3eaa5..15a2ff56b92 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -35,7 +35,7 @@ module Projects
def define_badges_variables
@ref = params[:ref] || @project.default_branch || 'master'
- @badges = [Gitlab::Badge::Build::Status,
+ @badges = [Gitlab::Badge::Pipeline::Status,
Gitlab::Badge::Coverage::Report]
@badges.map! do |badge|
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 2e5a6493134..fc63e30c8fb 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -20,9 +20,9 @@
#
class IssuableFinder
include CreatedAtFilter
-
+
NONE = '0'.freeze
- IRRELEVANT_PARAMS_FOR_CACHE_KEY = %i[utf8 sort page].freeze
+ IRRELEVANT_PARAMS_FOR_CACHE_KEY = %i[utf8 sort page state].freeze
attr_accessor :current_user, :params
@@ -89,8 +89,14 @@ class IssuableFinder
execute.find_by!(*params)
end
- def state_counter_cache_key(state)
- Digest::SHA1.hexdigest(state_counter_cache_key_components(state).flatten.join('-'))
+ def state_counter_cache_key
+ cache_key(state_counter_cache_key_components)
+ end
+
+ def clear_caches!
+ state_counter_cache_key_components_permutations.each do |components|
+ Rails.cache.delete(cache_key(components))
+ end
end
def group
@@ -417,12 +423,19 @@ class IssuableFinder
params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me'
end
- def state_counter_cache_key_components(state)
+ def state_counter_cache_key_components
opts = params.with_indifferent_access
- opts[:state] = state
opts.except!(*IRRELEVANT_PARAMS_FOR_CACHE_KEY)
opts.delete_if { |_, value| value.blank? }
['issuables_count', klass.to_ability_name, opts.sort]
end
+
+ def state_counter_cache_key_components_permutations
+ [state_counter_cache_key_components]
+ end
+
+ def cache_key(components)
+ Digest::SHA1.hexdigest(components.flatten.join('-'))
+ end
end
diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb
index 85230ff1293..0ec42a4e6eb 100644
--- a/app/finders/issues_finder.rb
+++ b/app/finders/issues_finder.rb
@@ -75,7 +75,7 @@ class IssuesFinder < IssuableFinder
current_user.blank? || for_counting || params[:for_counting]
end
- def state_counter_cache_key_components(state)
+ def state_counter_cache_key_components
extra_components = [
user_can_see_all_confidential_issues?,
user_cannot_see_confidential_issues?(for_counting: true)
@@ -84,6 +84,16 @@ class IssuesFinder < IssuableFinder
super + extra_components
end
+ def state_counter_cache_key_components_permutations
+ # Ignore the last two, as we'll provide both options for them.
+ components = super.first[0..-3]
+
+ [
+ components + [false, true],
+ components + [true, false]
+ ]
+ end
+
def by_assignee(items)
if assignee
items.assigned_to(assignee)
diff --git a/app/helpers/breadcrumbs_helper.rb b/app/helpers/breadcrumbs_helper.rb
new file mode 100644
index 00000000000..abe8edd6a8c
--- /dev/null
+++ b/app/helpers/breadcrumbs_helper.rb
@@ -0,0 +1,25 @@
+module BreadcrumbsHelper
+ def add_to_breadcrumbs(text, link)
+ @breadcrumbs_extra_links ||= []
+ @breadcrumbs_extra_links.push({
+ text: text,
+ link: link
+ })
+ end
+
+ def breadcrumb_title_link
+ return @breadcrumb_link if @breadcrumb_link
+
+ if controller.available_action?(:index)
+ url_for(action: "index")
+ else
+ request.path
+ end
+ end
+
+ def breadcrumb_title(title)
+ return if defined?(@breadcrumb_title)
+
+ @breadcrumb_title = title
+ end
+end
diff --git a/app/helpers/hooks_helper.rb b/app/helpers/hooks_helper.rb
new file mode 100644
index 00000000000..551b9cca6b1
--- /dev/null
+++ b/app/helpers/hooks_helper.rb
@@ -0,0 +1,17 @@
+module HooksHelper
+ def link_to_test_hook(hook, trigger)
+ path = case hook
+ when ProjectHook
+ project = hook.project
+ test_project_hook_path(project, hook, trigger: trigger)
+ when SystemHook
+ test_admin_hook_path(hook, trigger: trigger)
+ end
+
+ trigger_human_name = trigger.to_s.tr('_', ' ').camelize
+
+ link_to path, rel: 'nofollow' do
+ content_tag(:span, trigger_human_name)
+ end
+ end
+end
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index d0c518f81f7..425af547330 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -235,7 +235,7 @@ module IssuablesHelper
def issuables_count_for_state(issuable_type, state, finder: nil)
finder ||= public_send("#{issuable_type}_finder")
- cache_key = finder.state_counter_cache_key(state)
+ cache_key = finder.state_counter_cache_key
@counts ||= {}
@counts[cache_key] ||= Rails.cache.fetch(cache_key, expires_in: 2.minutes) do
diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb
index 3286a92a8a7..b30b2eb1d03 100644
--- a/app/helpers/page_layout_helper.rb
+++ b/app/helpers/page_layout_helper.rb
@@ -4,6 +4,10 @@ module PageLayoutHelper
@page_title.push(*titles.compact) if titles.any?
+ if show_new_nav? && titles.any? && !defined?(@breadcrumb_title)
+ @breadcrumb_title = @page_title.last
+ end
+
# Segments are seperated by middot
@page_title.join(" \u00b7 ")
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 26d11f9ab46..9a8d296d514 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -195,7 +195,7 @@ module ProjectsHelper
controller.controller_name,
controller.action_name,
current_application_settings.cache_key,
- 'v2.4'
+ 'v2.5'
]
key << pipeline_status_cache_key(project.pipeline_status) if project.pipeline_status.has_status?
diff --git a/app/helpers/triggers_helper.rb b/app/helpers/triggers_helper.rb
index a48d4475e97..ce435ca2241 100644
--- a/app/helpers/triggers_helper.rb
+++ b/app/helpers/triggers_helper.rb
@@ -8,6 +8,6 @@ module TriggersHelper
end
def service_trigger_url(service)
- "#{Settings.gitlab.url}/api/v3/projects/#{service.project_id}/services/#{service.to_param}/trigger"
+ "#{Settings.gitlab.url}/api/v4/projects/#{service.project_id}/services/#{service.to_param}/trigger"
end
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 432f3f242eb..416a2a33378 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -96,6 +96,14 @@ module Ci
BuildSuccessWorker.perform_async(id)
end
end
+
+ before_transition any => [:failed] do |build|
+ next if build.retries_max.zero?
+
+ if build.retries_count < build.retries_max
+ Ci::Build.retry(build, build.user)
+ end
+ end
end
def detailed_status(current_user)
@@ -130,6 +138,14 @@ module Ci
success? || failed? || canceled?
end
+ def retries_count
+ pipeline.builds.retried.where(name: self.name).count
+ end
+
+ def retries_max
+ self.options.fetch(:retry, 0).to_i
+ end
+
def latest?
!retried?
end
diff --git a/app/models/concerns/editable.rb b/app/models/concerns/editable.rb
index c62c7e1e936..28623d257a6 100644
--- a/app/models/concerns/editable.rb
+++ b/app/models/concerns/editable.rb
@@ -4,4 +4,8 @@ module Editable
def is_edited?
last_edited_at.present? && last_edited_at != created_at
end
+
+ def last_edited_by
+ super || User.ghost
+ end
end
diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb
index ee6165fd32d..a8c424a6614 100644
--- a/app/models/hooks/project_hook.rb
+++ b/app/models/hooks/project_hook.rb
@@ -1,11 +1,20 @@
class ProjectHook < WebHook
- belongs_to :project
+ TRIGGERS = {
+ push_hooks: :push_events,
+ tag_push_hooks: :tag_push_events,
+ issue_hooks: :issues_events,
+ confidential_issue_hooks: :confidential_issues_events,
+ note_hooks: :note_events,
+ merge_request_hooks: :merge_requests_events,
+ job_hooks: :job_events,
+ pipeline_hooks: :pipeline_events,
+ wiki_page_hooks: :wiki_page_events
+ }.freeze
+
+ TRIGGERS.each do |trigger, event|
+ scope trigger, -> { where(event => true) }
+ end
- scope :issue_hooks, -> { where(issues_events: true) }
- scope :confidential_issue_hooks, -> { where(confidential_issues_events: true) }
- scope :note_hooks, -> { where(note_events: true) }
- scope :merge_request_hooks, -> { where(merge_requests_events: true) }
- scope :job_hooks, -> { where(job_events: true) }
- scope :pipeline_hooks, -> { where(pipeline_events: true) }
- scope :wiki_page_hooks, -> { where(wiki_page_events: true) }
+ belongs_to :project
+ validates :project, presence: true
end
diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb
index 40e43c27f91..aef11514945 100644
--- a/app/models/hooks/service_hook.rb
+++ b/app/models/hooks/service_hook.rb
@@ -1,5 +1,6 @@
class ServiceHook < WebHook
belongs_to :service
+ validates :service, presence: true
def execute(data)
WebHookService.new(self, data, 'service_hook').execute
diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb
index 1584235ab00..180c479c41b 100644
--- a/app/models/hooks/system_hook.rb
+++ b/app/models/hooks/system_hook.rb
@@ -1,5 +1,13 @@
class SystemHook < WebHook
- scope :repository_update_hooks, -> { where(repository_update_events: true) }
+ TRIGGERS = {
+ repository_update_hooks: :repository_update_events,
+ push_hooks: :push_events,
+ tag_push_hooks: :tag_push_events
+ }.freeze
+
+ TRIGGERS.each do |trigger, event|
+ scope trigger, -> { where(event => true) }
+ end
default_value_for :push_events, false
default_value_for :repository_update_events, true
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index 7a9f8997959..5a70e114f56 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -1,22 +1,8 @@
class WebHook < ActiveRecord::Base
include Sortable
- default_value_for :push_events, true
- default_value_for :issues_events, false
- default_value_for :confidential_issues_events, false
- default_value_for :note_events, false
- default_value_for :merge_requests_events, false
- default_value_for :tag_push_events, false
- default_value_for :job_events, false
- default_value_for :pipeline_events, false
- default_value_for :repository_update_events, false
- default_value_for :enable_ssl_verification, true
-
has_many :web_hook_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
- scope :push_hooks, -> { where(push_events: true) }
- scope :tag_push_hooks, -> { where(tag_push_events: true) }
-
validates :url, presence: true, url: true
def execute(data, hook_name)
diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb
index 420102875a5..88c428b4aae 100644
--- a/app/models/project_services/gitlab_issue_tracker_service.rb
+++ b/app/models/project_services/gitlab_issue_tracker_service.rb
@@ -23,7 +23,7 @@ class GitlabIssueTrackerService < IssueTrackerService
project_issue_url(project, id: iid)
end
- def project_path
+ def issue_tracker_path
project_issues_path(project)
end
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index 1fa4cd4db30..6d6a3ae3647 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -20,8 +20,8 @@ class IssueTrackerService < Service
self.issues_url.gsub(':id', iid.to_s)
end
- def project_path
- read_attribute(:project_url)
+ def issue_tracker_path
+ project_url
end
def new_issue_path
diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb
index 62f7c057c5b..dee99bbb859 100644
--- a/app/models/project_services/kubernetes_service.rb
+++ b/app/models/project_services/kubernetes_service.rb
@@ -59,21 +59,21 @@ class KubernetesService < DeploymentService
def fields
[
{ type: 'text',
- name: 'namespace',
- title: 'Kubernetes namespace',
- placeholder: namespace_placeholder },
- { type: 'text',
name: 'api_url',
title: 'API URL',
placeholder: 'Kubernetes API URL, like https://kube.example.com/' },
- { type: 'text',
- name: 'token',
- title: 'Service token',
- placeholder: 'Service token' },
{ type: 'textarea',
name: 'ca_pem',
- title: 'Custom CA bundle',
- placeholder: 'Certificate Authority bundle (PEM format)' }
+ title: 'CA Certificate',
+ placeholder: 'Certificate Authority bundle (PEM format)' },
+ { type: 'text',
+ name: 'namespace',
+ title: 'Project namespace (optional/unique)',
+ placeholder: namespace_placeholder },
+ { type: 'text',
+ name: 'token',
+ title: 'Token',
+ placeholder: 'Service token' }
]
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 8f40af24e20..c26be6d05a2 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -385,9 +385,11 @@ class User < ActiveRecord::Base
# Return (create if necessary) the ghost user. The ghost user
# owns records previously belonging to deleted users.
def ghost
- unique_internal(where(ghost: true), 'ghost', 'ghost%s@example.com') do |u|
+ email = 'ghost%s@example.com'
+ unique_internal(where(ghost: true), 'ghost', email) do |u|
u.bio = 'This is a "Ghost User", created to hold all issues authored by users that have since been deleted. This user cannot be removed.'
u.name = 'Ghost User'
+ u.notification_email = email
end
end
end
diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb
index a1d67cbc244..eb345fead2d 100644
--- a/app/services/boards/issues/list_service.rb
+++ b/app/services/boards/issues/list_service.rb
@@ -33,17 +33,12 @@ module Boards
end
def filter_params
- set_default_scope
set_project
set_state
params
end
- def set_default_scope
- params[:scope] = 'all'
- end
-
def set_project
params[:project_id] = project.id
end
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index 4f35255fb53..273386776fa 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -135,7 +135,7 @@ module Ci
end
def pipeline_created_counter
- @pipeline_created_counter ||= Gitlab::Metrics.counter(:pipelines_created_count, "Pipelines created count")
+ @pipeline_created_counter ||= Gitlab::Metrics.counter(:pipelines_created_total, "Counter of pipelines created")
end
end
end
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index a03a7abfeb1..9078b1f0983 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -183,7 +183,7 @@ class IssuableBaseService < BaseService
after_create(issuable)
issuable.create_cross_references!(current_user)
execute_hooks(issuable)
- invalidate_cache_counts(issuable.assignees, issuable)
+ invalidate_cache_counts(issuable, users: issuable.assignees)
end
issuable
@@ -240,12 +240,12 @@ class IssuableBaseService < BaseService
old_assignees: old_assignees
)
- if old_assignees != issuable.assignees
- new_assignees = issuable.assignees.to_a
- affected_assignees = (old_assignees + new_assignees) - (old_assignees & new_assignees)
- invalidate_cache_counts(affected_assignees.compact, issuable)
- end
+ new_assignees = issuable.assignees.to_a
+ affected_assignees = (old_assignees + new_assignees) - (old_assignees & new_assignees)
+ # Don't clear the project cache, because it will be handled by the
+ # appropriate service (close / reopen / merge / etc.).
+ invalidate_cache_counts(issuable, users: affected_assignees.compact, skip_project_cache: true)
after_update(issuable)
issuable.create_new_cross_references!(current_user)
execute_hooks(issuable, 'update')
@@ -339,9 +339,18 @@ class IssuableBaseService < BaseService
create_labels_note(issuable, old_labels) if issuable.labels != old_labels
end
- def invalidate_cache_counts(users, issuable)
+ def invalidate_cache_counts(issuable, users: [], skip_project_cache: false)
users.each do |user|
user.public_send("invalidate_#{issuable.model_name.singular}_cache_counts")
end
+
+ unless skip_project_cache
+ case issuable
+ when Issue
+ IssuesFinder.new(nil, project_id: issuable.project_id).clear_caches!
+ when MergeRequest
+ MergeRequestsFinder.new(nil, project_id: issuable.target_project_id).clear_caches!
+ end
+ end
end
end
diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb
index 85c616ca576..ddef5281498 100644
--- a/app/services/issues/close_service.rb
+++ b/app/services/issues/close_service.rb
@@ -28,7 +28,7 @@ module Issues
notification_service.close_issue(issue, current_user) if notifications
todo_service.close_issue(issue, current_user)
execute_hooks(issue, 'close')
- invalidate_cache_counts(issue.assignees, issue)
+ invalidate_cache_counts(issue, users: issue.assignees)
end
issue
diff --git a/app/services/issues/reopen_service.rb b/app/services/issues/reopen_service.rb
index 80ea6312768..73b2e85cba3 100644
--- a/app/services/issues/reopen_service.rb
+++ b/app/services/issues/reopen_service.rb
@@ -8,7 +8,7 @@ module Issues
create_note(issue)
notification_service.reopen_issue(issue, current_user)
execute_hooks(issue, 'reopen')
- invalidate_cache_counts(issue.assignees, issue)
+ invalidate_cache_counts(issue, users: issue.assignees)
end
issue
diff --git a/app/services/merge_requests/close_service.rb b/app/services/merge_requests/close_service.rb
index 2ffc989ed71..c0ce01f7523 100644
--- a/app/services/merge_requests/close_service.rb
+++ b/app/services/merge_requests/close_service.rb
@@ -13,7 +13,7 @@ module MergeRequests
notification_service.close_mr(merge_request, current_user)
todo_service.close_merge_request(merge_request, current_user)
execute_hooks(merge_request, 'close')
- invalidate_cache_counts(merge_request.assignees, merge_request)
+ invalidate_cache_counts(merge_request, users: merge_request.assignees)
end
merge_request
diff --git a/app/services/merge_requests/post_merge_service.rb b/app/services/merge_requests/post_merge_service.rb
index f0d998731d7..261a8bfa200 100644
--- a/app/services/merge_requests/post_merge_service.rb
+++ b/app/services/merge_requests/post_merge_service.rb
@@ -13,7 +13,7 @@ module MergeRequests
create_note(merge_request)
notification_service.merge_mr(merge_request, current_user)
execute_hooks(merge_request, 'merge')
- invalidate_cache_counts(merge_request.assignees, merge_request)
+ invalidate_cache_counts(merge_request, users: merge_request.assignees)
end
private
diff --git a/app/services/merge_requests/reopen_service.rb b/app/services/merge_requests/reopen_service.rb
index f2fddf7f345..52f6d511f98 100644
--- a/app/services/merge_requests/reopen_service.rb
+++ b/app/services/merge_requests/reopen_service.rb
@@ -10,7 +10,7 @@ module MergeRequests
execute_hooks(merge_request, 'reopen')
merge_request.reload_diff(current_user)
merge_request.mark_as_unchecked
- invalidate_cache_counts(merge_request.assignees, merge_request)
+ invalidate_cache_counts(merge_request, users: merge_request.assignees)
end
merge_request
diff --git a/app/services/metrics_service.rb b/app/services/metrics_service.rb
index c92f070601c..a02eee4961b 100644
--- a/app/services/metrics_service.rb
+++ b/app/services/metrics_service.rb
@@ -31,6 +31,6 @@ class MetricsService
end
def multiprocess_metrics_path
- @multiprocess_metrics_path ||= Rails.root.join(ENV['prometheus_multiproc_dir']).freeze
+ ::Prometheus::Client.configuration.multiprocess_files_dir
end
end
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index ed476fc9d0c..bd58a54592f 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -4,7 +4,7 @@ class SystemHooksService
end
def execute_hooks(data, hooks_scope = :all)
- SystemHook.send(hooks_scope).each do |hook|
+ SystemHook.public_send(hooks_scope).find_each do |hook|
hook.async_execute(data, 'system_hooks')
end
end
diff --git a/app/services/test_hook_service.rb b/app/services/test_hook_service.rb
deleted file mode 100644
index 280c81f7d2d..00000000000
--- a/app/services/test_hook_service.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-class TestHookService
- def execute(hook, current_user)
- data = Gitlab::DataBuilder::Push.build_sample(hook.project, current_user)
- hook.execute(data, 'push_hooks')
- end
-end
diff --git a/app/services/test_hooks/base_service.rb b/app/services/test_hooks/base_service.rb
new file mode 100644
index 00000000000..74ba814afff
--- /dev/null
+++ b/app/services/test_hooks/base_service.rb
@@ -0,0 +1,41 @@
+module TestHooks
+ class BaseService
+ attr_accessor :hook, :current_user, :trigger
+
+ def initialize(hook, current_user, trigger)
+ @hook = hook
+ @current_user = current_user
+ @trigger = trigger
+ end
+
+ def execute
+ trigger_data_method = "#{trigger}_data"
+
+ if !self.respond_to?(trigger_data_method, true) ||
+ !hook.class::TRIGGERS.value?(trigger.to_sym)
+
+ return error('Testing not available for this hook')
+ end
+
+ error_message = catch(:validation_error) do
+ sample_data = self.__send__(trigger_data_method)
+
+ return hook.execute(sample_data, trigger)
+ end
+
+ error(error_message)
+ end
+
+ private
+
+ def error(message, http_status = nil)
+ result = {
+ message: message,
+ status: :error
+ }
+
+ result[:http_status] = http_status if http_status
+ result
+ end
+ end
+end
diff --git a/app/services/test_hooks/project_service.rb b/app/services/test_hooks/project_service.rb
new file mode 100644
index 00000000000..01d5d774cd5
--- /dev/null
+++ b/app/services/test_hooks/project_service.rb
@@ -0,0 +1,63 @@
+module TestHooks
+ class ProjectService < TestHooks::BaseService
+ private
+
+ def project
+ @project ||= hook.project
+ end
+
+ def push_events_data
+ throw(:validation_error, 'Ensure the project has at least one commit.') if project.empty_repo?
+
+ Gitlab::DataBuilder::Push.build_sample(project, current_user)
+ end
+
+ alias_method :tag_push_events_data, :push_events_data
+
+ def note_events_data
+ note = project.notes.first
+ throw(:validation_error, 'Ensure the project has notes.') unless note.present?
+
+ Gitlab::DataBuilder::Note.build(note, current_user)
+ end
+
+ def issues_events_data
+ issue = project.issues.first
+ throw(:validation_error, 'Ensure the project has issues.') unless issue.present?
+
+ issue.to_hook_data(current_user)
+ end
+
+ alias_method :confidential_issues_events_data, :issues_events_data
+
+ def merge_requests_events_data
+ merge_request = project.merge_requests.first
+ throw(:validation_error, 'Ensure the project has merge requests.') unless merge_request.present?
+
+ merge_request.to_hook_data(current_user)
+ end
+
+ def job_events_data
+ build = project.builds.first
+ throw(:validation_error, 'Ensure the project has CI jobs.') unless build.present?
+
+ Gitlab::DataBuilder::Build.build(build)
+ end
+
+ def pipeline_events_data
+ pipeline = project.pipelines.first
+ throw(:validation_error, 'Ensure the project has CI pipelines.') unless pipeline.present?
+
+ Gitlab::DataBuilder::Pipeline.build(pipeline)
+ end
+
+ def wiki_page_events_data
+ page = project.wiki.pages.first
+ if !project.wiki_enabled? || page.blank?
+ throw(:validation_error, 'Ensure the wiki is enabled and has pages.')
+ end
+
+ Gitlab::DataBuilder::WikiPage.build(page, current_user, 'create')
+ end
+ end
+end
diff --git a/app/services/test_hooks/system_service.rb b/app/services/test_hooks/system_service.rb
new file mode 100644
index 00000000000..76c3c19bd74
--- /dev/null
+++ b/app/services/test_hooks/system_service.rb
@@ -0,0 +1,48 @@
+module TestHooks
+ class SystemService < TestHooks::BaseService
+ private
+
+ def project
+ @project ||= begin
+ project = Project.first
+
+ throw(:validation_error, 'Ensure that at least one project exists.') unless project
+
+ project
+ end
+ end
+
+ def push_events_data
+ if project.empty_repo?
+ throw(:validation_error, "Ensure project \"#{project.human_name}\" has commits.")
+ end
+
+ Gitlab::DataBuilder::Push.build_sample(project, current_user)
+ end
+
+ def tag_push_events_data
+ if project.repository.tags.empty?
+ throw(:validation_error, "Ensure project \"#{project.human_name}\" has tags.")
+ end
+
+ Gitlab::DataBuilder::Push.build_sample(project, current_user)
+ end
+
+ def repository_update_events_data
+ commit = project.commit
+ ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{project.default_branch}"
+
+ unless commit
+ throw(:validation_error, "Ensure project \"#{project.human_name}\" has commits.")
+ end
+
+ change = Gitlab::DataBuilder::Repository.single_change(
+ commit.parent_id || Gitlab::Git::BLANK_SHA,
+ commit.id,
+ ref
+ )
+
+ Gitlab::DataBuilder::Repository.update(project, current_user, [change], [ref])
+ end
+ end
+end
diff --git a/app/services/users/migrate_to_ghost_user_service.rb b/app/services/users/migrate_to_ghost_user_service.rb
index 4628c4c6f6e..3a9c151cf9b 100644
--- a/app/services/users/migrate_to_ghost_user_service.rb
+++ b/app/services/users/migrate_to_ghost_user_service.rb
@@ -50,10 +50,12 @@ module Users
def migrate_issues
user.issues.update_all(author_id: ghost_user.id)
+ Issue.where(last_edited_by_id: user.id).update_all(last_edited_by_id: ghost_user.id)
end
def migrate_merge_requests
user.merge_requests.update_all(author_id: ghost_user.id)
+ MergeRequest.where(merge_user_id: user.id).update_all(merge_user_id: ghost_user.id)
end
def migrate_notes
diff --git a/app/services/web_hook_service.rb b/app/services/web_hook_service.rb
index 4241b912d5b..a5110a23cad 100644
--- a/app/services/web_hook_service.rb
+++ b/app/services/web_hook_service.rb
@@ -39,7 +39,11 @@ class WebHookService
execution_duration: Time.now - start_time
)
- [response.code, response.to_s]
+ {
+ status: :success,
+ http_status: response.code,
+ message: response.to_s
+ }
rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout => e
log_execution(
trigger: hook_name,
@@ -52,7 +56,10 @@ class WebHookService
Rails.logger.error("WebHook Error => #{e}")
- [nil, e.to_s]
+ {
+ status: :error,
+ message: e.to_s
+ }
end
def async_execute
diff --git a/app/services/wiki_pages/base_service.rb b/app/services/wiki_pages/base_service.rb
index 14317ea65c8..260c04a8b94 100644
--- a/app/services/wiki_pages/base_service.rb
+++ b/app/services/wiki_pages/base_service.rb
@@ -1,23 +1,9 @@
module WikiPages
class BaseService < ::BaseService
- def hook_data(page, action)
- hook_data = {
- object_kind: page.class.name.underscore,
- user: current_user.hook_attrs,
- project: @project.hook_attrs,
- wiki: @project.wiki.hook_attrs,
- object_attributes: page.hook_attrs
- }
-
- page_url = Gitlab::UrlBuilder.build(page)
- hook_data[:object_attributes].merge!(url: page_url, action: action)
- hook_data
- end
-
private
def execute_hooks(page, action = 'create')
- page_data = hook_data(page, action)
+ page_data = Gitlab::DataBuilder::WikiPage.build(page, current_user, action)
@project.execute_hooks(page_data, :wiki_page_hooks)
@project.execute_services(page_data, :wiki_page_hooks)
end
diff --git a/app/uploaders/gitlab_uploader.rb b/app/uploaders/gitlab_uploader.rb
index 0da7a025591..05a2091633a 100644
--- a/app/uploaders/gitlab_uploader.rb
+++ b/app/uploaders/gitlab_uploader.rb
@@ -16,7 +16,7 @@ class GitlabUploader < CarrierWave::Uploader::Base
def self.base_dir
return root_dir unless file_storage?
- File.join(root_dir, 'system')
+ File.join(root_dir, '-', 'system')
end
def self.file_storage?
diff --git a/app/uploaders/personal_file_uploader.rb b/app/uploaders/personal_file_uploader.rb
index 7f857765fbf..ef70871624b 100644
--- a/app/uploaders/personal_file_uploader.rb
+++ b/app/uploaders/personal_file_uploader.rb
@@ -3,6 +3,10 @@ class PersonalFileUploader < FileUploader
File.join(CarrierWave.root, model_path(model))
end
+ def self.base_dir
+ File.join(root_dir, 'system')
+ end
+
private
def secure_url
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index 26f7c1a473a..8bb2a563990 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -315,7 +315,9 @@
%fieldset
%legend Metrics - Prometheus
%p
- Enable a Prometheus metrics endpoint at `#{metrics_path}` to expose a variety of statistics on the health and performance of GitLab. Additional information on authenticating and connecting to the metrics endpoint is available
+ Enable a Prometheus metrics endpoint at
+ %code= metrics_path
+ to expose a variety of statistics on the health and performance of GitLab. Additional information on authenticating and connecting to the metrics endpoint is available
= link_to 'here', admin_health_check_path
\. This setting requires a
= link_to 'restart', help_page_path('administration/restart_gitlab')
@@ -327,10 +329,13 @@
= f.label :prometheus_metrics_enabled do
= f.check_box :prometheus_metrics_enabled
Enable Prometheus Metrics
- - unless Gitlab::Metrics.metrics_folder_present?
- .help-block
- %strong.cred WARNING:
- Environment variable `prometheus_multiproc_dir` does not exist or is not pointing to a valid directory.
+ - unless Gitlab::Metrics.metrics_folder_present?
+ .help-block
+ %strong.cred WARNING:
+ Environment variable
+ %code prometheus_multiproc_dir
+ does not exist or is not pointing to a valid directory.
+ = link_to icon('question-circle'), help_page_path('administration/monitoring/prometheus/gitlab_metrics', anchor: 'metrics-shared-directory')
%fieldset
%legend Profiling - Performance Bar
diff --git a/app/views/admin/applications/edit.html.haml b/app/views/admin/applications/edit.html.haml
index c596866bde2..13b583e6072 100644
--- a/app/views/admin/applications/edit.html.haml
+++ b/app/views/admin/applications/edit.html.haml
@@ -1,4 +1,5 @@
- page_title "Edit", @application.name, "Applications"
+
%h3.page-title Edit application
- @url = admin_application_path(@application)
= render 'form', application: @application
diff --git a/app/views/admin/applications/new.html.haml b/app/views/admin/applications/new.html.haml
index 6310d89bd6b..346c58877d9 100644
--- a/app/views/admin/applications/new.html.haml
+++ b/app/views/admin/applications/new.html.haml
@@ -1,4 +1,6 @@
+- breadcrumb_title "Applications"
- page_title "New Application"
+
%h3.page-title New application
- @url = admin_applications_path
= render 'form', application: @application
diff --git a/app/views/admin/broadcast_messages/edit.html.haml b/app/views/admin/broadcast_messages/edit.html.haml
index 45e053eb31d..8cbc4597e32 100644
--- a/app/views/admin/broadcast_messages/edit.html.haml
+++ b/app/views/admin/broadcast_messages/edit.html.haml
@@ -1,3 +1,4 @@
+- breadcrumb_title "Messages"
- page_title "Broadcast Messages"
= render 'form'
diff --git a/app/views/admin/broadcast_messages/index.html.haml b/app/views/admin/broadcast_messages/index.html.haml
index 4f2ae081d7a..b806882eee3 100644
--- a/app/views/admin/broadcast_messages/index.html.haml
+++ b/app/views/admin/broadcast_messages/index.html.haml
@@ -1,3 +1,4 @@
+- breadcrumb_title "Messages"
- page_title "Broadcast Messages"
%h3.page-title
diff --git a/app/views/admin/hooks/edit.html.haml b/app/views/admin/hooks/edit.html.haml
index 0e35a1905bf..665e8c7e74f 100644
--- a/app/views/admin/hooks/edit.html.haml
+++ b/app/views/admin/hooks/edit.html.haml
@@ -12,7 +12,7 @@
= render partial: 'form', locals: { form: f, hook: @hook }
.form-actions
= f.submit 'Save changes', class: 'btn btn-create'
- = link_to 'Test hook', test_admin_hook_path(@hook), class: 'btn btn-default'
+ = render 'shared/web_hooks/test_button', triggers: SystemHook::TRIGGERS, hook: @hook
= link_to 'Remove', admin_hook_path(@hook), method: :delete, class: 'btn btn-remove pull-right', data: { confirm: 'Are you sure?' }
%hr
diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml
index e92b8bc39f4..fed6002528d 100644
--- a/app/views/admin/hooks/index.html.haml
+++ b/app/views/admin/hooks/index.html.haml
@@ -22,12 +22,12 @@
- @hooks.each do |hook|
%li
.controls
- = link_to 'Test hook', test_admin_hook_path(hook), class: 'btn btn-sm'
+ = render 'shared/web_hooks/test_button', triggers: SystemHook::TRIGGERS, hook: hook, button_class: 'btn-small'
= link_to 'Edit', edit_admin_hook_path(hook), class: 'btn btn-sm'
= link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: 'btn btn-remove btn-sm'
.monospace= hook.url
%div
- - %w(repository_update_events push_events tag_push_events issues_events note_events merge_requests_events job_events).each do |trigger|
- - if hook.send(trigger)
- %span.label.label-gray= trigger.titleize
+ - SystemHook::TRIGGERS.each_value do |event|
+ - if hook.public_send(event)
+ %span.label.label-gray= event.to_s.titleize
%span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? 'enabled' : 'disabled'}
diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml
index 2da8f615470..126550ee10e 100644
--- a/app/views/admin/runners/index.html.haml
+++ b/app/views/admin/runners/index.html.haml
@@ -2,26 +2,6 @@
= render "admin/dashboard/head"
%div{ class: container_class }
-
- %p.prepend-top-default
- %span
- To register a new Runner you should enter the following registration
- token.
- With this token the Runner will request a unique Runner token and use
- that for future communication.
- %br
- Registration token is
- %code#runners-token= current_application_settings.runners_registration_token
-
- .bs-callout.clearfix
- .pull-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?' }
-
.bs-callout
%p
A 'Runner' is a process which runs a job.
@@ -46,6 +26,19 @@
%span.label.label-danger paused
\- Runner will not receive any new jobs
+ .bs-callout.clearfix
+ .pull-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?") }
+
+ = render partial: 'ci/runner/how_to_setup_runner',
+ locals: { registration_token: current_application_settings.runners_registration_token,
+ type: 'shared' }
+
.append-bottom-20.clearfix
.pull-left
= form_tag admin_runners_path, id: 'runners-search', class: 'form-inline', method: :get do
diff --git a/app/views/ci/runner/_how_to_setup_runner.html.haml b/app/views/ci/runner/_how_to_setup_runner.html.haml
new file mode 100644
index 00000000000..b75dab0acc5
--- /dev/null
+++ b/app/views/ci/runner/_how_to_setup_runner.html.haml
@@ -0,0 +1,16 @@
+- link = link_to _("GitLab Runner section"), 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'
+.bs-callout.help-callout
+ %h4= _("How to setup a #{type} Runner for a new project")
+
+ %ol
+ %li
+ = _("Install a Runner compatible with GitLab CI")
+ = (_("(checkout the %{link} for information on how to install it).") % { link: link }).html_safe
+ %li
+ = _("Specify the following URL during the Runner setup:")
+ %code= root_url(only_path: false)
+ %li
+ = _("Use the following registration token during setup:")
+ %code#registration_token= registration_token
+ %li
+ = _("Start the Runner!")
diff --git a/app/views/dashboard/_groups_head.html.haml b/app/views/dashboard/_groups_head.html.haml
index 4594c52b34b..5a379eae8f4 100644
--- a/app/views/dashboard/_groups_head.html.haml
+++ b/app/views/dashboard/_groups_head.html.haml
@@ -1,3 +1,7 @@
+- if show_new_nav? && current_user.can_create_group?
+ - content_for :breadcrumbs_extra do
+ = link_to "New group", new_group_path, class: "btn btn-new"
+
.top-area
%ul.nav-links
= nav_link(page: dashboard_groups_path) do
@@ -6,9 +10,8 @@
= nav_link(page: explore_groups_path) do
= link_to explore_groups_path, title: 'Explore public groups' do
Explore public groups
- .nav-controls
+ .nav-controls{ class: ("nav-controls-new-nav" if show_new_nav?) }
= render 'shared/groups/search_form'
= render 'shared/groups/dropdown'
- if current_user.can_create_group?
- = link_to new_group_path, class: "btn btn-new" do
- New group
+ = link_to "New group", new_group_path, class: "btn btn-new #{("visible-xs" if show_new_nav?)}"
diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml
index 64b737ee886..1f9a5b401b6 100644
--- a/app/views/dashboard/_projects_head.html.haml
+++ b/app/views/dashboard/_projects_head.html.haml
@@ -1,5 +1,10 @@
= content_for :flash_message do
= render 'shared/project_limit'
+
+- if show_new_nav? && current_user.can_create_project?
+ - content_for :breadcrumbs_extra do
+ = link_to "New project", new_project_path, class: 'btn btn-new'
+
.top-area.scrolling-tabs-container.inner-page-scroll-tabs
.fade-left= icon('angle-left')
.fade-right= icon('angle-right')
@@ -14,9 +19,8 @@
= link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do
Explore projects
- .nav-controls
+ .nav-controls{ class: ("nav-controls-new-nav" if show_new_nav?) }
= render 'shared/projects/search_form'
= render 'shared/projects/dropdown'
- if current_user.can_create_project?
- = link_to new_project_path, class: 'btn btn-new' do
- New project
+ = link_to "New project", new_project_path, class: "btn btn-new #{("visible-xs" if show_new_nav?)}"
diff --git a/app/views/dashboard/_snippets_head.html.haml b/app/views/dashboard/_snippets_head.html.haml
index 02e90bbfa55..fd5389106bb 100644
--- a/app/views/dashboard/_snippets_head.html.haml
+++ b/app/views/dashboard/_snippets_head.html.haml
@@ -1,3 +1,7 @@
+- if show_new_nav? && current_user
+ - content_for :breadcrumbs_extra do
+ = link_to "New snippet", new_snippet_path, class: "btn btn-new", title: "New snippet"
+
.top-area
%ul.nav-links
= nav_link(page: dashboard_snippets_path, html_options: {class: 'home'}) do
@@ -8,6 +12,5 @@
Explore Snippets
- if current_user
- .nav-controls.hidden-xs
- = link_to new_snippet_path, class: "btn btn-new", title: "New snippet" do
- New snippet
+ .nav-controls.hidden-xs{ class: ("hidden-sm hidden-md hidden-lg" if show_new_nav?) }
+ = link_to "New snippet", new_snippet_path, class: "btn btn-new", title: "New snippet"
diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml
index d6b46dee0e4..52e0012fd7d 100644
--- a/app/views/dashboard/issues.html.haml
+++ b/app/views/dashboard/issues.html.haml
@@ -1,11 +1,18 @@
+- @hide_top_links = true
- page_title "Issues"
- header_title "Issues", issues_dashboard_path(assignee_id: current_user.id)
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{current_user.name} issues")
+- if show_new_nav?
+ - content_for :breadcrumbs_extra do
+ = link_to params.merge(rss_url_options), class: 'btn has-tooltip append-right-10', title: 'Subscribe' do
+ = icon('rss')
+ = render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", with_feature_enabled: 'issues'
+
.top-area
= render 'shared/issuable/nav', type: :issues
- .nav-controls
+ .nav-controls{ class: ("visible-xs" if show_new_nav?) }
= link_to params.merge(rss_url_options), class: 'btn has-tooltip', title: 'Subscribe' do
= icon('rss')
= render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", with_feature_enabled: 'issues'
diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml
index 6f6afe161d1..c3fe14da2b2 100644
--- a/app/views/dashboard/merge_requests.html.haml
+++ b/app/views/dashboard/merge_requests.html.haml
@@ -1,9 +1,14 @@
+- @hide_top_links = true
- page_title "Merge Requests"
- header_title "Merge Requests", merge_requests_dashboard_path(assignee_id: current_user.id)
+- if show_new_nav?
+ - content_for :breadcrumbs_extra do
+ = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", with_feature_enabled: 'merge_requests'
+
.top-area
= render 'shared/issuable/nav', type: :merge_requests
- .nav-controls
+ .nav-controls{ class: ("visible-xs" if show_new_nav?) }
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", with_feature_enabled: 'merge_requests'
= render 'shared/issuable/filter', type: :merge_requests
diff --git a/app/views/dashboard/milestones/index.html.haml b/app/views/dashboard/milestones/index.html.haml
index ef1467c4d78..37dbcaf5cb8 100644
--- a/app/views/dashboard/milestones/index.html.haml
+++ b/app/views/dashboard/milestones/index.html.haml
@@ -2,10 +2,14 @@
- page_title 'Milestones'
- header_title 'Milestones', dashboard_milestones_path
+- if show_new_nav?
+ - content_for :breadcrumbs_extra do
+ = render 'shared/new_project_item_select', path: 'milestones/new', label: 'New milestone', include_groups: true
+
.top-area
= render 'shared/milestones_filter', counts: @milestone_states
- .nav-controls
+ .nav-controls{ class: ("visible-xs" if show_new_nav?) }
= render 'shared/new_project_item_select', path: 'milestones/new', label: 'New milestone', include_groups: true
.milestones
diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml
index 7ac6cf06fb9..ec6cb1a9624 100644
--- a/app/views/dashboard/projects/index.html.haml
+++ b/app/views/dashboard/projects/index.html.haml
@@ -1,6 +1,5 @@
- @no_container = true
- @hide_top_links = true
-- @breadcrumb_title = "Projects"
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml
index 99efe9c9b86..ae1d733a516 100644
--- a/app/views/dashboard/projects/starred.html.haml
+++ b/app/views/dashboard/projects/starred.html.haml
@@ -1,5 +1,6 @@
+- @hide_top_links = true
- @no_container = true
-
+- breadcrumb_title "Projects"
- page_title "Starred Projects"
- header_title "Projects", dashboard_projects_path
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index 52d6ebd8a14..9b615ec999e 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -1,3 +1,4 @@
+- @hide_top_links = true
- page_title "Todos"
- header_title "Todos", dashboard_todos_path
diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml
index e80d10dc8f1..bfd7dd25a7d 100644
--- a/app/views/devise/shared/_omniauth_box.html.haml
+++ b/app/views/devise/shared/_omniauth_box.html.haml
@@ -7,6 +7,6 @@
%span.light
- has_icon = provider_has_icon?(provider)
= link_to provider_image_tag(provider), omniauth_authorize_path(:user, provider), method: :post, class: 'oauth-login' + (has_icon ? ' oauth-image-link' : ' btn'), id: "oauth-login-#{provider}"
- %fieldset
+ %fieldset.prepend-top-10
= check_box_tag :remember_me
- = label_tag :remember_me, 'Remember Me'
+ = label_tag :remember_me, 'Remember me'
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index ffe07b217a7..2651ef37e67 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -1,3 +1,4 @@
+- @hide_top_links = true
- page_title "Groups"
- header_title "Groups", dashboard_groups_path
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index ec461755103..f00802e0af7 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -1,3 +1,4 @@
+- @hide_top_links = true
- page_title "Projects"
- header_title "Projects", dashboard_projects_path
diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml
index ec461755103..f00802e0af7 100644
--- a/app/views/explore/projects/starred.html.haml
+++ b/app/views/explore/projects/starred.html.haml
@@ -1,3 +1,4 @@
+- @hide_top_links = true
- page_title "Projects"
- header_title "Projects", dashboard_projects_path
diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml
index ec461755103..f00802e0af7 100644
--- a/app/views/explore/projects/trending.html.haml
+++ b/app/views/explore/projects/trending.html.haml
@@ -1,3 +1,4 @@
+- @hide_top_links = true
- page_title "Projects"
- header_title "Projects", dashboard_projects_path
diff --git a/app/views/explore/snippets/index.html.haml b/app/views/explore/snippets/index.html.haml
index e5706d04736..94fc4ac21d2 100644
--- a/app/views/explore/snippets/index.html.haml
+++ b/app/views/explore/snippets/index.html.haml
@@ -1,3 +1,4 @@
+- @hide_top_links = true
- page_title "Snippets"
- header_title "Snippets", snippets_path
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index 182dbe2f98a..735d9390699 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -1,12 +1,19 @@
- page_title "Issues"
+- group_issues_exists = group_issues(@group).exists?
= render "head_issues"
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{@group.name} issues")
-- if group_issues(@group).exists?
+- if show_new_nav? && group_issues_exists
+ - content_for :breadcrumbs_extra do
+ = link_to params.merge(rss_url_options), class: 'btn btn-default append-right-10' do
+ = icon('rss')
+ = render 'shared/new_project_item_select', path: 'issues/new', label: "New issue"
+
+- if group_issues_exists
.top-area
= render 'shared/issuable/nav', type: :issues
- .nav-controls
+ .nav-controls{ class: ("visible-xs" if show_new_nav?) }
= link_to params.merge(rss_url_options), class: 'btn' do
= icon('rss')
%span.icon-label
diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml
index 2bc00fb16c8..50179a47797 100644
--- a/app/views/groups/labels/index.html.haml
+++ b/app/views/groups/labels/index.html.haml
@@ -1,14 +1,18 @@
- page_title 'Labels'
+- if show_new_nav? && can?(current_user, :admin_label, @group)
+ - content_for :breadcrumbs_extra do
+ = link_to "New label", new_group_label_path(@group), class: "btn btn-new"
+
= render "groups/head_issues"
+
.top-area.adjust
.nav-text
Labels can be applied to issues and merge requests. Group labels are available for any project within the group.
- .nav-controls
+ .nav-controls{ class: ("visible-xs" if show_new_nav?) }
- if can?(current_user, :admin_label, @group)
- = link_to new_group_label_path(@group), class: "btn btn-new" do
- New label
+ = link_to "New label", new_group_label_path(@group), class: "btn btn-new"
.labels
.other-labels
diff --git a/app/views/groups/labels/new.html.haml b/app/views/groups/labels/new.html.haml
index 2be87460b1d..ae240490bbd 100644
--- a/app/views/groups/labels/new.html.haml
+++ b/app/views/groups/labels/new.html.haml
@@ -1,3 +1,4 @@
+- breadcrumb_title "Labels"
- page_title 'New Label'
- header_title group_title(@group, 'Labels', group_labels_path(@group))
diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml
index 45e39252e16..997c82c77d9 100644
--- a/app/views/groups/merge_requests.html.haml
+++ b/app/views/groups/merge_requests.html.haml
@@ -1,12 +1,16 @@
- page_title "Merge Requests"
+- if show_new_nav? && current_user
+ - content_for :breadcrumbs_extra do
+ = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request"
+
- if @group_merge_requests.empty?
= render 'shared/empty_states/merge_requests', project_select_button: true
- else
.top-area
= render 'shared/issuable/nav', type: :merge_requests
- if current_user
- .nav-controls
+ .nav-controls{ class: ("visible-xs" if show_new_nav?) }
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request"
= render 'shared/issuable/filter', type: :merge_requests
diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml
index 6ceb4092307..66c6cc9e279 100644
--- a/app/views/groups/milestones/index.html.haml
+++ b/app/views/groups/milestones/index.html.haml
@@ -1,13 +1,16 @@
- page_title "Milestones"
+- if show_new_nav? && can?(current_user, :admin_milestones, @group)
+ - content_for :breadcrumbs_extra do
+ = link_to "New milestone", new_group_milestone_path(@group), class: "btn btn-new"
+
= render "groups/head_issues"
.top-area
= render 'shared/milestones_filter', counts: @milestone_states
- .nav-controls
+ .nav-controls{ class: ("visible-xs" if show_new_nav?) }
- if can?(current_user, :admin_milestones, @group)
- = link_to new_group_milestone_path(@group), class: "btn btn-new" do
- New milestone
+ = link_to "New milestone", new_group_milestone_path(@group), class: "btn btn-new"
.milestones
%ul.content-list
diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml
index e24844661ee..eca7fb9ddb1 100644
--- a/app/views/groups/milestones/new.html.haml
+++ b/app/views/groups/milestones/new.html.haml
@@ -1,3 +1,4 @@
+- breadcrumb_title "Milestones"
- page_title "Milestones"
- header_title group_title(@group, "Milestones", group_milestones_path(@group))
diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml
index 000c7af2326..e9daac95ca1 100644
--- a/app/views/groups/new.html.haml
+++ b/app/views/groups/new.html.haml
@@ -1,3 +1,6 @@
+- @breadcrumb_link = dashboard_groups_path
+- breadcrumb_title "Groups"
+- @hide_top_links = true
- page_title 'New Group'
- header_title "Groups", dashboard_groups_path
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 80a8ba4a755..e07f61c94e4 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -1,4 +1,5 @@
- @no_container = true
+- breadcrumb_title "Group"
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, group_url(@group, rss_url_options), title: "#{@group.name} activity")
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index cc9219cb6fe..873220cc73d 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -10,12 +10,15 @@
- if content_for?(:sub_nav)
= yield :sub_nav
.content-wrapper{ class: "#{(layout_nav_class unless show_new_nav?)}" }
+ - if show_new_nav?
+ .mobile-overlay
.alert-wrapper
= render "layouts/broadcast"
- if show_new_nav?
- if content_for?(:new_global_flash)
= yield :new_global_flash
- = render "layouts/nav/breadcrumbs"
+ - unless @hide_breadcrumbs
+ = render "layouts/nav/breadcrumbs"
= render "layouts/flash"
= yield :flash_message
%div{ class: "#{(container_class unless @no_container)} #{@content_class}" }
diff --git a/app/views/layouts/header/_new.html.haml b/app/views/layouts/header/_new.html.haml
index 4697d91724b..76367bd9734 100644
--- a/app/views/layouts/header/_new.html.haml
+++ b/app/views/layouts/header/_new.html.haml
@@ -6,8 +6,8 @@
%h1.title
= link_to root_path, title: 'Dashboard' do
= brand_header_logo
- %span.hidden-xs
- GitLab
+ %span.logo-text.hidden-xs
+ = render 'shared/logo_type.svg'
- if current_user
= render "layouts/nav/new_dashboard"
diff --git a/app/views/layouts/nav/_breadcrumbs.html.haml b/app/views/layouts/nav/_breadcrumbs.html.haml
index b0c1ab7420f..4db84771f4e 100644
--- a/app/views/layouts/nav/_breadcrumbs.html.haml
+++ b/app/views/layouts/nav/_breadcrumbs.html.haml
@@ -1,19 +1,27 @@
-- breadcrumb_title = @breadcrumb_title || controller.controller_name.humanize
+- breadcrumb_link = breadcrumb_title_link
- hide_top_links = @hide_top_links || false
%nav.breadcrumbs{ role: "navigation" }
.breadcrumbs-container{ class: [container_class, @content_class] }
+ - if defined?(@new_sidebar)
+ = button_tag class: 'toggle-mobile-nav', type: 'button' do
+ %span.sr-only Open sidebar
+ = icon ('bars')
.breadcrumbs-links.js-title-container
- unless hide_top_links
.title
= link_to "GitLab", root_path
\/
+ - if content_for?(:header_title_before)
+ = yield :header_title_before
+ \/
= header_title
%h2.breadcrumbs-sub-title
%ul.list-unstyled
- - if content_for?(:sub_title_before)
- = yield :sub_title_before
- %li= link_to breadcrumb_title, request.path
+ - if @breadcrumbs_extra_links
+ - @breadcrumbs_extra_links.each do |extra|
+ %li= link_to extra[:text], extra[:link]
+ %li= link_to @breadcrumb_title, breadcrumb_link
- if content_for?(:breadcrumbs_extra)
.breadcrumbs-extra.hidden-xs= yield :breadcrumbs_extra
= yield :header_content
diff --git a/app/views/layouts/nav/_new_admin_sidebar.html.haml b/app/views/layouts/nav/_new_admin_sidebar.html.haml
index d7a9e530983..95443de40c2 100644
--- a/app/views/layouts/nav/_new_admin_sidebar.html.haml
+++ b/app/views/layouts/nav/_new_admin_sidebar.html.haml
@@ -1,8 +1,12 @@
.nav-sidebar
- = link_to admin_root_path, title: 'Admin Overview', class: 'context-header' do
- .avatar-container.s40.settings-avatar
- = icon('wrench')
- .project-title Admin Area
+ .context-header
+ = link_to admin_root_path, title: 'Admin Overview' do
+ .avatar-container.s40.settings-avatar
+ = icon('wrench')
+ .project-title Admin Area
+ = button_tag class: 'close-nav-button', type: 'button' do
+ %span.sr-only Close sidebar
+ = icon ('times')
%ul.sidebar-top-level-items
= nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts), html_options: {class: 'home'}) do
= link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do
@@ -13,7 +17,7 @@
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do
= link_to admin_root_path, title: 'Overview' do
%span
- Overview
+ Dashboard
= nav_link(controller: [:admin, :projects]) do
= link_to admin_projects_path, title: 'Projects' do
%span
diff --git a/app/views/layouts/nav/_new_dashboard.html.haml b/app/views/layouts/nav/_new_dashboard.html.haml
index 7109baa4dad..cfdfcbebc9f 100644
--- a/app/views/layouts/nav/_new_dashboard.html.haml
+++ b/app/views/layouts/nav/_new_dashboard.html.haml
@@ -3,7 +3,7 @@
= link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
Projects
- = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
+ = nav_link(controller: ['dashboard/groups', 'explore/groups']) do
= link_to dashboard_groups_path, class: 'dashboard-shortcuts-groups', title: 'Groups' do
Groups
diff --git a/app/views/layouts/nav/_new_group_sidebar.html.haml b/app/views/layouts/nav/_new_group_sidebar.html.haml
index 6e0c45739f1..a7897c09e79 100644
--- a/app/views/layouts/nav/_new_group_sidebar.html.haml
+++ b/app/views/layouts/nav/_new_group_sidebar.html.haml
@@ -1,9 +1,13 @@
.nav-sidebar
- = link_to group_path(@group), title: @group.name, class: 'context-header' do
- .avatar-container.s40.group-avatar
- = image_tag group_icon(@group), class: "avatar s40 avatar-tile"
- .group-title
- = @group.name
+ .context-header
+ = link_to group_path(@group), title: @group.name do
+ .avatar-container.s40.group-avatar
+ = image_tag group_icon(@group), class: "avatar s40 avatar-tile"
+ .group-title
+ = @group.name
+ = button_tag class: 'close-nav-button', type: 'button' do
+ %span.sr-only Close sidebar
+ = icon ('times')
%ul.sidebar-top-level-items
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: 'home' }) do
= link_to group_path(@group), title: 'About group' do
diff --git a/app/views/layouts/nav/_new_profile_sidebar.html.haml b/app/views/layouts/nav/_new_profile_sidebar.html.haml
index 033ea149cfb..239e6b949e2 100644
--- a/app/views/layouts/nav/_new_profile_sidebar.html.haml
+++ b/app/views/layouts/nav/_new_profile_sidebar.html.haml
@@ -1,8 +1,12 @@
.nav-sidebar
- = link_to profile_path, title: 'Profile Settings', class: 'context-header' do
- .avatar-container.s40.settings-avatar
- = icon('user')
- .project-title User Settings
+ .context-header
+ = link_to profile_path, title: 'Profile Settings' do
+ .avatar-container.s40.settings-avatar
+ = icon('user')
+ .project-title User Settings
+ = button_tag class: 'close-nav-button', type: 'button' do
+ %span.sr-only Close sidebar
+ = icon ('times')
%ul.sidebar-top-level-items
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path, title: 'Profile Settings' do
diff --git a/app/views/layouts/nav/_new_project_sidebar.html.haml b/app/views/layouts/nav/_new_project_sidebar.html.haml
index da8f9ce1908..15327847b17 100644
--- a/app/views/layouts/nav/_new_project_sidebar.html.haml
+++ b/app/views/layouts/nav/_new_project_sidebar.html.haml
@@ -1,10 +1,14 @@
.nav-sidebar
- can_edit = can?(current_user, :admin_project, @project)
- = link_to project_path(@project), title: @project.name, class: 'context-header' do
- .avatar-container.s40.project-avatar
- = project_icon(@project, alt: @project.name, class: 'avatar s40 avatar-tile')
- .project-title
- = @project.name
+ .context-header
+ = link_to project_path(@project), title: @project.name do
+ .avatar-container.s40.project-avatar
+ = project_icon(@project, alt: @project.name, class: 'avatar s40 avatar-tile')
+ .project-title
+ = @project.name
+ = button_tag class: 'close-nav-button', type: 'button' do
+ %span.sr-only Close sidebar
+ = icon ('times')
%ul.sidebar-top-level-items
= nav_link(path: ['projects#show', 'projects#activity', 'cycle_analytics#show'], html_options: { class: 'home' }) do
= link_to project_path(@project), title: 'About project', class: 'shortcuts-project' do
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index bd602071384..9aed498a8a0 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -24,6 +24,12 @@
%p
This setting allows you to turn on or off the new upcoming navigation concept.
.col-lg-8.syntax-theme
+ .nav-wip
+ %p
+ The new navigation is currently a work-in-progress concept and is currently only usable on wide-screens. There are a number of improvements that we are working on in order to further refine our navigation.
+ %p
+ %a{ href: 'https://gitlab.com/gitlab-org/gitlab-ce/issues/32794', target: 'blank' } Learn more
+ about the improvements that are coming soon!
= label_tag do
.preview= image_tag "old_nav.png"
%input.js-experiment-feature-toggle{ type: "radio", value: "false", name: "new_nav", checked: !show_new_nav? }
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index bac75a49075..a8ae0b92334 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -1,3 +1,4 @@
+- breadcrumb_title "Profile"
- @content_class = "limit-container-width" unless fluid_layout
= render 'profiles/head'
diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml
index 67792de3870..037cb30efb9 100644
--- a/app/views/profiles/two_factor_auths/show.html.haml
+++ b/app/views/profiles/two_factor_auths/show.html.haml
@@ -1,6 +1,10 @@
- page_title 'Two-Factor Authentication', 'Account'
-- header_title "Two-Factor Authentication", profile_two_factor_auth_path
+- if show_new_nav?
+ - add_to_breadcrumbs("Account", profile_account_path)
+- else
+ - header_title "Two-Factor Authentication", profile_two_factor_auth_path
- @content_class = "limit-container-width" unless fluid_layout
+
= render 'profiles/head'
- if inject_u2f_api?
diff --git a/app/views/projects/activity.html.haml b/app/views/projects/activity.html.haml
index ef8d8051cbf..9e2688e492e 100644
--- a/app/views/projects/activity.html.haml
+++ b/app/views/projects/activity.html.haml
@@ -1,5 +1,8 @@
- @no_container = true
+- if show_new_nav?
+ - add_to_breadcrumbs("Project", project_path(@project))
+
- page_title "Activity"
= render "projects/head"
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index f8cb612a2b4..992fe7f717f 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -1,3 +1,4 @@
+- breadcrumb_title "Repository"
- @no_container = true
- page_title "Edit", @blob.path, @ref
- content_for :page_specific_javascripts do
diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml
index 8620a470041..a4263774dfd 100644
--- a/app/views/projects/blob/new.html.haml
+++ b/app/views/projects/blob/new.html.haml
@@ -1,3 +1,4 @@
+- breadcrumb_title "Repository"
- page_title "New File", @path.presence, @ref
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js')
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index 6e2ae4717cd..7dd834e84b5 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -1,3 +1,4 @@
+- breadcrumb_title "Repository"
- @no_container = true
- page_title @blob.path, @ref
diff --git a/app/views/projects/boards/_show.html.haml b/app/views/projects/boards/_show.html.haml
index 07272ea2df1..2076e46fde8 100644
--- a/app/views/projects/boards/_show.html.haml
+++ b/app/views/projects/boards/_show.html.haml
@@ -3,8 +3,7 @@
- page_title "Boards"
- if show_new_nav?
- - content_for :sub_title_before do
- %li= link_to "Issues", project_issues_path(@project)
+ - add_to_breadcrumbs("Issues", project_issues_path(@project))
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue'
diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml
index 8bc1996452b..945a5c11d6d 100644
--- a/app/views/projects/branches/index.html.haml
+++ b/app/views/projects/branches/index.html.haml
@@ -2,11 +2,15 @@
- page_title "Branches"
= render "projects/commits/head"
+- if show_new_nav?
+ - add_to_breadcrumbs("Repository", project_tree_path(@project))
+
%div{ class: container_class }
.top-area.adjust
- .nav-text
- Protected branches can be managed in
- = link_to 'project settings', project_protected_branches_path(@project)
+ - if can?(current_user, :admin_project, @project)
+ .nav-text
+ Protected branches can be managed in
+ = link_to 'project settings', project_protected_branches_path(@project)
.nav-controls
= form_tag(filter_branches_path, method: :get) do
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index b8547c10c73..844ebb65148 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -1,9 +1,13 @@
- @no_container = true
+- breadcrumb_title _("Commits")
- page_title _("Commits"), @ref
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits")
+- if show_new_nav?
+ - add_to_breadcrumbs("Repository", project_tree_path(@project))
+
= content_for :sub_nav do
= render "head"
diff --git a/app/views/projects/compare/index.html.haml b/app/views/projects/compare/index.html.haml
index 2cf14859f30..05de21e8dbf 100644
--- a/app/views/projects/compare/index.html.haml
+++ b/app/views/projects/compare/index.html.haml
@@ -1,5 +1,7 @@
- @no_container = true
- page_title "Compare"
+- if show_new_nav?
+ - add_to_breadcrumbs("Repository", project_tree_path(@project))
= render "projects/commits/head"
%div{ class: container_class }
diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml
index a1bca2cf83a..8bc863f77b3 100644
--- a/app/views/projects/compare/show.html.haml
+++ b/app/views/projects/compare/show.html.haml
@@ -1,5 +1,8 @@
- @no_container = true
+- breadcrumb_title "Compare"
- page_title "#{params[:from]}...#{params[:to]}"
+- if show_new_nav?
+ - add_to_breadcrumbs("Repository", project_tree_path(@project))
= render "projects/commits/head"
%div{ class: container_class }
diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml
index 7e7b7335597..c704635ead3 100644
--- a/app/views/projects/cycle_analytics/show.html.haml
+++ b/app/views/projects/cycle_analytics/show.html.haml
@@ -1,5 +1,7 @@
- @no_container = true
- page_title "Cycle Analytics"
+- if show_new_nav?
+ - add_to_breadcrumbs("Project", project_path(@project))
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('cycle_analytics')
diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml
index 30cdbc5ae04..d0f723af5bf 100644
--- a/app/views/projects/environments/index.html.haml
+++ b/app/views/projects/environments/index.html.haml
@@ -2,6 +2,9 @@
- page_title "Environments"
= render "projects/pipelines/head"
+- if show_new_nav?
+ - add_to_breadcrumbs("Pipelines", project_pipelines_path(@project))
+
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag("environments")
diff --git a/app/views/projects/environments/new.html.haml b/app/views/projects/environments/new.html.haml
index 24638c77cbb..88f43a1e7e4 100644
--- a/app/views/projects/environments/new.html.haml
+++ b/app/views/projects/environments/new.html.haml
@@ -1,4 +1,5 @@
- @no_container = true
+- breadcrumb_title "Environments"
- page_title 'New Environment'
= render "projects/pipelines/head"
diff --git a/app/views/projects/graphs/charts.html.haml b/app/views/projects/graphs/charts.html.haml
index 464ac34d961..249b9d82ad9 100644
--- a/app/views/projects/graphs/charts.html.haml
+++ b/app/views/projects/graphs/charts.html.haml
@@ -1,5 +1,7 @@
- @no_container = true
- page_title "Charts"
+- if show_new_nav?
+ - add_to_breadcrumbs("Repository", project_tree_path(@project))
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_d3')
= page_specific_javascript_bundle_tag('graphs')
diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml
index 640e0d689ca..4256a8c4d7e 100644
--- a/app/views/projects/graphs/show.html.haml
+++ b/app/views/projects/graphs/show.html.haml
@@ -3,6 +3,10 @@
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_d3')
= page_specific_javascript_bundle_tag('graphs')
+
+- if show_new_nav?
+ - add_to_breadcrumbs("Repository", project_tree_path(@project))
+
= render 'projects/commits/head'
%div{ class: container_class }
diff --git a/app/views/projects/hooks/edit.html.haml b/app/views/projects/hooks/edit.html.haml
index 4944e0c8041..c8c17d2d828 100644
--- a/app/views/projects/hooks/edit.html.haml
+++ b/app/views/projects/hooks/edit.html.haml
@@ -13,9 +13,10 @@
= render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
= f.submit 'Save changes', class: 'btn btn-create'
- = link_to 'Test hook', test_project_hook_path(@project, @hook), class: 'btn btn-default'
+ = render 'shared/web_hooks/test_button', triggers: ProjectHook::TRIGGERS, hook: @hook
= link_to 'Remove', project_hook_path(@project, @hook), method: :delete, class: 'btn btn-remove pull-right', data: { confirm: 'Are you sure?' }
%hr
= render partial: 'projects/hook_logs/index', locals: { hook: @hook, hook_logs: @hook_logs, project: @project }
+
diff --git a/app/views/projects/issues/new.html.haml b/app/views/projects/issues/new.html.haml
index e8aae0f47e2..60fe442014f 100644
--- a/app/views/projects/issues/new.html.haml
+++ b/app/views/projects/issues/new.html.haml
@@ -1,3 +1,4 @@
+- breadcrumb_title "Issues"
- page_title "New Issue"
%h3.page-title
diff --git a/app/views/projects/jobs/_header.html.haml b/app/views/projects/jobs/_header.html.haml
index d81b8f6bb4c..83a2af1dc74 100644
--- a/app/views/projects/jobs/_header.html.haml
+++ b/app/views/projects/jobs/_header.html.haml
@@ -1,7 +1,7 @@
- show_controls = local_assigns.fetch(:show_controls, true)
- pipeline = @build.pipeline
-.content-block.build-header.top-area
+.content-block.build-header.top-area.page-content-header
.header-content
= render 'ci/status/badge', status: @build.detailed_status(current_user), link: false, title: @build.status_title
%strong
diff --git a/app/views/projects/jobs/index.html.haml b/app/views/projects/jobs/index.html.haml
index 8604c7d3ea4..d78891546f7 100644
--- a/app/views/projects/jobs/index.html.haml
+++ b/app/views/projects/jobs/index.html.haml
@@ -2,6 +2,9 @@
- page_title "Jobs"
= render "projects/pipelines/head"
+- if show_new_nav?
+ - add_to_breadcrumbs("Pipelines", project_pipelines_path(@project))
+
%div{ class: container_class }
.top-area
- build_path_proc = ->(scope) { project_jobs_path(@project, scope: scope) }
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index 8fbc4588902..d02ea5cccc3 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -1,6 +1,11 @@
- @no_container = true
- page_title "Labels"
- hide_class = ''
+
+- if show_new_nav? && can?(current_user, :admin_label, @project)
+ - content_for :breadcrumbs_extra do
+ = link_to "New label", new_namespace_project_label_path(@project.namespace, @project), class: "btn btn-new"
+
= render "shared/mr_head"
- if @labels.exists? || @prioritized_labels.exists?
@@ -9,7 +14,7 @@
.nav-text
Labels can be applied to issues and merge requests. Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging.
- .nav-controls
+ .nav-controls{ class: ("visible-xs" if show_new_nav?) }
- if can?(current_user, :admin_label, @project)
= link_to new_project_label_path(@project), class: "btn btn-new" do
New label
diff --git a/app/views/projects/labels/new.html.haml b/app/views/projects/labels/new.html.haml
index 79e90b7ca3b..562b6fb8d8c 100644
--- a/app/views/projects/labels/new.html.haml
+++ b/app/views/projects/labels/new.html.haml
@@ -1,4 +1,5 @@
- @no_container = true
+- breadcrumb_title "Labels"
- page_title "New Label"
= render "shared/mr_head"
diff --git a/app/views/projects/merge_requests/creations/new.html.haml b/app/views/projects/merge_requests/creations/new.html.haml
index 2e798ce780a..3220512d60d 100644
--- a/app/views/projects/merge_requests/creations/new.html.haml
+++ b/app/views/projects/merge_requests/creations/new.html.haml
@@ -1,3 +1,4 @@
+- breadcrumb_title "Merge Requests"
- page_title "New Merge Request"
- if @merge_request.can_be_created && !params[:change_branches]
diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml
index e53fcd6e425..a89387bc8f1 100644
--- a/app/views/projects/milestones/index.html.haml
+++ b/app/views/projects/milestones/index.html.haml
@@ -1,5 +1,10 @@
- @no_container = true
- page_title 'Milestones'
+
+- if show_new_nav?
+ - content_for :breadcrumbs_extra do
+ = link_to "New milestone", new_namespace_project_milestone_path(@project.namespace, @project), class: 'btn btn-new', title: 'New milestone'
+
= render "shared/mr_head"
%div{ class: container_class }
diff --git a/app/views/projects/milestones/new.html.haml b/app/views/projects/milestones/new.html.haml
index 586eb909afa..84ffbc0a926 100644
--- a/app/views/projects/milestones/new.html.haml
+++ b/app/views/projects/milestones/new.html.haml
@@ -1,4 +1,5 @@
- @no_container = true
+- breadcrumb_title "Milestones"
- page_title "New Milestone"
= render "shared/mr_head"
diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml
index e8c26636be9..ab948df4a3f 100644
--- a/app/views/projects/network/show.html.haml
+++ b/app/views/projects/network/show.html.haml
@@ -1,6 +1,9 @@
+- breadcrumb_title "Graph"
- page_title "Graph", @ref
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('network')
+- if show_new_nav?
+ - add_to_breadcrumbs("Repository", project_tree_path(@project))
= render "projects/commits/head"
= render "head"
%div{ class: container_class }
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index b0b7575f0d1..a2d7a21d5f6 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -1,3 +1,6 @@
+- @breadcrumb_link = dashboard_projects_path
+- breadcrumb_title "Projects"
+- @hide_top_links = true
- page_title 'New Project'
- header_title "Projects", dashboard_projects_path
- visibility_level = params.dig(:project, :visibility_level) || default_project_visibility
diff --git a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
index 97c0407a01d..7343d6e039c 100644
--- a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
+++ b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
@@ -4,7 +4,7 @@
= pipeline_schedule.description
%td.branch-name-cell
= icon('code-fork')
- - if pipeline_schedule.ref
+ - if pipeline_schedule.ref.present?
= link_to pipeline_schedule.ref, project_ref_path(@project, pipeline_schedule.ref), class: "ref-name"
%td
- if pipeline_schedule.last_pipeline
diff --git a/app/views/projects/pipeline_schedules/index.html.haml b/app/views/projects/pipeline_schedules/index.html.haml
index c4ee064ac43..8426b29bb14 100644
--- a/app/views/projects/pipeline_schedules/index.html.haml
+++ b/app/views/projects/pipeline_schedules/index.html.haml
@@ -1,9 +1,18 @@
+- breadcrumb_title "Schedules"
+
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'schedules_index'
- @no_container = true
- page_title _("Pipeline Schedules")
+
+- if show_new_nav? && can?(current_user, :create_pipeline_schedule, @project)
+ - content_for :breadcrumbs_extra do
+ = link_to _('New schedule'), new_namespace_project_pipeline_schedule_path(@project.namespace, @project), class: 'btn btn-create'
+
+ - add_to_breadcrumbs("Pipelines", project_pipelines_path(@project))
+
= render "projects/pipelines/head"
%div{ class: container_class }
@@ -13,7 +22,7 @@
= render "tabs", schedule_path_proc: schedule_path_proc, all_schedules: @all_schedules, scope: @scope
- if can?(current_user, :create_pipeline_schedule, @project)
- .nav-controls
+ .nav-controls{ class: ("visible-xs" if show_new_nav?) }
= link_to new_project_pipeline_schedule_path(@project), class: 'btn btn-create' do
%span= _('New schedule')
diff --git a/app/views/projects/pipeline_schedules/new.html.haml b/app/views/projects/pipeline_schedules/new.html.haml
index 87390d4dd02..c7237cb96d8 100644
--- a/app/views/projects/pipeline_schedules/new.html.haml
+++ b/app/views/projects/pipeline_schedules/new.html.haml
@@ -1,5 +1,10 @@
+- breadcrumb_title "Schedules"
+- @breadcrumb_link = namespace_project_pipeline_schedules_path(@project.namespace, @project)
- page_title _("New Pipeline Schedule")
+- if show_new_nav?
+ - add_to_breadcrumbs("Pipelines", project_pipelines_path(@project))
+
%h3.page-title
= _("Schedule a new pipeline")
%hr
diff --git a/app/views/projects/pipelines/charts.html.haml b/app/views/projects/pipelines/charts.html.haml
index 78002e8cd64..fd3ad69d85d 100644
--- a/app/views/projects/pipelines/charts.html.haml
+++ b/app/views/projects/pipelines/charts.html.haml
@@ -1,5 +1,7 @@
- @no_container = true
- page_title _("Charts"), _("Pipelines")
+- if show_new_nav?
+ - add_to_breadcrumbs("Pipelines", project_pipelines_path(@project))
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_d3')
= page_specific_javascript_bundle_tag('graphs')
diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml
index 308f2611e02..c966df62856 100644
--- a/app/views/projects/pipelines/new.html.haml
+++ b/app/views/projects/pipelines/new.html.haml
@@ -1,3 +1,4 @@
+- breadcrumb_title "Pipelines"
- page_title "New Pipeline"
%h3.page-title
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 25153fd0b6f..9f7c5a315eb 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -1,5 +1,8 @@
- page_title "Members"
+- if show_new_nav?
+ - add_to_breadcrumbs("Settings", edit_project_path(@project))
+
.row.prepend-top-default
.col-lg-12
%h4
diff --git a/app/views/projects/protected_branches/shared/_branches_list.html.haml b/app/views/projects/protected_branches/shared/_branches_list.html.haml
index 5c00bb6883c..2a0704bc7af 100644
--- a/app/views/projects/protected_branches/shared/_branches_list.html.haml
+++ b/app/views/projects/protected_branches/shared/_branches_list.html.haml
@@ -1,4 +1,4 @@
-.panel.panel-default.protected-branches-list
+.panel.panel-default.protected-branches-list.js-protected-branches-list
- if @protected_branches.empty?
.panel-heading
%h3.panel-title
@@ -23,6 +23,8 @@
- if can_admin_project
%th
%tbody
+ %tr
+ %td.flash-container{ colspan: 5 }
= yield
= paginate @protected_branches, theme: 'gitlab'
diff --git a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
index b619fa57e05..9f0c4f3b3a8 100644
--- a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
+++ b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
@@ -1,4 +1,4 @@
-= form_for [@project.namespace.becomes(Namespace), @project, @protected_branch] do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @protected_branch], html: { class: 'new-protected-branch js-new-protected-branch' } do |f|
.panel.panel-default
.panel-heading
%h3.panel-title
diff --git a/app/views/projects/protected_tags/shared/_tags_list.html.haml b/app/views/projects/protected_tags/shared/_tags_list.html.haml
index 6e3cd4ada71..3f42ae58438 100644
--- a/app/views/projects/protected_tags/shared/_tags_list.html.haml
+++ b/app/views/projects/protected_tags/shared/_tags_list.html.haml
@@ -1,4 +1,4 @@
-.panel.panel-default.protected-tags-list
+.panel.panel-default.protected-tags-list.js-protected-tags-list
- if @protected_tags.empty?
.panel-heading
%h3.panel-title
diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml
index f8835454140..28ccbf7eb15 100644
--- a/app/views/projects/runners/_specific_runners.html.haml
+++ b/app/views/projects/runners/_specific_runners.html.haml
@@ -1,21 +1,8 @@
%h3 Specific Runners
-.bs-callout.help-callout
- %h4 How to setup a specific Runner for a new project
-
- %ol
- %li
- Install a Runner compatible with GitLab CI
- (checkout the #{link_to 'GitLab Runner section', 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'} for information on how to install it).
- %li
- Specify the following URL during the Runner setup:
- %code= root_url(only_path: false)
- %li
- Use the following registration token during setup:
- %code= @project.runners_token
- %li
- Start the Runner!
-
+= render partial: 'ci/runner/how_to_setup_runner',
+ locals: { registration_token: @project.runners_token,
+ type: 'specific' }
- if @project_runners.any?
%h4.underlined-title Runners activated for this project
diff --git a/app/views/projects/services/edit.html.haml b/app/views/projects/services/edit.html.haml
index 0f1a76a104a..8056217bb1e 100644
--- a/app/views/projects/services/edit.html.haml
+++ b/app/views/projects/services/edit.html.haml
@@ -1,3 +1,8 @@
+- breadcrumb_title "Integrations"
- page_title @service.title, "Services"
+
+- if show_new_nav?
+ - add_to_breadcrumbs("Settings", edit_project_path(@project))
+
= render "projects/settings/head"
= render 'form'
diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml
index 6afb38c5709..0c4130857da 100644
--- a/app/views/projects/settings/ci_cd/show.html.haml
+++ b/app/views/projects/settings/ci_cd/show.html.haml
@@ -1,5 +1,9 @@
- @content_class = "limit-container-width" unless fluid_layout
- page_title "Pipelines"
+
+- if show_new_nav?
+ - add_to_breadcrumbs("Settings", edit_project_path(@project))
+
= render "projects/settings/head"
= render 'projects/runners/index'
diff --git a/app/views/projects/settings/integrations/_project_hook.html.haml b/app/views/projects/settings/integrations/_project_hook.html.haml
index 00700e286c3..d5792e95f5a 100644
--- a/app/views/projects/settings/integrations/_project_hook.html.haml
+++ b/app/views/projects/settings/integrations/_project_hook.html.haml
@@ -3,14 +3,14 @@
.col-md-8.col-lg-7
%strong.light-header= hook.url
%div
- - %w(push_events tag_push_events issues_events confidential_issues_events note_events merge_requests_events job_events pipeline_events wiki_page_events).each do |trigger|
- - if hook.send(trigger)
- %span.label.label-gray.deploy-project-label= trigger.titleize
+ - ProjectHook::TRIGGERS.each_value do |event|
+ - if hook.public_send(event)
+ %span.label.label-gray.deploy-project-label= event.to_s.titleize
.col-md-4.col-lg-5.text-right-lg.prepend-top-5
%span.append-right-10.inline
- SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
- = link_to "Edit", edit_project_hook_path(@project, hook), class: "btn btn-sm"
- = link_to "Test", test_project_hook_path(@project, hook), class: "btn btn-sm"
- = link_to project_hook_path(@project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-transparent" do
+ SSL Verification: #{hook.enable_ssl_verification ? 'enabled' : 'disabled'}
+ = link_to 'Edit', edit_project_hook_path(@project, hook), class: 'btn btn-sm'
+ = render 'shared/web_hooks/test_button', triggers: ProjectHook::TRIGGERS, hook: hook, button_class: 'btn-small'
+ = link_to project_hook_path(@project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-transparent' do
%span.sr-only Remove
= icon('trash')
diff --git a/app/views/projects/settings/integrations/show.html.haml b/app/views/projects/settings/integrations/show.html.haml
index 1d1d0849289..149da96d3f6 100644
--- a/app/views/projects/settings/integrations/show.html.haml
+++ b/app/views/projects/settings/integrations/show.html.haml
@@ -1,5 +1,7 @@
- @content_class = "limit-container-width" unless fluid_layout
- page_title 'Integrations'
+- if show_new_nav?
+ - add_to_breadcrumbs("Settings", edit_project_path(@project))
= render "projects/settings/head"
= render 'projects/hooks/index'
= render 'projects/services/index'
diff --git a/app/views/projects/settings/repository/show.html.haml b/app/views/projects/settings/repository/show.html.haml
index 0f20ecf8c69..cb37f3c7580 100644
--- a/app/views/projects/settings/repository/show.html.haml
+++ b/app/views/projects/settings/repository/show.html.haml
@@ -1,5 +1,9 @@
- page_title "Repository"
- @content_class = "limit-container-width" unless fluid_layout
+
+- if show_new_nav?
+ - add_to_breadcrumbs("Settings", edit_project_path(@project))
+
= render "projects/settings/head"
- content_for :page_specific_javascripts do
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index a73e111ad6d..49d0a6828fe 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -1,4 +1,5 @@
- @no_container = true
+- breadcrumb_title "Project"
- @content_class = "limit-container-width" unless fluid_layout
- flash_message_container = show_new_nav? ? :new_global_flash : :flash_message
diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml
index 4f8ce526c83..ccc5fe80755 100644
--- a/app/views/projects/snippets/index.html.haml
+++ b/app/views/projects/snippets/index.html.haml
@@ -1,19 +1,16 @@
- page_title "Snippets"
+- if show_new_nav? && can?(current_user, :create_project_snippet, @project)
+ - content_for :breadcrumbs_extra do
+ = link_to "New snippet", new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New snippet"
+
- if current_user
.top-area
- include_private = @project.team.member?(current_user) || current_user.admin?
= render partial: 'snippets/snippets_scope_menu', locals: { subject: @project, include_private: include_private }
- .nav-controls.hidden-xs
+ .nav-controls{ class: ("visible-xs" if show_new_nav?) }
- if can?(current_user, :create_project_snippet, @project)
- = link_to new_project_snippet_path(@project), class: "btn btn-new", title: "New snippet" do
- New snippet
-
-- if can?(current_user, :create_project_snippet, @project)
- .visible-xs
- &nbsp;
- = link_to new_project_snippet_path(@project), class: "btn btn-new btn-block", title: "New snippet" do
- New snippet
+ = link_to "New snippet", new_project_snippet_path(@project), class: "btn btn-new", title: "New snippet"
= render 'snippets/snippets'
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index bf97cbc1f68..00000e0667c 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -3,6 +3,9 @@
- page_title "Tags"
= render "projects/commits/head"
+- if show_new_nav?
+ - add_to_breadcrumbs("Repository", project_tree_path(@project))
+
.flex-list{ class: container_class }
.top-area.adjust
.nav-text.row-main-content
diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml
index f727f340bb9..c8587245f88 100644
--- a/app/views/projects/tree/show.html.haml
+++ b/app/views/projects/tree/show.html.haml
@@ -1,4 +1,5 @@
- @no_container = true
+- breadcrumb_title _("Repository")
- @content_class = "limit-container-width" unless fluid_layout
- page_title @path.presence || _("Files"), @ref
diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml
index 13591dd8e74..9dadd685ea2 100644
--- a/app/views/projects/wikis/show.html.haml
+++ b/app/views/projects/wikis/show.html.haml
@@ -1,4 +1,5 @@
- @content_class = "limit-container-width limit-container-width-sm" unless fluid_layout
+- breadcrumb_title "Wiki"
- page_title @page.title.capitalize, "Wiki"
.wiki-page-header.has-sidebar-toggle
diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml
index 215dbb3909e..499697f2777 100644
--- a/app/views/search/show.html.haml
+++ b/app/views/search/show.html.haml
@@ -1,3 +1,5 @@
+- @hide_top_links = true
+- breadcrumb_title "Search"
- page_title @search_term
.prepend-top-default
diff --git a/app/views/shared/_logo_type.svg b/app/views/shared/_logo_type.svg
new file mode 100644
index 00000000000..7e9276a0a83
--- /dev/null
+++ b/app/views/shared/_logo_type.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 617 169" enable-background="new 0 0 617 169"><path d="m315.26 2.969h-21.8l.1 162.5h88.3v-20.1h-66.5l-.1-142.4"/><path d="m-511.44-6.331c-5.5 5.7-14.6 11.4-27 11.4-16.6 0-23.3-8.2-23.3-18.9 0-16.1 11.2-23.8 35-23.8 4.5 0 11.7.5 15.4 1.2v30.1h-.1m-22.6-98.5c-17.6 0-33.8 6.2-46.4 16.7l7.7 13.4c8.9-5.2 19.8-10.4 35.5-10.4 17.9 0 25.8 9.2 25.8 24.6v7.9c-3.5-.7-10.7-1.2-15.1-1.2-38.2 0-57.6 13.4-57.6 41.4 0 25.1 15.4 37.7 38.7 37.7 15.7 0 30.8-7.2 36-18.9l4 15.9h15.4v-83.2c-.1-26.3-11.5-43.9-44-43.9" transform="translate(977.33 143.28)"/><path d="m-542.14 5.969c-8.2 0-15.4-1-20.8-3.5v-67.3-7.8c7.4-6.2 16.6-10.7 28.3-10.7 21.1 0 29.2 14.9 29.2 39 0 34.2-13.1 50.3-36.7 50.3m9.2-110.6c-19.5 0-30 13.3-30 13.3v-21l-.1-27.8h-9.8-11.5l.1 158.5c10.7 4.5 25.3 6.9 41.2 6.9 40.7 0 60.3-26 60.3-70.9-.1-35.5-18.2-59-50.2-59" transform="translate(1099.77 143.13)"/><path d="m-506.14-123.03c19.3 0 31.8 6.4 39.9 12.9l9.4-16.3c-12.7-11.2-29.9-17.2-48.3-17.2-46.4 0-78.9 28.3-78.9 85.4 0 59.8 35.1 83.1 75.2 83.1 20.1 0 37.2-4.7 48.4-9.4l-.5-63.9v-7.5-12.6h-59.5v20.1h38l.5 48.5c-5 2.5-13.6 4.5-25.3 4.5-32.2 0-53.8-20.3-53.8-63-.1-43.5 22.2-64.6 54.9-64.6" transform="translate(584.04 143.63)"/><path d="m-562.14-139.63h-21.3l.1 27.3v11.2 6.5 11.4 65 .2c0 26.3 11.4 43.9 43.9 43.9 4.5 0 8.9-.4 13.1-1.2v-19.1c-3.1.5-6.4.7-9.9.7-17.9 0-25.8-9.2-25.8-24.6v-65h35.7v-17.8h-35.7l-.1-38.5" transform="translate(793.57 142.58)"/><path d="m155.96 165.47h21.3v-124h-21.3v124"/><path d="m155.96 24.369h21.3v-21.3h-21.3v21.3"/></svg>
diff --git a/app/views/shared/_new_project_item_select.html.haml b/app/views/shared/_new_project_item_select.html.haml
index 9ed844cf5e7..c1acee1a211 100644
--- a/app/views/shared/_new_project_item_select.html.haml
+++ b/app/views/shared/_new_project_item_select.html.haml
@@ -1,19 +1,6 @@
- if @projects.any?
.project-item-select-holder
= project_select_tag :project_path, class: "project-item-select", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at' }, with_feature_enabled: local_assigns[:with_feature_enabled]
- %a.btn.btn-new.new-project-item-select-button
+ %a.btn.btn-new.new-project-item-select-button{ data: { relative_path: local_assigns[:path] } }
= local_assigns[:label]
= icon('caret-down')
-
- :javascript
- $('.new-project-item-select-button').on('click', function() {
- $('.project-item-select').select2('open');
- });
-
- var relativePath = '#{local_assigns[:path]}';
-
- $('.project-item-select').on('click', function() {
- window.location = $(this).val() + '/' + relativePath;
- });
-
- new ProjectSelect()
diff --git a/app/views/shared/empty_states/_issues.html.haml b/app/views/shared/empty_states/_issues.html.haml
index 046b127f73c..b0c0ab523c7 100644
--- a/app/views/shared/empty_states/_issues.html.haml
+++ b/app/views/shared/empty_states/_issues.html.haml
@@ -16,7 +16,8 @@
Also, issues are searchable and filterable.
- if project_select_button
= render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue'
- = link_to 'New issue', button_path, class: 'btn btn-new', title: 'New issue', id: 'new_issue_link'
+ - else
+ = link_to 'New issue', button_path, class: 'btn btn-new', title: 'New issue', id: 'new_issue_link'
- else
.text-center
%h4 There are no issues to show.
diff --git a/app/views/shared/issuable/_bulk_update_sidebar.html.haml b/app/views/shared/issuable/_bulk_update_sidebar.html.haml
index 964fe5220f7..0d507cc7a6e 100644
--- a/app/views/shared/issuable/_bulk_update_sidebar.html.haml
+++ b/app/views/shared/issuable/_bulk_update_sidebar.html.haml
@@ -1,9 +1,9 @@
- type = local_assigns.fetch(:type)
-%aside.issues-bulk-update.js-right-sidebar.right-sidebar.affix-top{ data: { "offset-top" => "50", "spy" => "affix" }, "aria-live" => "polite" }
+%aside.issues-bulk-update.js-right-sidebar.right-sidebar{ "aria-live" => "polite", data: { 'signed-in': current_user.present? } }
.issuable-sidebar.hidden
= form_tag [:bulk_update, @project.namespace.becomes(Namespace), @project, type], method: :post, class: "bulk-update" do
- .block
+ .block.issuable-sidebar-header
.filter-item.inline.update-issues-btn.pull-left
= button_tag "Update all", class: "btn update-selected-issues btn-info", disabled: true
= button_tag "Cancel", class: "btn btn-default js-bulk-update-menu-hide pull-right"
diff --git a/app/views/shared/web_hooks/_test_button.html.haml b/app/views/shared/web_hooks/_test_button.html.haml
new file mode 100644
index 00000000000..cf1d5e061c6
--- /dev/null
+++ b/app/views/shared/web_hooks/_test_button.html.haml
@@ -0,0 +1,12 @@
+- triggers = local_assigns.fetch(:triggers)
+- button_class = local_assigns.fetch(:button_class, '')
+- hook = local_assigns.fetch(:hook)
+
+.hook-test-button.dropdown.inline
+ %button.btn{ 'data-toggle' => 'dropdown', class: button_class }
+ Test
+ = icon('caret-down')
+ %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' }
+ - triggers.each_value do |event|
+ %li
+ = link_to_test_hook(hook, event)
diff --git a/app/views/snippets/new.html.haml b/app/views/snippets/new.html.haml
index ca8afb4bb6a..f01915107e3 100644
--- a/app/views/snippets/new.html.haml
+++ b/app/views/snippets/new.html.haml
@@ -1,3 +1,5 @@
+- @hide_top_links = true
+- breadcrumb_title "Snippets"
- page_title "New Snippet"
%h3.page-title
New Snippet
diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml
index 8818590362d..706f13dd004 100644
--- a/app/views/snippets/show.html.haml
+++ b/app/views/snippets/show.html.haml
@@ -1,3 +1,4 @@
+- @hide_top_links = true
- @content_class = "limit-container-width limited-inner-width-container" unless fluid_layout
- page_title "#{@snippet.title} (#{@snippet.to_reference})", "Snippets"
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index f246bd7a586..919ba5d15d3 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,3 +1,5 @@
+- @hide_top_links = true
+- @hide_breadcrumbs = true
- page_title @user.name
- page_description @user.bio
- content_for :page_specific_javascripts do
diff --git a/changelogs/unreleased/12151-add-since-and-until-params-to-issuables.yml b/changelogs/unreleased/12151-add-since-and-until-params-to-issuables.yml
deleted file mode 100644
index 2c915e62357..00000000000
--- a/changelogs/unreleased/12151-add-since-and-until-params-to-issuables.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Added "created_after" and "created_before" params to issuables
-merge_request: 12151
-author: Kyle Bishop @kybishop
diff --git a/changelogs/unreleased/12200-add-french-translation.yml b/changelogs/unreleased/12200-add-french-translation.yml
deleted file mode 100644
index f31d982e0b9..00000000000
--- a/changelogs/unreleased/12200-add-french-translation.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: "Adding French translations"
-merge_request: 12200
-author : Erwan "Dremor" Georget
diff --git a/changelogs/unreleased/13336-multiple-broadcast-messages.yml b/changelogs/unreleased/13336-multiple-broadcast-messages.yml
deleted file mode 100644
index 7dc73e1c6ea..00000000000
--- a/changelogs/unreleased/13336-multiple-broadcast-messages.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Display all current broadcast messages, not just the last one
-merge_request: 11113
-author: rickettm
diff --git a/changelogs/unreleased/18000-remember-me-for-oauth-login.yml b/changelogs/unreleased/18000-remember-me-for-oauth-login.yml
deleted file mode 100644
index 1ef92756a76..00000000000
--- a/changelogs/unreleased/18000-remember-me-for-oauth-login.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Honor the "Remember me" parameter for OAuth-based login
-merge_request: 11963
-author:
diff --git a/changelogs/unreleased/20628-add-oauth-implicit-grant.yml b/changelogs/unreleased/20628-add-oauth-implicit-grant.yml
deleted file mode 100644
index 58a28142feb..00000000000
--- a/changelogs/unreleased/20628-add-oauth-implicit-grant.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: "#20628 Enable implicit grant in GitLab as OAuth Provider"
-merge_request: 12384
-author: Mateusz Pytel
diff --git a/changelogs/unreleased/20817-please-add-coordinator-url-to-admin-area-runner-page.yml b/changelogs/unreleased/20817-please-add-coordinator-url-to-admin-area-runner-page.yml
new file mode 100644
index 00000000000..c4c3fc7ceb2
--- /dev/null
+++ b/changelogs/unreleased/20817-please-add-coordinator-url-to-admin-area-runner-page.yml
@@ -0,0 +1,4 @@
+---
+title: Add coordinator url to admin area runner page
+merge_request: 11603
+author:
diff --git a/changelogs/unreleased/23036-replace-dashboard-event-filters-spinach.yml b/changelogs/unreleased/23036-replace-dashboard-event-filters-spinach.yml
new file mode 100644
index 00000000000..807cd097178
--- /dev/null
+++ b/changelogs/unreleased/23036-replace-dashboard-event-filters-spinach.yml
@@ -0,0 +1,4 @@
+---
+title: Replaces dashboard/event_filters.feature spinach with rspec
+merge_request: 12651
+author: Alexander Randa (@randaalex)
diff --git a/changelogs/unreleased/23036-replace-dashboard-mr-spinach.yml b/changelogs/unreleased/23036-replace-dashboard-mr-spinach.yml
deleted file mode 100644
index 07c201de96e..00000000000
--- a/changelogs/unreleased/23036-replace-dashboard-mr-spinach.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Replace 'dashboard/merge_requests' spinach with rspec
-merge_request: 12440
-author: Alexander Randa (@randaalex)
diff --git a/changelogs/unreleased/23036-replace-dashboard-new-project-spinach.yml b/changelogs/unreleased/23036-replace-dashboard-new-project-spinach.yml
deleted file mode 100644
index a5f78202c93..00000000000
--- a/changelogs/unreleased/23036-replace-dashboard-new-project-spinach.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Replace 'dashboard/new-project.feature' spinach with rspec
-merge_request: 12550
-author: Alexander Randa (@randaalex)
diff --git a/changelogs/unreleased/23036-replace-dashboard-spinach.yml b/changelogs/unreleased/23036-replace-dashboard-spinach.yml
new file mode 100644
index 00000000000..b3197c4cfa6
--- /dev/null
+++ b/changelogs/unreleased/23036-replace-dashboard-spinach.yml
@@ -0,0 +1,4 @@
+---
+title: Replaces dashboard/dashboard.feature spinach with rspec
+merge_request: 12876
+author: Alexander Randa (@randaalex)
diff --git a/changelogs/unreleased/23036-replace-dashboard-todo-spinach.yml b/changelogs/unreleased/23036-replace-dashboard-todo-spinach.yml
deleted file mode 100644
index 65df9a836a5..00000000000
--- a/changelogs/unreleased/23036-replace-dashboard-todo-spinach.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Replace 'dashboard/todos' spinach with rspec
-merge_request: 12453
-author: Alexander Randa (@randaalex)
diff --git a/changelogs/unreleased/23036-replace-snippets-spinach.yml b/changelogs/unreleased/23036-replace-snippets-spinach.yml
deleted file mode 100644
index 545805b1302..00000000000
--- a/changelogs/unreleased/23036-replace-snippets-spinach.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Replace 'snippets/snippets.feature' spinach with rspec
-merge_request: 12385
-author: Alexander Randa @randaalex
diff --git a/changelogs/unreleased/23162-allow-creation-of-files-and-dirs-with-spaces-in-web-ui.yml b/changelogs/unreleased/23162-allow-creation-of-files-and-dirs-with-spaces-in-web-ui.yml
deleted file mode 100644
index 442406c3c04..00000000000
--- a/changelogs/unreleased/23162-allow-creation-of-files-and-dirs-with-spaces-in-web-ui.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Allow creation of files and directories with spaces through Web UI
-merge_request: 12608
-author:
diff --git a/changelogs/unreleased/23998-blame-age-map.yml b/changelogs/unreleased/23998-blame-age-map.yml
deleted file mode 100644
index 26a38f0939c..00000000000
--- a/changelogs/unreleased/23998-blame-age-map.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add blame view age mapping
-merge_request: 7198
-author: Jeff Stubler
diff --git a/changelogs/unreleased/2501-ce-port-update-welcome-page.yml b/changelogs/unreleased/2501-ce-port-update-welcome-page.yml
deleted file mode 100644
index cac8a522308..00000000000
--- a/changelogs/unreleased/2501-ce-port-update-welcome-page.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Update welcome page UX for new users
-merge_request: 12662
-author:
diff --git a/changelogs/unreleased/25102-files-view-button.yml b/changelogs/unreleased/25102-files-view-button.yml
deleted file mode 100644
index 4ba815d9464..00000000000
--- a/changelogs/unreleased/25102-files-view-button.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix mobile view of files view buttons
-merge_request:
-author:
diff --git a/changelogs/unreleased/25103-mobile-members-page-user-avatar-is-misaligned.yml b/changelogs/unreleased/25103-mobile-members-page-user-avatar-is-misaligned.yml
deleted file mode 100644
index 6688e79588f..00000000000
--- a/changelogs/unreleased/25103-mobile-members-page-user-avatar-is-misaligned.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Improve members view on mobile
-merge_request: 12619
-author:
diff --git a/changelogs/unreleased/25164-disable-fork-on-project-limit.yml b/changelogs/unreleased/25164-disable-fork-on-project-limit.yml
deleted file mode 100644
index 9fa824b161d..00000000000
--- a/changelogs/unreleased/25164-disable-fork-on-project-limit.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Disable fork button on project limit
-merge_request: 12145
-author: Ivan Chernov
diff --git a/changelogs/unreleased/26125-match-username-on-search.yml b/changelogs/unreleased/26125-match-username-on-search.yml
deleted file mode 100644
index 74e918bec16..00000000000
--- a/changelogs/unreleased/26125-match-username-on-search.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Inserts exact matches of name, username and email to the top of the search
- list
-merge_request: 12525
-author:
diff --git a/changelogs/unreleased/26212-upload-user-avatar-trough-api.yml b/changelogs/unreleased/26212-upload-user-avatar-trough-api.yml
deleted file mode 100644
index 667454ae95d..00000000000
--- a/changelogs/unreleased/26212-upload-user-avatar-trough-api.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Accept image for avatar in user API
-merge_request: 12143
-author: Ivan Chernov
diff --git a/changelogs/unreleased/27070-rename-slash-commands-to-quick-actions.yml b/changelogs/unreleased/27070-rename-slash-commands-to-quick-actions.yml
deleted file mode 100644
index 497239db808..00000000000
--- a/changelogs/unreleased/27070-rename-slash-commands-to-quick-actions.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rename "Slash commands" to "Quick actions" and deprecate "chat commands" in favor
- of "slash commands"
-merge_request:
-author:
diff --git a/changelogs/unreleased/27586-center-dropdown.yml b/changelogs/unreleased/27586-center-dropdown.yml
deleted file mode 100644
index 4935f7504f7..00000000000
--- a/changelogs/unreleased/27586-center-dropdown.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Center dropdown for mini graph
-merge_request:
-author:
diff --git a/changelogs/unreleased/27645-html-email-brackets-bug.yml b/changelogs/unreleased/27645-html-email-brackets-bug.yml
deleted file mode 100644
index e8004d03884..00000000000
--- a/changelogs/unreleased/27645-html-email-brackets-bug.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix an email parsing bug where brackets would be inserted in emails from some Outlook clients
-merge_request: 9045
-author: jneen
diff --git a/changelogs/unreleased/27697-make-arrow-icons-consistent-in-dropdown.yml b/changelogs/unreleased/27697-make-arrow-icons-consistent-in-dropdown.yml
deleted file mode 100644
index 92b5b59f46f..00000000000
--- a/changelogs/unreleased/27697-make-arrow-icons-consistent-in-dropdown.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Use fa-chevron-down on dropdown arrows for consistency
-merge_request: 9659
-author: TM Lee
diff --git a/changelogs/unreleased/28139-use-color-input-broadcast-messages.yml b/changelogs/unreleased/28139-use-color-input-broadcast-messages.yml
deleted file mode 100644
index 97ebabaff1c..00000000000
--- a/changelogs/unreleased/28139-use-color-input-broadcast-messages.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Use color inputs for broadcast messages
-merge_request:
-author:
diff --git a/changelogs/unreleased/28717-support-additional-prometheus-metrics.yml b/changelogs/unreleased/28717-support-additional-prometheus-metrics.yml
deleted file mode 100644
index 720a79b8e1c..00000000000
--- a/changelogs/unreleased/28717-support-additional-prometheus-metrics.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Additional Prometheus metrics support
-merge_request: 11712
-author:
diff --git a/changelogs/unreleased/29893-change-menu-locations.yml b/changelogs/unreleased/29893-change-menu-locations.yml
deleted file mode 100644
index d348adc2d74..00000000000
--- a/changelogs/unreleased/29893-change-menu-locations.yml
+++ /dev/null
@@ -1,3 +0,0 @@
----
-title: Moved "Members in a project" menu entry and path locations
-merge_request: 11560
diff --git a/changelogs/unreleased/30213-project-transfer-move-rollback.yml b/changelogs/unreleased/30213-project-transfer-move-rollback.yml
deleted file mode 100644
index 3eb1e399c54..00000000000
--- a/changelogs/unreleased/30213-project-transfer-move-rollback.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Rollback project repo move if there is an error in Projects::TransferService
-merge_request: 11877
-author:
diff --git a/changelogs/unreleased/30708-stop-using-deleted-at-to-filter-namespaces.yml b/changelogs/unreleased/30708-stop-using-deleted-at-to-filter-namespaces.yml
deleted file mode 100644
index 83ce3fb4d0a..00000000000
--- a/changelogs/unreleased/30708-stop-using-deleted-at-to-filter-namespaces.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Removes deleted_at and pending_delete occurrences in Project related queries
-merge_request: 12091
-author:
diff --git a/changelogs/unreleased/30725-reset-user-limits-when-unchecking-external-user.yml b/changelogs/unreleased/30725-reset-user-limits-when-unchecking-external-user.yml
deleted file mode 100644
index 3058404b3f8..00000000000
--- a/changelogs/unreleased/30725-reset-user-limits-when-unchecking-external-user.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Ensures default user limits when external user is unchecked
-merge_request: 12218
-author:
diff --git a/changelogs/unreleased/31397-job-detail-real-time.yml b/changelogs/unreleased/31397-job-detail-real-time.yml
deleted file mode 100644
index 90487a1e75a..00000000000
--- a/changelogs/unreleased/31397-job-detail-real-time.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Adds realtime feature to job show view header and sidebar info. Updates UX.
-merge_request:
-author:
diff --git a/changelogs/unreleased/31415-responsive-pipelines-table-2.yml b/changelogs/unreleased/31415-responsive-pipelines-table-2.yml
deleted file mode 100644
index 59402b85871..00000000000
--- a/changelogs/unreleased/31415-responsive-pipelines-table-2.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Create responsive mobile view for pipelines table
-merge_request:
-author:
diff --git a/changelogs/unreleased/31982-liberation-mono-linux.yml b/changelogs/unreleased/31982-liberation-mono-linux.yml
deleted file mode 100644
index c0f29cf4c47..00000000000
--- a/changelogs/unreleased/31982-liberation-mono-linux.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Change order of monospace fonts to fix bug on some linux distros
-merge_request:
-author:
diff --git a/changelogs/unreleased/32048-shared-runners-admin-buttons-have-odd-spacing.yml b/changelogs/unreleased/32048-shared-runners-admin-buttons-have-odd-spacing.yml
deleted file mode 100644
index 99e64b9b467..00000000000
--- a/changelogs/unreleased/32048-shared-runners-admin-buttons-have-odd-spacing.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix spacing on runner buttons.
-merge_request: !12535
-author:
diff --git a/changelogs/unreleased/32054-rails-should-use-timestamptz-database-type-for-postgresql.yml b/changelogs/unreleased/32054-rails-should-use-timestamptz-database-type-for-postgresql.yml
deleted file mode 100644
index 7fc9e0a4f0e..00000000000
--- a/changelogs/unreleased/32054-rails-should-use-timestamptz-database-type-for-postgresql.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add database helpers 'add_timestamps_with_timezone' and 'timestamps_with_timezone'
-merge_request: 11229
-author: @blackst0ne
diff --git a/changelogs/unreleased/32301-filter-archive-project-on-param-present.yml b/changelogs/unreleased/32301-filter-archive-project-on-param-present.yml
deleted file mode 100644
index d6534ed4e1a..00000000000
--- a/changelogs/unreleased/32301-filter-archive-project-on-param-present.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Filter archived project in API v3 only if param present
-merge_request: 12245
-author: Ivan Chernov
diff --git a/changelogs/unreleased/32408-enable-disable-all-restricted-visibility-levels.yml b/changelogs/unreleased/32408-enable-disable-all-restricted-visibility-levels.yml
deleted file mode 100644
index ebb27d118d7..00000000000
--- a/changelogs/unreleased/32408-enable-disable-all-restricted-visibility-levels.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Allow admins to disable all restricted visibility levels
-merge_request: 12649
-author:
diff --git a/changelogs/unreleased/32470-pag-links.yml b/changelogs/unreleased/32470-pag-links.yml
deleted file mode 100644
index d0fd284f3ee..00000000000
--- a/changelogs/unreleased/32470-pag-links.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: more visual contrast in pagination widget
-merge_request:
-author:
diff --git a/changelogs/unreleased/32517-disable-hover-state.yml b/changelogs/unreleased/32517-disable-hover-state.yml
deleted file mode 100644
index 31b02778963..00000000000
--- a/changelogs/unreleased/32517-disable-hover-state.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Removes hover style for nodes that are either links or buttons in the pipeline
- graph
-merge_request:
-author:
diff --git a/changelogs/unreleased/32815--Add-Custom-CI-Config-Path.yml b/changelogs/unreleased/32815--Add-Custom-CI-Config-Path.yml
deleted file mode 100644
index 7784d7d0ce0..00000000000
--- a/changelogs/unreleased/32815--Add-Custom-CI-Config-Path.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Allow customize CI config path
-merge_request: 12509
-author: Keith Pope
diff --git a/changelogs/unreleased/32834-task-note-only.yml b/changelogs/unreleased/32834-task-note-only.yml
deleted file mode 100644
index c9ea61ec4ec..00000000000
--- a/changelogs/unreleased/32834-task-note-only.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Prevent description change notes when toggling tasks
-merge_request: 12057
-author: Jared Deckard <jared.deckard@gmail.com>
diff --git a/changelogs/unreleased/32838-admin-panel-spacing.yml b/changelogs/unreleased/32838-admin-panel-spacing.yml
deleted file mode 100644
index ccd703fa43f..00000000000
--- a/changelogs/unreleased/32838-admin-panel-spacing.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add wells to admin dashboard overview to fix spacing problems
-merge_request:
-author:
diff --git a/changelogs/unreleased/33003-avatar-in-project-api.yml b/changelogs/unreleased/33003-avatar-in-project-api.yml
deleted file mode 100644
index 41d796ebb32..00000000000
--- a/changelogs/unreleased/33003-avatar-in-project-api.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Accept image for avatar in project API
-merge_request: 11988
-author: Ivan Chernov
diff --git a/changelogs/unreleased/33082-use-update_pipeline_schedule-for-edit-and-take_ownership-in-pipelineschedulescontroller.yml b/changelogs/unreleased/33082-use-update_pipeline_schedule-for-edit-and-take_ownership-in-pipelineschedulescontroller.yml
deleted file mode 100644
index d3172c405c3..00000000000
--- a/changelogs/unreleased/33082-use-update_pipeline_schedule-for-edit-and-take_ownership-in-pipelineschedulescontroller.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Use authorize_update_pipeline_schedule in PipelineSchedulesController
-merge_request: 11846
-author:
diff --git a/changelogs/unreleased/33130-remove-group-modal.yml b/changelogs/unreleased/33130-remove-group-modal.yml
deleted file mode 100644
index 4672d41ded5..00000000000
--- a/changelogs/unreleased/33130-remove-group-modal.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: "Remove group modal like remove project modal (requires typing + confirmation)"
-merge_request: 12569
-author: Diego Souza
diff --git a/changelogs/unreleased/33132-change-icon-color.yml b/changelogs/unreleased/33132-change-icon-color.yml
deleted file mode 100644
index c0e148f985b..00000000000
--- a/changelogs/unreleased/33132-change-icon-color.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Render CI statuses with warnings in orange
-merge_request:
-author:
diff --git a/changelogs/unreleased/33208-singup-active-state-underline.yml b/changelogs/unreleased/33208-singup-active-state-underline.yml
deleted file mode 100644
index cddb43214ea..00000000000
--- a/changelogs/unreleased/33208-singup-active-state-underline.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixes "sign in / Register" active state underline misalignment
-merge_request: 11890
-author: Frank Sierra
diff --git a/changelogs/unreleased/33360-generate-kubeconfig.yml b/changelogs/unreleased/33360-generate-kubeconfig.yml
deleted file mode 100644
index 96f0b1bc93f..00000000000
--- a/changelogs/unreleased/33360-generate-kubeconfig.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Provide KUBECONFIG from KubernetesService for runners
-merge_request: 12223
-author:
diff --git a/changelogs/unreleased/33381-display-issue-state-in-mr-widget-issue-links.yml b/changelogs/unreleased/33381-display-issue-state-in-mr-widget-issue-links.yml
deleted file mode 100644
index 4a7b02fec94..00000000000
--- a/changelogs/unreleased/33381-display-issue-state-in-mr-widget-issue-links.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Display issue state in issue links section of merge request widget
-merge_request: 12021
-author:
diff --git a/changelogs/unreleased/33441-supplement_simplified_chinese_translation_of_i18n.yml b/changelogs/unreleased/33441-supplement_simplified_chinese_translation_of_i18n.yml
deleted file mode 100644
index a7d8ac9054b..00000000000
--- a/changelogs/unreleased/33441-supplement_simplified_chinese_translation_of_i18n.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Supplement Simplified Chinese translation of Project Page & Repository Page
-merge_request: 11994
-author: Huang Tao
diff --git a/changelogs/unreleased/33442-supplement_traditional_chinese_in_hong_kong_translation_of_i18n.yml b/changelogs/unreleased/33442-supplement_traditional_chinese_in_hong_kong_translation_of_i18n.yml
deleted file mode 100644
index e383bab23d6..00000000000
--- a/changelogs/unreleased/33442-supplement_traditional_chinese_in_hong_kong_translation_of_i18n.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Supplement Traditional Chinese in Hong Kong translation of Project Page & Repository Page
-merge_request: 11995
-author: Huang Tao
diff --git a/changelogs/unreleased/33443-supplement_traditional_chinese_in_taiwan_translation_of_i18n.yml b/changelogs/unreleased/33443-supplement_traditional_chinese_in_taiwan_translation_of_i18n.yml
deleted file mode 100644
index d6b1b2524c6..00000000000
--- a/changelogs/unreleased/33443-supplement_traditional_chinese_in_taiwan_translation_of_i18n.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Supplement Traditional Chinese in Taiwan translation of Project Page & Repository Page
-merge_request: 12514
-author: Huang Tao
diff --git a/changelogs/unreleased/33445-document-delete-merge-branches-won-t-touch-protected-branches-docs.yml b/changelogs/unreleased/33445-document-delete-merge-branches-won-t-touch-protected-branches-docs.yml
deleted file mode 100644
index 385f18e2560..00000000000
--- a/changelogs/unreleased/33445-document-delete-merge-branches-won-t-touch-protected-branches-docs.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Document the Delete Merged Branches functionality
-merge_request:
-author:
diff --git a/changelogs/unreleased/33461-display-user-id.yml b/changelogs/unreleased/33461-display-user-id.yml
deleted file mode 100644
index cba94625b07..00000000000
--- a/changelogs/unreleased/33461-display-user-id.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Display own user id in account settings page
-merge_request: 12141
-author: Riccardo Padovani
diff --git a/changelogs/unreleased/33538-update-ci-dockerfile-now-that-chrome-headless-no-longer-in-beta.yml b/changelogs/unreleased/33538-update-ci-dockerfile-now-that-chrome-headless-no-longer-in-beta.yml
deleted file mode 100644
index 590472c0990..00000000000
--- a/changelogs/unreleased/33538-update-ci-dockerfile-now-that-chrome-headless-no-longer-in-beta.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Update QA Dockerfile to lock Chrome browser version
-merge_request: 12071
-author:
diff --git a/changelogs/unreleased/33561-supplement_bulgarian_translation_of_i18n.yml b/changelogs/unreleased/33561-supplement_bulgarian_translation_of_i18n.yml
deleted file mode 100644
index 4f2ba2e1de3..00000000000
--- a/changelogs/unreleased/33561-supplement_bulgarian_translation_of_i18n.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Supplement Bulgarian translation of Project Page & Repository Page
-merge_request: 12083
-author: Lyubomir Vasilev
diff --git a/changelogs/unreleased/33657-user-projects-api.yml b/changelogs/unreleased/33657-user-projects-api.yml
deleted file mode 100644
index a8d485865e9..00000000000
--- a/changelogs/unreleased/33657-user-projects-api.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add user projects API
-merge_request: 12596
-author: Ivan Chernov
diff --git a/changelogs/unreleased/33672-supplement_portuguese_brazil_translation_of_i18n.yml b/changelogs/unreleased/33672-supplement_portuguese_brazil_translation_of_i18n.yml
deleted file mode 100644
index d2bdc631d2a..00000000000
--- a/changelogs/unreleased/33672-supplement_portuguese_brazil_translation_of_i18n.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Supplement Portuguese Brazil translation of Project Page & Repository Page
-merge_request: 12156
-author: Huang Tao
diff --git a/changelogs/unreleased/33741-clarify-k8s-service-keys.yml b/changelogs/unreleased/33741-clarify-k8s-service-keys.yml
new file mode 100644
index 00000000000..91142a0d580
--- /dev/null
+++ b/changelogs/unreleased/33741-clarify-k8s-service-keys.yml
@@ -0,0 +1,5 @@
+---
+title: Clarifies and rearranges the input variables on the kubernetes integration
+ page and adjusts the docs slightly to meet the same order
+merge_request: !12188
+author:
diff --git a/changelogs/unreleased/33748-fix-n-plus-1-query-in-the-projects-api.yml b/changelogs/unreleased/33748-fix-n-plus-1-query-in-the-projects-api.yml
deleted file mode 100644
index 7402c33c5c6..00000000000
--- a/changelogs/unreleased/33748-fix-n-plus-1-query-in-the-projects-api.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Improve the performance of the project list API
-merge_request: 12679
-author:
diff --git a/changelogs/unreleased/33770-respect-blockquote-line-breaks.yml b/changelogs/unreleased/33770-respect-blockquote-line-breaks.yml
new file mode 100644
index 00000000000..3a45ad88270
--- /dev/null
+++ b/changelogs/unreleased/33770-respect-blockquote-line-breaks.yml
@@ -0,0 +1,4 @@
+---
+title: Respect blockquote line breaks in markdown
+merge_request:
+author:
diff --git a/changelogs/unreleased/33772-readonly-gitlab-ci-cache.yml b/changelogs/unreleased/33772-readonly-gitlab-ci-cache.yml
deleted file mode 100644
index c2bce368a58..00000000000
--- a/changelogs/unreleased/33772-readonly-gitlab-ci-cache.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Introduce cache policies for CI jobs
-merge_request: 12483
-author:
diff --git a/changelogs/unreleased/33837-remove-trash-on-registry-image.yml b/changelogs/unreleased/33837-remove-trash-on-registry-image.yml
deleted file mode 100644
index 2d337f5e6e4..00000000000
--- a/changelogs/unreleased/33837-remove-trash-on-registry-image.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove registry image delete button if user cant delete it
-merge_request: 12317
-author: Ivan Chernov
diff --git a/changelogs/unreleased/33846-no-runner-for-admin.yml b/changelogs/unreleased/33846-no-runner-for-admin.yml
deleted file mode 100644
index a2d46802c61..00000000000
--- a/changelogs/unreleased/33846-no-runner-for-admin.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add explicit message when no runners on admin
-merge_request: 12266
-author: Takuya Noguchi
diff --git a/changelogs/unreleased/33929-allow-to-enable-perf-bar-for-a-group.yml b/changelogs/unreleased/33929-allow-to-enable-perf-bar-for-a-group.yml
deleted file mode 100644
index 810cc8489b5..00000000000
--- a/changelogs/unreleased/33929-allow-to-enable-perf-bar-for-a-group.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Allow to enable the performance bar per user or Feature group
-merge_request: 12362
-author:
diff --git a/changelogs/unreleased/33949-deprecate-healthcheck-access-token.yml b/changelogs/unreleased/33949-deprecate-healthcheck-access-token.yml
deleted file mode 100644
index a08795e1a26..00000000000
--- a/changelogs/unreleased/33949-deprecate-healthcheck-access-token.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Deprecate Healthcheck Access Token in favor of IP whitelist
-merge_request:
-author:
diff --git a/changelogs/unreleased/34052-store-mr-ref-fetched-in-database.yml b/changelogs/unreleased/34052-store-mr-ref-fetched-in-database.yml
deleted file mode 100644
index 4bacfca7551..00000000000
--- a/changelogs/unreleased/34052-store-mr-ref-fetched-in-database.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Store merge request ref_fetched status in the database
-merge_request: 12424
-author:
diff --git a/changelogs/unreleased/34078-allow-to-enable-feature-flags-with-more-granularity.yml b/changelogs/unreleased/34078-allow-to-enable-feature-flags-with-more-granularity.yml
deleted file mode 100644
index 69d5d34b072..00000000000
--- a/changelogs/unreleased/34078-allow-to-enable-feature-flags-with-more-granularity.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Allow the feature flags to be enabled/disabled with more granularity
-merge_request: 12357
-author:
diff --git a/changelogs/unreleased/34116-milestone-filtering-on-group-issues.yml b/changelogs/unreleased/34116-milestone-filtering-on-group-issues.yml
deleted file mode 100644
index 8f8b5a96c2b..00000000000
--- a/changelogs/unreleased/34116-milestone-filtering-on-group-issues.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Change milestone endpoint for groups
-merge_request: 12374
-author: Takuya Noguchi
diff --git a/changelogs/unreleased/34141-allow-unauthenticated-access-to-the-users-api.yml b/changelogs/unreleased/34141-allow-unauthenticated-access-to-the-users-api.yml
deleted file mode 100644
index a3ade8db214..00000000000
--- a/changelogs/unreleased/34141-allow-unauthenticated-access-to-the-users-api.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Allow unauthenticated access to the /api/v4/users API
-merge_request: 12445
-author:
diff --git a/changelogs/unreleased/34169-add-simplified-chinese-translations-of-commits-page.yml b/changelogs/unreleased/34169-add-simplified-chinese-translations-of-commits-page.yml
deleted file mode 100644
index 1a631c3f0a4..00000000000
--- a/changelogs/unreleased/34169-add-simplified-chinese-translations-of-commits-page.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add Simplified Chinese translations of Commits Page
-merge_request: 12405
-author: Huang Tao
diff --git a/changelogs/unreleased/34171-add-traditional-chinese-in-hongkong-translations-of-commits-page.yml b/changelogs/unreleased/34171-add-traditional-chinese-in-hongkong-translations-of-commits-page.yml
deleted file mode 100644
index 3cf7c0b547f..00000000000
--- a/changelogs/unreleased/34171-add-traditional-chinese-in-hongkong-translations-of-commits-page.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add Traditional Chinese in HongKong translations of Commits Page
-merge_request: 12406
-author: Huang Tao
diff --git a/changelogs/unreleased/34172-add-traditional-chinese-in-taiwan-translations-of-commits-page.yml b/changelogs/unreleased/34172-add-traditional-chinese-in-taiwan-translations-of-commits-page.yml
deleted file mode 100644
index 224b9e1852f..00000000000
--- a/changelogs/unreleased/34172-add-traditional-chinese-in-taiwan-translations-of-commits-page.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add Traditional Chinese in Taiwan translations of Commits Page
-merge_request: 12407
-author: Huang Tao
diff --git a/changelogs/unreleased/34175-add-esperanto-translations-of-commits-page.yml b/changelogs/unreleased/34175-add-esperanto-translations-of-commits-page.yml
deleted file mode 100644
index b43a38f3794..00000000000
--- a/changelogs/unreleased/34175-add-esperanto-translations-of-commits-page.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add Esperanto translations of Commits Page
-merge_request: 12410
-author: Huang Tao
diff --git a/changelogs/unreleased/34176-add-bulgarian-translations-of-commits-page.yml b/changelogs/unreleased/34176-add-bulgarian-translations-of-commits-page.yml
deleted file mode 100644
index 9177ae3acd1..00000000000
--- a/changelogs/unreleased/34176-add-bulgarian-translations-of-commits-page.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add Bulgarian translations of Commits Page
-merge_request: 12411
-author: Huang Tao
diff --git a/changelogs/unreleased/34207-remove-bin-ci-upgrade-rb.yml b/changelogs/unreleased/34207-remove-bin-ci-upgrade-rb.yml
deleted file mode 100644
index 4fa385c3c27..00000000000
--- a/changelogs/unreleased/34207-remove-bin-ci-upgrade-rb.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove bin/ci/upgrade.rb as not working all
-merge_request: 12414
-author: Takuya Noguchi
diff --git a/changelogs/unreleased/34286-add-esperanto-translations-for-cycle-analytics-and-project-and-repository-pages.yml b/changelogs/unreleased/34286-add-esperanto-translations-for-cycle-analytics-and-project-and-repository-pages.yml
deleted file mode 100644
index af743f3e506..00000000000
--- a/changelogs/unreleased/34286-add-esperanto-translations-for-cycle-analytics-and-project-and-repository-pages.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add Esperanto translations for Cycle Analytics, Project, and Repository pages
-merge_request: 12442
-author: Huang Tao
diff --git a/changelogs/unreleased/34289-drop-gfm-on-milestone-issuable-title.yml b/changelogs/unreleased/34289-drop-gfm-on-milestone-issuable-title.yml
deleted file mode 100644
index 42e906d24c6..00000000000
--- a/changelogs/unreleased/34289-drop-gfm-on-milestone-issuable-title.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Drop GFM support for issuable title on milestone for consistency and performance
-merge_request:
-author: Takuya Noguchi
diff --git a/changelogs/unreleased/34309-drop-gfm-mr-ms.yml b/changelogs/unreleased/34309-drop-gfm-mr-ms.yml
deleted file mode 100644
index 07fe79e90ee..00000000000
--- a/changelogs/unreleased/34309-drop-gfm-mr-ms.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Drop GFM support for the title of Milestone/MergeRequest in template
-merge_request: 12451
-author: Takuya Noguchi
diff --git a/changelogs/unreleased/34403-issue-dropdown-persists-when-adding-issue-number-to-issue-description.yml b/changelogs/unreleased/34403-issue-dropdown-persists-when-adding-issue-number-to-issue-description.yml
deleted file mode 100644
index 4911315d018..00000000000
--- a/changelogs/unreleased/34403-issue-dropdown-persists-when-adding-issue-number-to-issue-description.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Closes any open Autocomplete of the markdown editor when the form is closed
-merge_request: 12521
-author:
diff --git a/changelogs/unreleased/34468-remove-extra-blank-on-admin-on-mobile.yml b/changelogs/unreleased/34468-remove-extra-blank-on-admin-on-mobile.yml
deleted file mode 100644
index 99291b4c75a..00000000000
--- a/changelogs/unreleased/34468-remove-extra-blank-on-admin-on-mobile.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Use smaller min-width for dropdown-menu-nav only on mobile
-merge_request: 12528
-author: Takuya Noguchi
diff --git a/changelogs/unreleased/34531-remove-scroll.yml b/changelogs/unreleased/34531-remove-scroll.yml
deleted file mode 100644
index c3c5289f66f..00000000000
--- a/changelogs/unreleased/34531-remove-scroll.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Update jobs page output to have a scrollable page
-merge_request: 12587
-author:
diff --git a/changelogs/unreleased/34544-add-italian-translation-of-cycle-analytics-page-&-project-page-&-repository-page.yml b/changelogs/unreleased/34544-add-italian-translation-of-cycle-analytics-page-&-project-page-&-repository-page.yml
deleted file mode 100644
index 31f4262c9f9..00000000000
--- a/changelogs/unreleased/34544-add-italian-translation-of-cycle-analytics-page-&-project-page-&-repository-page.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add Italian translation of Cycle Analytics Page & Project Page & Repository Page
-merge_request: 12578
-author: Huang Tao
diff --git a/changelogs/unreleased/34549-extract-devise-mappings-into-helper.yml b/changelogs/unreleased/34549-extract-devise-mappings-into-helper.yml
new file mode 100644
index 00000000000..e843bbac239
--- /dev/null
+++ b/changelogs/unreleased/34549-extract-devise-mappings-into-helper.yml
@@ -0,0 +1,4 @@
+---
+title: Extract "@request.env[devise.mapping] = Devise.mappings[:user]" to a test helper
+merge_request: 12742
+author: Jacopo Beschi @jacopo-beschi
diff --git a/changelogs/unreleased/34563-usage-ping-github.yml b/changelogs/unreleased/34563-usage-ping-github.yml
new file mode 100644
index 00000000000..3ab982beea3
--- /dev/null
+++ b/changelogs/unreleased/34563-usage-ping-github.yml
@@ -0,0 +1,4 @@
+---
+title: Add GitHub imported projects count to usage data
+merge_request:
+author:
diff --git a/changelogs/unreleased/34578-sidebar-padding.yml b/changelogs/unreleased/34578-sidebar-padding.yml
deleted file mode 100644
index dc4647298e6..00000000000
--- a/changelogs/unreleased/34578-sidebar-padding.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: fix left & right padding on sidebar
-merge_request:
-author:
diff --git a/changelogs/unreleased/34590-fix-dashboard-labels-dropdown.yml b/changelogs/unreleased/34590-fix-dashboard-labels-dropdown.yml
deleted file mode 100644
index 11c01d28dc2..00000000000
--- a/changelogs/unreleased/34590-fix-dashboard-labels-dropdown.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix dashboard labels dropdown
-merge_request: 12708
-author:
diff --git a/changelogs/unreleased/34653-minor-ux-cleanups-for-performance-dashboard.yml b/changelogs/unreleased/34653-minor-ux-cleanups-for-performance-dashboard.yml
deleted file mode 100644
index 736991318d7..00000000000
--- a/changelogs/unreleased/34653-minor-ux-cleanups-for-performance-dashboard.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Cleanup minor UX issues in the performance dashboard
-merge_request:
-author:
diff --git a/changelogs/unreleased/34655-label-field-for-setting-a-chart-s-legend-text-is-not-working.yml b/changelogs/unreleased/34655-label-field-for-setting-a-chart-s-legend-text-is-not-working.yml
deleted file mode 100644
index c7a68935e8c..00000000000
--- a/changelogs/unreleased/34655-label-field-for-setting-a-chart-s-legend-text-is-not-working.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixed the chart legend not being set correctly
-merge_request: 12628
-author:
diff --git a/changelogs/unreleased/34688-add-italian-translations-of-commits-page.yml b/changelogs/unreleased/34688-add-italian-translations-of-commits-page.yml
deleted file mode 100644
index 90a1f8c98fe..00000000000
--- a/changelogs/unreleased/34688-add-italian-translations-of-commits-page.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add Italian translations of Commits Page
-merge_request: 12645
-author: Huang Tao
diff --git a/changelogs/unreleased/34727-simplified-member-settings.yml b/changelogs/unreleased/34727-simplified-member-settings.yml
deleted file mode 100644
index 8c4844c001b..00000000000
--- a/changelogs/unreleased/34727-simplified-member-settings.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove two columned layout from project member settings
-merge_request:
-author:
diff --git a/changelogs/unreleased/34736-n-1-problem-on-milestone-page.yml b/changelogs/unreleased/34736-n-1-problem-on-milestone-page.yml
deleted file mode 100644
index 8df3a1a6940..00000000000
--- a/changelogs/unreleased/34736-n-1-problem-on-milestone-page.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: N+1 problems on milestone page
-merge_request: 12670
-author: Takuya Noguchi
diff --git a/changelogs/unreleased/34907-dont-show-pipeline-schedule-button-for-non-member.yml b/changelogs/unreleased/34907-dont-show-pipeline-schedule-button-for-non-member.yml
deleted file mode 100644
index 22c9c45bc75..00000000000
--- a/changelogs/unreleased/34907-dont-show-pipeline-schedule-button-for-non-member.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Do not show pipeline schedule button for non-member
-merge_request: 12757
-author: Takuya Noguchi
diff --git a/changelogs/unreleased/35035-sidebar-job-spaces.yml b/changelogs/unreleased/35035-sidebar-job-spaces.yml
deleted file mode 100644
index a9a0211efd9..00000000000
--- a/changelogs/unreleased/35035-sidebar-job-spaces.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix vertical space in job details sidebar
-merge_request:
-author:
diff --git a/changelogs/unreleased/35087-mr-status-misaligned.yml b/changelogs/unreleased/35087-mr-status-misaligned.yml
deleted file mode 100644
index 3be43125a61..00000000000
--- a/changelogs/unreleased/35087-mr-status-misaligned.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix alignment of controls in mr issuable list
-merge_request:
-author:
diff --git a/changelogs/unreleased/35253-desc-protected-branches-for-non-member.yml b/changelogs/unreleased/35253-desc-protected-branches-for-non-member.yml
new file mode 100644
index 00000000000..9b2a66da1c3
--- /dev/null
+++ b/changelogs/unreleased/35253-desc-protected-branches-for-non-member.yml
@@ -0,0 +1,4 @@
+---
+title: Hide description about protected branches to non-member
+merge_request: 12945
+author: Takuya Noguchi
diff --git a/changelogs/unreleased/35399-mini-graph-commits-box.yml b/changelogs/unreleased/35399-mini-graph-commits-box.yml
new file mode 100644
index 00000000000..ed080ed86b4
--- /dev/null
+++ b/changelogs/unreleased/35399-mini-graph-commits-box.yml
@@ -0,0 +1,4 @@
+---
+title: Fix margins in the mini graph for pipeline in commits box
+merge_request:
+author:
diff --git a/changelogs/unreleased/5971-webhook-testing.yml b/changelogs/unreleased/5971-webhook-testing.yml
new file mode 100644
index 00000000000..58233091977
--- /dev/null
+++ b/changelogs/unreleased/5971-webhook-testing.yml
@@ -0,0 +1,4 @@
+---
+title: Allow testing any events for project hooks and system hooks
+merge_request: 11728
+author: Alexander Randa (@randaalex)
diff --git a/changelogs/unreleased/adam-external-issue-references-spike.yml b/changelogs/unreleased/adam-external-issue-references-spike.yml
deleted file mode 100644
index aeec6688425..00000000000
--- a/changelogs/unreleased/adam-external-issue-references-spike.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Improve support for external issue references
-merge_request: 12485
-author:
diff --git a/changelogs/unreleased/add-ci_variables-environment_scope-mysql.yml b/changelogs/unreleased/add-ci_variables-environment_scope-mysql.yml
deleted file mode 100644
index 4948d415bed..00000000000
--- a/changelogs/unreleased/add-ci_variables-environment_scope-mysql.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Rename duplicated variables with the same key for projects. Add environment_scope
- column to variables and add unique constraint to make sure that no variables could
- be created with the same key within a project
-merge_request: 12363
-author:
diff --git a/changelogs/unreleased/add-group-members-counting-and-plan-related-data-on-namespaces-api.yml b/changelogs/unreleased/add-group-members-counting-and-plan-related-data-on-namespaces-api.yml
deleted file mode 100644
index f2591042e98..00000000000
--- a/changelogs/unreleased/add-group-members-counting-and-plan-related-data-on-namespaces-api.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add group members counting and plan related data on namespaces API
-merge_request:
-author:
diff --git a/changelogs/unreleased/bvl-rename-all-reserved-paths.yml b/changelogs/unreleased/bvl-rename-all-reserved-paths.yml
deleted file mode 100644
index f37f2fa94ae..00000000000
--- a/changelogs/unreleased/bvl-rename-all-reserved-paths.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Rename all reserved paths that could have been created
-merge_request: 11713
-author:
diff --git a/changelogs/unreleased/commit-comments-limited-width.yml b/changelogs/unreleased/commit-comments-limited-width.yml
deleted file mode 100644
index 97f50105495..00000000000
--- a/changelogs/unreleased/commit-comments-limited-width.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Limit commit & snippets comments width
-merge_request:
-author:
diff --git a/changelogs/unreleased/dashboard-milestone-tabs-loading-async.yml b/changelogs/unreleased/dashboard-milestone-tabs-loading-async.yml
deleted file mode 100644
index 357a623e0e8..00000000000
--- a/changelogs/unreleased/dashboard-milestone-tabs-loading-async.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixed dashboard milestone tabs not loading
-merge_request:
-author:
diff --git a/changelogs/unreleased/dm-blob-binaryness-change.yml b/changelogs/unreleased/dm-blob-binaryness-change.yml
deleted file mode 100644
index f3e3af26f12..00000000000
--- a/changelogs/unreleased/dm-blob-binaryness-change.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Detect if file that appears to be text in the first 1024 bytes is actually
- binary afer loading all data
-merge_request:
-author:
diff --git a/changelogs/unreleased/dm-commit-row-browse-button.yml b/changelogs/unreleased/dm-commit-row-browse-button.yml
deleted file mode 100644
index 4240a7de5de..00000000000
--- a/changelogs/unreleased/dm-commit-row-browse-button.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix inconsistent display of the "Browse files" button in the commit list
-merge_request:
-author:
diff --git a/changelogs/unreleased/dm-diff-viewers.yml b/changelogs/unreleased/dm-diff-viewers.yml
deleted file mode 100644
index e5b1352c8f1..00000000000
--- a/changelogs/unreleased/dm-diff-viewers.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Implement diff viewers
-merge_request:
-author:
diff --git a/changelogs/unreleased/dm-empty-state-new-merge-request.yml b/changelogs/unreleased/dm-empty-state-new-merge-request.yml
deleted file mode 100644
index 5fad7a0f883..00000000000
--- a/changelogs/unreleased/dm-empty-state-new-merge-request.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix 'New merge request' button for users who don't have push access to canonical
- project
-merge_request:
-author:
diff --git a/changelogs/unreleased/dm-group-page-name.yml b/changelogs/unreleased/dm-group-page-name.yml
deleted file mode 100644
index 233879364e3..00000000000
--- a/changelogs/unreleased/dm-group-page-name.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Show group name instead of path on group page
-merge_request:
-author:
diff --git a/changelogs/unreleased/dm-mail-room-check-without-omnibus.yml b/changelogs/unreleased/dm-mail-room-check-without-omnibus.yml
deleted file mode 100644
index 7fd252e9b8b..00000000000
--- a/changelogs/unreleased/dm-mail-room-check-without-omnibus.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Don't check if MailRoom is running on Omnibus
-merge_request:
-author:
diff --git a/changelogs/unreleased/dm-page-image-size.yml b/changelogs/unreleased/dm-page-image-size.yml
deleted file mode 100644
index b18c00470fc..00000000000
--- a/changelogs/unreleased/dm-page-image-size.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Limit OpenGraph image size to 64x64
-merge_request:
-author:
diff --git a/changelogs/unreleased/dm-readme-auxiliary-blob-viewer-without-wiki.yml b/changelogs/unreleased/dm-readme-auxiliary-blob-viewer-without-wiki.yml
deleted file mode 100644
index 8b026a4c289..00000000000
--- a/changelogs/unreleased/dm-readme-auxiliary-blob-viewer-without-wiki.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Don't show auxiliary blob viewer for README when there is no wiki
-merge_request:
-author:
diff --git a/changelogs/unreleased/dm-relative-submodule-url-trailing-whitespace.yml b/changelogs/unreleased/dm-relative-submodule-url-trailing-whitespace.yml
deleted file mode 100644
index 616241dd941..00000000000
--- a/changelogs/unreleased/dm-relative-submodule-url-trailing-whitespace.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Strip trailing whitespace in relative submodule URL
-merge_request:
-author:
diff --git a/changelogs/unreleased/dm-target-branch-slash-command-desc.yml b/changelogs/unreleased/dm-target-branch-slash-command-desc.yml
deleted file mode 100644
index 768ddf0416e..00000000000
--- a/changelogs/unreleased/dm-target-branch-slash-command-desc.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Update /target_branch slash command description to be more consistent
-merge_request:
-author:
diff --git a/changelogs/unreleased/dm-unnecessary-top-padding.yml b/changelogs/unreleased/dm-unnecessary-top-padding.yml
deleted file mode 100644
index 4557c06f8e7..00000000000
--- a/changelogs/unreleased/dm-unnecessary-top-padding.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove unnecessary top padding on group MR index
-merge_request:
-author:
diff --git a/changelogs/unreleased/doc-gitaly-network.yml b/changelogs/unreleased/doc-gitaly-network.yml
deleted file mode 100644
index 5376d8d5096..00000000000
--- a/changelogs/unreleased/doc-gitaly-network.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add option to run Gitaly on a remote server
-merge_request: 12381
-author:
diff --git a/changelogs/unreleased/dt-printing-to-api.yml b/changelogs/unreleased/dt-printing-to-api.yml
deleted file mode 100644
index 5253b57f21a..00000000000
--- a/changelogs/unreleased/dt-printing-to-api.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Added printing_merge_requst_link_enabled to the API
-merge_request:
-author: David Turner <dturner@twosigma.com>
diff --git a/changelogs/unreleased/enable-polling-env.yml b/changelogs/unreleased/enable-polling-env.yml
deleted file mode 100644
index b3f65f02574..00000000000
--- a/changelogs/unreleased/enable-polling-env.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Re-enable realtime for environments table
-merge_request:
-author:
diff --git a/changelogs/unreleased/enable-webpack-code-splitting.yml b/changelogs/unreleased/enable-webpack-code-splitting.yml
deleted file mode 100644
index d61c3b97d11..00000000000
--- a/changelogs/unreleased/enable-webpack-code-splitting.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable support for webpack code-splitting by dynamically setting publicPath
- at runtime
-merge_request: 12032
-author:
diff --git a/changelogs/unreleased/feature-add-support-for-services-configuration.yml b/changelogs/unreleased/feature-add-support-for-services-configuration.yml
deleted file mode 100644
index 88a3eacd774..00000000000
--- a/changelogs/unreleased/feature-add-support-for-services-configuration.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add support for image and services configuration in .gitlab-ci.yml
-merge_request: 8578
-author:
diff --git a/changelogs/unreleased/feature-gb-auto-retry-failed-ci-job.yml b/changelogs/unreleased/feature-gb-auto-retry-failed-ci-job.yml
new file mode 100644
index 00000000000..bdafc5929c0
--- /dev/null
+++ b/changelogs/unreleased/feature-gb-auto-retry-failed-ci-job.yml
@@ -0,0 +1,4 @@
+---
+title: Allow to configure automatic retry of a failed CI/CD job
+merge_request: 12909
+author:
diff --git a/changelogs/unreleased/feature-intermediate-12729-group-secret-variables.yml b/changelogs/unreleased/feature-intermediate-12729-group-secret-variables.yml
deleted file mode 100644
index 333895ffba9..00000000000
--- a/changelogs/unreleased/feature-intermediate-12729-group-secret-variables.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add Group secret variables
-merge_request: 12582
-author:
diff --git a/changelogs/unreleased/feature-intermediate-32568-adding-variables-to-pipelines-schedules.yml b/changelogs/unreleased/feature-intermediate-32568-adding-variables-to-pipelines-schedules.yml
deleted file mode 100644
index d497575b7f3..00000000000
--- a/changelogs/unreleased/feature-intermediate-32568-adding-variables-to-pipelines-schedules.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add variables to pipelines schedules
-merge_request: 12372
-author:
diff --git a/changelogs/unreleased/feature-no-hypen-at-end-of-commit-ref-slug.yml b/changelogs/unreleased/feature-no-hypen-at-end-of-commit-ref-slug.yml
deleted file mode 100644
index bbcf2946ea7..00000000000
--- a/changelogs/unreleased/feature-no-hypen-at-end-of-commit-ref-slug.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Omit trailing / leading hyphens in CI_COMMIT_REF_SLUG variable to make it usable as a hostname
-merge_request: 11218
-author: Stefan Hanreich
diff --git a/changelogs/unreleased/feature-unify-email-layouts.yml b/changelogs/unreleased/feature-unify-email-layouts.yml
deleted file mode 100644
index 7a2e3f20b6b..00000000000
--- a/changelogs/unreleased/feature-unify-email-layouts.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Update the devise mail templates to match the design of the pipeline emails
-merge_request: 10483
-author: Alexis Reigel
diff --git a/changelogs/unreleased/feature-user-agent-details-api.yml b/changelogs/unreleased/feature-user-agent-details-api.yml
deleted file mode 100644
index 839ec7d21cd..00000000000
--- a/changelogs/unreleased/feature-user-agent-details-api.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Allow admins to retrieve user agent details for an issue or snippet
-merge_request: 12655
-author:
diff --git a/changelogs/unreleased/feature-user-datetime-search-api-mysql.yml b/changelogs/unreleased/feature-user-datetime-search-api-mysql.yml
deleted file mode 100644
index 27ac50c6cc2..00000000000
--- a/changelogs/unreleased/feature-user-datetime-search-api-mysql.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add creation time filters to user search API for admins
-merge_request: 12682
-author:
diff --git a/changelogs/unreleased/fix-33991.yml b/changelogs/unreleased/fix-33991.yml
deleted file mode 100644
index 39732611b6e..00000000000
--- a/changelogs/unreleased/fix-33991.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Users can subscribe to group labels on the group labels page
-merge_request:
-author:
diff --git a/changelogs/unreleased/fix-assigned-issuable-lists.yml b/changelogs/unreleased/fix-assigned-issuable-lists.yml
deleted file mode 100644
index fc2cd18ddb6..00000000000
--- a/changelogs/unreleased/fix-assigned-issuable-lists.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add issuable-list class to shared mr/issue lists to fix new responsive layout
- design
-merge_request:
-author:
diff --git a/changelogs/unreleased/fix-exact-matches-of-username-and-email-on-top-of-the-user-search.yml b/changelogs/unreleased/fix-exact-matches-of-username-and-email-on-top-of-the-user-search.yml
deleted file mode 100644
index 2e0573beab6..00000000000
--- a/changelogs/unreleased/fix-exact-matches-of-username-and-email-on-top-of-the-user-search.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Exact matches of username and email are now on top of the user search
-merge_request: 12868
-author:
diff --git a/changelogs/unreleased/fix-gb-fix-skipped-pipeline-with-allowed-to-fail-jobs.yml b/changelogs/unreleased/fix-gb-fix-skipped-pipeline-with-allowed-to-fail-jobs.yml
deleted file mode 100644
index f59c6ecd90c..00000000000
--- a/changelogs/unreleased/fix-gb-fix-skipped-pipeline-with-allowed-to-fail-jobs.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix CI/CD status in case there are only allowed to failed jobs in the pipeline
-merge_request: 11166
-author:
diff --git a/changelogs/unreleased/fix-gb-recover-from-renaming-project-with-container-images.yml b/changelogs/unreleased/fix-gb-recover-from-renaming-project-with-container-images.yml
deleted file mode 100644
index 7adc53eb8fa..00000000000
--- a/changelogs/unreleased/fix-gb-recover-from-renaming-project-with-container-images.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Recover from renaming project that has container images
-merge_request: 12840
-author:
diff --git a/changelogs/unreleased/fix-mrs-merged-immediately.yml b/changelogs/unreleased/fix-mrs-merged-immediately.yml
deleted file mode 100644
index 41c06614e6d..00000000000
--- a/changelogs/unreleased/fix-mrs-merged-immediately.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Don't mark empty MRs as merged on push to the target branch
-merge_request:
-author:
diff --git a/changelogs/unreleased/fix-n-plus-one-in-url-builder.yml b/changelogs/unreleased/fix-n-plus-one-in-url-builder.yml
deleted file mode 100644
index 5781316cfd9..00000000000
--- a/changelogs/unreleased/fix-n-plus-one-in-url-builder.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Improve issue rendering performance with lots of notes from other users
-merge_request:
-author:
diff --git a/changelogs/unreleased/fix-overflow-slash-commands.yml b/changelogs/unreleased/fix-overflow-slash-commands.yml
deleted file mode 100644
index 98ec399e8cb..00000000000
--- a/changelogs/unreleased/fix-overflow-slash-commands.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixed overflow on mobile screens for the slash commands
-merge_request:
-author:
diff --git a/changelogs/unreleased/fix-runner_online_check.yml b/changelogs/unreleased/fix-runner_online_check.yml
deleted file mode 100644
index bc0de979b4c..00000000000
--- a/changelogs/unreleased/fix-runner_online_check.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix offline runner detection
-merge_request: 11751
-author: Alessio Caiazza
diff --git a/changelogs/unreleased/fix-sidebar-showing-mobile-merge-requests.yml b/changelogs/unreleased/fix-sidebar-showing-mobile-merge-requests.yml
deleted file mode 100644
index 856990a6126..00000000000
--- a/changelogs/unreleased/fix-sidebar-showing-mobile-merge-requests.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixed sidebar not collapsing on merge requests in mobile screens
-merge_request:
-author:
diff --git a/changelogs/unreleased/fix-sm-32790-pipeline_schedules-pages-throwing-error-500.yml b/changelogs/unreleased/fix-sm-32790-pipeline_schedules-pages-throwing-error-500.yml
new file mode 100644
index 00000000000..334d8ca4d9e
--- /dev/null
+++ b/changelogs/unreleased/fix-sm-32790-pipeline_schedules-pages-throwing-error-500.yml
@@ -0,0 +1,4 @@
+---
+title: Fix pipeline_schedules pages throwing error 500 (when ref is empty)
+merge_request: 12983
+author:
diff --git a/changelogs/unreleased/fix-u2f-for-opera.yml b/changelogs/unreleased/fix-u2f-for-opera.yml
deleted file mode 100644
index 0eafb8eff9a..00000000000
--- a/changelogs/unreleased/fix-u2f-for-opera.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix FIDO U2F for Opera browser
-merge_request: 12082
-author: Jakub Kramarz and Jonas Kalderstam
diff --git a/changelogs/unreleased/fix_docs_commits_multiple_files.yml b/changelogs/unreleased/fix_docs_commits_multiple_files.yml
deleted file mode 100644
index 36567354b28..00000000000
--- a/changelogs/unreleased/fix_docs_commits_multiple_files.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Documentation bugfix of invalid JSON payload example of Create a commit with
- multiple files and actions
-merge_request: 12117
-author: @blackst0ne
diff --git a/changelogs/unreleased/foreign-keys-for-project-model.yml b/changelogs/unreleased/foreign-keys-for-project-model.yml
deleted file mode 100644
index 3648b1c3735..00000000000
--- a/changelogs/unreleased/foreign-keys-for-project-model.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Speed up project removals by adding foreign keys with cascading deletes to various tables
-merge_request:
-author:
diff --git a/changelogs/unreleased/gitaly-mandatory.yml b/changelogs/unreleased/gitaly-mandatory.yml
deleted file mode 100644
index c060e0add29..00000000000
--- a/changelogs/unreleased/gitaly-mandatory.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove option to disable Gitaly
-merge_request: 12677
-author:
diff --git a/changelogs/unreleased/hb-fix-abuse-report-on-stale-user-profile.yml b/changelogs/unreleased/hb-fix-abuse-report-on-stale-user-profile.yml
deleted file mode 100644
index ec2f4f9c3d8..00000000000
--- a/changelogs/unreleased/hb-fix-abuse-report-on-stale-user-profile.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix errors caused by attempts to report already blocked or deleted users
-merge_request: 12502
-author: Horacio Bertorello
diff --git a/changelogs/unreleased/hb-hide-archived-labels-from-group-issue-tracker.yml b/changelogs/unreleased/hb-hide-archived-labels-from-group-issue-tracker.yml
deleted file mode 100644
index 3b465d84126..00000000000
--- a/changelogs/unreleased/hb-hide-archived-labels-from-group-issue-tracker.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Hide archived project labels from group issue tracker
-merge_request: 12547
-author: Horacio Bertorello
diff --git a/changelogs/unreleased/help-landing-page-customizations.yml b/changelogs/unreleased/help-landing-page-customizations.yml
deleted file mode 100644
index 58cab751ded..00000000000
--- a/changelogs/unreleased/help-landing-page-customizations.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Help landing page customizations
-merge_request: 11878
-author: Robin Bobbitt
diff --git a/changelogs/unreleased/issuable-sidebar-edit-button-field-focus.yml b/changelogs/unreleased/issuable-sidebar-edit-button-field-focus.yml
deleted file mode 100644
index 05d52fcad0f..00000000000
--- a/changelogs/unreleased/issuable-sidebar-edit-button-field-focus.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixed dropdown filter input not focusing after transition
-merge_request:
-author:
diff --git a/changelogs/unreleased/issue_20900.yml b/changelogs/unreleased/issue_20900.yml
deleted file mode 100644
index e8cef6d2bce..00000000000
--- a/changelogs/unreleased/issue_20900.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove issues/merge requests drag n drop and sorting from milestone view
-merge_request:
-author:
diff --git a/changelogs/unreleased/issue_30126_be.yml b/changelogs/unreleased/issue_30126_be.yml
deleted file mode 100644
index 96bb8d9574b..00000000000
--- a/changelogs/unreleased/issue_30126_be.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add native group milestones
-merge_request:
-author:
diff --git a/changelogs/unreleased/issue_33205.yml b/changelogs/unreleased/issue_33205.yml
deleted file mode 100644
index 54b442048d8..00000000000
--- a/changelogs/unreleased/issue_33205.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix API bug accepting wrong parameter to create merge request
-merge_request:
-author:
diff --git a/changelogs/unreleased/issueable-list-cleanup.yml b/changelogs/unreleased/issueable-list-cleanup.yml
deleted file mode 100644
index d3d67d04574..00000000000
--- a/changelogs/unreleased/issueable-list-cleanup.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Clean up UI of issuable lists and make more responsive
-merge_request:
-author:
diff --git a/changelogs/unreleased/karma-headless-chrome.yml b/changelogs/unreleased/karma-headless-chrome.yml
deleted file mode 100644
index af3e9b3b0f9..00000000000
--- a/changelogs/unreleased/karma-headless-chrome.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Replace PhantomJS with headless Chrome for karma test suite
-merge_request: 12036
-author:
diff --git a/changelogs/unreleased/monitoring-dashboard-fine-tuning-ux.yml b/changelogs/unreleased/monitoring-dashboard-fine-tuning-ux.yml
deleted file mode 100644
index f84d41b7929..00000000000
--- a/changelogs/unreleased/monitoring-dashboard-fine-tuning-ux.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Improve the overall UX for the new monitoring dashboard
-merge_request:
-author:
diff --git a/changelogs/unreleased/monitoring-dashboard-fix-y-label.yml b/changelogs/unreleased/monitoring-dashboard-fix-y-label.yml
deleted file mode 100644
index 8a0e9ca855c..00000000000
--- a/changelogs/unreleased/monitoring-dashboard-fix-y-label.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixed the y_label not setting correctly for each graph on the monitoring dashboard
-merge_request:
-author:
diff --git a/changelogs/unreleased/moved-submodules.yml b/changelogs/unreleased/moved-submodules.yml
deleted file mode 100644
index eee858717ed..00000000000
--- a/changelogs/unreleased/moved-submodules.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'Handle renamed submodules in repository browser'
-merge_request: 10798
-author: David Turner
diff --git a/changelogs/unreleased/mr-widget-memory-usage-tech-debt-fix.yml b/changelogs/unreleased/mr-widget-memory-usage-tech-debt-fix.yml
deleted file mode 100644
index 14b5493a246..00000000000
--- a/changelogs/unreleased/mr-widget-memory-usage-tech-debt-fix.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Changed utilities imports from ~ to relative paths
-merge_request:
-author:
diff --git a/changelogs/unreleased/pat-alert-when-signin-disabled.yml b/changelogs/unreleased/pat-alert-when-signin-disabled.yml
deleted file mode 100644
index dca3670aeb7..00000000000
--- a/changelogs/unreleased/pat-alert-when-signin-disabled.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Provide hint to create a personal access token for Git over HTTP
-merge_request: 12105
-author: Robin Bobbitt
diff --git a/changelogs/unreleased/pat-msg-on-auth-failure.yml b/changelogs/unreleased/pat-msg-on-auth-failure.yml
deleted file mode 100644
index c1b1528bb7a..00000000000
--- a/changelogs/unreleased/pat-msg-on-auth-failure.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Instruct user to use personal access token for Git over HTTP
-merge_request: 11986
-author: Robin Bobbitt
diff --git a/changelogs/unreleased/polish-sidebar-toggle.yml b/changelogs/unreleased/polish-sidebar-toggle.yml
deleted file mode 100644
index 41ec567fc52..00000000000
--- a/changelogs/unreleased/polish-sidebar-toggle.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove unused space in sidebar todo toggle when not signed in
-merge_request:
-author:
diff --git a/changelogs/unreleased/project-readme-limited-width.yml b/changelogs/unreleased/project-readme-limited-width.yml
deleted file mode 100644
index 17d87a5691e..00000000000
--- a/changelogs/unreleased/project-readme-limited-width.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Limit the width of the projects README text
-merge_request:
-author:
diff --git a/changelogs/unreleased/replace_spinach_spec_profile_notifications-feature.yml b/changelogs/unreleased/replace_spinach_spec_profile_notifications-feature.yml
deleted file mode 100644
index 38227ebfa7a..00000000000
--- a/changelogs/unreleased/replace_spinach_spec_profile_notifications-feature.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Replace 'profile/notifications.feature' spinach test with an rspec analog
-merge_request: 12345
-author: @blackst0ne
diff --git a/changelogs/unreleased/replase_spinach_spec_create-feature.yml b/changelogs/unreleased/replase_spinach_spec_create-feature.yml
deleted file mode 100644
index 0613d195d56..00000000000
--- a/changelogs/unreleased/replase_spinach_spec_create-feature.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Replace 'create.feature' spinach test with an rspec analog
-merge_request: 12343
-author: @blackst0ne
diff --git a/changelogs/unreleased/sh-add-mr-simple-mode.yml b/changelogs/unreleased/sh-add-mr-simple-mode.yml
deleted file mode 100644
index 0033ca28444..00000000000
--- a/changelogs/unreleased/sh-add-mr-simple-mode.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add a simple mode to merge request API
-merge_request:
-author:
diff --git a/changelogs/unreleased/sh-allow-force-repo-create.yml b/changelogs/unreleased/sh-allow-force-repo-create.yml
deleted file mode 100644
index 2a65ba807bb..00000000000
--- a/changelogs/unreleased/sh-allow-force-repo-create.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Make Project#ensure_repository force create a repo
-merge_request:
-author:
diff --git a/changelogs/unreleased/sh-bump-oauth2-gem.yml b/changelogs/unreleased/sh-bump-oauth2-gem.yml
deleted file mode 100644
index b894a64968b..00000000000
--- a/changelogs/unreleased/sh-bump-oauth2-gem.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Bump Faraday and dependent OAuth2 gem version to support no_proxy variable
-merge_request:
-author:
diff --git a/changelogs/unreleased/sh-fix-project-destroy-in-namespace.yml b/changelogs/unreleased/sh-fix-project-destroy-in-namespace.yml
deleted file mode 100644
index 9309f961345..00000000000
--- a/changelogs/unreleased/sh-fix-project-destroy-in-namespace.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Defer project destroys within a namespace in Groups::DestroyService#async_execute
-merge_request:
-author:
diff --git a/changelogs/unreleased/sh-log-application-controller-exceptions-sentry.yml b/changelogs/unreleased/sh-log-application-controller-exceptions-sentry.yml
deleted file mode 100644
index ec9ceab3d81..00000000000
--- a/changelogs/unreleased/sh-log-application-controller-exceptions-sentry.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Log rescued exceptions to Sentry
-merge_request:
-author:
diff --git a/changelogs/unreleased/sh-optimize-mr-api-emojis-and-labels.yml b/changelogs/unreleased/sh-optimize-mr-api-emojis-and-labels.yml
deleted file mode 100644
index 9589659cdc2..00000000000
--- a/changelogs/unreleased/sh-optimize-mr-api-emojis-and-labels.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove remaining N+1 queries in merge requests API with emojis and labels
-merge_request:
-author:
diff --git a/changelogs/unreleased/sh-optimize-project-commit-api.yml b/changelogs/unreleased/sh-optimize-project-commit-api.yml
deleted file mode 100644
index e6a8a80593c..00000000000
--- a/changelogs/unreleased/sh-optimize-project-commit-api.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Optimize creation of commit API by using Repository#commit instead of Repository#commits
-merge_request:
-author:
diff --git a/changelogs/unreleased/speed-up-graphs.yml b/changelogs/unreleased/speed-up-graphs.yml
deleted file mode 100644
index 7cb155af6fd..00000000000
--- a/changelogs/unreleased/speed-up-graphs.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Speed up used languages calculation on charts page
-merge_request:
-author:
diff --git a/changelogs/unreleased/speed-up-issue-counting-for-a-project.yml b/changelogs/unreleased/speed-up-issue-counting-for-a-project.yml
deleted file mode 100644
index 6bf03d9a382..00000000000
--- a/changelogs/unreleased/speed-up-issue-counting-for-a-project.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Cache open issue and merge request counts for project tabs to speed up project
- pages
-merge_request: 12457
-author:
diff --git a/changelogs/unreleased/speed-up-merge-request-all-commits-shas.yml b/changelogs/unreleased/speed-up-merge-request-all-commits-shas.yml
deleted file mode 100644
index 00f55edc2b7..00000000000
--- a/changelogs/unreleased/speed-up-merge-request-all-commits-shas.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Make loading new merge requests (those created after the 9.4 upgrade) faster
-merge_request:
-author:
diff --git a/changelogs/unreleased/stop-notification-recipient-service-modifying-participants.yml b/changelogs/unreleased/stop-notification-recipient-service-modifying-participants.yml
deleted file mode 100644
index 7e66ea4ca8b..00000000000
--- a/changelogs/unreleased/stop-notification-recipient-service-modifying-participants.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Ensure participants for issues, merge requests, etc. are calculated correctly
- when sending notifications
-merge_request:
-author:
diff --git a/changelogs/unreleased/tc-follow-up-mia.yml b/changelogs/unreleased/tc-follow-up-mia.yml
deleted file mode 100644
index 6327f02032e..00000000000
--- a/changelogs/unreleased/tc-follow-up-mia.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Undo adding the /reassign quick action
-merge_request: 12701
-author:
diff --git a/changelogs/unreleased/tc-link-to-commit-on-help-page.yml b/changelogs/unreleased/tc-link-to-commit-on-help-page.yml
deleted file mode 100644
index 3d11ba43d1f..00000000000
--- a/changelogs/unreleased/tc-link-to-commit-on-help-page.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Make the revision on the `/help` page clickable
-merge_request: 12016
-author:
diff --git a/changelogs/unreleased/tc-refactor-projects-finder-init-collection.yml b/changelogs/unreleased/tc-refactor-projects-finder-init-collection.yml
deleted file mode 100644
index 7bcbd6468c7..00000000000
--- a/changelogs/unreleased/tc-refactor-projects-finder-init-collection.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add User#full_private_access? to check if user has access to all private groups & projects
-merge_request: 12373
-author:
diff --git a/changelogs/unreleased/workhorse-2-3-0.yml b/changelogs/unreleased/workhorse-2-3-0.yml
deleted file mode 100644
index 17992c8b0ff..00000000000
--- a/changelogs/unreleased/workhorse-2-3-0.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Upgrade GitLab Workhorse to v2.3.0
-merge_request: 12676
-author:
diff --git a/changelogs/unreleased/zj-commit-status-sortable-name.yml b/changelogs/unreleased/zj-commit-status-sortable-name.yml
deleted file mode 100644
index 1be9ac6380f..00000000000
--- a/changelogs/unreleased/zj-commit-status-sortable-name.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Handle nameless legacy jobs
-merge_request:
-author:
diff --git a/changelogs/unreleased/zj-faster-charts-page.yml b/changelogs/unreleased/zj-faster-charts-page.yml
deleted file mode 100644
index 9afcf111328..00000000000
--- a/changelogs/unreleased/zj-faster-charts-page.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Improve performance of the pipeline charts page
-merge_request: 12378
-author:
diff --git a/changelogs/unreleased/zj-pipeline-badge-improvements.yml b/changelogs/unreleased/zj-pipeline-badge-improvements.yml
new file mode 100644
index 00000000000..735192ede2d
--- /dev/null
+++ b/changelogs/unreleased/zj-pipeline-badge-improvements.yml
@@ -0,0 +1,4 @@
+---
+title: Update build badges to be pipeline badges and display passing instead of success
+merge_request:
+author:
diff --git a/changelogs/unreleased/zj-review-apps-usage-data.yml b/changelogs/unreleased/zj-review-apps-usage-data.yml
deleted file mode 100644
index 7d224d0fc32..00000000000
--- a/changelogs/unreleased/zj-review-apps-usage-data.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add review apps to usage metrics
-merge_request: 12185
-author:
diff --git a/changelogs/unreleased/zj-usage-ping-only-gl-pipelines.yml b/changelogs/unreleased/zj-usage-ping-only-gl-pipelines.yml
deleted file mode 100644
index 0ace7b99657..00000000000
--- a/changelogs/unreleased/zj-usage-ping-only-gl-pipelines.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Split pipelines as internal and external in the usage data
-merge_request: 12277
-author:
diff --git a/config/boot.rb b/config/boot.rb
index 2d01092acd5..f2830ae3166 100644
--- a/config/boot.rb
+++ b/config/boot.rb
@@ -4,8 +4,3 @@ require 'rubygems'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
-
-# set default directory for multiproces metrics gathering
-if ENV['RAILS_ENV'] == 'development' || ENV['RAILS_ENV'] == 'test'
- ENV['prometheus_multiproc_dir'] ||= 'tmp/prometheus_multiproc_dir'
-end
diff --git a/config/environments/test.rb b/config/environments/test.rb
index 986107150cf..278144b8943 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -45,7 +45,7 @@ Rails.application.configure do
config.active_job.queue_adapter = :test
if ENV['CI'] && !ENV['RAILS_ENABLE_TEST_LOG']
- config.logger = Logger.new(nil)
+ config.logger = ActiveSupport::TaggedLogging.new(Logger.new(nil))
config.log_level = :fatal
end
end
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index d0ab2dab0af..cb007813b65 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -383,13 +383,13 @@ production: &base
# service_validate_url: '/cas/p3/serviceValidate',
# logout_url: '/cas/logout'} }
# - { name: 'authentiq',
- # # for client credentials (client ID and secret), go to https://www.authentiq.com/
+ # # for client credentials (client ID and secret), go to https://www.authentiq.com/developers
# app_id: 'YOUR_CLIENT_ID',
# app_secret: 'YOUR_CLIENT_SECRET',
# args: {
# scope: 'aq:name email~rs address aq:push'
- # # redirect_uri parameter is optional except when 'gitlab.host' in this file is set to 'localhost'
- # # redirect_uri: 'YOUR_REDIRECT_URI'
+ # # callback_url parameter is optional except when 'gitlab.host' in this file is set to 'localhost'
+ # # callback_url: 'YOUR_CALLBACK_URL'
# }
# }
# - { name: 'github',
diff --git a/config/initializers/7_prometheus_metrics.rb b/config/initializers/7_prometheus_metrics.rb
new file mode 100644
index 00000000000..987324a86c9
--- /dev/null
+++ b/config/initializers/7_prometheus_metrics.rb
@@ -0,0 +1,12 @@
+require 'prometheus/client'
+
+Prometheus::Client.configure do |config|
+ config.logger = Rails.logger
+
+ config.initial_mmap_file_size = 4 * 1024
+ config.multiprocess_files_dir = ENV['prometheus_multiproc_dir']
+
+ if Rails.env.development? && Rails.env.test?
+ config.multiprocess_files_dir ||= Rails.root.join('tmp/prometheus_multiproc_dir')
+ end
+end
diff --git a/config/initializers/lograge.rb b/config/initializers/lograge.rb
index 14902316240..c9a515dfcd5 100644
--- a/config/initializers/lograge.rb
+++ b/config/initializers/lograge.rb
@@ -13,7 +13,7 @@ unless Sidekiq.server?
# Add request parameters to log output
config.lograge.custom_options = lambda do |event|
{
- time: event.time,
+ time: event.time.utc.iso8601(3),
params: event.payload[:params].except(%w(controller action format))
}
end
diff --git a/config/prometheus/additional_metrics.yml b/config/prometheus/additional_metrics.yml
index 60355e9140c..61d39e7bfcf 100644
--- a/config/prometheus/additional_metrics.yml
+++ b/config/prometheus/additional_metrics.yml
@@ -1,3 +1,24 @@
+- group: HA Proxy
+ priority: 10
+ metrics:
+ - title: "Throughput"
+ y_label: "Requests / Sec"
+ required_metrics:
+ - haproxy_frontend_http_requests_total
+ weight: 1
+ queries:
+ - query_range: 'sum(rate(haproxy_frontend_http_requests_total{%{environment_filter}}[2m]))'
+ label: Total
+ unit: req / sec
+ - title: "HTTP Error Rate"
+ y_label: "Error Rate (%)"
+ required_metrics:
+ - haproxy_frontend_http_responses_total
+ weight: 1
+ queries:
+ - query_range: 'sum(rate(haproxy_frontend_http_responses_total{code="5xx",%{environment_filter}}[2m])) / sum(rate(haproxy_frontend_http_responses_total{%{environment_filter}}[2m]))'
+ label: HTTP Errors
+ unit: "%"
- group: AWS Elastic Load Balancer
priority: 10
metrics:
@@ -56,7 +77,7 @@
- nginx_responses_total
weight: 1
queries:
- - query_range: 'sum(nginx_responses_total{status_code="5xx", %{environment_filter}}) / sum(nginx_responses_total{server_zone!="*", server_zone!="_", %{environment_filter}})'
+ - query_range: 'sum(rate(nginx_responses_total{status_code="5xx", %{environment_filter}}[2m])) / sum(rate(nginx_requests_total{server_zone!="*", server_zone!="_", %{environment_filter}}[2m]))'
label: HTTP Errors
unit: "%"
- group: Kubernetes
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 62cab25c763..06928c7b9ce 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -272,7 +272,7 @@ constraints(ProjectUrlConstrainer.new) do
namespace :registry do
resources :repository, only: [] do
resources :tags, only: [:destroy],
- constraints: { id: Gitlab::Regex.container_registry_reference_regex }
+ constraints: { id: Gitlab::Regex.container_registry_tag_regex }
end
end
@@ -379,7 +379,9 @@ constraints(ProjectUrlConstrainer.new) do
collection do
scope '*ref', constraints: { ref: Gitlab::PathRegex.git_reference_regex } do
constraints format: /svg/ do
- get :build
+ # Keep around until 10.0, see gitlab-org/gitlab-ce#35307
+ get :build, to: "badges#pipeline"
+ get :pipeline
get :coverage
end
end
diff --git a/config/routes/uploads.rb b/config/routes/uploads.rb
index a49e244af1a..e9c9aa8b2f9 100644
--- a/config/routes/uploads.rb
+++ b/config/routes/uploads.rb
@@ -1,21 +1,21 @@
scope path: :uploads do
# Note attachments and User/Group/Project avatars
- get "system/:model/:mounted_as/:id/:filename",
+ get "-/system/:model/:mounted_as/:id/:filename",
to: "uploads#show",
constraints: { model: /note|user|group|project/, mounted_as: /avatar|attachment/, filename: /[^\/]+/ }
# show uploads for models, snippets (notes) available for now
- get ':model/:id/:secret/:filename',
+ get 'system/:model/:id/:secret/:filename',
to: 'uploads#show',
constraints: { model: /personal_snippet/, id: /\d+/, filename: /[^\/]+/ }
# show temporary uploads
- get 'temp/:secret/:filename',
+ get 'system/temp/:secret/:filename',
to: 'uploads#show',
constraints: { filename: /[^\/]+/ }
# Appearance
- get "system/:model/:mounted_as/:id/:filename",
+ get "-/system/:model/:mounted_as/:id/:filename",
to: "uploads#show",
constraints: { model: /appearance/, mounted_as: /logo|header_logo/, filename: /.+/ }
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 1113241e402..a7d92bc53b7 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -56,7 +56,7 @@ var config = {
pipelines_details: './pipelines/pipeline_details_bundle.js',
profile: './profile/profile_bundle.js',
prometheus_metrics: './prometheus_metrics',
- protected_branches: './protected_branches/protected_branches_bundle.js',
+ protected_branches: './protected_branches',
protected_tags: './protected_tags',
sidebar: './sidebar/sidebar_bundle.js',
schedule_form: './pipeline_schedules/pipeline_schedule_form_bundle.js',
diff --git a/db/migrate/20170710083355_clean_stage_id_reference_migration.rb b/db/migrate/20170710083355_clean_stage_id_reference_migration.rb
new file mode 100644
index 00000000000..681203eaf40
--- /dev/null
+++ b/db/migrate/20170710083355_clean_stage_id_reference_migration.rb
@@ -0,0 +1,18 @@
+class CleanStageIdReferenceMigration < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ ##
+ # `MigrateStageIdReferenceInBackground` background migration cleanup.
+ #
+ def up
+ Gitlab::BackgroundMigration.steal('MigrateBuildStageIdReference')
+ end
+
+ def down
+ # noop
+ end
+end
diff --git a/db/migrate/20170717074009_move_system_upload_folder.rb b/db/migrate/20170717074009_move_system_upload_folder.rb
new file mode 100644
index 00000000000..cce31794115
--- /dev/null
+++ b/db/migrate/20170717074009_move_system_upload_folder.rb
@@ -0,0 +1,60 @@
+class MoveSystemUploadFolder < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+
+ def up
+ unless file_storage?
+ say 'Using object storage, no need to move.'
+ return
+ end
+
+ unless File.directory?(old_directory)
+ say "#{old_directory} doesn't exist, no need to move it."
+ return
+ end
+
+ FileUtils.mkdir_p(File.join(base_directory, '-'))
+
+ say "Moving #{old_directory} -> #{new_directory}"
+ FileUtils.mv(old_directory, new_directory)
+ FileUtils.ln_s(new_directory, old_directory)
+ end
+
+ def down
+ unless file_storage?
+ say 'Using object storage, no need to move.'
+ return
+ end
+
+ unless File.directory?(new_directory)
+ say "#{new_directory} doesn't exist, no need to move it."
+ return
+ end
+
+ if File.symlink?(old_directory)
+ say "Removing #{old_directory} -> #{new_directory} symlink"
+ FileUtils.rm(old_directory)
+ end
+
+ say "Moving #{new_directory} -> #{old_directory}"
+ FileUtils.mv(new_directory, old_directory)
+ end
+
+ def new_directory
+ File.join(base_directory, '-', 'system')
+ end
+
+ def old_directory
+ File.join(base_directory, 'system')
+ end
+
+ def base_directory
+ File.join(Rails.root, 'public', 'uploads')
+ end
+
+ def file_storage?
+ CarrierWave::Uploader::Base.storage == CarrierWave::Storage::File
+ end
+end
diff --git a/db/post_migrate/20170406111121_clean_upload_symlinks.rb b/db/post_migrate/20170406111121_clean_upload_symlinks.rb
index 3ac9a6c10bc..fc3a4acc0bb 100644
--- a/db/post_migrate/20170406111121_clean_upload_symlinks.rb
+++ b/db/post_migrate/20170406111121_clean_upload_symlinks.rb
@@ -6,7 +6,7 @@ class CleanUploadSymlinks < ActiveRecord::Migration
disable_ddl_transaction!
DOWNTIME = false
- DIRECTORIES_TO_MOVE = %w(user project note group appeareance)
+ DIRECTORIES_TO_MOVE = %w(user project note group appearance)
def up
return unless file_storage?
diff --git a/db/post_migrate/20170612071012_move_personal_snippets_files.rb b/db/post_migrate/20170612071012_move_personal_snippets_files.rb
new file mode 100644
index 00000000000..33043364bde
--- /dev/null
+++ b/db/post_migrate/20170612071012_move_personal_snippets_files.rb
@@ -0,0 +1,91 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+class MovePersonalSnippetsFiles < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+
+ def up
+ return unless file_storage?
+
+ @source_relative_location = File.join('/uploads', 'personal_snippet')
+ @destination_relative_location = File.join('/uploads', 'system', 'personal_snippet')
+
+ move_personal_snippet_files
+ end
+
+ def down
+ return unless file_storage?
+
+ @source_relative_location = File.join('/uploads', 'system', 'personal_snippet')
+ @destination_relative_location = File.join('/uploads', 'personal_snippet')
+
+ move_personal_snippet_files
+ end
+
+ def move_personal_snippet_files
+ query = "SELECT uploads.path, uploads.model_id, snippets.description FROM uploads "\
+ "INNER JOIN snippets ON snippets.id = uploads.model_id WHERE uploader = 'PersonalFileUploader'"
+ select_all(query).each do |upload|
+ secret = upload['path'].split('/')[0]
+ file_name = upload['path'].split('/')[1]
+
+ next unless move_file(upload['model_id'], secret, file_name)
+ update_markdown(upload['model_id'], secret, file_name, upload['description'])
+ end
+ end
+
+ def move_file(snippet_id, secret, file_name)
+ source_dir = File.join(base_directory, @source_relative_location, snippet_id.to_s, secret)
+ destination_dir = File.join(base_directory, @destination_relative_location, snippet_id.to_s, secret)
+
+ source_file_path = File.join(source_dir, file_name)
+ destination_file_path = File.join(destination_dir, file_name)
+
+ unless File.exist?(source_file_path)
+ say "Source file `#{source_file_path}` doesn't exist. Skipping."
+ return
+ end
+
+ say "Moving file #{source_file_path} -> #{destination_file_path}"
+
+ FileUtils.mkdir_p(destination_dir)
+ FileUtils.move(source_file_path, destination_file_path)
+
+ true
+ end
+
+ def update_markdown(snippet_id, secret, file_name, description)
+ source_markdown_path = File.join(@source_relative_location, snippet_id.to_s, secret, file_name)
+ destination_markdown_path = File.join(@destination_relative_location, snippet_id.to_s, secret, file_name)
+
+ source_markdown = "](#{source_markdown_path})"
+ destination_markdown = "](#{destination_markdown_path})"
+
+ if description.present?
+ description = description.gsub(source_markdown, destination_markdown)
+ quoted_description = quote_string(description)
+
+ execute("UPDATE snippets SET description = '#{quoted_description}', description_html = NULL "\
+ "WHERE id = #{snippet_id}")
+ end
+
+ query = "SELECT id, note FROM notes WHERE noteable_id = #{snippet_id} "\
+ "AND noteable_type = 'Snippet' AND note IS NOT NULL"
+ select_all(query).each do |note|
+ text = note['note'].gsub(source_markdown, destination_markdown)
+ quoted_text = quote_string(text)
+
+ execute("UPDATE notes SET note = '#{quoted_text}', note_html = NULL WHERE id = #{note['id']}")
+ end
+ end
+
+ def base_directory
+ File.join(Rails.root, 'public')
+ end
+
+ def file_storage?
+ CarrierWave::Uploader::Base.storage == CarrierWave::Storage::File
+ end
+end
diff --git a/db/post_migrate/20170613111224_clean_appearance_symlinks.rb b/db/post_migrate/20170613111224_clean_appearance_symlinks.rb
new file mode 100644
index 00000000000..acb895e426f
--- /dev/null
+++ b/db/post_migrate/20170613111224_clean_appearance_symlinks.rb
@@ -0,0 +1,52 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class CleanAppearanceSymlinks < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+
+ def up
+ return unless file_storage?
+
+ symlink_location = File.join(old_upload_dir, dir)
+
+ return unless File.symlink?(symlink_location)
+ say "removing symlink: #{symlink_location}"
+ FileUtils.rm(symlink_location)
+ end
+
+ def down
+ return unless file_storage?
+
+ symlink = File.join(old_upload_dir, dir)
+ destination = File.join(new_upload_dir, dir)
+
+ return if File.directory?(symlink)
+ return unless File.directory?(destination)
+
+ say "Creating symlink #{symlink} -> #{destination}"
+ FileUtils.ln_s(destination, symlink)
+ end
+
+ def file_storage?
+ CarrierWave::Uploader::Base.storage == CarrierWave::Storage::File
+ end
+
+ def dir
+ 'appearance'
+ end
+
+ def base_directory
+ Rails.root
+ end
+
+ def old_upload_dir
+ File.join(base_directory, "public", "uploads")
+ end
+
+ def new_upload_dir
+ File.join(base_directory, "public", "uploads", "system")
+ end
+end
diff --git a/db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb b/db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb
new file mode 100644
index 00000000000..26b99b61424
--- /dev/null
+++ b/db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb
@@ -0,0 +1,40 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class CleanupMoveSystemUploadFolderSymlink < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ if File.symlink?(old_directory)
+ say "Removing #{old_directory} -> #{new_directory} symlink"
+ FileUtils.rm(old_directory)
+ else
+ say "Symlink #{old_directory} non existant, nothing to do."
+ end
+ end
+
+ def down
+ if File.directory?(new_directory)
+ say "Symlinking #{old_directory} -> #{new_directory}"
+ FileUtils.ln_s(new_directory, old_directory)
+ else
+ say "#{new_directory} doesn't exist, skipping."
+ end
+ end
+
+ def new_directory
+ File.join(base_directory, '-', 'system')
+ end
+
+ def old_directory
+ File.join(base_directory, 'system')
+ end
+
+ def base_directory
+ File.join(Rails.root, 'public', 'uploads')
+ end
+end
diff --git a/db/post_migrate/20170717150329_enqueue_migrate_system_uploads_to_new_folder.rb b/db/post_migrate/20170717150329_enqueue_migrate_system_uploads_to_new_folder.rb
new file mode 100644
index 00000000000..87069dce006
--- /dev/null
+++ b/db/post_migrate/20170717150329_enqueue_migrate_system_uploads_to_new_folder.rb
@@ -0,0 +1,20 @@
+class EnqueueMigrateSystemUploadsToNewFolder < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ OLD_FOLDER = 'uploads/system/'
+ NEW_FOLDER = 'uploads/-/system/'
+
+ disable_ddl_transaction!
+
+ def up
+ BackgroundMigrationWorker.perform_async('MigrateSystemUploadsToNewFolder',
+ [OLD_FOLDER, NEW_FOLDER])
+ end
+
+ def down
+ BackgroundMigrationWorker.perform_async('MigrateSystemUploadsToNewFolder',
+ [NEW_FOLDER, OLD_FOLDER])
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 9c8c64fe2d0..284b2068166 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: 20170713104829) do
+ActiveRecord::Schema.define(version: 20170717150329) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
diff --git a/doc/README.md b/doc/README.md
index 1a7638b3d7e..2b3b0998fcc 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -115,6 +115,7 @@ Manage files and branches from the UI (user interface):
- [Project Services](user/project/integrations/project_services.md): Integrate a project with external services, such as CI and chat.
- [GitLab Integration](integration/README.md): Integrate with multiple third-party services with GitLab to allow external issue trackers and external authentication.
+- [Trello Power-Up](integration/trello_power_up.md): Integrate with GitLab's Trello Power-Up
----
diff --git a/doc/administration/auth/authentiq.md b/doc/administration/auth/authentiq.md
index fb1a16b0f96..1528f1d2b17 100644
--- a/doc/administration/auth/authentiq.md
+++ b/doc/administration/auth/authentiq.md
@@ -32,7 +32,7 @@ Authentiq will generate a Client ID and the accompanying Client Secret for you t
"app_id" => "YOUR_CLIENT_ID",
"app_secret" => "YOUR_CLIENT_SECRET",
"args" => {
- scope: 'aq:name email~rs aq:push'
+ "scope": 'aq:name email~rs address aq:push'
}
}
]
@@ -45,21 +45,20 @@ Authentiq will generate a Client ID and the accompanying Client Secret for you t
app_id: 'YOUR_CLIENT_ID',
app_secret: 'YOUR_CLIENT_SECRET',
args: {
- scope: 'aq:name email~rs aq:push'
+ scope: 'aq:name email~rs address aq:push'
}
}
```
5. The `scope` is set to request the user's name, email (required and signed), and permission to send push notifications to sign in on subsequent visits.
-See [OmniAuth Authentiq strategy](https://github.com/AuthentiqID/omniauth-authentiq#scopes-and-redirect-uri-configuration) for more information on scopes and modifiers.
+See [OmniAuth Authentiq strategy](https://github.com/AuthentiqID/omniauth-authentiq/wiki/Scopes,-callback-url-configuration-and-responses) for more information on scopes and modifiers.
6. Change `YOUR_CLIENT_ID` and `YOUR_CLIENT_SECRET` to the Client credentials you received in step 1.
7. Save the configuration file.
-8. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../restart_gitlab.md#installations-from-source)
- for the changes to take effect if you installed GitLab via Omnibus or from source respectively.
+8. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect if you installed GitLab via Omnibus or from source respectively.
On the sign in page there should now be an Authentiq icon below the regular sign in form. Click the icon to begin the authentication process.
diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md
index c8987dea5e2..3449f9e15ce 100644
--- a/doc/administration/auth/ldap.md
+++ b/doc/administration/auth/ldap.md
@@ -228,7 +228,7 @@ Tip: If you want to limit access to the nested members of an Active Directory
group you can use the following syntax:
```
-(memberOf:1.2.840.113556.1.4.1941=CN=My Group,DC=Example,DC=com)
+(memberOf:1.2.840.113556.1.4.1941:=CN=My Group,DC=Example,DC=com)
```
Find more information about this "LDAP_MATCHING_RULE_IN_CHAIN" filter at
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index 7c5505de8a2..6baae20d16a 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -20,30 +20,45 @@ it, the client IP needs to be [included in a whitelist][whitelist].
Currently the embedded Prometheus server is not automatically configured to
collect metrics from this endpoint. We recommend setting up another Prometheus
server, because the embedded server configuration is overwritten once every
-[reconfigure of GitLab][reconfigure]. In the future this will not be required.
+[reconfigure of GitLab][reconfigure]. In the future this will not be required.
## Metrics available
In this experimental phase, only a few metrics are available:
-| Metric | Type | Description |
-| --------------------------------- | --------- | ----------- |
-| db_ping_timeout | Gauge | Whether or not the last database ping timed out |
-| db_ping_success | Gauge | Whether or not the last database ping succeeded |
-| db_ping_latency_seconds | Gauge | Round trip time of the database ping |
-| filesystem_access_latency_seconds | Gauge | Latency in accessing a specific filesystem |
-| filesystem_accessible | Gauge | Whether or not a specific filesystem is accessible |
-| filesystem_write_latency_seconds | Gauge | Write latency of a specific filesystem |
-| filesystem_writable | Gauge | Whether or not the filesystem is writable |
-| filesystem_read_latency_seconds | Gauge | Read latency of a specific filesystem |
-| filesystem_readable | Gauge | Whether or not the filesystem is readable |
-| http_requests_total | Counter | Rack request count |
-| http_request_duration_seconds | Histogram | HTTP response time from rack middleware |
-| rack_uncaught_errors_total | Counter | Rack connections handling uncaught errors count |
-| redis_ping_timeout | Gauge | Whether or not the last redis ping timed out |
-| redis_ping_success | Gauge | Whether or not the last redis ping succeeded |
-| redis_ping_latency_seconds | Gauge | Round trip time of the redis ping |
-| user_session_logins_total | Counter | Counter of how many users have logged in |
+| Metric | Type | Since | Description |
+|:--------------------------------- |:--------- |:----- |:----------- |
+| db_ping_timeout | Gauge | 9.4 | Whether or not the last database ping timed out |
+| db_ping_success | Gauge | 9.4 | Whether or not the last database ping succeeded |
+| db_ping_latency_seconds | Gauge | 9.4 | Round trip time of the database ping |
+| filesystem_access_latency_seconds | Gauge | 9.4 | Latency in accessing a specific filesystem |
+| filesystem_accessible | Gauge | 9.4 | Whether or not a specific filesystem is accessible |
+| filesystem_write_latency_seconds | Gauge | 9.4 | Write latency of a specific filesystem |
+| filesystem_writable | Gauge | 9.4 | Whether or not the filesystem is writable |
+| filesystem_read_latency_seconds | Gauge | 9.4 | Read latency of a specific filesystem |
+| filesystem_readable | Gauge | 9.4 | Whether or not the filesystem is readable |
+| http_requests_total | Counter | 9.4 | Rack request count |
+| http_request_duration_seconds | Histogram | 9.4 | HTTP response time from rack middleware |
+| pipelines_created_total | Counter | 9.4 | Counter of pipelines created |
+| rack_uncaught_errors_total | Counter | 9.4 | Rack connections handling uncaught errors count |
+| redis_ping_timeout | Gauge | 9.4 | Whether or not the last redis ping timed out |
+| redis_ping_success | Gauge | 9.4 | Whether or not the last redis ping succeeded |
+| redis_ping_latency_seconds | Gauge | 9.4 | Round trip time of the redis ping |
+| user_session_logins_total | Counter | 9.4 | Counter of how many users have logged in |
+
+## Metrics shared directory
+
+GitLab's Prometheus client requires a directory to store metrics data shared between multi-process services.
+Those files are shared among all instances running under Unicorn server.
+The directory needs to be accessible to all running Unicorn's processes otherwise
+metrics will not function correctly.
+
+For best performance its advisable that this directory will be located in `tmpfs`.
+
+Its location is configured using environment variable `prometheus_multiproc_dir`.
+
+If GitLab is installed using Omnibus and `tmpfs` is available then metrics
+directory will be automatically configured.
[← Back to the main Prometheus page](index.md)
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 0d892c74d00..61ae89a64c0 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -1257,17 +1257,21 @@ endpoint can be accessed without authentication if the project is publicly
accessible.
```
-GET /projects/search/:query
+GET /projects
```
Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `query` | string | yes | A string contained in the project name |
+| `search` | string | yes | A string contained in the project name |
| `order_by` | string | no | Return requests ordered by `id`, `name`, `created_at` or `last_activity_at` fields |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order |
+```bash
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects?search=test
+```
+
## Start the Housekeeping task for a Project
>**Note:** This feature was introduced in GitLab 9.0
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 724843a4d56..e12ef6e2685 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -395,6 +395,7 @@ job_name:
| after_script | no | Override a set of commands that are executed after job |
| environment | no | Defines a name of environment to which deployment is done by this job |
| coverage | no | Define code coverage settings for a given job |
+| retry | no | Define how many times a job can be auto-retried in case of a failure |
### script
@@ -1129,9 +1130,33 @@ A simple example:
```yaml
job1:
+ script: rspec
coverage: '/Code coverage: \d+\.\d+/'
```
+### retry
+
+**Notes:**
+- [Introduced][ce-3442] in GitLab 9.5.
+
+`retry` allows you to configure how many times a job is going to be retried in
+case of a failure.
+
+When a job fails, and has `retry` configured it is going to be processed again
+up to the amount of times specified by the `retry` keyword.
+
+If `retry` is set to 2, and a job succeeds in a second run (first retry), it won't be retried
+again. `retry` value has to be a positive integer, equal or larger than 0, but
+lower or equal to 2 (two retries maximum, three runs in total).
+
+A simple example:
+
+```yaml
+test:
+ script: rspec
+ retry: 2
+```
+
## Git Strategy
> Introduced in GitLab 8.9 as an experimental feature. May change or be removed
@@ -1506,3 +1531,4 @@ CI with various languages.
[variables]: ../variables/README.md
[ce-7983]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7983
[ce-7447]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7447
+[ce-3442]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3442
diff --git a/doc/development/background_migrations.md b/doc/development/background_migrations.md
index 72a34aa7de9..e67db9ff142 100644
--- a/doc/development/background_migrations.md
+++ b/doc/development/background_migrations.md
@@ -31,6 +31,18 @@ It's also possible for different migrations to be executed at the same time.
This means that different background migrations should not migrate data in a
way that would cause conflicts.
+## Idempotence
+
+Background migrations are executed in a context of a Sidekiq process.
+Usual Sidekiq rules apply, especially the rule that jobs should be small
+and idempotent.
+
+See [Sidekiq best practices guidelines](https://github.com/mperham/sidekiq/wiki/Best-Practices)
+for more details.
+
+Make sure that in case that your migration job is going to be retried data
+integrity is guarateed.
+
## How It Works
Background migrations are simple classes that define a `perform` method. A
@@ -212,3 +224,27 @@ end
This migration will then process any jobs for the ExtractServicesUrl migration
and continue once all jobs have been processed. Once done you can safely remove
the `services.properties` column.
+
+## Testing
+
+It is required to write tests for background migrations' scheduling migration
+(either a regular migration or a post deployment migration), background
+migration itself and a cleanup migration. You can use the `:migration` RSpec
+tag when testing a regular / post deployment migration.
+See [README][migrations-readme].
+
+When you do that, keep in mind that `before` and `after` RSpec hooks are going
+to migrate you database down and up, which can result in other background
+migrations being called. That means that using `spy` test doubles with
+`have_received` is encouraged, instead of using regular test doubles, because
+your expectations defined in a `it` block can conflict with what is being
+called in RSpec hooks. See [gitlab-org/gitlab-ce#35351][issue-rspec-hooks]
+for more details.
+
+## Best practices
+
+1. Make sure that background migration jobs are idempotent.
+1. Make sure that tests you write are not false positives.
+
+[migrations-readme]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/migrations/README.md
+[issue-rspec-hooks]: https://gitlab.com/gitlab-org/gitlab-ce/issues/35351
diff --git a/doc/development/gotchas.md b/doc/development/gotchas.md
index 565d4b33457..c2ca8966a3f 100644
--- a/doc/development/gotchas.md
+++ b/doc/development/gotchas.md
@@ -3,35 +3,6 @@
The purpose of this guide is to document potential "gotchas" that contributors
might encounter or should avoid during development of GitLab CE and EE.
-## Do not `describe` symbols
-
-Consider the following model spec:
-
-```ruby
-require 'rails_helper'
-
-describe User do
- describe :to_param do
- it 'converts the username to a param' do
- user = described_class.new(username: 'John Smith')
-
- expect(user.to_param).to eq 'john-smith'
- end
- end
-end
-```
-
-When run, this spec doesn't do what we might expect:
-
-```sh
-spec/models/user_spec.rb|6 error| Failure/Error: u = described_class.new NoMethodError: undefined method `new' for :to_param:Symbol
-```
-
-### Solution
-
-Except for the top-level `describe` block, always provide a String argument to
-`describe`.
-
## Do not assert against the absolute value of a sequence-generated attribute
Consider the following factory:
diff --git a/doc/development/sidekiq_style_guide.md b/doc/development/sidekiq_style_guide.md
index e3a20f29a09..1e9fdbc65e2 100644
--- a/doc/development/sidekiq_style_guide.md
+++ b/doc/development/sidekiq_style_guide.md
@@ -36,3 +36,10 @@ slow jobs blocking work (even for different jobs) on the shared queue.
Each Sidekiq worker must be tested using RSpec, just like any other class. These
tests should be placed in `spec/workers`.
+
+## Removing or renaming queues
+
+Try to avoid renaming or removing queues in minor and patch releases.
+During online update instance can have pending jobs and removing the queue can
+lead to those jobs being stuck forever. If you can't write migration for those
+Sidekiq jobs, please consider doing rename or remove queue in major release only. \ No newline at end of file
diff --git a/doc/development/testing.md b/doc/development/testing.md
index fc84932354b..e6aa4ae8f2f 100644
--- a/doc/development/testing.md
+++ b/doc/development/testing.md
@@ -195,7 +195,6 @@ Please consult the [dedicated "Frontend testing" guide](./fe_guide/testing.md).
- Use `context` to test branching logic.
- Use multi-line `do...end` blocks for `before` and `after`, even when it would
fit on a single line.
-- Don't `describe` symbols (see [Gotchas](gotchas.md#dont-describe-symbols)).
- Don't assert against the absolute value of a sequence-generated attribute (see [Gotchas](gotchas.md#dont-assert-against-the-absolute-value-of-a-sequence-generated-attribute)).
- Don't supply the `:each` argument to hooks since it's the default.
- Prefer `not_to` to `to_not` (_this is enforced by RuboCop_).
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 5e981b0b3e7..8ded607bcab 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -64,7 +64,7 @@ up-to-date and install it.
Install the required packages (needed to compile Ruby and native extensions to Ruby gems):
- sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate python-docutils pkg-config cmake
+ sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libre2-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate python-docutils pkg-config cmake
If you want to use Kerberos for user authentication, then install libkrb5-dev:
diff --git a/doc/integration/img/enable_trello_powerup.png b/doc/integration/img/enable_trello_powerup.png
new file mode 100644
index 00000000000..65d01f1c38c
--- /dev/null
+++ b/doc/integration/img/enable_trello_powerup.png
Binary files differ
diff --git a/doc/integration/img/trello_card_with_gitlab_powerup.png b/doc/integration/img/trello_card_with_gitlab_powerup.png
new file mode 100644
index 00000000000..2965dc35855
--- /dev/null
+++ b/doc/integration/img/trello_card_with_gitlab_powerup.png
Binary files differ
diff --git a/doc/integration/trello_power_up.md b/doc/integration/trello_power_up.md
new file mode 100644
index 00000000000..d264486a872
--- /dev/null
+++ b/doc/integration/trello_power_up.md
@@ -0,0 +1,42 @@
+# Trello Power-Up
+
+GitLab's Trello Power-Up enables you to seamlessly attach
+GitLab **merge requests** to Trello cards.
+
+![GitLab Trello PowerUp - Trello card](img/trello_card_with_gitlab_powerup.png)
+
+## Configuring the Power-Up
+
+In order to get started, you will need to configure your Power-Up.
+
+In Trello:
+
+1. Go to your Trello board
+1. Select `Power-Ups` to see a listing of all the available Power-Ups
+1. Look for a row that says `GitLab` and select the `Enable` button
+1. Select the `Settings` (gear) icon
+1. In the popup menu, select `Authorize Account`
+
+In this popup, fill in your `API URL` and `Personal Access Token`. After that, you will be able to attach any merge request to any Trello card on your selected Trello board.
+
+## What is my API URL?
+
+Your API URL should be your GitLab instance URL with `/api/v4` appended in the end of the URL.
+For example, if your GitLab instance URL is `https://gitlab.com`, your API URL would be `https://gitlab.com/api/v4`.
+If your instance's URL is `https://example.com`, your API URL will be `https://example.com/api/v4`.
+
+![configure GitLab Trello PowerUp in Trello](img/enable_trello_powerup.png)
+
+## What is my Personal Access Token?
+
+Your GitLab's personal access token will enable your GitLab account to be accessed
+from Trello.
+
+> Find it in GitLab by clicking on your avatar (upright corner), from which you access
+your user **Settings** > **Access Tokens**.
+
+Learn more about generating a personal access token in the
+[Personal Access Token Documentation][personal-access-token-documentation].
+Don't forget to check the API scope checkbox!
+
+[personal-access-token-documentation]: ../user/profile/personal_access_tokens.html
diff --git a/doc/user/project/integrations/img/webhook_testing.png b/doc/user/project/integrations/img/webhook_testing.png
new file mode 100644
index 00000000000..176dcec9d8a
--- /dev/null
+++ b/doc/user/project/integrations/img/webhook_testing.png
Binary files differ
diff --git a/doc/user/project/integrations/kubernetes.md b/doc/user/project/integrations/kubernetes.md
index bfe2672e098..f4000523938 100644
--- a/doc/user/project/integrations/kubernetes.md
+++ b/doc/user/project/integrations/kubernetes.md
@@ -19,10 +19,10 @@ of your project and select the **Kubernetes** service to configure it.
The Kubernetes service takes the following arguments:
-1. Kubernetes namespace
1. API URL
-1. Service token
1. Custom CA bundle
+1. Kubernetes namespace
+1. Service token
The API URL is the URL that GitLab uses to access the Kubernetes API. Kubernetes
exposes several APIs - we want the "base" URL that is common to all of them,
diff --git a/doc/user/project/integrations/prometheus_library/haproxy.md b/doc/user/project/integrations/prometheus_library/haproxy.md
new file mode 100644
index 00000000000..309da610cc0
--- /dev/null
+++ b/doc/user/project/integrations/prometheus_library/haproxy.md
@@ -0,0 +1,20 @@
+# Monitoring HA Proxy
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12621) in GitLab 9.4
+
+GitLab has support for automatically detecting and monitoring HA Proxy. This is provided by leveraging the [HA Proxy Exporter](https://github.com/hnlq715/nginx-vts-exporter), which translates HA Proxy statistics into a Prometheus readable form.
+
+## Metrics supported
+
+| Name | Query |
+| ---- | ----- |
+| Throughput (req/sec) | sum(rate(haproxy_frontend_http_requests_total{%{environment_filter}}[2m])) |
+| HTTP Error Rate (%) | sum(rate(haproxy_frontend_http_requests_total{code="5xx",%{environment_filter}}[2m])) / sum(rate(haproxy_frontend_http_requests_total{%{environment_filter}}[2m])) |
+
+## Configuring Prometheus to monitor for HA Proxy metrics
+
+To get started with NGINX monitoring, you should install and configure the [HA Proxy exporter](https://github.com/prometheus/haproxy_exporter) which parses these statistics and translates them into a Prometheus monitoring endpoint.
+
+## Specifying the Environment label
+
+In order to isolate and only display relevant metrics for a given environment
+however, GitLab needs a method to detect which labels are associated. To do this, GitLab will [look for an `environment` label](metrics.md#identifying-environments).
diff --git a/doc/user/project/integrations/prometheus_library/metrics.md b/doc/user/project/integrations/prometheus_library/metrics.md
index 55146e57370..546e1f51df5 100644
--- a/doc/user/project/integrations/prometheus_library/metrics.md
+++ b/doc/user/project/integrations/prometheus_library/metrics.md
@@ -4,6 +4,7 @@
GitLab offers automatic detection of select [Prometheus exporters](https://prometheus.io/docs/instrumenting/exporters/). Currently supported exporters are:
* [Kubernetes](kubernetes.md)
* [NGINX](nginx.md)
+* [HA Proxy](haproxy.md)
* [Amazon Cloud Watch](cloudwatch.md)
We have tried to surface the most important metrics for each exporter, and will be continuing to add support for additional exporters in future releases. If you would like to add support for other official exporters, [contributions](#adding-to-the-library) are welcome.
diff --git a/doc/user/project/integrations/prometheus_library/nginx.md b/doc/user/project/integrations/prometheus_library/nginx.md
index fe238e74e36..b3470773996 100644
--- a/doc/user/project/integrations/prometheus_library/nginx.md
+++ b/doc/user/project/integrations/prometheus_library/nginx.md
@@ -9,7 +9,7 @@ GitLab has support for automatically detecting and monitoring NGINX. This is pro
| ---- | ----- |
| Throughput (req/sec) | sum(rate(nginx_requests_total{server_zone!="*", server_zone!="_", %{environment_filter}}[2m])) |
| Latency (ms) | avg(nginx_upstream_response_msecs_avg{%{environment_filter}}) * 1000 |
-| HTTP Error Rate (%) | sum(nginx_responses_total{status_code="5xx", %{environment_filter}}) / sum(nginx_responses_total{server_zone!="*", server_zone!="_", %{environment_filter}}) |
+| HTTP Error Rate (%) | sum(rate(haproxy_frontend_http_responses_total{code="5xx",%{environment_filter}}[2m])) / sum(rate(haproxy_frontend_http_responses_total{%{environment_filter}}[2m])) |
## Configuring Prometheus to monitor for NGINX metrics
diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md
index 023c6932e41..c03a2df9a72 100644
--- a/doc/user/project/integrations/webhooks.md
+++ b/doc/user/project/integrations/webhooks.md
@@ -1014,6 +1014,13 @@ X-Gitlab-Event: Build Hook
}
```
+## Testing webhooks
+
+You can trigger the webhook manually. Sample data from the project will be used.Sample data will take from the project.
+> For example: for triggering `Push Events` your project should have at least one commit.
+
+![Webhook testing](img/webhook_testing.png)
+
## Troubleshoot webhooks
Gitlab stores each perform of the webhook.
@@ -1056,7 +1063,7 @@ Pick an unused port (e.g. 8000) and start the script: `ruby print_http_body.rb
8000`. Then add your server as a webhook receiver in GitLab as
`http://my.host:8000/`.
-When you press 'Test Hook' in GitLab, you should see something like this in the
+When you press 'Test' in GitLab, you should see something like this in the
console:
```
diff --git a/features/dashboard/dashboard.feature b/features/dashboard/dashboard.feature
deleted file mode 100644
index 1af4d46dec9..00000000000
--- a/features/dashboard/dashboard.feature
+++ /dev/null
@@ -1,70 +0,0 @@
-@dashboard
-Feature: Dashboard
- Background:
- Given I sign in as a user
- And I own project "Shop"
- And project "Shop" has push event
- And project "Shop" has CI enabled
- And project "Shop" has CI build
- And project "Shop" has labels: "bug", "feature", "enhancement"
- And project "Shop" has issue: "bug report"
- And I visit dashboard page
-
- Scenario: I should see projects list
- Then I should see "New Project" link
- Then I should see "Shop" project link
- Then I should see "Shop" project CI status
-
- @javascript
- Scenario: I should see activity list
- And I visit dashboard activity page
- Then I should see project "Shop" activity feed
-
- Scenario: I should see groups list
- Given I have group with projects
- And I visit dashboard page
- Then I should see groups list
-
- @javascript
- Scenario: I should see last push widget
- Then I should see last push widget
- And I click "Create Merge Request" link
- Then I see prefilled new Merge Request page
-
- @javascript
- Scenario: Sorting Issues
- Given I visit dashboard issues page
- And I sort the list by "Oldest updated"
- And I visit dashboard activity page
- And I visit dashboard issues page
- Then The list should be sorted by "Oldest updated"
-
- @javascript
- Scenario: Filtering Issues by label
- Given project "Shop" has issue "Bugfix1" with label "feature"
- When I visit dashboard issues page
- And I filter the list by label "feature"
- Then I should see "Bugfix1" in issues list
-
- @javascript
- Scenario: Visiting Project's issues after sorting
- Given I visit dashboard issues page
- And I sort the list by "Oldest updated"
- And I visit project "Shop" issues page
- Then The list should be sorted by "Oldest updated"
-
- @javascript
- Scenario: Sorting Merge Requests
- Given I visit dashboard merge requests page
- And I sort the list by "Oldest updated"
- And I visit dashboard activity page
- And I visit dashboard merge requests page
- Then The list should be sorted by "Oldest updated"
-
- @javascript
- Scenario: Visiting Project's merge requests after sorting
- Given project "Shop" has a "Bugfix MR" merge request open
- And I visit dashboard merge requests page
- And I sort the list by "Oldest updated"
- And I visit project "Shop" merge requests page
- Then The list should be sorted by "Oldest updated"
diff --git a/features/dashboard/event_filters.feature b/features/dashboard/event_filters.feature
deleted file mode 100644
index 8c3ff64164f..00000000000
--- a/features/dashboard/event_filters.feature
+++ /dev/null
@@ -1,58 +0,0 @@
-@dashboard
-Feature: Event Filters
- Background:
- Given I sign in as a user
- And I own a project
- And this project has push event
- And this project has new member event
- And this project has merge request event
- And I visit dashboard activity page
-
- @javascript
- Scenario: I should see all events
- Then I should see push event
- And I should see new member event
- And I should see merge request event
-
- @javascript
- Scenario: I should see only pushed events
- When I click "push" event filter
- Then I should see push event
- And I should not see new member event
- And I should not see merge request event
-
- @javascript
- Scenario: I should see only joined events
- When I click "team" event filter
- Then I should see new member event
- And I should not see push event
- And I should not see merge request event
-
- @javascript
- Scenario: I should see only merged events
- When I click "merge" event filter
- Then I should see merge request event
- And I should not see push event
- And I should not see new member event
-
- @javascript
- Scenario: I should see only selected events while page reloaded
- When I click "push" event filter
- And I visit dashboard activity page
- Then I should see push event
- And I should not see new member event
- When I click "team" event filter
- And I visit dashboard activity page
- Then I should not see push event
- And I should see new member event
- And I should not see merge request event
- When I click "push" event filter
- And I visit dashboard activity page
- Then I should see push event
- And I should not see new member event
- And I should not see merge request event
- When I click "merge" event filter
- And I visit dashboard activity page
- Then I should see merge request event
- And I should not see push event
- And I should not see new member event
diff --git a/features/project/badges/build.feature b/features/project/badges/build.feature
deleted file mode 100644
index bcf80ed620e..00000000000
--- a/features/project/badges/build.feature
+++ /dev/null
@@ -1,27 +0,0 @@
-Feature: Project Badges Build
- Background:
- Given I sign in as a user
- And I own a project
- And project has CI enabled
- And project has a recent build
-
- Scenario: I want to see a badge for successfully built project
- Given recent build is successful
- When I display builds badge for a master branch
- Then I should see a build success badge
-
- Scenario: I want to see a badge for project with failed builds
- Given recent build failed
- When I display builds badge for a master branch
- Then I should see a build failed badge
-
- Scenario: I want to see a badge for project with running builds
- Given recent build is successful
- And project has another build that is running
- When I display builds badge for a master branch
- Then I should see a build running badge
-
- Scenario: I want to see a fresh badge on each request
- Given recent build is successful
- When I display builds badge for a master branch
- Then I should see a badge that has not been cached
diff --git a/features/steps/dashboard/dashboard.rb b/features/steps/dashboard/dashboard.rb
deleted file mode 100644
index 0960f49aad3..00000000000
--- a/features/steps/dashboard/dashboard.rb
+++ /dev/null
@@ -1,83 +0,0 @@
-class Spinach::Features::Dashboard < Spinach::FeatureSteps
- include SharedAuthentication
- include SharedPaths
- include SharedProject
- include SharedIssuable
-
- step 'I should see "New Project" link' do
- expect(page).to have_link "New project"
- end
-
- step 'I should see "Shop" project link' do
- expect(page).to have_link "Shop"
- end
-
- step 'I should see "Shop" project CI status' do
- expect(page).to have_link "Commit: skipped"
- end
-
- step 'I should see last push widget' do
- expect(page).to have_content "You pushed to fix"
- expect(page).to have_link "Create merge request"
- end
-
- step 'I click "Create merge request" link' do
- find_link("Create merge request", visible: false).trigger('click')
- end
-
- step 'I see prefilled new Merge Request page' do
- expect(page).to have_selector('.merge-request-form')
- expect(current_path).to eq project_new_merge_request_path(@project)
- expect(find("#merge_request_target_project_id").value).to eq @project.id.to_s
- expect(find("input#merge_request_source_branch").value).to eq "fix"
- expect(find("input#merge_request_target_branch").value).to eq "master"
- end
-
- step 'I have group with projects' do
- @group = create(:group)
- @project = create(:empty_project, namespace: @group)
- @event = create(:closed_issue_event, project: @project)
-
- @project.team << [current_user, :master]
- end
-
- step 'I should see projects list' do
- @user.authorized_projects.all.each do |project|
- expect(page).to have_link project.name_with_namespace
- end
- end
-
- step 'I should see groups list' do
- Group.all.each do |group|
- expect(page).to have_link group.name
- end
- end
-
- step 'group has a projects that does not belongs to me' do
- @forbidden_project1 = create(:empty_project, group: @group)
- @forbidden_project2 = create(:empty_project, group: @group)
- end
-
- step 'I should see 1 project at group list' do
- expect(find('span.last_activity/span')).to have_content('1')
- end
-
- step 'I filter the list by label "feature"' do
- page.within ".labels-filter" do
- find('.dropdown').click
- click_link "feature"
- end
- end
-
- step 'I should see "Bugfix1" in issues list' do
- page.within "ul.content-list" do
- expect(page).to have_content "Bugfix1"
- end
- end
-
- step 'project "Shop" has issue "Bugfix1" with label "feature"' do
- project = Project.find_by(name: "Shop")
- issue = create(:issue, title: "Bugfix1", project: project, assignees: [current_user])
- issue.labels << project.labels.find_by(title: 'feature')
- end
-end
diff --git a/features/steps/dashboard/event_filters.rb b/features/steps/dashboard/event_filters.rb
deleted file mode 100644
index a745254cc31..00000000000
--- a/features/steps/dashboard/event_filters.rb
+++ /dev/null
@@ -1,92 +0,0 @@
-class Spinach::Features::EventFilters < Spinach::FeatureSteps
- include WaitForRequests
- include SharedAuthentication
- include SharedPaths
- include SharedProject
-
- step 'I should see push event' do
- expect(page).to have_selector('span.pushed')
- end
-
- step 'I should not see push event' do
- expect(page).not_to have_selector('span.pushed')
- end
-
- step 'I should see new member event' do
- expect(page).to have_selector('span.joined')
- end
-
- step 'I should not see new member event' do
- expect(page).not_to have_selector('span.joined')
- end
-
- step 'I should see merge request event' do
- expect(page).to have_selector('span.accepted')
- end
-
- step 'I should not see merge request event' do
- expect(page).not_to have_selector('span.accepted')
- end
-
- step 'this project has push event' do
- data = {
- before: Gitlab::Git::BLANK_SHA,
- after: "0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e",
- ref: "refs/heads/new_design",
- user_id: @user.id,
- user_name: @user.name,
- repository: {
- name: @project.name,
- url: "localhost/rubinius",
- description: "",
- homepage: "localhost/rubinius",
- private: true
- }
- }
-
- @event = Event.create(
- project: @project,
- action: Event::PUSHED,
- data: data,
- author_id: @user.id
- )
- end
-
- step 'this project has new member event' do
- user = create(:user, { name: "John Doe" })
- Event.create(
- project: @project,
- author_id: user.id,
- action: Event::JOINED
- )
- end
-
- step 'this project has merge request event' do
- merge_request = create :merge_request, author: @user, source_project: @project, target_project: @project
- Event.create(
- project: @project,
- action: Event::MERGED,
- target_id: merge_request.id,
- target_type: "MergeRequest",
- author_id: @user.id
- )
- end
-
- When 'I click "push" event filter' do
- wait_for_requests
- click_link("Push events")
- wait_for_requests
- end
-
- When 'I click "team" event filter' do
- wait_for_requests
- click_link("Team")
- wait_for_requests
- end
-
- When 'I click "merge" event filter' do
- wait_for_requests
- click_link("Merge events")
- wait_for_requests
- end
-end
diff --git a/features/steps/groups.rb b/features/steps/groups.rb
index 0aedc422563..6b288b47da4 100644
--- a/features/steps/groups.rb
+++ b/features/steps/groups.rb
@@ -81,7 +81,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
step 'I should see new group "Owned" avatar' do
expect(owned_group.avatar).to be_instance_of AvatarUploader
- expect(owned_group.avatar.url).to eq "/uploads/system/group/avatar/#{Group.find_by(name: "Owned").id}/banana_sample.gif"
+ expect(owned_group.avatar.url).to eq "/uploads/-/system/group/avatar/#{Group.find_by(name: "Owned").id}/banana_sample.gif"
end
step 'I should see the "Remove avatar" button' do
diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb
index 254c26bb6af..4b88cb5e27f 100644
--- a/features/steps/profile/profile.rb
+++ b/features/steps/profile/profile.rb
@@ -36,7 +36,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
step 'I should see new avatar' do
expect(@user.avatar).to be_instance_of AvatarUploader
- expect(@user.avatar.url).to eq "/uploads/system/user/avatar/#{@user.id}/banana_sample.gif"
+ expect(@user.avatar.url).to eq "/uploads/-/system/user/avatar/#{@user.id}/banana_sample.gif"
end
step 'I should see the "Remove avatar" button' do
diff --git a/features/steps/project/badges/build.rb b/features/steps/project/badges/build.rb
deleted file mode 100644
index 5a9094ee9d3..00000000000
--- a/features/steps/project/badges/build.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-class Spinach::Features::ProjectBadgesBuild < Spinach::FeatureSteps
- include SharedAuthentication
- include SharedProject
- include SharedBuilds
- include RepoHelpers
-
- step 'I display builds badge for a master branch' do
- visit build_project_badges_path(@project, ref: :master, format: :svg)
- end
-
- step 'I should see a build success badge' do
- expect_badge('success')
- end
-
- step 'I should see a build failed badge' do
- expect_badge('failed')
- end
-
- step 'I should see a build running badge' do
- expect_badge('running')
- end
-
- step 'I should see a badge that has not been cached' do
- expect(page.response_headers['Cache-Control']).to include 'no-cache'
- end
-
- def expect_badge(status)
- svg = Nokogiri::XML.parse(page.body)
- expect(page.response_headers['Content-Type']).to include('image/svg+xml')
- expect(svg.at(%Q{text:contains("#{status}")})).to be_truthy
- end
-end
diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb
index 7d34331db46..170e2f16c80 100644
--- a/features/steps/project/project.rb
+++ b/features/steps/project/project.rb
@@ -38,7 +38,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps
step 'I should see new project avatar' do
expect(@project.avatar).to be_instance_of AvatarUploader
url = @project.avatar.url
- expect(url).to eq "/uploads/system/project/avatar/#{@project.id}/banana_sample.gif"
+ expect(url).to eq "/uploads/-/system/project/avatar/#{@project.id}/banana_sample.gif"
end
step 'I should see the "Remove avatar" button' do
diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb
index 729e2b8982c..da1cdd9f897 100644
--- a/features/steps/shared/project.rb
+++ b/features/steps/shared/project.rb
@@ -239,11 +239,6 @@ module SharedProject
create(:label, project: project, title: 'enhancement')
end
- step 'project "Shop" has issue: "bug report"' do
- project = Project.find_by(name: "Shop")
- create(:issue, project: project, title: "bug report")
- end
-
step 'project "Shop" has CI enabled' do
project = Project.find_by(name: "Shop")
project.enable_ci
diff --git a/lib/api/access_requests.rb b/lib/api/access_requests.rb
index c9b5f58c557..cdacf9839e5 100644
--- a/lib/api/access_requests.rb
+++ b/lib/api/access_requests.rb
@@ -68,6 +68,7 @@ module API
delete ":id/access_requests/:user_id" do
source = find_source(source_type, params[:id])
+ status 204
::Members::DestroyService.new(source, current_user, params)
.execute(:requesters)
end
diff --git a/lib/api/award_emoji.rb b/lib/api/award_emoji.rb
index 56f19f89642..5a028fc9d0b 100644
--- a/lib/api/award_emoji.rb
+++ b/lib/api/award_emoji.rb
@@ -88,6 +88,7 @@ module API
unauthorized! unless award.user == current_user || current_user.admin?
+ status 204
award.destroy
end
end
diff --git a/lib/api/broadcast_messages.rb b/lib/api/broadcast_messages.rb
index 395c401203c..9980aec4752 100644
--- a/lib/api/broadcast_messages.rb
+++ b/lib/api/broadcast_messages.rb
@@ -91,6 +91,7 @@ module API
delete ':id' do
message = find_message
+ status 204
message.destroy
end
end
diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb
index d5c2f3d5094..42e7c1486b0 100644
--- a/lib/api/deploy_keys.rb
+++ b/lib/api/deploy_keys.rb
@@ -125,6 +125,7 @@ module API
key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id])
not_found!('Deploy Key') unless key
+ status 204
key.destroy
end
end
diff --git a/lib/api/environments.rb b/lib/api/environments.rb
index 945771d46f3..c774a5c6685 100644
--- a/lib/api/environments.rb
+++ b/lib/api/environments.rb
@@ -79,6 +79,7 @@ module API
environment = user_project.environments.find(params[:environment_id])
+ status 204
environment.destroy
end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index ebbaed0cbb7..49c3b2278c7 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -125,6 +125,8 @@ module API
delete ":id" do
group = find_group!(params[:id])
authorize! :admin_group, group
+
+ status 204
::Groups::DestroyService.new(group, current_user).execute
end
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 64be08094ed..14b26f28ebf 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -224,6 +224,7 @@ module API
not_found!('Issue') unless issue
authorize!(:destroy_issue, issue)
+ status 204
issue.destroy
end
diff --git a/lib/api/labels.rb b/lib/api/labels.rb
index 20b25529d0c..4520c98d951 100644
--- a/lib/api/labels.rb
+++ b/lib/api/labels.rb
@@ -56,6 +56,7 @@ module API
label = user_project.labels.find_by(title: params[:name])
not_found!('Label') unless label
+ status 204
label.destroy
end
diff --git a/lib/api/members.rb b/lib/api/members.rb
index c200e46a328..bb970b7cd54 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -96,6 +96,7 @@ module API
# Ensure that memeber exists
source.members.find_by!(user_id: params[:user_id])
+ status 204
::Members::DestroyService.new(source, current_user, declared_params).execute
end
end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index ac33b2b801c..6e2e13e0a24 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -137,6 +137,7 @@ module API
merge_request = find_project_merge_request(params[:merge_request_iid])
authorize!(:destroy_merge_request, merge_request)
+ status 204
merge_request.destroy
end
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index 01ca62b593f..65ff89edf65 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -131,6 +131,7 @@ module API
note = user_project.notes.find(params[:note_id])
authorize! :admin_note, note
+ status 204
::Notes::DestroyService.new(user_project, current_user).execute(note)
end
end
diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb
index 7a345289617..649dd891f56 100644
--- a/lib/api/project_hooks.rb
+++ b/lib/api/project_hooks.rb
@@ -96,6 +96,7 @@ module API
delete ":id/hooks/:hook_id" do
hook = user_project.hooks.find(params.delete(:hook_id))
+ status 204
hook.destroy
end
end
diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb
index 3320eadff0d..f3d905b0068 100644
--- a/lib/api/project_snippets.rb
+++ b/lib/api/project_snippets.rb
@@ -116,6 +116,7 @@ module API
not_found!('Snippet') unless snippet
authorize! :admin_project_snippet, snippet
+ status 204
snippet.destroy
end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index c459257158d..89dda88d3f5 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -361,6 +361,7 @@ module API
authorize! :remove_fork_project, user_project
if user_project.forked?
+ status 204
user_project.forked_project_link.destroy
else
not_modified!
@@ -405,6 +406,7 @@ module API
link = user_project.project_group_links.find_by(group_id: params[:group_id])
not_found!('Group Link') unless link
+ status 204
link.destroy
end
diff --git a/lib/api/runner.rb b/lib/api/runner.rb
index 4552115b3e2..405d25ca3c1 100644
--- a/lib/api/runner.rb
+++ b/lib/api/runner.rb
@@ -45,6 +45,7 @@ module API
end
delete '/' do
authenticate_runner!
+ status 204
Ci::Runner.find_by_token(params[:token]).destroy
end
diff --git a/lib/api/runners.rb b/lib/api/runners.rb
index db6c7c59092..5bf5a18e42f 100644
--- a/lib/api/runners.rb
+++ b/lib/api/runners.rb
@@ -79,6 +79,7 @@ module API
runner = get_runner(params[:id])
authenticate_delete_runner!(runner)
+ status 204
runner.destroy!
end
end
@@ -134,6 +135,7 @@ module API
runner = runner_project.runner
forbidden!("Only one project associated with the runner. Please remove the runner instead") if runner.projects.count == 1
+ status 204
runner_project.destroy
end
end
diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb
index fd634037a77..35ece56c65c 100644
--- a/lib/api/snippets.rb
+++ b/lib/api/snippets.rb
@@ -123,6 +123,7 @@ module API
authorize! :destroy_personal_snippet, snippet
+ status 204
snippet.destroy
end
diff --git a/lib/api/system_hooks.rb b/lib/api/system_hooks.rb
index ed7b23b474a..c0179037440 100644
--- a/lib/api/system_hooks.rb
+++ b/lib/api/system_hooks.rb
@@ -66,6 +66,7 @@ module API
hook = SystemHook.find_by(id: params[:id])
not_found!('System hook') unless hook
+ status 204
hook.destroy
end
end
diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb
index a9f2ca2608e..cb0619105e1 100644
--- a/lib/api/triggers.rb
+++ b/lib/api/triggers.rb
@@ -142,6 +142,7 @@ module API
trigger = user_project.triggers.find(params.delete(:trigger_id))
return not_found!('Trigger') unless trigger
+ status 204
trigger.destroy
end
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 81c68ea2658..a590f2692a2 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -235,6 +235,7 @@ module API
key = user.keys.find_by(id: params[:key_id])
not_found!('Key') unless key
+ status 204
key.destroy
end
@@ -306,6 +307,7 @@ module API
user = User.find_by(id: params[:id])
not_found!('User') unless user
+ status 204
user.delete_async(deleted_by: current_user, params: params)
end
@@ -406,6 +408,7 @@ module API
requires :impersonation_token_id, type: Integer, desc: 'The ID of the impersonation token'
end
delete ':impersonation_token_id' do
+ status 204
find_impersonation_token.revoke!
end
end
@@ -483,6 +486,7 @@ module API
key = current_user.keys.find_by(id: params[:key_id])
not_found!('Key') unless key
+ status 204
key.destroy
end
@@ -534,6 +538,7 @@ module API
email = current_user.emails.find_by(id: params[:email_id])
not_found!('Email') unless email
+ status 204
Emails::DestroyService.new(current_user, email: email.email).execute
end
diff --git a/lib/api/variables.rb b/lib/api/variables.rb
index 7fa528fb2d3..7c0fdd3d1be 100644
--- a/lib/api/variables.rb
+++ b/lib/api/variables.rb
@@ -88,6 +88,7 @@ module API
variable = user_project.variables.find_by(key: params[:key])
not_found!('Variable') unless variable
+ status 204
variable.destroy
end
end
diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb
index 6b82b2b4f13..31f66dd5a58 100644
--- a/lib/ci/api/entities.rb
+++ b/lib/ci/api/entities.rb
@@ -52,7 +52,7 @@ module Ci
# when old API will be removed (planned for August 2017).
model.options.dup.tap do |options|
options[:image] = options[:image][:name] if options[:image].is_a?(Hash)
- options[:services].map! do |service|
+ options[:services]&.map! do |service|
if service.is_a?(Hash)
service[:name]
else
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index cf3a0336792..3a4911b23b0 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -83,7 +83,8 @@ module Ci
before_script: job[:before_script],
script: job[:script],
after_script: job[:after_script],
- environment: job[:environment]
+ environment: job[:environment],
+ retry: job[:retry]
}.compact }
end
diff --git a/lib/gitlab/background_migration.rb b/lib/gitlab/background_migration.rb
index b0741b1fba7..d3f66877672 100644
--- a/lib/gitlab/background_migration.rb
+++ b/lib/gitlab/background_migration.rb
@@ -26,7 +26,7 @@ module Gitlab
next unless migration_class == steal_class
begin
- perform(migration_class, migration_args, retries: 3) if job.delete
+ perform(migration_class, migration_args) if job.delete
rescue Exception # rubocop:disable Lint/RescueException
BackgroundMigrationWorker # enqueue this migration again
.perform_async(migration_class, migration_args)
diff --git a/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder.rb b/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder.rb
new file mode 100644
index 00000000000..0881244ed49
--- /dev/null
+++ b/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder.rb
@@ -0,0 +1,26 @@
+module Gitlab
+ module BackgroundMigration
+ class MigrateSystemUploadsToNewFolder
+ include Gitlab::Database::MigrationHelpers
+ attr_reader :old_folder, :new_folder
+
+ class Upload < ActiveRecord::Base
+ self.table_name = 'uploads'
+ include EachBatch
+ end
+
+ def perform(old_folder, new_folder)
+ replace_sql = replace_sql(uploads[:path], old_folder, new_folder)
+ affected_uploads = Upload.where(uploads[:path].matches("#{old_folder}%"))
+
+ affected_uploads.each_batch do |batch|
+ batch.update_all("path = #{replace_sql}")
+ end
+ end
+
+ def uploads
+ Arel::Table.new('uploads')
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/badge/build/metadata.rb b/lib/gitlab/badge/pipeline/metadata.rb
index 2ee35a0d4c1..db1e9f8cfb8 100644
--- a/lib/gitlab/badge/build/metadata.rb
+++ b/lib/gitlab/badge/pipeline/metadata.rb
@@ -1,8 +1,8 @@
module Gitlab
module Badge
- module Build
+ module Pipeline
##
- # Class that describes build badge metadata
+ # Class that describes pipeline badge metadata
#
class Metadata < Badge::Metadata
def initialize(badge)
@@ -11,11 +11,11 @@ module Gitlab
end
def title
- 'build status'
+ 'pipeline status'
end
def image_url
- build_project_badges_url(@project, @ref, format: :svg)
+ pipeline_project_badges_url(@project, @ref, format: :svg)
end
def link_url
diff --git a/lib/gitlab/badge/build/status.rb b/lib/gitlab/badge/pipeline/status.rb
index b762d85b6e5..5fee7a93475 100644
--- a/lib/gitlab/badge/build/status.rb
+++ b/lib/gitlab/badge/pipeline/status.rb
@@ -1,8 +1,8 @@
module Gitlab
module Badge
- module Build
+ module Pipeline
##
- # Build status badge
+ # Pipeline status badge
#
class Status < Badge::Base
attr_reader :project, :ref
@@ -15,7 +15,7 @@ module Gitlab
end
def entity
- 'build'
+ 'pipeline'
end
def status
@@ -25,11 +25,11 @@ module Gitlab
end
def metadata
- @metadata ||= Build::Metadata.new(self)
+ @metadata ||= Pipeline::Metadata.new(self)
end
def template
- @template ||= Build::Template.new(self)
+ @template ||= Pipeline::Template.new(self)
end
end
end
diff --git a/lib/gitlab/badge/build/template.rb b/lib/gitlab/badge/pipeline/template.rb
index bc0e0cd441d..e09db32262d 100644
--- a/lib/gitlab/badge/build/template.rb
+++ b/lib/gitlab/badge/pipeline/template.rb
@@ -1,12 +1,13 @@
module Gitlab
module Badge
- module Build
+ module Pipeline
##
- # Class that represents a build badge template.
+ # Class that represents a pipeline badge template.
#
# Template object will be passed to badge.svg.erb template.
#
class Template < Badge::Template
+ STATUS_RENAME = { 'success' => 'passed' }.freeze
STATUS_COLOR = {
success: '#4c1',
failed: '#e05d44',
@@ -27,11 +28,11 @@ module Gitlab
end
def value_text
- @status.to_s
+ STATUS_RENAME[@status.to_s] || @status.to_s
end
def key_width
- 38
+ 62
end
def value_width
diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb
index 176301bcca1..32f5c6ab142 100644
--- a/lib/gitlab/ci/config/entry/job.rb
+++ b/lib/gitlab/ci/config/entry/job.rb
@@ -11,7 +11,7 @@ module Gitlab
ALLOWED_KEYS = %i[tags script only except type image services allow_failure
type stage when artifacts cache dependencies before_script
- after_script variables environment coverage].freeze
+ after_script variables environment coverage retry].freeze
validations do
validates :config, allowed_keys: ALLOWED_KEYS
@@ -23,6 +23,9 @@ module Gitlab
with_options allow_nil: true do
validates :tags, array_of_strings: true
validates :allow_failure, boolean: true
+ validates :retry, numericality: { only_integer: true,
+ greater_than_or_equal_to: 0,
+ less_than_or_equal_to: 2 }
validates :when,
inclusion: { in: %w[on_success on_failure always manual],
message: 'should be on_success, on_failure, ' \
@@ -76,9 +79,9 @@ module Gitlab
helpers :before_script, :script, :stage, :type, :after_script,
:cache, :image, :services, :only, :except, :variables,
- :artifacts, :commands, :environment, :coverage
+ :artifacts, :commands, :environment, :coverage, :retry
- attributes :script, :tags, :allow_failure, :when, :dependencies
+ attributes :script, :tags, :allow_failure, :when, :dependencies, :retry
def compose!(deps = nil)
super do
@@ -142,6 +145,7 @@ module Gitlab
environment: environment_defined? ? environment_value : nil,
environment_name: environment_defined? ? environment_value[:name] : nil,
coverage: coverage_defined? ? coverage_value : nil,
+ retry: retry_defined? ? retry_value.to_i : nil,
artifacts: artifacts_value,
after_script: after_script_value,
ignore: ignored? }
diff --git a/lib/gitlab/ci/trace/stream.rb b/lib/gitlab/ci/trace/stream.rb
index c4c0623df6c..8503ecf8700 100644
--- a/lib/gitlab/ci/trace/stream.rb
+++ b/lib/gitlab/ci/trace/stream.rb
@@ -67,14 +67,15 @@ module Gitlab
def extract_coverage(regex)
return unless valid?
- return unless regex
+ return unless regex.present?
- regex = Regexp.new(regex)
+ regex = Gitlab::UntrustedRegexp.new(regex)
match = ""
reverse_line do |line|
- matches = line.scan(regex)
+ line.chomp!
+ matches = regex.scan(line)
next unless matches.is_a?(Array)
next if matches.empty?
diff --git a/lib/gitlab/data_builder/push.rb b/lib/gitlab/data_builder/push.rb
index e81d19a7a2e..8c8729b6557 100644
--- a/lib/gitlab/data_builder/push.rb
+++ b/lib/gitlab/data_builder/push.rb
@@ -74,6 +74,8 @@ module Gitlab
build(project, user, commits.last&.id, commits.first&.id, ref, commits)
end
+ private
+
def checkout_sha(repository, newrev, ref)
# Checkout sha is nil when we remove branch or tag
return if Gitlab::Git.blank_ref?(newrev)
diff --git a/lib/gitlab/data_builder/wiki_page.rb b/lib/gitlab/data_builder/wiki_page.rb
new file mode 100644
index 00000000000..226974b698c
--- /dev/null
+++ b/lib/gitlab/data_builder/wiki_page.rb
@@ -0,0 +1,22 @@
+module Gitlab
+ module DataBuilder
+ module WikiPage
+ extend self
+
+ def build(wiki_page, user, action)
+ wiki = wiki_page.wiki
+
+ {
+ object_kind: wiki_page.class.name.underscore,
+ user: user.hook_attrs,
+ project: wiki.project.hook_attrs,
+ wiki: wiki.hook_attrs,
+ object_attributes: wiki_page.hook_attrs.merge(
+ url: Gitlab::UrlBuilder.build(wiki_page),
+ action: action
+ )
+ }
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb
index edd7795eef0..85e6db0a689 100644
--- a/lib/gitlab/ee_compat_check.rb
+++ b/lib/gitlab/ee_compat_check.rb
@@ -237,6 +237,10 @@ module Gitlab
branch_name.parameterize << '.patch'
end
+ def patch_url
+ "https://gitlab.com/gitlab-org/gitlab-ce/-/jobs/#{ENV['CI_JOB_ID']}/artifacts/raw/ee_compat_check/patches/#{ce_patch_name}"
+ end
+
def step(desc, cmd = nil)
puts "\n=> #{desc}\n"
@@ -303,14 +307,11 @@ module Gitlab
2. Apply your branch's patch to EE
- # In the CE repo
- $ git fetch origin master
- $ git diff --binary origin/master...HEAD -- > #{ce_branch}.patch
-
# In the EE repo
$ git fetch origin master
$ git checkout -b #{ee_branch_prefix} origin/master
- $ git apply --3way path/to/#{ce_branch}.patch
+ $ wget #{patch_url}
+ $ git apply --3way #{ce_patch_name}
At this point you might have conflicts such as:
@@ -324,7 +325,7 @@ module Gitlab
If the patch couldn't be applied cleanly, use the following command:
# In the EE repo
- $ git apply --reject path/to/#{ce_branch}.patch
+ $ git apply --reject #{ce_patch_name}
This option makes git apply the parts of the patch that are applicable,
and leave the rejected hunks in corresponding `.rej` files.
diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb
index 76a562f356e..09511cc6504 100644
--- a/lib/gitlab/git/commit.rb
+++ b/lib/gitlab/git/commit.rb
@@ -98,17 +98,13 @@ module Gitlab
# Commit.between(repo, '29eda46b', 'master')
#
def between(repo, base, head)
- commits = Gitlab::GitalyClient.migrate(:commits_between) do |is_enabled|
+ Gitlab::GitalyClient.migrate(:commits_between) do |is_enabled|
if is_enabled
repo.gitaly_commit_client.between(base, head)
else
- repo.commits_between(base, head)
+ repo.commits_between(base, head).map { |c| decorate(c) }
end
end
-
- commits.map do |commit|
- decorate(commit)
- end
rescue Rugged::ReferenceError
[]
end
@@ -135,6 +131,16 @@ module Gitlab
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/326
def find_all(repo, options = {})
+ Gitlab::GitalyClient.migrate(:find_all_commits) do |is_enabled|
+ if is_enabled
+ find_all_by_gitaly(repo, options)
+ else
+ find_all_by_rugged(repo, options)
+ end
+ end
+ end
+
+ def find_all_by_rugged(repo, options = {})
actual_options = options.dup
allowed_options = [:ref, :max_count, :skip, :order]
@@ -173,6 +179,10 @@ module Gitlab
[]
end
+ def find_all_by_gitaly(repo, options = {})
+ Gitlab::GitalyClient::CommitService.new(repo).find_all_commits(options)
+ end
+
def decorate(commit, ref = nil)
Gitlab::Git::Commit.new(commit, ref)
end
@@ -214,11 +224,12 @@ module Gitlab
def initialize(raw_commit, head = nil)
raise "Nil as raw commit passed" unless raw_commit
- if raw_commit.is_a?(Hash)
+ case raw_commit
+ when Hash
init_from_hash(raw_commit)
- elsif raw_commit.is_a?(Rugged::Commit)
+ when Rugged::Commit
init_from_rugged(raw_commit)
- elsif raw_commit.is_a?(Gitaly::GitCommit)
+ when Gitlab::GitalyClient::Commit
init_from_gitaly(raw_commit)
else
raise "Invalid raw commit type: #{raw_commit.class}"
@@ -298,7 +309,14 @@ module Gitlab
end
def parents
- raw_commit.parents.map { |c| Gitlab::Git::Commit.new(c) }
+ case raw_commit
+ when Rugged::Commit
+ raw_commit.parents.map { |c| Gitlab::Git::Commit.new(c) }
+ when Gitlab::GitalyClient::Commit
+ parent_ids.map { |oid| self.class.find(raw_commit.repository, oid) }.compact
+ else
+ raise NotImplementedError, "commit source doesn't support #parents"
+ end
end
def stats
diff --git a/lib/gitlab/git/diff.rb b/lib/gitlab/git/diff.rb
index cf95f673667..9e00abefd02 100644
--- a/lib/gitlab/git/diff.rb
+++ b/lib/gitlab/git/diff.rb
@@ -234,6 +234,8 @@ module Gitlab
@new_file = diff.from_id == BLANK_SHA
@renamed_file = diff.from_path != diff.to_path
@deleted_file = diff.to_id == BLANK_SHA
+
+ collapse! if diff.respond_to?(:collapsed) && diff.collapsed
end
def prune_diff_if_eligible
diff --git a/lib/gitlab/git/diff_collection.rb b/lib/gitlab/git/diff_collection.rb
index 0d8fe185ac5..87ed9c3ea26 100644
--- a/lib/gitlab/git/diff_collection.rb
+++ b/lib/gitlab/git/diff_collection.rb
@@ -7,16 +7,28 @@ module Gitlab
DEFAULT_LIMITS = { max_files: 100, max_lines: 5000 }.freeze
+ attr_reader :limits
+
+ delegate :max_files, :max_lines, :max_bytes, :safe_max_files, :safe_max_lines, :safe_max_bytes, to: :limits
+
+ def self.collection_limits(options = {})
+ limits = {}
+ limits[:max_files] = options.fetch(:max_files, DEFAULT_LIMITS[:max_files])
+ limits[:max_lines] = options.fetch(:max_lines, DEFAULT_LIMITS[:max_lines])
+ limits[:max_bytes] = limits[:max_files] * 5.kilobytes # Average 5 KB per file
+ limits[:safe_max_files] = [limits[:max_files], DEFAULT_LIMITS[:max_files]].min
+ limits[:safe_max_lines] = [limits[:max_lines], DEFAULT_LIMITS[:max_lines]].min
+ limits[:safe_max_bytes] = limits[:safe_max_files] * 5.kilobytes # Average 5 KB per file
+
+ OpenStruct.new(limits)
+ end
+
def initialize(iterator, options = {})
@iterator = iterator
- @max_files = options.fetch(:max_files, DEFAULT_LIMITS[:max_files])
- @max_lines = options.fetch(:max_lines, DEFAULT_LIMITS[:max_lines])
- @max_bytes = @max_files * 5.kilobytes # Average 5 KB per file
- @safe_max_files = [@max_files, DEFAULT_LIMITS[:max_files]].min
- @safe_max_lines = [@max_lines, DEFAULT_LIMITS[:max_lines]].min
- @safe_max_bytes = @safe_max_files * 5.kilobytes # Average 5 KB per file
+ @limits = self.class.collection_limits(options)
@enforce_limits = !!options.fetch(:limits, true)
@expanded = !!options.fetch(:expanded, true)
+ @from_gitaly = options.fetch(:from_gitaly, false)
@line_count = 0
@byte_count = 0
@@ -26,9 +38,23 @@ module Gitlab
end
def each(&block)
- Gitlab::GitalyClient.migrate(:commit_raw_diffs) do
- each_patch(&block)
+ @array.each(&block)
+
+ return if @overflow
+ return if @iterator.nil?
+
+ Gitlab::GitalyClient.migrate(:commit_raw_diffs) do |is_enabled|
+ if is_enabled && @from_gitaly
+ each_gitaly_patch(&block)
+ else
+ each_rugged_patch(&block)
+ end
end
+
+ @populated = true
+
+ # Allow iterator to be garbage-collected. It cannot be reused anyway.
+ @iterator = nil
end
def empty?
@@ -74,23 +100,32 @@ module Gitlab
end
def over_safe_limits?(files)
- files >= @safe_max_files || @line_count > @safe_max_lines || @byte_count >= @safe_max_bytes
+ files >= safe_max_files || @line_count > safe_max_lines || @byte_count >= safe_max_bytes
end
- def each_patch
- i = 0
- @array.each do |diff|
- yield diff
+ def each_gitaly_patch
+ i = @array.length
+
+ @iterator.each do |raw|
+ diff = Gitlab::Git::Diff.new(raw, expanded: !@enforce_limits || @expanded)
+
+ if raw.overflow_marker
+ @overflow = true
+ break
+ end
+
+ yield @array[i] = diff
i += 1
end
+ end
- return if @overflow
- return if @iterator.nil?
+ def each_rugged_patch
+ i = @array.length
@iterator.each do |raw|
@empty = false
- if @enforce_limits && i >= @max_files
+ if @enforce_limits && i >= max_files
@overflow = true
break
end
@@ -106,7 +141,7 @@ module Gitlab
@line_count += diff.line_count
@byte_count += diff.diff.bytesize
- if @enforce_limits && (@line_count >= @max_lines || @byte_count >= @max_bytes)
+ if @enforce_limits && (@line_count >= max_lines || @byte_count >= max_bytes)
# This last Diff instance pushes us over the lines limit. We stop and
# discard it.
@overflow = true
@@ -116,11 +151,6 @@ module Gitlab
yield @array[i] = diff
i += 1
end
-
- @populated = true
-
- # Allow iterator to be garbage-collected. It cannot be reused anyway.
- @iterator = nil
end
end
end
diff --git a/lib/gitlab/git/tree.rb b/lib/gitlab/git/tree.rb
index 8122ff0e81f..8e959c57c7c 100644
--- a/lib/gitlab/git/tree.rb
+++ b/lib/gitlab/git/tree.rb
@@ -17,30 +17,13 @@ module Gitlab
def where(repository, sha, path = nil)
path = nil if path == '' || path == '/'
- commit = repository.lookup(sha)
- root_tree = commit.tree
-
- tree = if path
- id = find_id_by_path(repository, root_tree.oid, path)
- if id
- repository.lookup(id)
- else
- []
- end
- else
- root_tree
- end
-
- tree.map do |entry|
- new(
- id: entry[:oid],
- root_id: root_tree.oid,
- name: entry[:name],
- type: entry[:type],
- mode: entry[:filemode].to_s(8),
- path: path ? File.join(path, entry[:name]) : entry[:name],
- commit_id: sha
- )
+ Gitlab::GitalyClient.migrate(:tree_entries) do |is_enabled|
+ if is_enabled
+ client = Gitlab::GitalyClient::CommitService.new(repository)
+ client.tree_entries(repository, sha, path)
+ else
+ tree_entries_from_rugged(repository, sha, path)
+ end
end
end
@@ -74,6 +57,34 @@ module Gitlab
entry[:oid]
end
end
+
+ def tree_entries_from_rugged(repository, sha, path)
+ commit = repository.lookup(sha)
+ root_tree = commit.tree
+
+ tree = if path
+ id = find_id_by_path(repository, root_tree.oid, path)
+ if id
+ repository.lookup(id)
+ else
+ []
+ end
+ else
+ root_tree
+ end
+
+ tree.map do |entry|
+ new(
+ id: entry[:oid],
+ root_id: root_tree.oid,
+ name: entry[:name],
+ type: entry[:type],
+ mode: entry[:filemode].to_s(8),
+ path: path ? File.join(path, entry[:name]) : entry[:name],
+ commit_id: sha
+ )
+ end
+ end
end
def initialize(options)
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index 197a94487eb..435e41e36fb 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -86,8 +86,8 @@ module Gitlab
feature.enabled?
end
- def self.migrate(feature)
- is_enabled = feature_enabled?(feature)
+ def self.migrate(feature, status: MigrationStatus::OPT_IN)
+ is_enabled = feature_enabled?(feature, status: status)
metric_name = feature.to_s
metric_name += "_gitaly" if is_enabled
diff --git a/lib/gitlab/gitaly_client/commit.rb b/lib/gitlab/gitaly_client/commit.rb
new file mode 100644
index 00000000000..61fe462d762
--- /dev/null
+++ b/lib/gitlab/gitaly_client/commit.rb
@@ -0,0 +1,14 @@
+module Gitlab
+ module GitalyClient
+ class Commit
+ attr_reader :repository, :gitaly_commit
+
+ delegate :id, :subject, :body, :author, :committer, :parent_ids, to: :gitaly_commit
+
+ def initialize(repository, gitaly_commit)
+ @repository = repository
+ @gitaly_commit = gitaly_commit
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb
index 8f5738fed06..c6e52b530b3 100644
--- a/lib/gitlab/gitaly_client/commit_service.rb
+++ b/lib/gitlab/gitaly_client/commit_service.rb
@@ -23,9 +23,13 @@ module Gitlab
def diff_from_parent(commit, options = {})
request_params = commit_diff_request_params(commit, options)
request_params[:ignore_whitespace_change] = options.fetch(:ignore_whitespace_change, false)
+ request_params[:enforce_limits] = options.fetch(:limits, true)
+ request_params[:collapse_diffs] = request_params[:enforce_limits] || !options.fetch(:expanded, true)
+ request_params.merge!(Gitlab::Git::DiffCollection.collection_limits(options).to_h)
+
request = Gitaly::CommitDiffRequest.new(request_params)
response = GitalyClient.call(@repository.storage, :diff_service, :commit_diff, request)
- Gitlab::Git::DiffCollection.new(GitalyClient::DiffStitcher.new(response), options)
+ Gitlab::Git::DiffCollection.new(GitalyClient::DiffStitcher.new(response), options.merge(from_gitaly: true))
end
def commit_deltas(commit)
@@ -56,6 +60,31 @@ module Gitlab
entry
end
+ def tree_entries(repository, revision, path)
+ request = Gitaly::GetTreeEntriesRequest.new(
+ repository: @gitaly_repo,
+ revision: revision,
+ path: path.presence || '.'
+ )
+
+ response = GitalyClient.call(@repository.storage, :commit_service, :get_tree_entries, request)
+
+ response.flat_map do |message|
+ message.entries.map do |gitaly_tree_entry|
+ entry_path = gitaly_tree_entry.path.dup
+ Gitlab::Git::Tree.new(
+ id: gitaly_tree_entry.oid,
+ root_id: gitaly_tree_entry.root_oid,
+ type: gitaly_tree_entry.type.downcase,
+ mode: gitaly_tree_entry.mode.to_s(8),
+ name: File.basename(entry_path),
+ path: entry_path,
+ commit_id: gitaly_tree_entry.commit_oid
+ )
+ end
+ end
+ end
+
def commit_count(ref)
request = Gitaly::CountCommitsRequest.new(
repository: @gitaly_repo,
@@ -76,6 +105,19 @@ module Gitlab
consume_commits_response(response)
end
+ def find_all_commits(opts = {})
+ request = Gitaly::FindAllCommitsRequest.new(
+ repository: @gitaly_repo,
+ revision: opts[:ref].to_s,
+ max_count: opts[:max_count].to_i,
+ skip: opts[:skip].to_i
+ )
+ request.order = opts[:order].upcase if opts[:order].present?
+
+ response = GitalyClient.call(@repository.storage, :commit_service, :find_all_commits, request)
+ consume_commits_response(response)
+ end
+
private
def commit_diff_request_params(commit, options = {})
@@ -90,7 +132,12 @@ module Gitlab
end
def consume_commits_response(response)
- response.flat_map { |r| r.commits }
+ response.flat_map do |message|
+ message.commits.map do |gitaly_commit|
+ commit = GitalyClient::Commit.new(@repository, gitaly_commit)
+ Gitlab::Git::Commit.new(commit)
+ end
+ end
end
end
end
diff --git a/lib/gitlab/gitaly_client/diff.rb b/lib/gitlab/gitaly_client/diff.rb
index 1e117b7e74a..d459c9a88fb 100644
--- a/lib/gitlab/gitaly_client/diff.rb
+++ b/lib/gitlab/gitaly_client/diff.rb
@@ -1,7 +1,7 @@
module Gitlab
module GitalyClient
class Diff
- FIELDS = %i(from_path to_path old_mode new_mode from_id to_id patch).freeze
+ FIELDS = %i(from_path to_path old_mode new_mode from_id to_id patch overflow_marker collapsed).freeze
attr_accessor(*FIELDS)
diff --git a/lib/gitlab/i18n.rb b/lib/gitlab/i18n.rb
index f3d489aad0d..cc282d1415b 100644
--- a/lib/gitlab/i18n.rb
+++ b/lib/gitlab/i18n.rb
@@ -7,13 +7,16 @@ module Gitlab
'es' => 'Español',
'de' => 'Deutsch',
'fr' => 'Français',
- 'pt_BR' => 'Português(Brasil)',
+ 'pt_BR' => 'Português (Brasil)',
'zh_CN' => '简体中文',
- 'zh_HK' => '繁體中文(香港)',
- 'zh_TW' => '繁體中文(臺灣)',
+ 'zh_HK' => '繁體中文 (香港)',
+ 'zh_TW' => '繁體中文 (臺灣)',
'bg' => 'български',
+ 'ru' => 'Русский',
'eo' => 'Esperanto',
- 'it' => 'Italiano'
+ 'it' => 'Italiano',
+ 'uk' => 'Українська',
+ 'ja' => '日本語'
}.freeze
def available_locales
diff --git a/lib/gitlab/metrics/prometheus.rb b/lib/gitlab/metrics/prometheus.rb
index fb7bbc7cfc7..460dab47276 100644
--- a/lib/gitlab/metrics/prometheus.rb
+++ b/lib/gitlab/metrics/prometheus.rb
@@ -6,9 +6,11 @@ module Gitlab
include Gitlab::CurrentSettings
def metrics_folder_present?
- ENV.has_key?('prometheus_multiproc_dir') &&
- ::Dir.exist?(ENV['prometheus_multiproc_dir']) &&
- ::File.writable?(ENV['prometheus_multiproc_dir'])
+ multiprocess_files_dir = ::Prometheus::Client.configuration.multiprocess_files_dir
+
+ multiprocess_files_dir &&
+ ::Dir.exist?(multiprocess_files_dir) &&
+ ::File.writable?(multiprocess_files_dir)
end
def prometheus_metrics_enabled?
diff --git a/lib/gitlab/path_regex.rb b/lib/gitlab/path_regex.rb
index d81f825ef96..60a32d5d5ea 100644
--- a/lib/gitlab/path_regex.rb
+++ b/lib/gitlab/path_regex.rb
@@ -49,7 +49,6 @@ module Gitlab
sent_notifications
services
snippets
- system
teams
u
unicorn_test
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index c1ee20b6977..1adc5ec952a 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -19,17 +19,23 @@ module Gitlab
"It must start with letter, digit, emoji or '_'."
end
- def container_registry_reference_regex
- Gitlab::PathRegex.git_reference_regex
- end
-
##
- # Docker Distribution Registry 2.4.1 repository name rules
+ # Docker Distribution Registry repository / tag name rules
+ #
+ # See https://github.com/docker/distribution/blob/master/reference/regexp.go.
#
def container_repository_name_regex
@container_repository_regex ||= %r{\A[a-z0-9]+(?:[-._/][a-z0-9]+)*\Z}
end
+ ##
+ # We do not use regexp anchors here because these are not allowed when
+ # used as a routing constraint.
+ #
+ def container_registry_tag_regex
+ @container_registry_tag_regex ||= /[\w][\w.-]{0,127}/
+ end
+
def environment_name_regex_chars
'a-zA-Z0-9_/\\$\\{\\}\\. -'
end
diff --git a/lib/gitlab/route_map.rb b/lib/gitlab/route_map.rb
index 877aa6e6a28..f3952657983 100644
--- a/lib/gitlab/route_map.rb
+++ b/lib/gitlab/route_map.rb
@@ -18,7 +18,11 @@ module Gitlab
mapping = @map.find { |mapping| mapping[:source] === path }
return unless mapping
- path.sub(mapping[:source], mapping[:public])
+ if mapping[:source].is_a?(String)
+ path.sub(mapping[:source], mapping[:public])
+ else
+ mapping[:source].replace(path, mapping[:public])
+ end
end
private
@@ -35,7 +39,7 @@ module Gitlab
source_pattern = source_pattern[1...-1].gsub('\/', '/')
begin
- source_pattern = /\A#{source_pattern}\z/
+ source_pattern = Gitlab::UntrustedRegexp.new('\A' + source_pattern + '\z')
rescue RegexpError => e
raise FormatError, "Route map entry source is not a valid regular expression: #{e}"
end
diff --git a/lib/gitlab/untrusted_regexp.rb b/lib/gitlab/untrusted_regexp.rb
new file mode 100644
index 00000000000..187a9e1145f
--- /dev/null
+++ b/lib/gitlab/untrusted_regexp.rb
@@ -0,0 +1,73 @@
+module Gitlab
+ # An untrusted regular expression is any regexp containing patterns sourced
+ # from user input.
+ #
+ # Ruby's built-in regular expression library allows patterns which complete in
+ # exponential time, permitting denial-of-service attacks.
+ #
+ # Not all regular expression features are available in untrusted regexes, and
+ # there is a strict limit on total execution time. See the RE2 documentation
+ # at https://github.com/google/re2/wiki/Syntax for more details.
+ class UntrustedRegexp
+ delegate :===, to: :regexp
+
+ def initialize(pattern)
+ @regexp = RE2::Regexp.new(pattern, log_errors: false)
+
+ raise RegexpError.new(regexp.error) unless regexp.ok?
+ end
+
+ def replace_all(text, rewrite)
+ RE2.GlobalReplace(text, regexp, rewrite)
+ end
+
+ def scan(text)
+ text = text.dup # modified in-place
+ results = []
+
+ loop do
+ match = scan_regexp.match(text)
+ break unless match
+
+ # Ruby scan returns empty strings, not nil
+ groups = match.to_a.map(&:to_s)
+
+ results <<
+ if regexp.number_of_capturing_groups.zero?
+ groups[0]
+ else
+ groups[1..-1]
+ end
+
+ matchsize = match.end(0)
+
+ # No further matches
+ break unless matchsize.present?
+
+ text.slice!(0, matchsize)
+ break unless text.present?
+ end
+
+ results
+ end
+
+ def replace(text, rewrite)
+ RE2.Replace(text, regexp, rewrite)
+ end
+
+ private
+
+ attr_reader :regexp
+
+ # RE2 scan operates differently to Ruby scan when there are no capture
+ # groups, so work around it
+ def scan_regexp
+ @scan_regexp ||=
+ if regexp.number_of_capturing_groups.zero?
+ RE2::Regexp.new('(' + regexp.source + ')')
+ else
+ regexp
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index f19b325a126..dba071d7e47 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -39,6 +39,7 @@ module Gitlab
notes: Note.count,
pages_domains: PagesDomain.count,
projects: Project.count,
+ projects_imported_from_github: Project.where(import_type: 'github').count,
projects_prometheus_active: PrometheusService.active.count,
protected_branches: ProtectedBranch.count,
releases: Release.count,
diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb
index 48f3d950779..c60bd91ea6e 100644
--- a/lib/gitlab/visibility_level.rb
+++ b/lib/gitlab/visibility_level.rb
@@ -89,12 +89,12 @@ module Gitlab
end
def level_name(level)
- level_name = 'Unknown'
+ level_name = N_('VisibilityLevel|Unknown')
options.each do |name, lvl|
level_name = name if lvl == level.to_i
end
- level_name
+ s_(level_name)
end
def level_value(level)
diff --git a/lib/tasks/gitlab/task_helpers.rb b/lib/tasks/gitlab/task_helpers.rb
index 964aa0fe1bc..28b2d86eed2 100644
--- a/lib/tasks/gitlab/task_helpers.rb
+++ b/lib/tasks/gitlab/task_helpers.rb
@@ -153,7 +153,6 @@ module Gitlab
clone_repo(repo, target_dir) unless Dir.exist?(target_dir)
checkout_version(version, target_dir)
- reset_to_version(version, target_dir)
end
def clone_repo(repo, target_dir)
@@ -161,12 +160,8 @@ module Gitlab
end
def checkout_version(version, target_dir)
- run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} fetch --quiet])
- run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} checkout --quiet #{version}])
- end
-
- def reset_to_version(version, target_dir)
- run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} reset --hard #{version}])
+ run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} fetch --quiet origin #{version}])
+ run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} checkout -f --quiet FETCH_HEAD --])
end
end
end
diff --git a/locale/bg/gitlab.po b/locale/bg/gitlab.po
index 0cc16404e1b..1774c911d71 100644
--- a/locale/bg/gitlab.po
+++ b/locale/bg/gitlab.po
@@ -4,11 +4,11 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-06-28 13:32+0200\n"
+"POT-Creation-Date: 2017-07-05 08:50-0500\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2017-07-05 08:18-0400\n"
+"PO-Revision-Date: 2017-07-13 08:13-0400\n"
"Last-Translator: Lyubomir Vasilev <lyubomirv@abv.bg>\n"
"Language-Team: Bulgarian (https://translate.zanata.org/project/view/GitLab)\n"
"Language: bg\n"
@@ -641,6 +641,12 @@ msgstr "Всички"
msgid "PipelineSchedules|Inactive"
msgstr "Неактивно"
+msgid "PipelineSchedules|Input variable key"
+msgstr "Въведете ключ за променливата"
+
+msgid "PipelineSchedules|Input variable value"
+msgstr "Въведете стойността на променливата"
+
msgid "PipelineSchedules|Next Run"
msgstr "Следващо изпълнение"
@@ -650,12 +656,18 @@ msgstr "Нищо"
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "Въведете кратко описание за тази схема"
+msgid "PipelineSchedules|Remove variable row"
+msgstr "Премахване на реда за променлива"
+
msgid "PipelineSchedules|Take ownership"
msgstr "Поемане на собствеността"
msgid "PipelineSchedules|Target"
msgstr "Цел"
+msgid "PipelineSchedules|Variables"
+msgstr "Променливи"
+
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "собствен"
@@ -1149,6 +1161,15 @@ msgid "Withdraw Access Request"
msgstr "Оттегляне на заявката за достъп"
msgid ""
+"You are going to remove %{group_name}.\n"
+"Removed groups CANNOT be restored!\n"
+"Are you ABSOLUTELY sure?"
+msgstr ""
+"На път сте да премахнете „%{group_name}“.\n"
+"Ако я премахнете, групата НЕ може да бъде възстановена!\n"
+"НАИСТИНА ли искате това?"
+
+msgid ""
"You are going to remove %{project_name_with_namespace}.\n"
"Removed project CANNOT be restored!\n"
"Are you ABSOLUTELY sure?"
diff --git a/locale/eo/gitlab.po b/locale/eo/gitlab.po
index 5218f6ae7b9..62dbc2621f4 100644
--- a/locale/eo/gitlab.po
+++ b/locale/eo/gitlab.po
@@ -4,11 +4,11 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-06-28 13:32+0200\n"
+"POT-Creation-Date: 2017-07-05 08:50-0500\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2017-07-05 08:18-0400\n"
+"PO-Revision-Date: 2017-07-13 08:46-0400\n"
"Last-Translator: Lyubomir Vasilev <lyubomirv@abv.bg>\n"
"Language-Team: Esperanto (https://translate.zanata.org/project/view/GitLab)\n"
"Language: eo\n"
@@ -642,6 +642,12 @@ msgstr "Ĉiuj"
msgid "PipelineSchedules|Inactive"
msgstr "Malŝaltitaj"
+msgid "PipelineSchedules|Input variable key"
+msgstr "Entajpu ŝlosilon por la variablo"
+
+msgid "PipelineSchedules|Input variable value"
+msgstr "Entajpu la valoron de la variablo"
+
msgid "PipelineSchedules|Next Run"
msgstr "Sekvanta plenumo"
@@ -651,12 +657,18 @@ msgstr "Nenio"
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "Entajpu mallongan priskribon pri ĉi tiu ĉenstablo"
+msgid "PipelineSchedules|Remove variable row"
+msgstr "Forigi la variablan linion"
+
msgid "PipelineSchedules|Take ownership"
msgstr "Akiri posedon"
msgid "PipelineSchedules|Target"
msgstr "Celo"
+msgid "PipelineSchedules|Variables"
+msgstr "Variabloj"
+
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "Propra"
@@ -1151,6 +1163,15 @@ msgid "Withdraw Access Request"
msgstr "Nuligi la peton pri atingeblo"
msgid ""
+"You are going to remove %{group_name}.\n"
+"Removed groups CANNOT be restored!\n"
+"Are you ABSOLUTELY sure?"
+msgstr ""
+"Vi forigos „%{group_name}“.\n"
+"Oni NE POVAS malfari la forigon de grupo!\n"
+"Ĉu vi estas ABSOLUTE certa?"
+
+msgid ""
"You are going to remove %{project_name_with_namespace}.\n"
"Removed project CANNOT be restored!\n"
"Are you ABSOLUTELY sure?"
diff --git a/locale/es/gitlab.po b/locale/es/gitlab.po
index 760a60f89d4..5c669d51a68 100644
--- a/locale/es/gitlab.po
+++ b/locale/es/gitlab.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"PO-Revision-Date: 2017-07-12 12:35-0500\n"
+"PO-Revision-Date: 2017-07-13 12:10-0500\n"
"Language-Team: Spanish\n"
"Language: es\n"
"MIME-Version: 1.0\n"
@@ -1059,6 +1059,9 @@ msgstr "Privado"
msgid "VisibilityLevel|Public"
msgstr "Público"
+msgid "VisibilityLevel|Unknown"
+msgstr "Desconocido"
+
msgid "Want to see the data? Please ask an administrator for access."
msgstr "¿Quieres ver los datos? Por favor pide acceso al administrador."
diff --git a/locale/fr/gitlab.po b/locale/fr/gitlab.po
index 2000fa433b4..959654c7849 100644
--- a/locale/fr/gitlab.po
+++ b/locale/fr/gitlab.po
@@ -1,32 +1,308 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the gitlab package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# Dremor <egeorget@opmbx.org>, 2017. #zanata
+# Huang Tao <htve@outlook.com>, 2017. #zanata
+# Rémy Coutable <remy@rymai.me>, 2017. #zanata
msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-07-05 08:50-0500\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2017-06-14 04:21-0400\n"
+"PO-Revision-Date: 2017-07-19 09:45-0400\n"
"Last-Translator: Dremor <egeorget@opmbx.org>\n"
-"Language-Team: French (https://www.transifex.com/gitlab-fr/teams/75145/fr/)\n"
+"Language-Team: French (https://translate.zanata.org/project/view/GitLab)\n"
"Language: fr\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Zanata 3.9.6\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+
+msgid "%s additional commit has been omitted to prevent performance issues."
+msgid_plural ""
+"%s additional commits have been omitted to prevent performance issues."
+msgstr[0] ""
+"%s validation supplémentaire a été masquée afin d'éviter de créer de "
+"problèmes de performances."
+msgstr[1] ""
+"%s validations supplémentaires ont été masquées afin d'éviter de créer de "
+"problèmes de performances."
+
+msgid "%d commit"
+msgid_plural "%d commits"
+msgstr[0] "%d validation"
+msgstr[1] "%d validations"
+
+msgid "%{commit_author_link} committed %{commit_timeago}"
+msgstr "%{commit_author_link} a validé %{commit_timeago}"
+
+msgid "1 pipeline"
+msgid_plural "%d pipelines"
+msgstr[0] "1 pipeline"
+msgstr[1] "%d pipelines"
+
+msgid "A collection of graphs regarding Continuous Integration"
+msgstr "Un ensemble de graphiques concernant l’Intégration Continue (CI)"
+
+msgid "About auto deploy"
+msgstr "A propos de l'auto-déploiement"
+
+msgid "Active"
+msgstr "Actif"
+
+msgid "Activity"
+msgstr "Activité"
+
+msgid "Add Changelog"
+msgstr "Ajouter un journal des modifications"
+
+msgid "Add Contribution guide"
+msgstr "Ajouter un guide de contribution"
+
+msgid "Add License"
+msgstr "Ajouter une licence"
+
+msgid "Add an SSH key to your profile to pull or push via SSH."
+msgstr ""
+"Ajoutez une clef SSH à votre profil pour pouvoir récupérer et pousser par "
+"SSH."
+
+msgid "Add new directory"
+msgstr "Ajouter un nouveau dossier"
+
+msgid "Archived project! Repository is read-only"
+msgstr "Projet archivé ! Le dépôt est en lecture seule"
+
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr "Êtes-vous sûr de vouloir supprimer ce pipeline programmé"
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr "Attachez un fichier par glisser &amp; déposer ou %{upload_link}"
+
+msgid "Branch"
+msgid_plural "Branches"
+msgstr[0] "Branche"
+msgstr[1] "Branches"
+
+msgid ""
+"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, "
+"choose a GitLab CI Yaml template and commit your changes. "
+"%{link_to_autodeploy_doc}"
+msgstr ""
+"La branche <strong>%{branch_name}</strong> a été crée. Pour mettre en place "
+"le déploiement automatisé, sélectionnez un modèle de fichier Yaml pour "
+"l'intégration continue (CI) de GitLab, et validez les modifications. "
+"%{link_to_autodeploy_doc}"
+
+msgid "BranchSwitcherPlaceholder|Search branches"
+msgstr "Rechercher la branche"
+
+msgid "BranchSwitcherTitle|Switch branch"
+msgstr "Changer de branche"
+
+msgid "Branches"
+msgstr "Branches"
+
+msgid "Browse Directory"
+msgstr "Parcourir le dossier"
+
+msgid "Browse File"
+msgstr "Parcourir le fichier"
+
+msgid "Browse Files"
+msgstr "Parcourir les fichiers"
+
+msgid "Browse files"
+msgstr "Parcourir les fichiers"
msgid "ByAuthor|by"
msgstr "par"
+msgid "CI configuration"
+msgstr "Configuration de l'intégration continue (CI)"
+
+msgid "Cancel"
+msgstr "Annuler"
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr "Sélectionner dans la branche"
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr "Annuler dans la branche"
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr "Sélectionner"
+
+msgid "ChangeTypeAction|Revert"
+msgstr "Annuler"
+
+msgid "Changelog"
+msgstr "Journal des modifications"
+
+msgid "Charts"
+msgstr "Graphiques"
+
+msgid "Cherry-pick this commit"
+msgstr "Sélectionner cette validation"
+
+msgid "Cherry-pick this merge request"
+msgstr "Sélectionner cette demande de fusion"
+
+msgid "CiStatusLabel|canceled"
+msgstr "annulé"
+
+msgid "CiStatusLabel|created"
+msgstr "créé"
+
+msgid "CiStatusLabel|failed"
+msgstr "échoué"
+
+msgid "CiStatusLabel|manual action"
+msgstr "action manuelle"
+
+msgid "CiStatusLabel|passed"
+msgstr "passé"
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr "passé avec des avertissements"
+
+msgid "CiStatusLabel|pending"
+msgstr "en attente"
+
+msgid "CiStatusLabel|skipped"
+msgstr "ignoré"
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr "en attente d'action manuelle"
+
+msgid "CiStatusText|blocked"
+msgstr "bloqué"
+
+msgid "CiStatusText|canceled"
+msgstr "annulé "
+
+msgid "CiStatusText|created"
+msgstr "créé"
+
+msgid "CiStatusText|failed"
+msgstr "échoué"
+
+msgid "CiStatusText|manual"
+msgstr "manuel"
+
+msgid "CiStatusText|passed"
+msgstr "passé"
+
+msgid "CiStatusText|pending"
+msgstr "en attente"
+
+msgid "CiStatusText|skipped"
+msgstr "ignoré"
+
+msgid "CiStatus|running"
+msgstr "en cours"
+
msgid "Commit"
msgid_plural "Commits"
msgstr[0] "Validation"
msgstr[1] "Validations"
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
-msgstr "L’analyseur de cycle permet d’avoir une vue d’ensemble du temps nécessaire pour aller d’une idée à sa mise en production pour votre projet."
+msgid "Commit duration in minutes for last 30 commits"
+msgstr "Durée des 30 derniers pipelines en minutes"
+
+msgid "Commit message"
+msgstr "Message de validation"
+
+msgid "CommitBoxTitle|Commit"
+msgstr "Validation"
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr "Ajout de %{file_name}"
+
+msgid "Commits"
+msgstr "Validations"
+
+msgid "Commits feed"
+msgstr "Flux de validations"
+
+msgid "Commits|History"
+msgstr "Historique"
+
+msgid "Committed by"
+msgstr "Validé par"
+
+msgid "Compare"
+msgstr "Comparer"
+
+msgid "Contribution guide"
+msgstr "Guilde de contribution"
+
+msgid "Contributors"
+msgstr "Contributeurs"
+
+msgid "Copy URL to clipboard"
+msgstr "Copier l'URL dans le presse-papier"
+
+msgid "Copy commit SHA to clipboard"
+msgstr "Copier le SHA de la validation"
+
+msgid "Create New Directory"
+msgstr "Créer un nouveau dossier"
+
+msgid ""
+"Create a personal access token on your account to pull or push via "
+"%{protocol}."
+msgstr ""
+"Créer un jeton d’accès personnel pour votre compte afin de récupérer ou "
+"pousser par %{protocol}."
+
+msgid "Create directory"
+msgstr "Créer un dossier"
+
+msgid "Create empty bare repository"
+msgstr "Créer un dépôt vide"
+
+msgid "Create merge request"
+msgstr "Créer une demande de fusion"
+
+msgid "Create new..."
+msgstr "Créer nouveau..."
+
+msgid "CreateNewFork|Fork"
+msgstr "Fourcher"
+
+msgid "CreateTag|Tag"
+msgstr "Étiquette"
+
+msgid "CreateTokenToCloneLink|create a personal access token"
+msgstr "Créer un jeton d'accès personnel"
+
+msgid "Cron Timezone"
+msgstr "Fuseau horaire de Cron"
+
+msgid "Cron syntax"
+msgstr "Syntaxe Cron"
+
+msgid "Custom notification events"
+msgstr "Événements de notification personnalisés"
+
+msgid ""
+"Custom notification levels are the same as participating levels. With custom "
+"notification levels you will also receive notifications for select events. "
+"To find out more, check out %{notification_link}."
+msgstr ""
+"Le niveau de notification Personnalisé est similaire au niveau Participation."
+" Cependant, il permet également de recevoir des notifications pour des "
+"événements sélectionnés. Pour plus d’information, vous pouvez consulter "
+"%{notification_link}."
+
+msgid "Cycle Analytics"
+msgstr "Analyseur de cycle"
+
+msgid ""
+"Cycle Analytics gives an overview of how much time it takes to go from idea "
+"to production in your project."
+msgstr ""
+"L’analyseur de cycle permet d’avoir une vue d’ensemble du temps nécessaire "
+"pour aller d’une idée à sa mise en production pour votre projet."
msgid "CycleAnalyticsStage|Code"
msgstr "Code"
@@ -49,31 +325,169 @@ msgstr "Pré-production"
msgid "CycleAnalyticsStage|Test"
msgstr "Test"
+msgid "Define a custom pattern with cron syntax"
+msgstr "Définir un schéma personnalisé avec une syntaxe Cron"
+
+msgid "Delete"
+msgstr "Supprimer"
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] "Déploiement"
msgstr[1] "Déploiements"
+msgid "Description"
+msgstr "Description"
+
+msgid "Directory name"
+msgstr "Nom du dossier"
+
+msgid "Don't show again"
+msgstr "Ne plus montrer"
+
+msgid "Download"
+msgstr "Télécharger"
+
+msgid "Download tar"
+msgstr "Télécharger tar"
+
+msgid "Download tar.bz2"
+msgstr "Télécharger tar.bz2"
+
+msgid "Download tar.gz"
+msgstr "Télécharger tar.gz"
+
+msgid "Download zip"
+msgstr "Télécharger zip"
+
+msgid "DownloadArtifacts|Download"
+msgstr "Télécharger"
+
+msgid "DownloadCommit|Email Patches"
+msgstr "Patch email"
+
+msgid "DownloadCommit|Plain Diff"
+msgstr "Diff simple"
+
+msgid "DownloadSource|Download"
+msgstr "Télécharger"
+
+msgid "Edit"
+msgstr "Éditer"
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr "Éditer le pipeline programmé %{id}"
+
+msgid "Every day (at 4:00am)"
+msgstr "Chaque jour (à 4:00 du matin)"
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr "Chaque mois (le 1er à 4:00 du matin)"
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr "Chaque semaine (dimanche à 4:00 du matin)"
+
+msgid "Failed to change the owner"
+msgstr "Échec du changement de propriétaire"
+
+msgid "Failed to remove the pipeline schedule"
+msgstr "Échec de la suppression du pipeline programmé"
+
+msgid "Files"
+msgstr "Fichiers"
+
+msgid "Filter by commit message"
+msgstr "Filtrer par message de validation"
+
+msgid "Find by path"
+msgstr "Rechercher par chemin"
+
+msgid "Find file"
+msgstr "Rechercher un fichier"
+
msgid "FirstPushedBy|First"
msgstr "En premier"
msgid "FirstPushedBy|pushed by"
msgstr "poussé par"
+msgid "Fork"
+msgid_plural "Forks"
+msgstr[0] "Fourche"
+msgstr[1] "Fourches"
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr "Fouché depuis"
+
msgid "From issue creation until deploy to production"
msgstr "Depuis la création de l'incident jusqu'au déploiement en production"
msgid "From merge request merge until deploy to production"
-msgstr "Depuis la fusion de la demande de fusion jusqu'au déploiement en production"
+msgstr ""
+"Depuis la fusion de la demande de fusion jusqu'au déploiement en production"
+
+msgid "Go to your fork"
+msgstr "Aller à votre fourche"
+
+msgid "GoToYourFork|Fork"
+msgstr "Fourche"
+
+msgid "Home"
+msgstr "Accueil"
+
+msgid "Housekeeping successfully started"
+msgstr "Maintenance démarrée avec succès"
+
+msgid "Import repository"
+msgstr "Importer un dépôt"
+
+msgid "Interval Pattern"
+msgstr "Schéma d’intervalle"
msgid "Introducing Cycle Analytics"
msgstr "Introduction à l'analyseur de cycle"
+msgid "Jobs for last month"
+msgstr "Tâches pour le mois dernier"
+
+msgid "Jobs for last week"
+msgstr "Tâches pour la semaine dernière"
+
+msgid "Jobs for last year"
+msgstr "Tâches pour l'année dernière"
+
+msgid "LFSStatus|Disabled"
+msgstr "Désactivé"
+
+msgid "LFSStatus|Enabled"
+msgstr "Activé"
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "Le dernier %d jour"
msgstr[1] "Les derniers %d jours"
+msgid "Last Pipeline"
+msgstr "Dernier pipeline"
+
+msgid "Last Update"
+msgstr "Dernière mise à jour"
+
+msgid "Last commit"
+msgstr "Dernière validation"
+
+msgid "Learn more in the"
+msgstr "En apprendre plus dans le"
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr "documentation concernant la programmation de pipeline"
+
+msgid "Leave group"
+msgstr "Quitter le groupe"
+
+msgid "Leave project"
+msgstr "Quitter le projet"
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] "Limiter l'affichage au plus à %d évènement"
@@ -82,29 +496,276 @@ msgstr[1] "Limiter l'affichage au plus à %d évènements"
msgid "Median"
msgstr "Médian"
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr "ajouter une clef SSH"
+
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "Nouvel incident"
msgstr[1] "Nouveaux incidents"
+msgid "New Pipeline Schedule"
+msgstr "Nouveau pipeline programmé"
+
+msgid "New branch"
+msgstr "Nouvelle branche"
+
+msgid "New directory"
+msgstr "Nouveau dossier"
+
+msgid "New file"
+msgstr "Nouveau Fichier"
+
+msgid "New issue"
+msgstr "Nouvel incident"
+
+msgid "New merge request"
+msgstr "Nouvelle demande de fusion"
+
+msgid "New schedule"
+msgstr "Nouveau programme"
+
+msgid "New snippet"
+msgstr "Nouvel extrait de code"
+
+msgid "New tag"
+msgstr "Nouvelle étiquette"
+
+msgid "No repository"
+msgstr "Pas de dépôt"
+
+msgid "No schedules"
+msgstr "Aucun programme"
+
msgid "Not available"
msgstr "Indisponible"
msgid "Not enough data"
msgstr "Données insuffisantes"
+msgid "Notification events"
+msgstr "Événement de notifications"
+
+msgid "NotificationEvent|Close issue"
+msgstr "Clore l'incident"
+
+msgid "NotificationEvent|Close merge request"
+msgstr "Clore la demande de fusion"
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr "Pipeline échoué"
+
+msgid "NotificationEvent|Merge merge request"
+msgstr "Fusionner le demande de fusion"
+
+msgid "NotificationEvent|New issue"
+msgstr "Nouvel incident"
+
+msgid "NotificationEvent|New merge request"
+msgstr "Nouvelle demande de fusion"
+
+msgid "NotificationEvent|New note"
+msgstr "Nouvelle note"
+
+msgid "NotificationEvent|Reassign issue"
+msgstr "Réassigner l'incident"
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr "Réassigner la demande de fusion"
+
+msgid "NotificationEvent|Reopen issue"
+msgstr "Ré-ouvrir l'incident"
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr "Pipeline réussi"
+
+msgid "NotificationLevel|Custom"
+msgstr "Personnalisé"
+
+msgid "NotificationLevel|Disabled"
+msgstr "Désactivé"
+
+msgid "NotificationLevel|Global"
+msgstr "Global"
+
+msgid "NotificationLevel|On mention"
+msgstr "En cas de mention"
+
+msgid "NotificationLevel|Participate"
+msgstr "Participation"
+
+msgid "NotificationLevel|Watch"
+msgstr "Surveillé"
+
+msgid "OfSearchInADropdown|Filter"
+msgstr "Filtre"
+
msgid "OpenedNDaysAgo|Opened"
msgstr "Ouvert"
+msgid "Options"
+msgstr "Options"
+
+msgid "Owner"
+msgstr "Propriétaire"
+
+msgid "Pipeline"
+msgstr "Pipeline"
+
msgid "Pipeline Health"
msgstr "Santé du Pipeline"
+msgid "Pipeline Schedule"
+msgstr "Programmation de pipeline"
+
+msgid "Pipeline Schedules"
+msgstr "Programmations de pipeline"
+
+msgid "PipelineCharts|Failed:"
+msgstr "Échecs : "
+
+msgid "PipelineCharts|Overall statistics"
+msgstr "Statistiques générales"
+
+msgid "PipelineCharts|Success ratio:"
+msgstr "Ratio de succès : "
+
+msgid "PipelineCharts|Successful:"
+msgstr "Succès :"
+
+msgid "PipelineCharts|Total:"
+msgstr "Total :"
+
+msgid "PipelineSchedules|Activated"
+msgstr "Activé"
+
+msgid "PipelineSchedules|Active"
+msgstr "Actif"
+
+msgid "PipelineSchedules|All"
+msgstr "Tous"
+
+msgid "PipelineSchedules|Inactive"
+msgstr "Inactif"
+
+msgid "PipelineSchedules|Input variable key"
+msgstr "Nom de la variable"
+
+msgid "PipelineSchedules|Input variable value"
+msgstr "Valeur de la variable"
+
+msgid "PipelineSchedules|Next Run"
+msgstr "Prochaine exécution"
+
+msgid "PipelineSchedules|None"
+msgstr "Aucune"
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr "Indiquez une courte description"
+
+msgid "PipelineSchedules|Remove variable row"
+msgstr "Supprimer la variable"
+
+msgid "PipelineSchedules|Take ownership"
+msgstr "S’approprier"
+
+msgid "PipelineSchedules|Target"
+msgstr "Cible"
+
+msgid "PipelineSchedules|Variables"
+msgstr "Variables"
+
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr "Personnalisé"
+
+msgid "Pipelines"
+msgstr "Pipelines"
+
+msgid "Pipelines charts"
+msgstr "Graphique des pipelines"
+
+msgid "Pipeline|all"
+msgstr "Tous"
+
+msgid "Pipeline|success"
+msgstr "Succès"
+
+msgid "Pipeline|with stage"
+msgstr "avec l'étape"
+
+msgid "Pipeline|with stages"
+msgstr "avec les étapes"
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr "Projet '%{project_name}' en attente de suppression."
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr "Projet '%{project_name}' créé avec succès."
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr "Projet '%{project_name}' mis à jour avec succès."
+
+msgid "Project '%{project_name}' will be deleted."
+msgstr "Le projet '%{project_name}' sera supprimé."
+
+msgid "Project access must be granted explicitly to each user."
+msgstr ""
+"L’accès au projet doit être explicitement accordé à chaque utilisateur."
+
+msgid "Project export could not be deleted."
+msgstr "L'export du projet n'a pas pu être supprimé."
+
+msgid "Project export has been deleted."
+msgstr "L'export du projet a été supprimé."
+
+msgid ""
+"Project export link has expired. Please generate a new export from your "
+"project settings."
+msgstr ""
+"Le lien de l’export du projet a expiré. Merci de générer un nouvel export "
+"depuis les paramètres du projet."
+
+msgid "Project export started. A download link will be sent by email."
+msgstr ""
+"L'export du projet a débuté. Un lien de téléchargement sera envoyé par "
+"courriel."
+
+msgid "Project home"
+msgstr "Accueil du projet"
+
+msgid "ProjectFeature|Disabled"
+msgstr "Désactivé"
+
+msgid "ProjectFeature|Everyone with access"
+msgstr "Toute personne ayant accès"
+
+msgid "ProjectFeature|Only team members"
+msgstr "Seulement les membres de l'équipe"
+
+msgid "ProjectFileTree|Name"
+msgstr "Nom"
+
+msgid "ProjectLastActivity|Never"
+msgstr "Jamais"
+
msgid "ProjectLifecycle|Stage"
msgstr "Étape"
+msgid "ProjectNetworkGraph|Graph"
+msgstr "Graphique "
+
msgid "Read more"
msgstr "Lire plus"
+msgid "Readme"
+msgstr "LisezMoi"
+
+msgid "RefSwitcher|Branches"
+msgstr "Branches"
+
+msgid "RefSwitcher|Tags"
+msgstr "Étiquettes"
+
msgid "Related Commits"
msgstr "Validations liés"
@@ -123,43 +784,201 @@ msgstr "Demandes de fusion liées"
msgid "Related Merged Requests"
msgstr "Demandes fusionnées liées"
+msgid "Remind later"
+msgstr "Me le rappeler ultérieurement"
+
+msgid "Remove project"
+msgstr "Supprimer le projet"
+
+msgid "Request Access"
+msgstr "Demander l'accès"
+
+msgid "Revert this commit"
+msgstr "Annuler cette validation"
+
+msgid "Revert this merge request"
+msgstr "Annuler cette demande de fusion"
+
+msgid "Save pipeline schedule"
+msgstr "Sauvegarder le pipeline programmé"
+
+msgid "Schedule a new pipeline"
+msgstr "Programmer un nouveau pipeline"
+
+msgid "Scheduling Pipelines"
+msgstr "Programmer des pipelines"
+
+msgid "Search branches and tags"
+msgstr "Rechercher dans les branches et les étiquettes"
+
+msgid "Select Archive Format"
+msgstr "Sélectionnez le format de l'archive"
+
+msgid "Select a timezone"
+msgstr "Sélectionnez un fuseau horaire"
+
+msgid "Select target branch"
+msgstr "Sélectionnez une branche cible"
+
+msgid "Set a password on your account to pull or push via %{protocol}."
+msgstr ""
+"Définissez un mot de passe pour votre compte pour pouvoir tirer ou pousser "
+"par %{protocol}."
+
+msgid "Set up CI"
+msgstr "Mettre en place l'intégration continue (CI)"
+
+msgid "Set up Koding"
+msgstr "Mettre en place Koding"
+
+msgid "Set up auto deploy"
+msgstr "Mettre en place l’auto-déploiement"
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr "définir un mot de passe"
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "Affichage de %d évènement"
msgstr[1] "Affichage de %d évènements"
-msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
-msgstr "L’étape de développement montre le temps entre la première validation et la création de la demande de fusion. Les données seront automatiquement ajoutées ici une fois que vous aurez créé votre première demande de fusion."
+msgid "Source code"
+msgstr "Code source"
+
+msgid "StarProject|Star"
+msgstr "S'abonner"
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr "Créer une %{new_merge_request} avec ces changements"
+
+msgid "Switch branch/tag"
+msgstr "Changer de branche / d'étiquette"
+
+msgid "Tag"
+msgid_plural "Tags"
+msgstr[0] "Étiquette"
+msgstr[1] "Étiquettes"
+
+msgid "Tags"
+msgstr "Étiquettes"
+
+msgid "Target Branch"
+msgstr "Branche cible"
+
+msgid ""
+"The coding stage shows the time from the first commit to creating the merge "
+"request. The data will automatically be added here once you create your "
+"first merge request."
+msgstr ""
+"L’étape de développement montre le temps entre la première validation et la "
+"création de la demande de fusion. Les données seront automatiquement "
+"ajoutées ici une fois que vous aurez créé votre première demande de fusion."
msgid "The collection of events added to the data gathered for that stage."
-msgstr "L’ensemble d’évènements ajoutés aux données récupérées pour cette étape."
+msgstr ""
+"L’ensemble d’évènements ajoutés aux données récupérées pour cette étape."
-msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
-msgstr "L'étape des incidents montre le temps nécessaire entre la création d'un incident et son assignation à un jalon, ou son ajout à une liste d'un tableau d'incident. Débutez à créer des incidents pour voir des données pour cette étape."
+msgid "The fork relationship has been removed."
+msgstr "La relation de fourche a été supprimée."
+
+msgid ""
+"The issue stage shows the time it takes from creating an issue to assigning "
+"the issue to a milestone, or add the issue to a list on your Issue Board. "
+"Begin creating issues to see data for this stage."
+msgstr ""
+"L'étape des incidents montre le temps nécessaire entre la création d'un "
+"incident et son assignation à un jalon, ou son ajout à une liste d'un "
+"tableau d'incidents. Débutez à créer des incidents pour voir des données "
+"pour cette étape."
msgid "The phase of the development lifecycle."
msgstr "Les étapes du cycle de développement."
-msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
-msgstr "L’étape de planification montre le temps entre l’étape précédente et l’envoi de votre première validation. Ce temps sera automatiquement ajouté quand vous pousserez votre première validation."
+msgid ""
+"The pipelines schedule runs pipelines in the future, repeatedly, for "
+"specific branches or tags. Those scheduled pipelines will inherit limited "
+"project access based on their associated user."
+msgstr ""
+"Les pipelines programmés exécutent des pipelines dans le futur, de façon "
+"répétée, pour les branches et étiquettes spécifiées. Ces pipelines "
+"programmés héritent d’un accès partiel au projet basé sur l’utilisateur qui "
+"leurs est associé."
+
+msgid ""
+"The planning stage shows the time from the previous step to pushing your "
+"first commit. This time will be added automatically once you push your first "
+"commit."
+msgstr ""
+"L’étape de planification montre le temps entre l’étape précédente et l’envoi "
+"de votre première validation. Ce temps sera automatiquement ajouté quand "
+"vous pousserez votre première validation."
+
+msgid ""
+"The production stage shows the total time it takes between creating an issue "
+"and deploying the code to production. The data will be automatically added "
+"once you have completed the full idea to production cycle."
+msgstr ""
+"L’étape de mise en production montre le temps nécessaire entre la création "
+"d’un incident et le déploiement du code en production. Les données seront "
+"automatiquement ajoutées une fois que vous aurez complété le cycle complet, "
+"depuis l’idée jusqu’à la mise en production."
+
+msgid "The project can be accessed by any logged in user."
+msgstr ""
+"Votre projet peut être accédé par n’importe quel utilisateur authentifié"
-msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
-msgstr "L’étape de mise en production montre le temps nécessaire entre la création d’un incident et le déploiement du code en production. Les données seront automatiquement ajoutées une fois que vous aurez complété le cycle complet, depuis l’idée jusqu’à la mise en production."
+msgid "The project can be accessed without any authentication."
+msgstr "Votre projet peut être accédé sans aucune authentification."
-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 "L’étape d’évaluation montre le temps entre la création de la demande de fusion et la fusion effective de celle-ci. Ces données seront automatiquement ajoutées après que vous ayez fusionné votre première demande de fusion."
+msgid "The repository for this project does not exist."
+msgstr "Le dépôt pour ce projet n'existe pas."
-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 "L’étape de pré-production indique le temps entre la fusion de la RF et le déploiement du code dans l’environnent de production. Les données seront automatiquement ajoutées une fois que vous déploierez en production pour la première fois."
+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 ""
+"L’étape d’évaluation montre le temps entre la création de la demande de "
+"fusion et la fusion effective de celle-ci. Ces données seront "
+"automatiquement ajoutées après que vous ayez fusionné votre première demande "
+"de fusion."
-msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
-msgstr "L’étape de test montre le temps que le CI de GitLab met pour exécuter chaque pipeline liés à la demande de fusion. Les données seront automatiquement ajoutées après que votre premier pipeline s’achèvera."
+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 ""
+"L’étape de pré-production indique le temps entre la fusion de la DF et le "
+"déploiement du code dans l’environnent de production. Les données seront "
+"automatiquement ajoutées une fois que vous déploierez en production pour la "
+"première fois."
+
+msgid ""
+"The testing stage shows the time GitLab CI takes to run every pipeline for "
+"the related merge request. The data will automatically be added after your "
+"first pipeline finishes running."
+msgstr ""
+"L’étape de test montre le temps que le CI de GitLab met pour exécuter chaque "
+"pipeline liés à la demande de fusion. Les données seront automatiquement "
+"ajoutées après que votre premier pipeline s’achèvera."
msgid "The time taken by each data entry gathered by that stage."
msgstr "Le temps pris par chaque entrée récoltée durant cette étape."
-msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
-msgstr "La valeur située au point médian d’une série de valeur observée. C.à.d., entre 3, 5, 9, le médian est 5. Entre 3, 5, 7, 8, le médian est (5+7)/2 = 6."
+msgid ""
+"The value lying at the midpoint of a series of observed values. E.g., "
+"between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 ="
+" 6."
+msgstr ""
+"La valeur située au point médian d’une série de valeur observée. C.à.d., "
+"entre 3, 5, 9, le médian est 5. Entre 3, 5, 7, 8, le médian est (5+7)/2 = 6."
+
+msgid ""
+"This means you can not push code until you create an empty repository or "
+"import existing one."
+msgstr ""
+"Cela signifie que vous ne pouvez pas pousser du code tant que vous ne créez "
+"pas un dépôt vide, ou importez une dépôt existant."
msgid "Time before an issue gets scheduled"
msgstr "Temps avant qu’un incident ne soit planifié"
@@ -173,6 +992,129 @@ msgstr "Temps entre la création d'une demande de fusion et sa fusion/clôture"
msgid "Time until first merge request"
msgstr "Temps jusqu’à la première demande de fusion"
+msgid "Timeago|%s days ago"
+msgstr "Il y a %s jours"
+
+msgid "Timeago|%s days remaining"
+msgstr "Il reste %s jours"
+
+msgid "Timeago|%s hours remaining"
+msgstr "Il reste %s heures"
+
+msgid "Timeago|%s minutes ago"
+msgstr "Il y a %s minutes"
+
+msgid "Timeago|%s minutes remaining"
+msgstr "Il reste %s minutes"
+
+msgid "Timeago|%s months ago"
+msgstr "Il y a %s mois"
+
+msgid "Timeago|%s months remaining"
+msgstr "Il reste %s mois"
+
+msgid "Timeago|%s seconds remaining"
+msgstr "Il reste %s secondes"
+
+msgid "Timeago|%s weeks ago"
+msgstr "Il y a %s semaines"
+
+msgid "Timeago|%s weeks remaining"
+msgstr "Il reste %s semaines"
+
+msgid "Timeago|%s years ago"
+msgstr "Il y a %s ans"
+
+msgid "Timeago|%s years remaining"
+msgstr "Il reste %s ans"
+
+msgid "Timeago|1 day remaining"
+msgstr "Il reste un jour"
+
+msgid "Timeago|1 hour remaining"
+msgstr "Il reste une heure"
+
+msgid "Timeago|1 minute remaining"
+msgstr "Il reste une minute"
+
+msgid "Timeago|1 month remaining"
+msgstr "Il reste un mois"
+
+msgid "Timeago|1 week remaining"
+msgstr "Il reste une semaine"
+
+msgid "Timeago|1 year remaining"
+msgstr "Il reste un an"
+
+msgid "Timeago|Past due"
+msgstr "En retard"
+
+msgid "Timeago|a day ago"
+msgstr "Il y a un jour"
+
+msgid "Timeago|a month ago"
+msgstr "Il y a un mois"
+
+msgid "Timeago|a week ago"
+msgstr "Il y a une semaine"
+
+msgid "Timeago|a while"
+msgstr "Il y a un moment"
+
+msgid "Timeago|a year ago"
+msgstr "Il y a un an"
+
+msgid "Timeago|about %s hours ago"
+msgstr "Il y a environ %s heures"
+
+msgid "Timeago|about a minute ago"
+msgstr "Il y a environ une minute"
+
+msgid "Timeago|about an hour ago"
+msgstr "Il y a environ une heure"
+
+msgid "Timeago|in %s days"
+msgstr "Dans %s jours"
+
+msgid "Timeago|in %s hours"
+msgstr "Dans %s heures"
+
+msgid "Timeago|in %s minutes"
+msgstr "Dans %s minutes"
+
+msgid "Timeago|in %s months"
+msgstr "Dans %s mois"
+
+msgid "Timeago|in %s seconds"
+msgstr "Dans %s secondes"
+
+msgid "Timeago|in %s weeks"
+msgstr "Dans %s semaines"
+
+msgid "Timeago|in %s years"
+msgstr "Dans %s années"
+
+msgid "Timeago|in 1 day"
+msgstr "Dans 1 jour"
+
+msgid "Timeago|in 1 hour"
+msgstr "Dans 1 heure"
+
+msgid "Timeago|in 1 minute"
+msgstr "Dans 1 minute"
+
+msgid "Timeago|in 1 month"
+msgstr "Dans 1 mois"
+
+msgid "Timeago|in 1 week"
+msgstr "Dans 1 semaine"
+
+msgid "Timeago|in 1 year"
+msgstr "Dans 1 an"
+
+msgid "Timeago|less than a minute ago"
+msgstr "il y a moins d'une minute"
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "hr"
@@ -192,16 +1134,141 @@ msgstr "Temps total"
msgid "Total test time for all commits/merges"
msgstr "Temps total de test pour toutes les validations/fusions"
+msgid "Unstar"
+msgstr "Se désabonner"
+
+msgid "Upload New File"
+msgstr "Téléverser un nouveau fichier"
+
+msgid "Upload file"
+msgstr "Téléverser un fichier"
+
+msgid "UploadLink|click to upload"
+msgstr "Cliquez pour envoyer"
+
+msgid "Use your global notification setting"
+msgstr "Utiliser vos paramètres de notification globaux"
+
+msgid "View open merge request"
+msgstr "Afficher la demande de fusion"
+
+msgid "VisibilityLevel|Internal"
+msgstr "Interne"
+
+msgid "VisibilityLevel|Private"
+msgstr "Privé"
+
+msgid "VisibilityLevel|Public"
+msgstr "Public"
+
msgid "Want to see the data? Please ask an administrator for access."
-msgstr "Vous voulez voir les données ? Merci de contacter un administrateur pour en obtenir l’accès."
+msgstr ""
+"Vous voulez voir les données ? Merci de contacter un administrateur pour en "
+"obtenir l’accès."
msgid "We don't have enough data to show this stage."
msgstr "Nous n'avons pas suffisamment de données pour afficher cette étape."
+msgid "Withdraw Access Request"
+msgstr "Retirer la demande d'accès"
+
+msgid ""
+"You are going to remove %{group_name}.\n"
+"Removed groups CANNOT be restored!\n"
+"Are you ABSOLUTELY sure?"
+msgstr ""
+"Vous êtes sur le point de supprimer %{group_name}. Les groupes supprimés NE "
+"PEUVENT PAS être restaurés ! Êtes vous ABSOLUMENT sûr ?"
+
+msgid ""
+"You are going to remove %{project_name_with_namespace}.\n"
+"Removed project CANNOT be restored!\n"
+"Are you ABSOLUTELY sure?"
+msgstr ""
+"Vous êtes sur le point de supprimer %{project_name_with_namespace}.\n"
+"Les projets supprimés NE PEUVENT PAS être restaurés !\n"
+"Êtes vous ABSOLUMENT sûr ? "
+
+msgid ""
+"You are going to remove the fork relationship to source project "
+"%{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr ""
+"Vous allez supprimer la relation de fourche avec le projet source "
+"%{forked_from_project}. Êtes-vous VRAIMENT sûr."
+
+msgid ""
+"You are going to transfer %{project_name_with_namespace} to another owner. "
+"Are you ABSOLUTELY sure?"
+msgstr ""
+"Vous allez transférer %{project_name_with_namespace} à un nouveau "
+"propriétaire. Êtes vous VRAIMENT sûr ?"
+
+msgid "You can only add files when you are on a branch"
+msgstr "Vous ne pouvez ajouter de fichier que dans une branche"
+
+msgid "You have reached your project limit"
+msgstr "Vous avez atteint votre limite de projet"
+
+msgid "You must sign in to star a project"
+msgstr "Vous devez vous identifier pour vous abonner à un projet"
+
msgid "You need permission."
msgstr "Vous avez besoin d’une autorisation."
+msgid "You will not get any notifications via email"
+msgstr "Vous ne recevrez aucune notification par courriel"
+
+msgid "You will only receive notifications for the events you choose"
+msgstr ""
+"Vous ne recevrez de notification que pour les évènements que vous aurez "
+"choisis"
+
+msgid ""
+"You will only receive notifications for threads you have participated in"
+msgstr ""
+"Vous ne recevrez de notification que pour les sujets auxquels vous avez "
+"participé"
+
+msgid "You will receive notifications for any activity"
+msgstr "Vous recevrez des notifications pour n’importe quelles activités"
+
+msgid ""
+"You will receive notifications only for comments in which you were "
+"@mentioned"
+msgstr ""
+"Vous ne recevrez de notifications que pour les commentaires où vous êtes "
+"@mentionné"
+
+msgid ""
+"You won't be able to pull or push project code via %{protocol} until you "
+"%{set_password_link} on your account"
+msgstr ""
+"Vous ne pourrez pas récupérer ou pousser de code par %{protocol} tant que "
+"vous n'aurez pas %{set_password_link} pour votre compte"
+
+msgid ""
+"You won't be able to pull or push project code via SSH until you "
+"%{add_ssh_key_link} to your profile"
+msgstr ""
+"Vous ne pourrez pas récupérer ou pousser de code par SSH tant que vous "
+"n’aurez pas %{add_ssh_key_link} dans votre profil"
+
+msgid "Your name"
+msgstr "Votre nom"
+
msgid "day"
msgid_plural "days"
msgstr[0] "jour"
msgstr[1] "jours"
+
+msgid "new merge request"
+msgstr "nouvelle demande de fusion"
+
+msgid "notification emails"
+msgstr "courriels de notification"
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] "parent"
+msgstr[1] "parents"
+
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 8f33c494de9..babef3ed0af 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-07-12 12:31-0500\n"
-"PO-Revision-Date: 2017-07-12 12:31-0500\n"
+"POT-Creation-Date: 2017-07-13 12:07-0500\n"
+"PO-Revision-Date: 2017-07-13 12:07-0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
@@ -1060,6 +1060,9 @@ msgstr ""
msgid "VisibilityLevel|Public"
msgstr ""
+msgid "VisibilityLevel|Unknown"
+msgstr ""
+
msgid "Want to see the data? Please ask an administrator for access."
msgstr ""
diff --git a/locale/ja/gitlab.po b/locale/ja/gitlab.po
new file mode 100644
index 00000000000..cf74abf81bc
--- /dev/null
+++ b/locale/ja/gitlab.po
@@ -0,0 +1,1204 @@
+# Arthur Charron <arthur.charron@hotmail.fr>, 2017. #zanata
+# Huang Tao <htve@outlook.com>, 2017. #zanata
+# Kohei Ota <inductor@kela.jp>, 2017. #zanata
+# Taisuke Inoue <taisuke.inoue.jp@gmail.com>, 2017. #zanata
+# Takuya Noguchi <takninnovationresearch@gmail.com>, 2017. #zanata
+# YANO Tethurou <tetuyano+zana@gmail.com>, 2017. #zanata
+msgid ""
+msgstr ""
+"Project-Id-Version: gitlab 1.0.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-07-05 08:50-0500\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Last-Translator: YANO TETTER <tetuyano+zana@gmail.com>\n"
+"PO-Revision-Date: 2017-07-19 09:45-0400\n"
+"Last-Translator: YANO Tethurou <tetuyano+zana@gmail.com>\n"
+"Language: ja\n"
+"X-Generator: Zanata 3.9.6\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+
+msgid "%s additional commit has been omitted to prevent performance issues."
+msgid_plural ""
+"%s additional commits have been omitted to prevent performance issues."
+msgstr[0] "パフォーマンス低下を避けるため %s 個のコミットを省略しました。"
+
+msgid "%d commit"
+msgid_plural "%d commits"
+msgstr[0] "%d個のコミット"
+
+msgid "%{commit_author_link} committed %{commit_timeago}"
+msgstr "%{commit_author_link}は%{commit_timeago}前、コミットしました。"
+
+msgid "1 pipeline"
+msgid_plural "%d pipelines"
+msgstr[0] "%d 個のパイプライン"
+
+msgid "A collection of graphs regarding Continuous Integration"
+msgstr "CIについてのグラフ"
+
+msgid "About auto deploy"
+msgstr "自動デプロイについて"
+
+msgid "Active"
+msgstr "有効"
+
+msgid "Activity"
+msgstr "アクティビティー"
+
+msgid "Add Changelog"
+msgstr "変更履歴を追加"
+
+msgid "Add Contribution guide"
+msgstr "貢献者向けガイドを追加"
+
+msgid "Add License"
+msgstr "ライセンスを追加"
+
+msgid "Add an SSH key to your profile to pull or push via SSH."
+msgstr "SSHでプルやプッシュする場合は、プロフィールにSSH鍵を追加してください。"
+
+msgid "Add new directory"
+msgstr "新規ディレクトリを追加"
+
+msgid "Archived project! Repository is read-only"
+msgstr "アーカイブ済みプロジェクト!(レポジトリーは読み取り専用です)"
+
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr "このパイプラインスケジュールを削除しますか?"
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr "ドラッグ&ドロップまたは %{upload_link} でファイルを添付"
+
+msgid "Branch"
+msgid_plural "Branches"
+msgstr[0] "ブランチ"
+
+msgid ""
+"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, "
+"choose a GitLab CI Yaml template and commit your changes. "
+"%{link_to_autodeploy_doc}"
+msgstr ""
+"<strong>%{branch_name}</strong> ブランチが作成されました。自動デプロイを設定するには、GitLab CI Yaml "
+"テンプレートを選択して、変更をコミットしてください。 %{link_to_autodeploy_doc}"
+
+msgid "BranchSwitcherPlaceholder|Search branches"
+msgstr "ブランチを検索"
+
+msgid "BranchSwitcherTitle|Switch branch"
+msgstr "ブランチを切替"
+
+msgid "Branches"
+msgstr "ブランチ"
+
+msgid "Browse Directory"
+msgstr "ディレクトリを表示"
+
+msgid "Browse File"
+msgstr "ファイルを表示"
+
+msgid "Browse Files"
+msgstr "ファイルを表示"
+
+msgid "Browse files"
+msgstr "ファイルを表示"
+
+msgid "ByAuthor|by"
+msgstr "作者"
+
+msgid "CI configuration"
+msgstr "CI 設定"
+
+msgid "Cancel"
+msgstr "キャンセル"
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr "ピック先ブランチ:"
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr "リバート先ブランチ:"
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr "チェリーピック"
+
+msgid "ChangeTypeAction|Revert"
+msgstr "リバート"
+
+msgid "Changelog"
+msgstr "変更履歴"
+
+msgid "Charts"
+msgstr "チャート"
+
+msgid "Cherry-pick this commit"
+msgstr "このコミットをチェリーピック"
+
+msgid "Cherry-pick this merge request"
+msgstr "このマージリクエストをチェリーピック"
+
+msgid "CiStatusLabel|canceled"
+msgstr "キャンセル"
+
+msgid "CiStatusLabel|created"
+msgstr "作成済み"
+
+msgid "CiStatusLabel|failed"
+msgstr "失敗"
+
+msgid "CiStatusLabel|manual action"
+msgstr "手動実行"
+
+msgid "CiStatusLabel|passed"
+msgstr "成功"
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr "成功(警告あり)"
+
+msgid "CiStatusLabel|pending"
+msgstr "開始待ち"
+
+msgid "CiStatusLabel|skipped"
+msgstr "スキップ済み"
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr "手動実行待ち"
+
+msgid "CiStatusText|blocked"
+msgstr "ブロック"
+
+msgid "CiStatusText|canceled"
+msgstr "キャンセル"
+
+msgid "CiStatusText|created"
+msgstr "作成済み"
+
+msgid "CiStatusText|failed"
+msgstr "失敗"
+
+msgid "CiStatusText|manual"
+msgstr "手動"
+
+msgid "CiStatusText|passed"
+msgstr "成功"
+
+msgid "CiStatusText|pending"
+msgstr "実行待ち"
+
+msgid "CiStatusText|skipped"
+msgstr "スキップ済み"
+
+msgid "CiStatus|running"
+msgstr "実行中"
+
+msgid "Commit"
+msgid_plural "Commits"
+msgstr[0] "コミット"
+
+msgid "Commit duration in minutes for last 30 commits"
+msgstr "直近30コミットの所要時間(分)"
+
+msgid "Commit message"
+msgstr "コミットメッセージ"
+
+msgid "CommitBoxTitle|Commit"
+msgstr "コミット"
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr "%{file_name} を追加"
+
+msgid "Commits"
+msgstr "コミット"
+
+msgid "Commits feed"
+msgstr "コミットフィード"
+
+msgid "Commits|History"
+msgstr "履歴"
+
+msgid "Committed by"
+msgstr "コミット担当者: "
+
+msgid "Compare"
+msgstr "比較"
+
+msgid "Contribution guide"
+msgstr "貢献者向けガイド"
+
+msgid "Contributors"
+msgstr "貢献者"
+
+msgid "Copy URL to clipboard"
+msgstr "クリップボードにURLをコピー"
+
+msgid "Copy commit SHA to clipboard"
+msgstr "コミットのSHAをクリップボードにコピー"
+
+msgid "Create New Directory"
+msgstr "新規ディレクトリを作成"
+
+msgid ""
+"Create a personal access token on your account to pull or push via "
+"%{protocol}."
+msgstr "%{protocol} でプッシュやプルするためのあなた個人用アクセストークンを作成"
+
+msgid "Create directory"
+msgstr "ディレクトリを作成"
+
+msgid "Create empty bare repository"
+msgstr "空のbareレポジトリーを作成"
+
+msgid "Create merge request"
+msgstr "マージリクエストを作成"
+
+msgid "Create new..."
+msgstr "新規作成"
+
+msgid "CreateNewFork|Fork"
+msgstr "フォーク"
+
+msgid "CreateTag|Tag"
+msgstr "タグ"
+
+msgid "CreateTokenToCloneLink|create a personal access token"
+msgstr "個人用アクセストークンを作成"
+
+msgid "Cron Timezone"
+msgstr "Cron のタイムゾーン"
+
+msgid "Cron syntax"
+msgstr "Cron の構文"
+
+msgid "Custom notification events"
+msgstr "カスタム通知設定"
+
+msgid ""
+"Custom notification levels are the same as participating levels. With custom "
+"notification levels you will also receive notifications for select events. "
+"To find out more, check out %{notification_link}."
+msgstr ""
+"\"カスタム\" の通知レベルの基本は \"参加\" "
+"と同じです。また、カスタム通知に設定することで選択したカスタムイベントの通知を受け取ることもできます。もっと詳しく知りたい場合は "
+"%{notification_link} を見てください。"
+
+msgid "Cycle Analytics"
+msgstr "サイクル分析"
+
+msgid ""
+"Cycle Analytics gives an overview of how much time it takes to go from idea "
+"to production in your project."
+msgstr ""
+"サイクル分析により、あなたのプロジェクトがアイディアの段階からプロダクション環境にリリースされるまでどれぐらい時間がかかったか俯瞰することができます。"
+
+msgid "CycleAnalyticsStage|Code"
+msgstr "コード"
+
+msgid "CycleAnalyticsStage|Issue"
+msgstr "課題"
+
+msgid "CycleAnalyticsStage|Plan"
+msgstr "計画"
+
+msgid "CycleAnalyticsStage|Production"
+msgstr "プロダクション"
+
+msgid "CycleAnalyticsStage|Review"
+msgstr "レビュー"
+
+msgid "CycleAnalyticsStage|Staging"
+msgstr "ステージング"
+
+msgid "CycleAnalyticsStage|Test"
+msgstr "テスト"
+
+msgid "Define a custom pattern with cron syntax"
+msgstr "Cron 構文でカスタムなパターンを指定する"
+
+msgid "Delete"
+msgstr "削除"
+
+msgid "Deploy"
+msgid_plural "Deploys"
+msgstr[0] "デプロイ"
+
+msgid "Description"
+msgstr "説明"
+
+msgid "Directory name"
+msgstr "ディレクトリ名"
+
+msgid "Don't show again"
+msgstr "次回から表示しない"
+
+msgid "Download"
+msgstr "ダウンロード"
+
+msgid "Download tar"
+msgstr "tar形式でダウンロード"
+
+msgid "Download tar.bz2"
+msgstr "tar.bz2形式でダウンロード"
+
+msgid "Download tar.gz"
+msgstr "tar.gz形式でダウンロード"
+
+msgid "Download zip"
+msgstr "zip形式でダウンロード"
+
+msgid "DownloadArtifacts|Download"
+msgstr "ダウンロード"
+
+msgid "DownloadCommit|Email Patches"
+msgstr "パッチをメールで送信"
+
+msgid "DownloadCommit|Plain Diff"
+msgstr "プレーン差分"
+
+msgid "DownloadSource|Download"
+msgstr "ダウンロード"
+
+msgid "Edit"
+msgstr "編集"
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr "パイプラインスケジュール %{id} を編集"
+
+msgid "Every day (at 4:00am)"
+msgstr "毎日 (午前4:00)"
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr "毎月 (1日の午前4:00)"
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr "毎週 (日曜日の午前4:00)"
+
+msgid "Failed to change the owner"
+msgstr "オーナーを変更できませんでした"
+
+msgid "Failed to remove the pipeline schedule"
+msgstr "パイプラインスケジュールを削除できませんでした"
+
+msgid "Files"
+msgstr "ファイル"
+
+msgid "Filter by commit message"
+msgstr "コミットメッセージで絞り込み"
+
+msgid "Find by path"
+msgstr "パスで検索"
+
+msgid "Find file"
+msgstr "ファイルを検索"
+
+msgid "FirstPushedBy|First"
+msgstr "初回"
+
+msgid "FirstPushedBy|pushed by"
+msgstr "プッシュした人"
+
+msgid "Fork"
+msgid_plural "Forks"
+msgstr[0] "フォーク"
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr "フォーク元"
+
+msgid "From issue creation until deploy to production"
+msgstr "課題が登録されてからプロダクションにデプロイされるまで"
+
+msgid "From merge request merge until deploy to production"
+msgstr "マージリクエストがマージされてからプロダクションにデプロイされるまで"
+
+msgid "Go to your fork"
+msgstr "自分のフォークへ移動"
+
+msgid "GoToYourFork|Fork"
+msgstr "フォーク"
+
+msgid "Home"
+msgstr "ホーム"
+
+msgid "Housekeeping successfully started"
+msgstr "ハウスキーピングは正常に起動しました。"
+
+msgid "Import repository"
+msgstr "レポジトリーをインポート"
+
+msgid "Interval Pattern"
+msgstr "間隔のパターン"
+
+msgid "Introducing Cycle Analytics"
+msgstr "サイクル分析のご紹介"
+
+msgid "Jobs for last month"
+msgstr "先月のジョブ"
+
+msgid "Jobs for last week"
+msgstr "先週のジョブ"
+
+msgid "Jobs for last year"
+msgstr "昨年のジョブ"
+
+msgid "LFSStatus|Disabled"
+msgstr "無効"
+
+msgid "LFSStatus|Enabled"
+msgstr "有効"
+
+msgid "Last %d day"
+msgid_plural "Last %d days"
+msgstr[0] "過去%d日間"
+
+msgid "Last Pipeline"
+msgstr "最新パイプライン"
+
+msgid "Last Update"
+msgstr "最新アップデート"
+
+msgid "Last commit"
+msgstr "最新コミット"
+
+msgid "Learn more in the"
+msgstr "詳しく見る:"
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr "詳しくはパイプラインスケジュールのドキュメントを参照"
+
+msgid "Leave group"
+msgstr "グループを離脱"
+
+msgid "Leave project"
+msgstr "プロジェクトを離脱"
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] "イベント表示数を最大 %d 個に制限"
+
+msgid "Median"
+msgstr "中央値"
+
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr "SSH 鍵を追加"
+
+msgid "New Issue"
+msgid_plural "New Issues"
+msgstr[0] "新規課題"
+
+msgid "New Pipeline Schedule"
+msgstr "新規パイプラインスケジュール"
+
+msgid "New branch"
+msgstr "新規ブランチ"
+
+msgid "New directory"
+msgstr "新規ディレクトリ"
+
+msgid "New file"
+msgstr "新規ファイル"
+
+msgid "New issue"
+msgstr "新規課題"
+
+msgid "New merge request"
+msgstr "新規マージリクエスト"
+
+msgid "New schedule"
+msgstr "新規スケジュール"
+
+msgid "New snippet"
+msgstr "新規スニペット"
+
+msgid "New tag"
+msgstr "新規タグ"
+
+msgid "No repository"
+msgstr "レポジトリーはありません"
+
+msgid "No schedules"
+msgstr "スケジュールなし"
+
+msgid "Not available"
+msgstr "利用できません"
+
+msgid "Not enough data"
+msgstr "データ不足"
+
+msgid "Notification events"
+msgstr "イベント通知"
+
+msgid "NotificationEvent|Close issue"
+msgstr "課題をクローズ"
+
+msgid "NotificationEvent|Close merge request"
+msgstr "マージリクエストをクローズ"
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr "パイプラインに失敗"
+
+msgid "NotificationEvent|Merge merge request"
+msgstr "マージリクエストをマージ"
+
+msgid "NotificationEvent|New issue"
+msgstr "新規課題"
+
+msgid "NotificationEvent|New merge request"
+msgstr "新規マージリクエスト"
+
+msgid "NotificationEvent|New note"
+msgstr "新規ノート"
+
+msgid "NotificationEvent|Reassign issue"
+msgstr "課題の担当者を変更"
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr "マージリクエスト担当者を変更"
+
+msgid "NotificationEvent|Reopen issue"
+msgstr "課題を再オープン"
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr "パイプライン成功"
+
+msgid "NotificationLevel|Custom"
+msgstr "カスタム"
+
+msgid "NotificationLevel|Disabled"
+msgstr "無効"
+
+msgid "NotificationLevel|Global"
+msgstr "全体設定"
+
+msgid "NotificationLevel|On mention"
+msgstr "メンション時"
+
+msgid "NotificationLevel|Participate"
+msgstr "参加"
+
+msgid "NotificationLevel|Watch"
+msgstr "すべて通知"
+
+msgid "OfSearchInADropdown|Filter"
+msgstr "フィルター"
+
+msgid "OpenedNDaysAgo|Opened"
+msgstr "オープンされたのは"
+
+msgid "Options"
+msgstr "オプション"
+
+msgid "Owner"
+msgstr "オーナー"
+
+msgid "Pipeline"
+msgstr "パイプライン"
+
+msgid "Pipeline Health"
+msgstr "パイプラインの進捗状況"
+
+msgid "Pipeline Schedule"
+msgstr "パイプラインスケジュール"
+
+msgid "Pipeline Schedules"
+msgstr "パイプラインスケジュール"
+
+msgid "PipelineCharts|Failed:"
+msgstr "失敗:"
+
+msgid "PipelineCharts|Overall statistics"
+msgstr "全体統計"
+
+msgid "PipelineCharts|Success ratio:"
+msgstr "成功比率:"
+
+msgid "PipelineCharts|Successful:"
+msgstr "成功:"
+
+msgid "PipelineCharts|Total:"
+msgstr "合計:"
+
+msgid "PipelineSchedules|Activated"
+msgstr "アクティブ"
+
+msgid "PipelineSchedules|Active"
+msgstr "アクティブ"
+
+msgid "PipelineSchedules|All"
+msgstr "全件"
+
+msgid "PipelineSchedules|Inactive"
+msgstr "無効"
+
+msgid "PipelineSchedules|Input variable key"
+msgstr "変数の名前を入力"
+
+msgid "PipelineSchedules|Input variable value"
+msgstr "変数の値を入力"
+
+msgid "PipelineSchedules|Next Run"
+msgstr "次の実行"
+
+msgid "PipelineSchedules|None"
+msgstr "なし"
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr "このパイプラインについて簡単に記述してください。"
+
+msgid "PipelineSchedules|Remove variable row"
+msgstr "変数を削除"
+
+msgid "PipelineSchedules|Take ownership"
+msgstr "権限を取得する"
+
+msgid "PipelineSchedules|Target"
+msgstr "ターゲット"
+
+msgid "PipelineSchedules|Variables"
+msgstr "変数"
+
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr "カスタム"
+
+msgid "Pipelines"
+msgstr "パイプライン"
+
+msgid "Pipelines charts"
+msgstr "パイプラインチャート"
+
+msgid "Pipeline|all"
+msgstr "全件"
+
+msgid "Pipeline|success"
+msgstr "成功"
+
+msgid "Pipeline|with stage"
+msgstr "ステージあり"
+
+msgid "Pipeline|with stages"
+msgstr "ステージあり"
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr "'%{project_name}' プロジェクトは削除処理待ちです。"
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr "'%{project_name}' プロジェクトは正常に作成されました。"
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr "'%{project_name}' プロジェクトは正常に更新されました。"
+
+msgid "Project '%{project_name}' will be deleted."
+msgstr "'%{project_name}' プロジェクトは削除されます。"
+
+msgid "Project access must be granted explicitly to each user."
+msgstr "ユーザーごとにプロジェクトアクセスの権限を指定しなければなりません。"
+
+msgid "Project export could not be deleted."
+msgstr "プロジェクトのエクスポートを削除できませんでした。"
+
+msgid "Project export has been deleted."
+msgstr "プロジェクトのエクスポートを削除しました。"
+
+msgid ""
+"Project export link has expired. Please generate a new export from your "
+"project settings."
+msgstr "プロジェクトのエクスポートリンクは期限切れになりました。プロジェクト設定にて新しくエクスポートリンクを作成してください。"
+
+msgid "Project export started. A download link will be sent by email."
+msgstr "プロジェクトのエクスポートを開始しました。ダウンロードのリンクはメールで送信します"
+
+msgid "Project home"
+msgstr "プロジェクトホーム"
+
+msgid "ProjectFeature|Disabled"
+msgstr "無効"
+
+msgid "ProjectFeature|Everyone with access"
+msgstr "アクセス権限を持っている人"
+
+msgid "ProjectFeature|Only team members"
+msgstr "チームメンバーのみ"
+
+msgid "ProjectFileTree|Name"
+msgstr "名前"
+
+msgid "ProjectLastActivity|Never"
+msgstr "記録なし"
+
+msgid "ProjectLifecycle|Stage"
+msgstr "ステージ"
+
+msgid "ProjectNetworkGraph|Graph"
+msgstr "ネットワークグラフ"
+
+msgid "Read more"
+msgstr "続きを読む"
+
+msgid "Readme"
+msgstr "Readme"
+
+msgid "RefSwitcher|Branches"
+msgstr "ブランチ"
+
+msgid "RefSwitcher|Tags"
+msgstr "タグ"
+
+msgid "Related Commits"
+msgstr "関連するコミット"
+
+msgid "Related Deployed Jobs"
+msgstr "関連するデプロイ済ジョブ"
+
+msgid "Related Issues"
+msgstr "関連する課題"
+
+msgid "Related Jobs"
+msgstr "関連するジョブ"
+
+msgid "Related Merge Requests"
+msgstr "関連するマージリクエスト"
+
+msgid "Related Merged Requests"
+msgstr "関連するマージリクエスト"
+
+msgid "Remind later"
+msgstr "後で通知"
+
+msgid "Remove project"
+msgstr "プロジェクトを削除"
+
+msgid "Request Access"
+msgstr "アクセス権限をリクエストする"
+
+msgid "Revert this commit"
+msgstr "このコミットをリバート"
+
+msgid "Revert this merge request"
+msgstr "このマージリクエストをリバート"
+
+msgid "Save pipeline schedule"
+msgstr "パイプラインスケジュールを保存"
+
+msgid "Schedule a new pipeline"
+msgstr "新しいパイプラインのスケジュールを作成"
+
+msgid "Scheduling Pipelines"
+msgstr "パイプラインスケジューリング"
+
+msgid "Search branches and tags"
+msgstr "ブランチまたはタグを検索"
+
+msgid "Select Archive Format"
+msgstr "アーカイブのフォーマットを選択"
+
+msgid "Select a timezone"
+msgstr "タイムゾーンを選択"
+
+msgid "Select target branch"
+msgstr "ターゲットブランチを選択"
+
+msgid "Set a password on your account to pull or push via %{protocol}."
+msgstr "%{protocol} プロコトル経由でプル、プッシュするためにアカウントのパスワードを設定。"
+
+msgid "Set up CI"
+msgstr "CI を設定"
+
+msgid "Set up Koding"
+msgstr "Koding を設定"
+
+msgid "Set up auto deploy"
+msgstr "自動デプロイを設定"
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr "パスワードを設定"
+
+msgid "Showing %d event"
+msgid_plural "Showing %d events"
+msgstr[0] "%d のイベントを表示中"
+
+msgid "Source code"
+msgstr "ソースコード"
+
+msgid "StarProject|Star"
+msgstr "スターを付ける"
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr "この変更で %{new_merge_request} を作成する"
+
+msgid "Switch branch/tag"
+msgstr "ブランチ・タグ切り替え"
+
+msgid "Tag"
+msgid_plural "Tags"
+msgstr[0] "タグ"
+
+msgid "Tags"
+msgstr "タグ"
+
+msgid "Target Branch"
+msgstr "ターゲットブランチ"
+
+msgid ""
+"The coding stage shows the time from the first commit to creating the merge "
+"request. The data will automatically be added here once you create your "
+"first merge request."
+msgstr ""
+"コーディングステージでは、最初のコミットからマージリクエストが作成されるまでの時間が表示されます。このデータは最初のマージリクエストが作成されたときに自動的に追加されます。"
+
+msgid "The collection of events added to the data gathered for that stage."
+msgstr "このステージで計測データに追加されたイベントリスト"
+
+msgid "The fork relationship has been removed."
+msgstr "フォークのリレーションが削除されました。"
+
+msgid ""
+"The issue stage shows the time it takes from creating an issue to assigning "
+"the issue to a milestone, or add the issue to a list on your Issue Board. "
+"Begin creating issues to see data for this stage."
+msgstr ""
+"課題ステージでは、課題が登録されてからマイルストーンに割り当てられるか、課題ボードのリストに追加されるまでの時間が表示されます。このリストに表示するには課題を最初に作成してください。"
+
+msgid "The phase of the development lifecycle."
+msgstr "開発ライフサイクルの段階"
+
+msgid ""
+"The pipelines schedule runs pipelines in the future, repeatedly, for "
+"specific branches or tags. Those scheduled pipelines will inherit limited "
+"project access based on their associated user."
+msgstr ""
+"パイプラインスケジュールは指定のブランチまたはタグに対して自動的にパイプラインを実行します。計画済みパイプラインはそれらの紐付けられたユーザーのプロジェクトと同じ権限を継承します。"
+
+msgid ""
+"The planning stage shows the time from the previous step to pushing your "
+"first commit. This time will be added automatically once you push your first "
+"commit."
+msgstr ""
+"計画ステージでは、課題ステージに登録されてからプッシュされた最初のコミット時刻までの時間が表示されます。最初のコミットがプッシュされときに自動的に追加されます。"
+
+msgid ""
+"The production stage shows the total time it takes between creating an issue "
+"and deploying the code to production. The data will be automatically added "
+"once you have completed the full idea to production cycle."
+msgstr ""
+"プロダクションステージでは、課題が作成されてからプロダクションへデプロイされるまでの時間が表示されます。アイディアの時点からプロダクションまでの全ステージが完了したときに自動的に追加されます。"
+
+msgid "The project can be accessed by any logged in user."
+msgstr "プロジェクトは、ログインユーザーであれば誰でもアクセスできます。"
+
+msgid "The project can be accessed without any authentication."
+msgstr "プロジェクトは、ログインなしに誰でもアクセスできます。"
+
+msgid "The repository for this project does not exist."
+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 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 ""
+"ステージングステージでは、マージリクエストがマージされてからコードがプロダクション環境にデプロイされるまでの時間が表示されます。このデータは最初にプロダクションにデプロイしたときに自動的に追加されます。"
+
+msgid ""
+"The testing stage shows the time GitLab CI takes to run every pipeline for "
+"the related merge request. The data will automatically be added after your "
+"first pipeline finishes running."
+msgstr ""
+"テスティングステージでは、GitLab CI "
+"が関連するマージリクエストの各パイプラインを実行する時間が表示されます。このデータは最初のパイプラインが完了したときに自動的に追加されます。"
+
+msgid "The time taken by each data entry gathered by that stage."
+msgstr "このステージに収集されたデータ毎の時間"
+
+msgid ""
+"The value lying at the midpoint of a series of observed values. E.g., "
+"between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 ="
+" 6."
+msgstr ""
+"得られた一連のデータを小さい順に並べたときに中央に位置する値。例えば、3, 5, 9の中央値は5。3, 5, 7, 8の中央値は (5+7)/2 = "
+"6。"
+
+msgid ""
+"This means you can not push code until you create an empty repository or "
+"import existing one."
+msgstr "空レポジトリーを作成または既存レポジトリーをインポートをしなければ、コードのプッシュはできません。"
+
+msgid "Time before an issue gets scheduled"
+msgstr "課題が計画されるまでの時間"
+
+msgid "Time before an issue starts implementation"
+msgstr "課題の実装が開始されるまでの時間"
+
+msgid "Time between merge request creation and merge/close"
+msgstr "マージリクエストが作成されてからマージまたはクローズされるまでの時間"
+
+msgid "Time until first merge request"
+msgstr "最初のマージリクエストまでの時間"
+
+msgid "Timeago|%s days ago"
+msgstr "%s日前"
+
+msgid "Timeago|%s days remaining"
+msgstr "残り %s日間"
+
+msgid "Timeago|%s hours remaining"
+msgstr "残り %s時間"
+
+msgid "Timeago|%s minutes ago"
+msgstr "%s分前"
+
+msgid "Timeago|%s minutes remaining"
+msgstr "残り %s分間"
+
+msgid "Timeago|%s months ago"
+msgstr "%sヶ月前"
+
+msgid "Timeago|%s months remaining"
+msgstr "残り %sヶ月"
+
+msgid "Timeago|%s seconds remaining"
+msgstr "残り %s 秒"
+
+msgid "Timeago|%s weeks ago"
+msgstr "%s週間前"
+
+msgid "Timeago|%s weeks remaining"
+msgstr "残り %s週間"
+
+msgid "Timeago|%s years ago"
+msgstr "%s年前"
+
+msgid "Timeago|%s years remaining"
+msgstr "残り %s年間"
+
+msgid "Timeago|1 day remaining"
+msgstr "残り 1日間"
+
+msgid "Timeago|1 hour remaining"
+msgstr "残り 1時間"
+
+msgid "Timeago|1 minute remaining"
+msgstr "残り 1分間"
+
+msgid "Timeago|1 month remaining"
+msgstr "残り 1ヶ月"
+
+msgid "Timeago|1 week remaining"
+msgstr "残り 1週間"
+
+msgid "Timeago|1 year remaining"
+msgstr "残り 1年間"
+
+msgid "Timeago|Past due"
+msgstr "期限オーバー"
+
+msgid "Timeago|a day ago"
+msgstr "1日前"
+
+msgid "Timeago|a month ago"
+msgstr "1ヶ月前"
+
+msgid "Timeago|a week ago"
+msgstr "1週間前"
+
+msgid "Timeago|a while"
+msgstr "しばらく前"
+
+msgid "Timeago|a year ago"
+msgstr "1年前"
+
+msgid "Timeago|about %s hours ago"
+msgstr "約%s時間前"
+
+msgid "Timeago|about a minute ago"
+msgstr "約1分間前"
+
+msgid "Timeago|about an hour ago"
+msgstr "約1時間前"
+
+msgid "Timeago|in %s days"
+msgstr "%s日間以内"
+
+msgid "Timeago|in %s hours"
+msgstr "%s時間以内"
+
+msgid "Timeago|in %s minutes"
+msgstr "%s分間以内"
+
+msgid "Timeago|in %s months"
+msgstr "%sヶ月以内"
+
+msgid "Timeago|in %s seconds"
+msgstr "%s秒以内"
+
+msgid "Timeago|in %s weeks"
+msgstr "%s週間以内"
+
+msgid "Timeago|in %s years"
+msgstr "%s年間以内"
+
+msgid "Timeago|in 1 day"
+msgstr "1日以内"
+
+msgid "Timeago|in 1 hour"
+msgstr "1時間以内"
+
+msgid "Timeago|in 1 minute"
+msgstr "1分以内"
+
+msgid "Timeago|in 1 month"
+msgstr "1ヶ月以内"
+
+msgid "Timeago|in 1 week"
+msgstr "1週間以内"
+
+msgid "Timeago|in 1 year"
+msgstr "1年以内"
+
+msgid "Timeago|less than a minute ago"
+msgstr "1分未満"
+
+msgid "Time|hr"
+msgid_plural "Time|hrs"
+msgstr[0] "時間"
+
+msgid "Time|min"
+msgid_plural "Time|mins"
+msgstr[0] "分"
+
+msgid "Time|s"
+msgstr "秒"
+
+msgid "Total Time"
+msgstr "合計時間"
+
+msgid "Total test time for all commits/merges"
+msgstr "すべてのコミット/マージの合計テスト時間"
+
+msgid "Unstar"
+msgstr "スターを外す"
+
+msgid "Upload New File"
+msgstr "新規ファイルをアップロード"
+
+msgid "Upload file"
+msgstr "ファイルをアップロード"
+
+msgid "UploadLink|click to upload"
+msgstr "クリックしてアップロード"
+
+msgid "Use your global notification setting"
+msgstr "全体通知設定を利用"
+
+msgid "View open merge request"
+msgstr "オープンなマージリクエストを表示"
+
+msgid "VisibilityLevel|Internal"
+msgstr "内部"
+
+msgid "VisibilityLevel|Private"
+msgstr "プライベート"
+
+msgid "VisibilityLevel|Public"
+msgstr "パブリック"
+
+msgid "Want to see the data? Please ask an administrator for access."
+msgstr "このデータを参照したいですか?アクセスするには管理者に問い合わせてください。"
+
+msgid "We don't have enough data to show this stage."
+msgstr "データ不足のため、このステージの表示はできません。"
+
+msgid "Withdraw Access Request"
+msgstr "アクセスリクエストを取り消す"
+
+msgid ""
+"You are going to remove %{group_name}.\n"
+"Removed groups CANNOT be restored!\n"
+"Are you ABSOLUTELY sure?"
+msgstr "%{group_name} グループを削除しようとしています。\n"
+"削除されたグループは絶対に元に戻せません!\n"
+"本当によろしいですか?"
+
+msgid ""
+"You are going to remove %{project_name_with_namespace}.\n"
+"Removed project CANNOT be restored!\n"
+"Are you ABSOLUTELY sure?"
+msgstr ""
+"%{project_name_with_namespace} プロジェクトを削除しようとしています。\n"
+"削除されたプロジェクトは絶対に元には戻せません!\n"
+"本当によろしいですか?"
+
+msgid ""
+"You are going to remove the fork relationship to source project "
+"%{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr "元のプロジェクト (%{forked_from_project}) とのリレーションを削除しようとしています。\n"
+"本当によろしいですか?"
+
+msgid ""
+"You are going to transfer %{project_name_with_namespace} to another owner. "
+"Are you ABSOLUTELY sure?"
+msgstr "%{project_name_with_namespace} プロジェクトを別のオーナーに移譲しようとしています。本当によろしいですか?"
+
+msgid "You can only add files when you are on a branch"
+msgstr "ファイルを追加するには、どこかのブランチにいなければいけません"
+
+msgid "You have reached your project limit"
+msgstr "プロジェクト数の上限に達しています"
+
+msgid "You must sign in to star a project"
+msgstr "プロジェクトにスターをつけたい場合はログインしてください"
+
+msgid "You need permission."
+msgstr "権限が必要です"
+
+msgid "You will not get any notifications via email"
+msgstr "通知メールを送信しません"
+
+msgid "You will only receive notifications for the events you choose"
+msgstr "選択したイベントのみ通知します"
+
+msgid ""
+"You will only receive notifications for threads you have participated in"
+msgstr "参加したスレッドのみ通知します"
+
+msgid "You will receive notifications for any activity"
+msgstr "全てのアクティビティーを通知します"
+
+msgid ""
+"You will receive notifications only for comments in which you were "
+"@mentioned"
+msgstr "あなたが @mentioned でコメントされた時のみ通知します"
+
+msgid ""
+"You won't be able to pull or push project code via %{protocol} until you "
+"%{set_password_link} on your account"
+msgstr ""
+"%{set_password_link} でアカウントのパスワードがセットされていないので、プロジェクトに %{protocol} "
+"でソースコードをプッシュ、プルできません"
+
+msgid ""
+"You won't be able to pull or push project code via SSH until you "
+"%{add_ssh_key_link} to your profile"
+msgstr "%{add_ssh_key_link} をプロファイルに追加していないので、プロジェクトにソースコードをプッシュ、プルできません"
+
+msgid "Your name"
+msgstr "名前"
+
+msgid "day"
+msgid_plural "days"
+msgstr[0] "日"
+
+msgid "new merge request"
+msgstr "新規マージリクエスト"
+
+msgid "notification emails"
+msgstr "メール通知"
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] "親"
+
diff --git a/locale/ja/gitlab.po.time_stamp b/locale/ja/gitlab.po.time_stamp
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/locale/ja/gitlab.po.time_stamp
diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po
index 1ea39894bb8..c4918a4c920 100644
--- a/locale/pt_BR/gitlab.po
+++ b/locale/pt_BR/gitlab.po
@@ -6,20 +6,41 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-06-15 21:59-0500\n"
+"POT-Creation-Date: 2017-06-28 13:32+0200\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2017-07-05 02:56-0400\n"
-"Last-Translator: Huang Tao <htve@outlook.com>\n"
-"Language-Team: Portuguese (Brazil)\n"
+"PO-Revision-Date: 2017-07-12 09:05-0400\n"
+"Last-Translator: Leandro Nunes dos Santos <leandronunes@gmail.com>\n"
+"Language-Team: Portuguese (Brazil) (https://translate.zanata.org/project/view/GitLab)\n"
"Language: pt-BR\n"
"X-Generator: Zanata 3.9.6\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+msgid "%s additional commit has been omitted to prevent performance issues."
+msgid_plural ""
+"%s additional commits have been omitted to prevent performance issues."
+msgstr[0] ""
+"%s commit adicional foi omitido para prevenir problemas de performance."
+msgstr[1] ""
+"%s commits adicionais foram omitidos para prevenir problemas de performance."
+
+msgid "%d commit"
+msgid_plural "%d commits"
+msgstr[0] "%d commit"
+msgstr[1] "%d commits"
+
msgid "%{commit_author_link} committed %{commit_timeago}"
msgstr "%{commit_author_link} fez commit %{commit_timeago}"
+msgid "1 pipeline"
+msgid_plural "%d pipelines"
+msgstr[0] "1 pipeline"
+msgstr[1] "%d pipelines"
+
+msgid "A collection of graphs regarding Continuous Integration"
+msgstr "Uma coleção de gráficos sobre Integração Contínua"
+
msgid "About auto deploy"
msgstr "Sobre a implantação automática"
@@ -67,9 +88,24 @@ msgstr ""
"implantação automática, selecione um modelo de Yaml do GitLab CI e registre "
"suas mudanças. %{link_to_autodeploy_doc}"
+msgid "BranchSwitcherPlaceholder|Search branches"
+msgstr "BranchSwitcherPlaceholder|Procurar por branches"
+
+msgid "BranchSwitcherTitle|Switch branch"
+msgstr "BranchSwitcherTitle|Mudar de branch"
+
msgid "Branches"
msgstr "Branches"
+msgid "Browse Directory"
+msgstr "Navegar no Diretório"
+
+msgid "Browse File"
+msgstr "Pesquisar Arquivo"
+
+msgid "Browse Files"
+msgstr "Pesquisar Arquivos"
+
msgid "Browse files"
msgstr "Navegar pelos arquivos"
@@ -165,6 +201,9 @@ msgid_plural "Commits"
msgstr[0] "Commit"
msgstr[1] "Commits"
+msgid "Commit duration in minutes for last 30 commits"
+msgstr "Duração do commit em minutos para os últimos 30 commits"
+
msgid "Commit message"
msgstr "Mensagem de commit"
@@ -177,6 +216,9 @@ msgstr "Adicionar %{file_name}"
msgid "Commits"
msgstr "Commits"
+msgid "Commits feed"
+msgstr "Feed de commits"
+
msgid "Commits|History"
msgstr "Histórico"
@@ -201,6 +243,13 @@ msgstr "Copiar SHA do commit para a área de transferência"
msgid "Create New Directory"
msgstr "Criar Novo Diretório"
+msgid ""
+"Create a personal access token on your account to pull or push via "
+"%{protocol}."
+msgstr ""
+"Crie um token de acesso pessoal na sua conta para dar pull ou push via "
+"%{protocol}."
+
msgid "Create directory"
msgstr "Criar diretório"
@@ -219,6 +268,9 @@ msgstr "Fork"
msgid "CreateTag|Tag"
msgstr "Tag"
+msgid "CreateTokenToCloneLink|create a personal access token"
+msgstr "CreateTokenToCloneLink|criar um token de acesso pessoal"
+
msgid "Cron Timezone"
msgstr "Fuso horário do cron"
@@ -340,6 +392,9 @@ msgstr "Erro ao excluir o agendamento do pipeline"
msgid "Files"
msgstr "Arquivos"
+msgid "Filter by commit message"
+msgstr "Filtrar por mensagem de commit"
+
msgid "Find by path"
msgstr "Localizar por caminho"
@@ -388,6 +443,15 @@ msgstr "Padrão de intervalo"
msgid "Introducing Cycle Analytics"
msgstr "Apresentando a Análise de Ciclo"
+msgid "Jobs for last month"
+msgstr "Jobs no último mês"
+
+msgid "Jobs for last week"
+msgstr "Jobs na última semana"
+
+msgid "Jobs for last year"
+msgstr "Jobs no último ano"
+
msgid "LFSStatus|Disabled"
msgstr "Desabilitado"
@@ -553,6 +617,21 @@ msgstr "Agendamento da Pipeline"
msgid "Pipeline Schedules"
msgstr "Agendamentos da Pipeline"
+msgid "PipelineCharts|Failed:"
+msgstr "PipelineCharts|Falhou:"
+
+msgid "PipelineCharts|Overall statistics"
+msgstr "PipelineCharts|Estatísticas gerais"
+
+msgid "PipelineCharts|Success ratio:"
+msgstr "PipelineCharts|Taxa de sucesso:"
+
+msgid "PipelineCharts|Successful:"
+msgstr "PipelineCharts|Sucesso:"
+
+msgid "PipelineCharts|Total:"
+msgstr "PipelineCharts|Total:"
+
msgid "PipelineSchedules|Activated"
msgstr "Ativado"
@@ -583,6 +662,18 @@ msgstr "Destino"
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "Personalizado"
+msgid "Pipelines"
+msgstr "Pipelines"
+
+msgid "Pipelines charts"
+msgstr "Gráficos de pipelines"
+
+msgid "Pipeline|all"
+msgstr "Pipeline|todos"
+
+msgid "Pipeline|success"
+msgstr "Pipeline|sucesso"
+
msgid "Pipeline|with stage"
msgstr "com etapa"
@@ -713,10 +804,10 @@ msgstr "Selecionar fuso horário"
msgid "Select target branch"
msgstr "Selecionar branch de destino"
-msgid "Set a password on your account to pull or push via %{protocol}"
+msgid "Set a password on your account to pull or push via %{protocol}."
msgstr ""
"Defina uma senha para sua conta para aceitar ou entregar código via "
-"%{protocol}"
+"%{protocol}."
msgid "Set up CI"
msgstr "Configurar CI"
@@ -1032,9 +1123,15 @@ msgstr "Enviar Novo Arquivo"
msgid "Upload file"
msgstr "Enviar arquivo"
+msgid "UploadLink|click to upload"
+msgstr "UploadLink|clique para fazer upload"
+
msgid "Use your global notification setting"
msgstr "Utilizar configuração de notificação global"
+msgid "View open merge request"
+msgstr "Ver merge request aberto"
+
msgid "VisibilityLevel|Internal"
msgstr "Interno"
diff --git a/locale/ru/gitlab.po b/locale/ru/gitlab.po
new file mode 100644
index 00000000000..4643bed98e2
--- /dev/null
+++ b/locale/ru/gitlab.po
@@ -0,0 +1,1233 @@
+# SAS <Stepanov.sa@bashkortostan.ru>, 2017. #zanata
+# Huang Tao <htve@outlook.com>, 2017. #zanata
+msgid ""
+msgstr ""
+"Project-Id-Version: gitlab 1.0.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-06-28 13:32+0200\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"PO-Revision-Date: 2017-07-11 05:13-0400\n"
+"Last-Translator: SAS <Stepanov.sa@bashkortostan.ru>\n"
+"Language-Team: Russian (https://translate.zanata.org/project/view/GitLab)\n"
+"Language: ru\n"
+"X-Generator: Zanata 3.9.6\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+
+msgid "%s additional commit has been omitted to prevent performance issues."
+msgid_plural ""
+"%s additional commits have been omitted to prevent performance issues."
+msgstr[0] ""
+"%s добавленный коммит был исключен для предотвращения проблем с "
+"производительностью."
+msgstr[1] ""
+"%s добавленные коммиты были исключены для предотвращения проблем с "
+"производительностью."
+msgstr[2] ""
+"%s добавленные коммиты были исключены для предотвращения проблем с "
+"производительностью."
+
+msgid "%d commit"
+msgid_plural "%d commits"
+msgstr[0] "%d коммит"
+msgstr[1] "%d коммит(а|ов)"
+msgstr[2] "%d коммит(а|ов)"
+
+msgid "%{commit_author_link} committed %{commit_timeago}"
+msgstr "%{commit_author_link} закоммичено %{commit_timeago}"
+
+msgid "1 pipeline"
+msgid_plural "%d pipelines"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "A collection of graphs regarding Continuous Integration"
+msgstr ""
+
+msgid "About auto deploy"
+msgstr "Автоматическое развертывание"
+
+msgid "Active"
+msgstr "Активный"
+
+msgid "Activity"
+msgstr "Активность"
+
+msgid "Add Changelog"
+msgstr "Добавить в журнал изменений"
+
+msgid "Add Contribution guide"
+msgstr "Добавить руководство для контрибьютеров"
+
+msgid "Add License"
+msgstr "Добавить лицензию"
+
+msgid "Add an SSH key to your profile to pull or push via SSH."
+msgstr ""
+"Добавьте ключ SSH в свой профиль, чтобы отправлять или получать код через "
+"SSH."
+
+msgid "Add new directory"
+msgstr "Добавить новую директорию"
+
+msgid "Archived project! Repository is read-only"
+msgstr "Архивный проект! Репозиторий доступен только для чтения"
+
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr "Вы действительно хотите удалить это расписание конвейера?"
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr "Приложить файл через drag &amp; drop или %{upload_link}"
+
+msgid "Branch"
+msgid_plural "Branches"
+msgstr[0] "Ветка"
+msgstr[1] "Ветки"
+msgstr[2] "Ветки"
+
+msgid ""
+"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, "
+"choose a GitLab CI Yaml template and commit your changes. "
+"%{link_to_autodeploy_doc}"
+msgstr ""
+"Ветка <strong>%{branch_name}</strong> создана. Для настройки автоматического "
+"развертывания выберете GitLab CI Yaml-шаблон и зафиксируйте изменения. "
+"%{link_to_autodeploy_doc}"
+
+msgid "BranchSwitcherPlaceholder|Search branches"
+msgstr "BranchSwitcherPlaceholder|Поиск веток"
+
+msgid "BranchSwitcherTitle|Switch branch"
+msgstr "BranchSwitcherTitle|Переключить ветку"
+
+msgid "Branches"
+msgstr "Ветки"
+
+msgid "Browse Directory"
+msgstr "Просмотр директории"
+
+msgid "Browse File"
+msgstr "Просмотр файла"
+
+msgid "Browse Files"
+msgstr "Просмотр файлов"
+
+msgid "Browse files"
+msgstr "Просмотр файлов"
+
+msgid "ByAuthor|by"
+msgstr "ByAuthor|по автору"
+
+msgid "CI configuration"
+msgstr "Настройка CI"
+
+msgid "Cancel"
+msgstr "Отмена"
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr "ChangeTypeActionLabel|Выбрать в ветке"
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr "ChangeTypeActionLabel|Отменить в ветке"
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr "ChangeTypeAction|Подобрать"
+
+msgid "ChangeTypeAction|Revert"
+msgstr "ChangeTypeAction|Отменить"
+
+msgid "Changelog"
+msgstr "Журнал изменений"
+
+msgid "Charts"
+msgstr "Графики"
+
+msgid "Cherry-pick this commit"
+msgstr "Подобрать в этом коммите"
+
+msgid "Cherry-pick this merge request"
+msgstr "Побрать в этом запросе на слияние"
+
+msgid "CiStatusLabel|canceled"
+msgstr "CiStatusLabel|отменено"
+
+msgid "CiStatusLabel|created"
+msgstr "CiStatusLabel|создано"
+
+msgid "CiStatusLabel|failed"
+msgstr "CiStatusLabel|неудачно"
+
+msgid "CiStatusLabel|manual action"
+msgstr "CiStatusLabel|ручное действие"
+
+msgid "CiStatusLabel|passed"
+msgstr "CiStatusLabel|пройдено"
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr "CiStatusLabel|пройдено с предупреждениями"
+
+msgid "CiStatusLabel|pending"
+msgstr "CiStatusLabel|в ожидании"
+
+msgid "CiStatusLabel|skipped"
+msgstr "CiStatusLabel|пропущено"
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr "CiStatusLabel|ожидание ручных действий"
+
+msgid "CiStatusText|blocked"
+msgstr "CiStatusText|блокировано"
+
+msgid "CiStatusText|canceled"
+msgstr "CiStatusText|отменено"
+
+msgid "CiStatusText|created"
+msgstr "CiStatusText|создано"
+
+msgid "CiStatusText|failed"
+msgstr "CiStatusText|неудачно"
+
+msgid "CiStatusText|manual"
+msgstr "CiStatusText|ручное"
+
+msgid "CiStatusText|passed"
+msgstr "CiStatusText|пройдено"
+
+msgid "CiStatusText|pending"
+msgstr "CiStatusText|в ожидании"
+
+msgid "CiStatusText|skipped"
+msgstr "CiStatusText|пропущено"
+
+msgid "CiStatus|running"
+msgstr "CiStatus|выполняется"
+
+msgid "Commit"
+msgid_plural "Commits"
+msgstr[0] "Коммит"
+msgstr[1] "Коммиты"
+msgstr[2] "Коммиты"
+
+msgid "Commit duration in minutes for last 30 commits"
+msgstr ""
+
+msgid "Commit message"
+msgstr "Описание коммита"
+
+msgid "CommitBoxTitle|Commit"
+msgstr "CommitBoxTitle|Коммит"
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr "CommitMessage|Добавить %{file_name}"
+
+msgid "Commits"
+msgstr "Коммиты"
+
+msgid "Commits feed"
+msgstr ""
+
+msgid "Commits|History"
+msgstr "Commits|История"
+
+msgid "Committed by"
+msgstr "Коммит"
+
+msgid "Compare"
+msgstr "Сравнение"
+
+msgid "Contribution guide"
+msgstr "Руководство контрибьютора"
+
+msgid "Contributors"
+msgstr "Контрибьюторы"
+
+msgid "Copy URL to clipboard"
+msgstr "Копировать URL в буфер обмена"
+
+msgid "Copy commit SHA to clipboard"
+msgstr "Копировать SHA коммита в буфер обмена"
+
+msgid "Create New Directory"
+msgstr "Создать новую директорию"
+
+msgid ""
+"Create a personal access token on your account to pull or push via "
+"%{protocol}."
+msgstr ""
+
+msgid "Create directory"
+msgstr "Создать директорию"
+
+msgid "Create empty bare repository"
+msgstr "Создать пустой пустой репозиторий"
+
+msgid "Create merge request"
+msgstr "Создать запрос на объединение"
+
+msgid "Create new..."
+msgstr "Новый"
+
+msgid "CreateNewFork|Fork"
+msgstr "CreateNewFork|Форк"
+
+msgid "CreateTag|Tag"
+msgstr "CreateTag|Тэг"
+
+msgid "CreateTokenToCloneLink|create a personal access token"
+msgstr ""
+
+msgid "Cron Timezone"
+msgstr "Временная зона Cron"
+
+msgid "Cron syntax"
+msgstr "Синтаксис Cron"
+
+msgid "Custom notification events"
+msgstr " Настраиваемые уведомления о событиях"
+
+msgid ""
+"Custom notification levels are the same as participating levels. With custom "
+"notification levels you will also receive notifications for select events. "
+"To find out more, check out %{notification_link}."
+msgstr ""
+"Настраиваемые уровни уведомлений аналогичны уровню уведомлений в "
+"соответствии с участием. С настраиваемыми уровнями уведомлений вы также "
+"будете получать уведомления о выбранных событиях. Чтобы узнать больше, "
+"посмотрите %{notification_link}."
+
+msgid "Cycle Analytics"
+msgstr "Аналитика цикла разработки"
+
+msgid ""
+"Cycle Analytics gives an overview of how much time it takes to go from idea "
+"to production in your project."
+msgstr ""
+
+msgid "CycleAnalyticsStage|Code"
+msgstr "CycleAnalyticsStage|Код"
+
+msgid "CycleAnalyticsStage|Issue"
+msgstr "CycleAnalyticsStage|Обращение"
+
+msgid "CycleAnalyticsStage|Plan"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Production"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Review"
+msgstr "CycleAnalyticsStage|Ревьюв"
+
+msgid "CycleAnalyticsStage|Staging"
+msgstr ""
+
+msgid "CycleAnalyticsStage|Test"
+msgstr ""
+
+msgid "Define a custom pattern with cron syntax"
+msgstr "Определить настраиваемый шаблон с синтаксисом cron"
+
+msgid "Delete"
+msgstr "Удалить"
+
+msgid "Deploy"
+msgid_plural "Deploys"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Description"
+msgstr "Описание"
+
+msgid "Directory name"
+msgstr "Наименование директории"
+
+msgid "Don't show again"
+msgstr "Не показывать снова"
+
+msgid "Download"
+msgstr "Загрузить"
+
+msgid "Download tar"
+msgstr "Загрузить tar"
+
+msgid "Download tar.bz2"
+msgstr "Загрузить tar.bz2"
+
+msgid "Download tar.gz"
+msgstr "Загрузить tar.gz"
+
+msgid "Download zip"
+msgstr "Загрузить zip"
+
+msgid "DownloadArtifacts|Download"
+msgstr "DownloadArtifacts|Загрузка"
+
+msgid "DownloadCommit|Email Patches"
+msgstr "DownloadCommit|Email-патчи"
+
+msgid "DownloadCommit|Plain Diff"
+msgstr "DownloadCommit|Plain Diff"
+
+msgid "DownloadSource|Download"
+msgstr "DownloadSource|Загрузка"
+
+msgid "Edit"
+msgstr "Редактировать"
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr "Изменить расписание конвейера %{id}"
+
+msgid "Every day (at 4:00am)"
+msgstr "Ежедневно (в 4:00)"
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr "Ежемесячно (каждое 1-е число в 4:00)"
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr "Еженедельно (по воскресениями в 4:00)"
+
+msgid "Failed to change the owner"
+msgstr "Не удалось изменить владельца"
+
+msgid "Failed to remove the pipeline schedule"
+msgstr "Не удалось удалить расписание конвейера"
+
+msgid "Files"
+msgstr "Файлы"
+
+msgid "Filter by commit message"
+msgstr "Фильтр по комментариями к коммитам"
+
+msgid "Find by path"
+msgstr "Поиск по пути"
+
+msgid "Find file"
+msgstr "Найти файл"
+
+msgid "FirstPushedBy|First"
+msgstr ""
+
+msgid "FirstPushedBy|pushed by"
+msgstr ""
+
+msgid "Fork"
+msgid_plural "Forks"
+msgstr[0] "Форк"
+msgstr[1] "Форки"
+msgstr[2] "Форки"
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr "ForkedFromProjectPath|Форк от "
+
+msgid "From issue creation until deploy to production"
+msgstr ""
+
+msgid "From merge request merge until deploy to production"
+msgstr ""
+
+msgid "Go to your fork"
+msgstr "Перейти к вашему форку"
+
+msgid "GoToYourFork|Fork"
+msgstr "GoToYourFork|Форк"
+
+msgid "Home"
+msgstr "Домашняя"
+
+msgid "Housekeeping successfully started"
+msgstr "Очистка успешно запущена"
+
+msgid "Import repository"
+msgstr "Импорт репозитория"
+
+msgid "Interval Pattern"
+msgstr "Шаблон интервала"
+
+msgid "Introducing Cycle Analytics"
+msgstr ""
+
+msgid "Jobs for last month"
+msgstr ""
+
+msgid "Jobs for last week"
+msgstr ""
+
+msgid "Jobs for last year"
+msgstr ""
+
+msgid "LFSStatus|Disabled"
+msgstr "LFSStatus|Отключено"
+
+msgid "LFSStatus|Enabled"
+msgstr "LFSStatus|Включено"
+
+msgid "Last %d day"
+msgid_plural "Last %d days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Last Pipeline"
+msgstr "Последний конвейер"
+
+msgid "Last Update"
+msgstr "Последнее обновление"
+
+msgid "Last commit"
+msgstr "Последний коммит"
+
+msgid "Learn more in the"
+msgstr "Узнайте больше в"
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr "Подробнее в|документации по расписаниям конвейеров"
+
+msgid "Leave group"
+msgstr "Покинуть группу"
+
+msgid "Leave project"
+msgstr "Покинуть проект"
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Median"
+msgstr "Медиана"
+
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr "MissingSSHKeyWarningLink|добавить ключ SSH"
+
+msgid "New Issue"
+msgid_plural "New Issues"
+msgstr[0] "Новое обращение"
+msgstr[1] "Новые обращения"
+msgstr[2] "Новые обращения"
+
+msgid "New Pipeline Schedule"
+msgstr "Новое расписание конвейера"
+
+msgid "New branch"
+msgstr "Новая ветка"
+
+msgid "New directory"
+msgstr "Новая директория"
+
+msgid "New file"
+msgstr "Новый файл"
+
+msgid "New issue"
+msgstr "Новое обращение"
+
+msgid "New merge request"
+msgstr "Новый запрос на объединение"
+
+msgid "New schedule"
+msgstr "Новое расписание"
+
+msgid "New snippet"
+msgstr "Новый сниппет"
+
+msgid "New tag"
+msgstr "Новый тэг"
+
+msgid "No repository"
+msgstr "Нет репозитория"
+
+msgid "No schedules"
+msgstr "Нет расписания"
+
+msgid "Not available"
+msgstr ""
+
+msgid "Not enough data"
+msgstr ""
+
+msgid "Notification events"
+msgstr "Уведомления о событиях"
+
+msgid "NotificationEvent|Close issue"
+msgstr "NotificationEvent|Обращение закрыто"
+
+msgid "NotificationEvent|Close merge request"
+msgstr "Запрос на объединение закрыт"
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr "NotificationEvent|Неудача в конвейере"
+
+msgid "NotificationEvent|Merge merge request"
+msgstr "NotificationEvent|Объединить запрос на слияние"
+
+msgid "NotificationEvent|New issue"
+msgstr "NotificationEvent|Новое обращение"
+
+msgid "NotificationEvent|New merge request"
+msgstr "NotificationEvent|Новый запрос на слияние"
+
+msgid "NotificationEvent|New note"
+msgstr "NotificationEvent|Новая заметка"
+
+msgid "NotificationEvent|Reassign issue"
+msgstr "NotificationEvent|Переназначить обращение"
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr "NotificationEvent|Переназначить запрос на слияние"
+
+msgid "NotificationEvent|Reopen issue"
+msgstr "NotificationEvent|Переоткрыть обращение"
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr "NotificationEvent|Успешно в конвейере"
+
+msgid "NotificationLevel|Custom"
+msgstr "NotificationLevel|Настраиваемый"
+
+msgid "NotificationLevel|Disabled"
+msgstr "NotificationLevel|Отключено"
+
+msgid "NotificationLevel|Global"
+msgstr "NotificationLevel|Глобальный"
+
+msgid "NotificationLevel|On mention"
+msgstr "NotificationLevel|С упоминанием"
+
+msgid "NotificationLevel|Participate"
+msgstr "NotificationLevel|По участию"
+
+msgid "NotificationLevel|Watch"
+msgstr "NotificationLevel|Отслеживать"
+
+msgid "OfSearchInADropdown|Filter"
+msgstr "OfSearchInADropdown|Фильтр"
+
+msgid "OpenedNDaysAgo|Opened"
+msgstr "OpenedNDaysAgo|Открыто"
+
+msgid "Options"
+msgstr "Настройки"
+
+msgid "Owner"
+msgstr "Владелец"
+
+msgid "Pipeline"
+msgstr "Конвейер"
+
+msgid "Pipeline Health"
+msgstr ""
+
+msgid "Pipeline Schedule"
+msgstr "Расписание конвейера"
+
+msgid "Pipeline Schedules"
+msgstr "Расписания конвейеров"
+
+msgid "PipelineCharts|Failed:"
+msgstr ""
+
+msgid "PipelineCharts|Overall statistics"
+msgstr ""
+
+msgid "PipelineCharts|Success ratio:"
+msgstr ""
+
+msgid "PipelineCharts|Successful:"
+msgstr ""
+
+msgid "PipelineCharts|Total:"
+msgstr ""
+
+msgid "PipelineSchedules|Activated"
+msgstr "PipelineSchedules|Активировано"
+
+msgid "PipelineSchedules|Active"
+msgstr "PipelineSchedules|Активно"
+
+msgid "PipelineSchedules|All"
+msgstr "PipelineSchedules|Все"
+
+msgid "PipelineSchedules|Inactive"
+msgstr "PipelineSchedules|Неактивно"
+
+msgid "PipelineSchedules|Next Run"
+msgstr "PipelineSchedules|Следующий запуск"
+
+msgid "PipelineSchedules|None"
+msgstr "PipelineSchedules|None"
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr "PipelineSchedules|Предоставьте краткое описание этого конвейера"
+
+msgid "PipelineSchedules|Take ownership"
+msgstr "PipelineSchedules|Стать владельцем"
+
+msgid "PipelineSchedules|Target"
+msgstr "PipelineSchedules|Цель"
+
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr "PipelineSheduleIntervalPattern|Настраиваемый"
+
+msgid "Pipelines"
+msgstr ""
+
+msgid "Pipelines charts"
+msgstr ""
+
+msgid "Pipeline|all"
+msgstr ""
+
+msgid "Pipeline|success"
+msgstr ""
+
+msgid "Pipeline|with stage"
+msgstr "Pipeline|со стадией"
+
+msgid "Pipeline|with stages"
+msgstr "Pipeline|со стадиями"
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr "Проект '%{project_name}' добавлен в очередь на удаление."
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr "Проект '%{project_name}' успешно создан."
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr "Проект '%{project_name}' успешно обновлен."
+
+msgid "Project '%{project_name}' will be deleted."
+msgstr "Проект '%{project_name}' удален."
+
+msgid "Project access must be granted explicitly to each user."
+msgstr "Доступ к проекту должен предоставляться явно каждому пользователю."
+
+msgid "Project export could not be deleted."
+msgstr "Невозможно удалить экспорт проекта."
+
+msgid "Project export has been deleted."
+msgstr "Экспорт проекта удален."
+
+msgid ""
+"Project export link has expired. Please generate a new export from your "
+"project settings."
+msgstr ""
+"Истек срок действия ссылки на проект. Создайте новый экспорт в ваших "
+"настройках проекта."
+
+msgid "Project export started. A download link will be sent by email."
+msgstr ""
+"Начат экспорт проекта. Ссылка для скачивания будет отправлена по электронной "
+"почте."
+
+msgid "Project home"
+msgstr "Домашняя страница проекта"
+
+msgid "ProjectFeature|Disabled"
+msgstr "ProjectFeature|Отключено"
+
+msgid "ProjectFeature|Everyone with access"
+msgstr "ProjectFeature|Все с доступом"
+
+msgid "ProjectFeature|Only team members"
+msgstr "ProjectFeature|Только члены команды"
+
+msgid "ProjectFileTree|Name"
+msgstr "ProjectFileTree|Имя"
+
+msgid "ProjectLastActivity|Never"
+msgstr "ProjectLastActivity|Никогда"
+
+msgid "ProjectLifecycle|Stage"
+msgstr ""
+
+msgid "ProjectNetworkGraph|Graph"
+msgstr "ProjectNetworkGraph|Граф"
+
+msgid "Read more"
+msgstr ""
+
+msgid "Readme"
+msgstr "Readme"
+
+msgid "RefSwitcher|Branches"
+msgstr "RefSwitcher|Ветки"
+
+msgid "RefSwitcher|Tags"
+msgstr "RefSwitcher|Тэги"
+
+msgid "Related Commits"
+msgstr ""
+
+msgid "Related Deployed Jobs"
+msgstr ""
+
+msgid "Related Issues"
+msgstr ""
+
+msgid "Related Jobs"
+msgstr ""
+
+msgid "Related Merge Requests"
+msgstr "Связанные запросы на слияние"
+
+msgid "Related Merged Requests"
+msgstr "Связанные объединенные запросы"
+
+msgid "Remind later"
+msgstr "Напомнить позже"
+
+msgid "Remove project"
+msgstr "Удалить проект"
+
+msgid "Request Access"
+msgstr "Запрос доступа"
+
+msgid "Revert this commit"
+msgstr "Отменить это изменение"
+
+msgid "Revert this merge request"
+msgstr "Отменить этот запрос на слияние"
+
+msgid "Save pipeline schedule"
+msgstr "Сохранить расписание конвейра"
+
+msgid "Schedule a new pipeline"
+msgstr "Расписание нового конвейера"
+
+msgid "Scheduling Pipelines"
+msgstr "Планирование конвейеров"
+
+msgid "Search branches and tags"
+msgstr "Найти ветки и тэги"
+
+msgid "Select Archive Format"
+msgstr "Выбрать формат архива"
+
+msgid "Select a timezone"
+msgstr "Выбор временной зоны"
+
+msgid "Select target branch"
+msgstr "Выбор целевой ветки"
+
+msgid "Set a password on your account to pull or push via %{protocol}."
+msgstr ""
+"Установите пароль в своем аккаунте, чтобы отправлять или получать код через "
+"%{protocol}."
+
+msgid "Set up CI"
+msgstr "Настройка CI"
+
+msgid "Set up Koding"
+msgstr "Настройка Koding"
+
+msgid "Set up auto deploy"
+msgstr "Настройка автоматического развертывания"
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr "SetPasswordToCloneLink|установить пароль"
+
+msgid "Showing %d event"
+msgid_plural "Showing %d events"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Source code"
+msgstr "Исходный код"
+
+msgid "StarProject|Star"
+msgstr "StarProject|Отметить"
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr "Начать %{new_merge_request} с этих изменений"
+
+msgid "Switch branch/tag"
+msgstr "Переключить ветка/тэг"
+
+msgid "Tag"
+msgid_plural "Tags"
+msgstr[0] "Тэг"
+msgstr[1] "Тэги"
+msgstr[2] "Тэги"
+
+msgid "Tags"
+msgstr "Тэги"
+
+msgid "Target Branch"
+msgstr "Целевая ветка"
+
+msgid ""
+"The coding stage shows the time from the first commit to creating the merge "
+"request. The data will automatically be added here once you create your "
+"first merge request."
+msgstr ""
+
+msgid "The collection of events added to the data gathered for that stage."
+msgstr ""
+
+msgid "The fork relationship has been removed."
+msgstr "Связь форка удалена."
+
+msgid ""
+"The issue stage shows the time it takes from creating an issue to assigning "
+"the issue to a milestone, or add the issue to a list on your Issue Board. "
+"Begin creating issues to see data for this stage."
+msgstr ""
+"Стадия обращения время, которое потребуется с момента создания обращения до "
+"назначения обращению вехи, или добавления обращения в вашу доску обращений. "
+"Начните создавать обращения, чтобы увидеть сведения для этой стадии. "
+
+msgid "The phase of the development lifecycle."
+msgstr "Фаза жизненного цикла разработки."
+
+msgid ""
+"The pipelines schedule runs pipelines in the future, repeatedly, for "
+"specific branches or tags. Those scheduled pipelines will inherit limited "
+"project access based on their associated user."
+msgstr ""
+"Расписание конвейеров запускает в будущем неоднократно конвейеры, для "
+"определенных ветвей или тэгов. Запланированные конвейеры наследуют "
+"ограничения на доступ к проекту на основе связанного с ними пользователя."
+
+msgid ""
+"The planning stage shows the time from the previous step to pushing your "
+"first commit. This time will be added automatically once you push your first "
+"commit."
+msgstr ""
+
+msgid ""
+"The production stage shows the total time it takes between creating an issue "
+"and deploying the code to production. The data will be automatically added "
+"once you have completed the full idea to production cycle."
+msgstr ""
+
+msgid "The project can be accessed by any logged in user."
+msgstr "Доступ к проекту возможен любым зарегистрированным пользователем."
+
+msgid "The project can be accessed without any authentication."
+msgstr "Доступ к проекту возможен без какой-либо проверки подлинности."
+
+msgid "The repository for this project does not exist."
+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 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 ""
+
+msgid ""
+"The testing stage shows the time GitLab CI takes to run every pipeline for "
+"the related merge request. The data will automatically be added after your "
+"first pipeline finishes running."
+msgstr ""
+
+msgid "The time taken by each data entry gathered by that stage."
+msgstr ""
+
+msgid ""
+"The value lying at the midpoint of a series of observed values. E.g., "
+"between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 ="
+" 6."
+msgstr ""
+
+msgid ""
+"This means you can not push code until you create an empty repository or "
+"import existing one."
+msgstr ""
+"Это означает, что вы не можете пушить код, пока не создадите пустой "
+"репозиторий или не импортируете существующий."
+
+msgid "Time before an issue gets scheduled"
+msgstr ""
+
+msgid "Time before an issue starts implementation"
+msgstr ""
+
+msgid "Time between merge request creation and merge/close"
+msgstr "Время между созданием запроса слияния и слиянием / закрытием"
+
+msgid "Time until first merge request"
+msgstr ""
+
+msgid "Timeago|%s days ago"
+msgstr "Timeago|%s дн(я|ей) назад"
+
+msgid "Timeago|%s days remaining"
+msgstr "Timeago|Осталось %s дн(я|ей)"
+
+msgid "Timeago|%s hours remaining"
+msgstr "Timeago|Осталось %s часов"
+
+msgid "Timeago|%s minutes ago"
+msgstr "Timeago|%s минут назад"
+
+msgid "Timeago|%s minutes remaining"
+msgstr "Timeago|Осталось %s минут(а|ы)"
+
+msgid "Timeago|%s months ago"
+msgstr "Timeago|%s минут(а|ы) назад"
+
+msgid "Timeago|%s months remaining"
+msgstr "Timeago|Осталось %s месяцев(а)"
+
+msgid "Timeago|%s seconds remaining"
+msgstr "Timeago|Осталось %s секунд(ы)"
+
+msgid "Timeago|%s weeks ago"
+msgstr "Timeago|%s недель(и) назад"
+
+msgid "Timeago|%s weeks remaining"
+msgstr "Timeago|Осталось %s недель(и)"
+
+msgid "Timeago|%s years ago"
+msgstr "Timeago|%s лет/года назад"
+
+msgid "Timeago|%s years remaining"
+msgstr "Timeago|Осталось %s лет/года"
+
+msgid "Timeago|1 day remaining"
+msgstr "Timeago|Остался день"
+
+msgid "Timeago|1 hour remaining"
+msgstr "Timeago|Остался час"
+
+msgid "Timeago|1 minute remaining"
+msgstr "Timeago|Осталась одна минута"
+
+msgid "Timeago|1 month remaining"
+msgstr "Timeago|Остался месяц"
+
+msgid "Timeago|1 week remaining"
+msgstr "Timeago|Осталась неделя"
+
+msgid "Timeago|1 year remaining"
+msgstr "Timeago|Остался год"
+
+msgid "Timeago|Past due"
+msgstr "Timeago|Просрочено"
+
+msgid "Timeago|a day ago"
+msgstr "Timeago|день назад"
+
+msgid "Timeago|a month ago"
+msgstr "Timeago|месяц назад"
+
+msgid "Timeago|a week ago"
+msgstr "Timeago|неделю назад"
+
+msgid "Timeago|a while"
+msgstr "Timeago|какое-то время"
+
+msgid "Timeago|a year ago"
+msgstr "Timeago|год назад"
+
+msgid "Timeago|about %s hours ago"
+msgstr "Timeago|около %s часов назад"
+
+msgid "Timeago|about a minute ago"
+msgstr "Timeago|около минуты назад"
+
+msgid "Timeago|about an hour ago"
+msgstr "Timeago|около часа назад"
+
+msgid "Timeago|in %s days"
+msgstr "Timeago|через %s дня(ей)"
+
+msgid "Timeago|in %s hours"
+msgstr "Timeago|через %s часа(ов)"
+
+msgid "Timeago|in %s minutes"
+msgstr "Timeago|через %s минут(ы)"
+
+msgid "Timeago|in %s months"
+msgstr "Timeago|через %s месяц(а|ев)"
+
+msgid "Timeago|in %s seconds"
+msgstr "Timeago|через %s секунд(ы)"
+
+msgid "Timeago|in %s weeks"
+msgstr "Timeago|через %s недели"
+
+msgid "Timeago|in %s years"
+msgstr "Timeago|через %s лет/года"
+
+msgid "Timeago|in 1 day"
+msgstr "Timeago|через день"
+
+msgid "Timeago|in 1 hour"
+msgstr "Timeago|через час"
+
+msgid "Timeago|in 1 minute"
+msgstr "Timeago|через минуту"
+
+msgid "Timeago|in 1 month"
+msgstr "Timeago|через месяц"
+
+msgid "Timeago|in 1 week"
+msgstr "Timeago|через неделю"
+
+msgid "Timeago|in 1 year"
+msgstr "Timeago|через год"
+
+msgid "Timeago|less than a minute ago"
+msgstr "Timeago|менее чем минуту назад"
+
+msgid "Time|hr"
+msgid_plural "Time|hrs"
+msgstr[0] "ч"
+msgstr[1] "ч"
+msgstr[2] "ч"
+
+msgid "Time|min"
+msgid_plural "Time|mins"
+msgstr[0] "мин"
+msgstr[1] "мин"
+msgstr[2] "мин"
+
+msgid "Time|s"
+msgstr "с"
+
+msgid "Total Time"
+msgstr "Общее время"
+
+msgid "Total test time for all commits/merges"
+msgstr ""
+
+msgid "Unstar"
+msgstr "Снять отметку"
+
+msgid "Upload New File"
+msgstr "Выгрузить новый файл"
+
+msgid "Upload file"
+msgstr "Выгрузить файл"
+
+msgid "UploadLink|click to upload"
+msgstr "UploadLink|кликните для выгрузки"
+
+msgid "Use your global notification setting"
+msgstr "Используются глобальный настройки уведомлений"
+
+msgid "View open merge request"
+msgstr "Просмотреть открытый запрос на слияние"
+
+msgid "VisibilityLevel|Internal"
+msgstr "VisibilityLevel|Ограниченный"
+
+msgid "VisibilityLevel|Private"
+msgstr "VisibilityLevel|Приватный"
+
+msgid "VisibilityLevel|Public"
+msgstr "VisibilityLevel|Публичный"
+
+msgid "Want to see the data? Please ask an administrator for access."
+msgstr ""
+
+msgid "We don't have enough data to show this stage."
+msgstr ""
+
+msgid "Withdraw Access Request"
+msgstr "Отменить запрос доступа"
+
+msgid ""
+"You are going to remove %{project_name_with_namespace}.\n"
+"Removed project CANNOT be restored!\n"
+"Are you ABSOLUTELY sure?"
+msgstr ""
+"Вы хотите удалить %{project_name_with_namespace}.\n"
+"Удаленный проект НЕ МОЖЕТ быть восстановлен!\n"
+"Вы АБСОЛЮТНО уверены?"
+
+msgid ""
+"You are going to remove the fork relationship to source project "
+"%{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr ""
+"Вы собираетесь удалить связь форка с исходным проектом "
+"%{forked_from_project}. Вы АБСОЛЮТНО уверены?"
+
+msgid ""
+"You are going to transfer %{project_name_with_namespace} to another owner. "
+"Are you ABSOLUTELY sure?"
+msgstr ""
+"Вы собираетесь передать проект %{project_name_with_namespace} другому "
+"владельцу. Вы АБСОЛЮТНО уверены?"
+
+msgid "You can only add files when you are on a branch"
+msgstr "Вы можете добавлять только файлы, когда находитесь в ветке"
+
+msgid "You have reached your project limit"
+msgstr "Вы достигли ограничения в вашем проекте"
+
+msgid "You must sign in to star a project"
+msgstr "Необходимо войти, чтобы оценить проект"
+
+msgid "You need permission."
+msgstr "Вам нужно разрешение."
+
+msgid "You will not get any notifications via email"
+msgstr "Вы не получите никаких уведомлений по электронной почте"
+
+msgid "You will only receive notifications for the events you choose"
+msgstr "Вы будете получать уведомления только о выбранных вами событиях"
+
+msgid ""
+"You will only receive notifications for threads you have participated in"
+msgstr ""
+"Вы будете получать уведомления только о тех тредах, в которых вы участвовали"
+
+msgid "You will receive notifications for any activity"
+msgstr "Вы будете получать уведомления о любых действиях"
+
+msgid ""
+"You will receive notifications only for comments in which you were "
+"@mentioned"
+msgstr ""
+"Вы будете получать уведомления только для комментариев, в которых вы были "
+"@упомянуты"
+
+msgid ""
+"You won't be able to pull or push project code via %{protocol} until you "
+"%{set_password_link} on your account"
+msgstr ""
+"Вы не сможете получать и отправлять код проекта через %{protocol} пока "
+"%{set_password_link} в ваш аккаунт"
+
+msgid ""
+"You won't be able to pull or push project code via SSH until you "
+"%{add_ssh_key_link} to your profile"
+msgstr ""
+"Вы не сможете получать и отправлять код проекта через SSH пока "
+"%{add_ssh_key_link} в ваш профиль."
+
+msgid "Your name"
+msgstr "Ваше имя"
+
+msgid "day"
+msgid_plural "days"
+msgstr[0] "день"
+msgstr[1] "дни"
+msgstr[2] "дни"
+
+msgid "new merge request"
+msgstr "новый запрос на слияние"
+
+msgid "notification emails"
+msgstr "email для уведомлений"
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] "источник"
+msgstr[1] "источники"
+msgstr[2] "источники"
+
diff --git a/locale/ru/gitlab.po.time_stamp b/locale/ru/gitlab.po.time_stamp
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/locale/ru/gitlab.po.time_stamp
diff --git a/locale/uk/gitlab.po b/locale/uk/gitlab.po
new file mode 100644
index 00000000000..59a7eb6e1b3
--- /dev/null
+++ b/locale/uk/gitlab.po
@@ -0,0 +1,1234 @@
+# Андрей Витюк <andruwa13@gmail.com>, 2017. #zanata
+# Huang Tao <htve@outlook.com>, 2017. #zanata
+msgid ""
+msgstr ""
+"Project-Id-Version: gitlab 1.0.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-06-28 13:32+0200\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"PO-Revision-Date: 2017-07-12 09:05-0400\n"
+"Last-Translator: Андрей Витюк <andruwa13@gmail.com>\n"
+"Language-Team: Ukrainian (https://translate.zanata.org/project/view/GitLab)\n"
+"Language: uk\n"
+"X-Generator: Zanata 3.9.6\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+msgid "%s additional commit has been omitted to prevent performance issues."
+msgid_plural ""
+"%s additional commits have been omitted to prevent performance issues."
+msgstr[0] ""
+"%s доданий Комміт був виключений для запобігання проблем з продуктивністю."
+msgstr[1] ""
+"%s доданих коммітів були виключені для запобігання проблем з продуктивністю."
+msgstr[2] ""
+"%s доданих коммітів були виключені для запобігання проблем з продуктивністю."
+
+msgid "%d commit"
+msgid_plural "%d commits"
+msgstr[0] "%d комміт"
+msgstr[1] "%d комміта"
+msgstr[2] "%d коммітів"
+
+msgid "%{commit_author_link} committed %{commit_timeago}"
+msgstr "%{commit_author_link} комміт %{commit_timeago}"
+
+msgid "1 pipeline"
+msgid_plural "%d pipelines"
+msgstr[0] "1 конвеєр"
+msgstr[1] "%d конвеєра"
+msgstr[2] "%d конвеєрів"
+
+msgid "A collection of graphs regarding Continuous Integration"
+msgstr "Це набір графічних елементів для безперервної інтеграції"
+
+msgid "About auto deploy"
+msgstr "Про авто розгортання"
+
+msgid "Active"
+msgstr "Активний"
+
+msgid "Activity"
+msgstr "Активність"
+
+msgid "Add Changelog"
+msgstr "Додати список змін (Changelog)"
+
+msgid "Add Contribution guide"
+msgstr "Додати керівництво для контрибуторів"
+
+msgid "Add License"
+msgstr "Додати ліцензію"
+
+msgid "Add an SSH key to your profile to pull or push via SSH."
+msgstr ""
+"Додати SSH ключа в свій профіль, щоб мати можливість завантажити чи "
+"надіслати зміни через SSH."
+
+msgid "Add new directory"
+msgstr "Додати новий каталог"
+
+msgid "Archived project! Repository is read-only"
+msgstr "Заархівований проект! Репозиторій доступний лише для читання"
+
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr "Ви впевнені, що хочете видалити цей розклад для Конвеєра?"
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr "Прикріпити файл за допомогою перетягування або %{upload_link}"
+
+msgid "Branch"
+msgid_plural "Branches"
+msgstr[0] "Гілка"
+msgstr[1] "Гілки"
+msgstr[2] "Гілок"
+
+msgid ""
+"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, "
+"choose a GitLab CI Yaml template and commit your changes. "
+"%{link_to_autodeploy_doc}"
+msgstr ""
+"Гілка <strong>%{branch_name}</strong> створена. Для настройки автоматичного "
+"розгортання виберіть GitLab CI Yaml-шаблон і закоммітьте зміни. "
+"%{link_to_autodeploy_doc}"
+
+msgid "BranchSwitcherPlaceholder|Search branches"
+msgstr "Пошук гілок"
+
+msgid "BranchSwitcherTitle|Switch branch"
+msgstr "Переключити гілку"
+
+msgid "Branches"
+msgstr "Гілки"
+
+msgid "Browse Directory"
+msgstr "Переглянути каталог"
+
+msgid "Browse File"
+msgstr "Переглянути файл"
+
+msgid "Browse Files"
+msgstr "Перегляд файлів"
+
+msgid "Browse files"
+msgstr "Перегляд файлів"
+
+msgid "ByAuthor|by"
+msgstr "від"
+
+msgid "CI configuration"
+msgstr "Налаштування CI"
+
+msgid "Cancel"
+msgstr "Скасувати"
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr "Вибрати в гілці"
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr "Скасувати у гілці"
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr "Cherry-pick"
+
+msgid "ChangeTypeAction|Revert"
+msgstr "Скасувати"
+
+msgid "Changelog"
+msgstr "Список змін (Changelog)"
+
+msgid "Charts"
+msgstr "Графіки"
+
+msgid "Cherry-pick this commit"
+msgstr "Cherry-pick в цьому комміті"
+
+msgid "Cherry-pick this merge request"
+msgstr "Cherry-pick в цьому запиті на злиття"
+
+msgid "CiStatusLabel|canceled"
+msgstr "скасовано"
+
+msgid "CiStatusLabel|created"
+msgstr "створено"
+
+msgid "CiStatusLabel|failed"
+msgstr "невдало"
+
+msgid "CiStatusLabel|manual action"
+msgstr "вручну"
+
+msgid "CiStatusLabel|passed"
+msgstr "виконано"
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr "виконано з попередженнями"
+
+msgid "CiStatusLabel|pending"
+msgstr "в очікуванні"
+
+msgid "CiStatusLabel|skipped"
+msgstr "пропущено"
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr "Очікування ручних дій"
+
+msgid "CiStatusText|blocked"
+msgstr "заблоковано"
+
+msgid "CiStatusText|canceled"
+msgstr "скасовано"
+
+msgid "CiStatusText|created"
+msgstr "створено"
+
+msgid "CiStatusText|failed"
+msgstr "невдало"
+
+msgid "CiStatusText|manual"
+msgstr "вручну"
+
+msgid "CiStatusText|passed"
+msgstr "виконано"
+
+msgid "CiStatusText|pending"
+msgstr "в очікуванні"
+
+msgid "CiStatusText|skipped"
+msgstr "пропущено"
+
+msgid "CiStatus|running"
+msgstr "виконується"
+
+msgid "Commit"
+msgid_plural "Commits"
+msgstr[0] "Комміт"
+msgstr[1] "Комміта"
+msgstr[2] "Коммітів"
+
+msgid "Commit duration in minutes for last 30 commits"
+msgstr "Комміт тривалість у хвилинах за останні 30 коммітів"
+
+msgid "Commit message"
+msgstr "Комміт повідомлення"
+
+msgid "CommitBoxTitle|Commit"
+msgstr "Комміт"
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr "Додати %{file_name}"
+
+msgid "Commits"
+msgstr "Комміти"
+
+msgid "Commits feed"
+msgstr "Канал коммітів"
+
+msgid "Commits|History"
+msgstr "Історія"
+
+msgid "Committed by"
+msgstr "Комміт від"
+
+msgid "Compare"
+msgstr "Порівняти"
+
+msgid "Contribution guide"
+msgstr "Керівництво контрибуторів"
+
+msgid "Contributors"
+msgstr "Контрибутори"
+
+msgid "Copy URL to clipboard"
+msgstr "Скопіювати URL в буфер обміну"
+
+msgid "Copy commit SHA to clipboard"
+msgstr "Скопіювати ідентифікатор в буфер обміну"
+
+msgid "Create New Directory"
+msgstr "Створити новий каталог"
+
+msgid ""
+"Create a personal access token on your account to pull or push via "
+"%{protocol}."
+msgstr ""
+"Створити токен доступу для вашого аккауета, щоб відправляти або отримувати "
+"через %{protocol}."
+
+msgid "Create directory"
+msgstr "Створити каталог"
+
+msgid "Create empty bare repository"
+msgstr "Створити порожній репозиторій"
+
+msgid "Create merge request"
+msgstr "Створити запит на злиття"
+
+msgid "Create new..."
+msgstr "Створити..."
+
+msgid "CreateNewFork|Fork"
+msgstr "Форк"
+
+msgid "CreateTag|Tag"
+msgstr "Тег"
+
+msgid "CreateTokenToCloneLink|create a personal access token"
+msgstr "Створити токен для особистого доступу"
+
+msgid "Cron Timezone"
+msgstr "Часовий пояс Cron"
+
+msgid "Cron syntax"
+msgstr "Синтаксис Cron"
+
+msgid "Custom notification events"
+msgstr "Користувацькі налаштування повідомлень про події"
+
+msgid ""
+"Custom notification levels are the same as participating levels. With custom "
+"notification levels you will also receive notifications for select events. "
+"To find out more, check out %{notification_link}."
+msgstr ""
+"Спеціальні рівні повідомлення співпадають з рівнем участі. За допомогою "
+"спеціальних рівнів сповіщень ви також отримуватимете сповіщення про вибрані "
+"події. Щоб дізнатись більше, перегляньте %{notification_link}."
+
+msgid "Cycle Analytics"
+msgstr "Аналіз циклу"
+
+msgid ""
+"Cycle Analytics gives an overview of how much time it takes to go from idea "
+"to production in your project."
+msgstr ""
+"Аналітика циклу дає огляд того, скільки часу потрібно, щоб перейти від ідеї "
+"до виробництва у вашому проекті."
+
+msgid "CycleAnalyticsStage|Code"
+msgstr "Код"
+
+msgid "CycleAnalyticsStage|Issue"
+msgstr "Проблема"
+
+msgid "CycleAnalyticsStage|Plan"
+msgstr "Планування"
+
+msgid "CycleAnalyticsStage|Production"
+msgstr "ПРОД"
+
+msgid "CycleAnalyticsStage|Review"
+msgstr "Затвердження"
+
+msgid "CycleAnalyticsStage|Staging"
+msgstr "ДЕВ"
+
+msgid "CycleAnalyticsStage|Test"
+msgstr "Тестування"
+
+msgid "Define a custom pattern with cron syntax"
+msgstr "Визначте власний шаблон за допомогою синтаксису cron"
+
+msgid "Delete"
+msgstr "Видалити"
+
+msgid "Deploy"
+msgid_plural "Deploys"
+msgstr[0] "Розгортання"
+msgstr[1] "Розгортання"
+msgstr[2] "Розгортань"
+
+msgid "Description"
+msgstr "Опис"
+
+msgid "Directory name"
+msgstr "Ім'я каталогу"
+
+msgid "Don't show again"
+msgstr "Не показувати знову"
+
+msgid "Download"
+msgstr "Завантажити"
+
+msgid "Download tar"
+msgstr "Завантажити в форматі tar"
+
+msgid "Download tar.bz2"
+msgstr "Завантажити в форматі tar.bz2"
+
+msgid "Download tar.gz"
+msgstr "Завантажити в форматі tar.gz"
+
+msgid "Download zip"
+msgstr "Завантажити в форматі zip"
+
+msgid "DownloadArtifacts|Download"
+msgstr "Завантажити"
+
+msgid "DownloadCommit|Email Patches"
+msgstr "Email-патчи"
+
+msgid "DownloadCommit|Plain Diff"
+msgstr "Plain Diff"
+
+msgid "DownloadSource|Download"
+msgstr "Завантажити"
+
+msgid "Edit"
+msgstr "Редагувати"
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr "Редагувати Розклад Конвеєра % {id}"
+
+msgid "Every day (at 4:00am)"
+msgstr "Кожен день (в 4:00 ранку)"
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr "Кожен місяць (1-го числа о 4:00 ранку)"
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr "Щотижня (в неділю о 4:00 ранку)"
+
+msgid "Failed to change the owner"
+msgstr "Не вдалося змінити власника"
+
+msgid "Failed to remove the pipeline schedule"
+msgstr "Не вдалося видалити розклад Конвеєра"
+
+msgid "Files"
+msgstr "Файли"
+
+msgid "Filter by commit message"
+msgstr "Фільтрувати повідомлення коммітів"
+
+msgid "Find by path"
+msgstr "Пошук по шляху"
+
+msgid "Find file"
+msgstr "Знайти файл"
+
+msgid "FirstPushedBy|First"
+msgstr "Перший"
+
+msgid "FirstPushedBy|pushed by"
+msgstr "Надіслані зміни від"
+
+msgid "Fork"
+msgid_plural "Forks"
+msgstr[0] "Форк"
+msgstr[1] "Форки"
+msgstr[2] "Форків"
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr "Форк від"
+
+msgid "From issue creation until deploy to production"
+msgstr "З моменту створення проблеми до розгортання на ПРОД"
+
+msgid "From merge request merge until deploy to production"
+msgstr "З об'єднання запиту злиття до розгортання на ПРОД"
+
+msgid "Go to your fork"
+msgstr "Перейти до вашого форку"
+
+msgid "GoToYourFork|Fork"
+msgstr "Форк"
+
+msgid "Home"
+msgstr "Початок"
+
+msgid "Housekeeping successfully started"
+msgstr "Очищення успішно розпочато"
+
+msgid "Import repository"
+msgstr "Імпорт репозеторія"
+
+msgid "Interval Pattern"
+msgstr "Шаблон інтервалу"
+
+msgid "Introducing Cycle Analytics"
+msgstr "Представляємо аналітику циклу"
+
+msgid "Jobs for last month"
+msgstr "Завдання за останній місяць"
+
+msgid "Jobs for last week"
+msgstr "Завдання за останній тиждень"
+
+msgid "Jobs for last year"
+msgstr "Завдання за останній рік"
+
+msgid "LFSStatus|Disabled"
+msgstr "Вимкнено"
+
+msgid "LFSStatus|Enabled"
+msgstr "Увімкнено"
+
+msgid "Last %d day"
+msgid_plural "Last %d days"
+msgstr[0] "Останній %d день"
+msgstr[1] "Останніх %d дні"
+msgstr[2] "Останніх %d днів"
+
+msgid "Last Pipeline"
+msgstr "Останній Конвеєр"
+
+msgid "Last Update"
+msgstr "Останнє оновлення"
+
+msgid "Last commit"
+msgstr "Останній комміт"
+
+msgid "Learn more in the"
+msgstr "Дізнайтесь більше"
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr "Детальніше в документації по розкладами конвеєрів"
+
+msgid "Leave group"
+msgstr "Залишити групу"
+
+msgid "Leave project"
+msgstr "Залишити проект"
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Median"
+msgstr "Медіана"
+
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr "додати SSH ключ"
+
+msgid "New Issue"
+msgid_plural "New Issues"
+msgstr[0] "Нова проблема"
+msgstr[1] "Нові проблеми"
+msgstr[2] "Новах проблем"
+
+msgid "New Pipeline Schedule"
+msgstr "Новий розклад Конвеєра"
+
+msgid "New branch"
+msgstr "Нова гілка"
+
+msgid "New directory"
+msgstr "Новий каталог"
+
+msgid "New file"
+msgstr "Новий файл"
+
+msgid "New issue"
+msgstr "Нова проблема"
+
+msgid "New merge request"
+msgstr "Новий запит на злиття"
+
+msgid "New schedule"
+msgstr "Новий Розклад"
+
+msgid "New snippet"
+msgstr "Новий сніппет"
+
+msgid "New tag"
+msgstr "Новий тег"
+
+msgid "No repository"
+msgstr "Немає репозеторія"
+
+msgid "No schedules"
+msgstr "немає Розкладів"
+
+msgid "Not available"
+msgstr "Недоступний"
+
+msgid "Not enough data"
+msgstr "Недостатньо даних"
+
+msgid "Notification events"
+msgstr "Повідомлення про події"
+
+msgid "NotificationEvent|Close issue"
+msgstr "Проблема закрита"
+
+msgid "NotificationEvent|Close merge request"
+msgstr "Запит на об'єднання закритий"
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr "Невдача в конвеєрі"
+
+msgid "NotificationEvent|Merge merge request"
+msgstr "Об'єднати запит на злиття"
+
+msgid "NotificationEvent|New issue"
+msgstr "Нова проблема"
+
+msgid "NotificationEvent|New merge request"
+msgstr "Новий запит на злиття"
+
+msgid "NotificationEvent|New note"
+msgstr "Нова нотатка"
+
+msgid "NotificationEvent|Reassign issue"
+msgstr "Перепризначити проблему"
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr "Перепризначити запит на злиття"
+
+msgid "NotificationEvent|Reopen issue"
+msgstr "Повторне відкриття проблему"
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr "Успішно в Конвеєрі"
+
+msgid "NotificationLevel|Custom"
+msgstr "Власні"
+
+msgid "NotificationLevel|Disabled"
+msgstr "Вимкнено"
+
+msgid "NotificationLevel|Global"
+msgstr "Загальні"
+
+msgid "NotificationLevel|On mention"
+msgstr "Коли вас згадують"
+
+msgid "NotificationLevel|Participate"
+msgstr "Берете участь"
+
+msgid "NotificationLevel|Watch"
+msgstr "Відстежувати"
+
+msgid "OfSearchInADropdown|Filter"
+msgstr "Фільтр"
+
+msgid "OpenedNDaysAgo|Opened"
+msgstr "Відкрито"
+
+msgid "Options"
+msgstr "Параметри"
+
+msgid "Owner"
+msgstr "Власник"
+
+msgid "Pipeline"
+msgstr "Конвеєр"
+
+msgid "Pipeline Health"
+msgstr "Стан Конвеєра"
+
+msgid "Pipeline Schedule"
+msgstr "Розклад Конвеєра"
+
+msgid "Pipeline Schedules"
+msgstr "Розклади Конвеєрів"
+
+msgid "PipelineCharts|Failed:"
+msgstr "Не вдалося:"
+
+msgid "PipelineCharts|Overall statistics"
+msgstr "Загальна статистика"
+
+msgid "PipelineCharts|Success ratio:"
+msgstr "Коефіцієнт успіху:"
+
+msgid "PipelineCharts|Successful:"
+msgstr "Успішні:"
+
+msgid "PipelineCharts|Total:"
+msgstr "Всього:"
+
+msgid "PipelineSchedules|Activated"
+msgstr "Активовано"
+
+msgid "PipelineSchedules|Active"
+msgstr "Активні"
+
+msgid "PipelineSchedules|All"
+msgstr "Всі"
+
+msgid "PipelineSchedules|Inactive"
+msgstr "Неактивні"
+
+msgid "PipelineSchedules|Next Run"
+msgstr "Наступний запуск"
+
+msgid "PipelineSchedules|None"
+msgstr "Немає"
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr "Задайте короткий опис для цього Конвеєру"
+
+msgid "PipelineSchedules|Take ownership"
+msgstr "Стати власником"
+
+msgid "PipelineSchedules|Target"
+msgstr "Ціль"
+
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr "Власні"
+
+msgid "Pipelines"
+msgstr "Конвеєри"
+
+msgid "Pipelines charts"
+msgstr "Чарти Конвеєрів"
+
+msgid "Pipeline|all"
+msgstr "всі"
+
+msgid "Pipeline|success"
+msgstr "успіх"
+
+msgid "Pipeline|with stage"
+msgstr "зі стадією"
+
+msgid "Pipeline|with stages"
+msgstr "зі стадіями"
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr "Проект '%{project_name}' доданий в чергу на видалення."
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr "Проект '%{project_name}' успішно створений."
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr "Проект '%{project_name}' успішно оновлено."
+
+msgid "Project '%{project_name}' will be deleted."
+msgstr "Проект '%{project_name}' видалений."
+
+msgid "Project access must be granted explicitly to each user."
+msgstr "Доступ до проекту повинен надаватися кожному користувачеві."
+
+msgid "Project export could not be deleted."
+msgstr "Неможливо видалити експорт проекту."
+
+msgid "Project export has been deleted."
+msgstr "Експорт проекту видалений."
+
+msgid ""
+"Project export link has expired. Please generate a new export from your "
+"project settings."
+msgstr ""
+"Закінчився термін дії посилання на проект. Створіть новий експорт в ваших "
+"настройках проекту."
+
+msgid "Project export started. A download link will be sent by email."
+msgstr ""
+"Розпочато експорт проекту. Посилання для скачування буде надіслана "
+"електронною поштою."
+
+msgid "Project home"
+msgstr "Домашня сторінка проекту"
+
+msgid "ProjectFeature|Disabled"
+msgstr "Вимкнено"
+
+msgid "ProjectFeature|Everyone with access"
+msgstr "Все з доступом"
+
+msgid "ProjectFeature|Only team members"
+msgstr "Тільки члени команди"
+
+msgid "ProjectFileTree|Name"
+msgstr "Ім'я"
+
+msgid "ProjectLastActivity|Never"
+msgstr "Ніколи"
+
+msgid "ProjectLifecycle|Stage"
+msgstr "Етап"
+
+msgid "ProjectNetworkGraph|Graph"
+msgstr "Графік"
+
+msgid "Read more"
+msgstr "Докладніше"
+
+msgid "Readme"
+msgstr "Прочитай Мене"
+
+msgid "RefSwitcher|Branches"
+msgstr "Гілки"
+
+msgid "RefSwitcher|Tags"
+msgstr "Теги"
+
+msgid "Related Commits"
+msgstr "Пов'язані Комміти"
+
+msgid "Related Deployed Jobs"
+msgstr "Пов’язані розгорнуті задачі (Jobs)"
+
+msgid "Related Issues"
+msgstr "Пов’язані Проблеми (Issues)"
+
+msgid "Related Jobs"
+msgstr "Пов’язані Задачі (Jobs)"
+
+msgid "Related Merge Requests"
+msgstr "Пов'язані запити на злиття"
+
+msgid "Related Merged Requests"
+msgstr "Пов'язані об'єднані запити"
+
+msgid "Remind later"
+msgstr "Нагадати пізніше"
+
+msgid "Remove project"
+msgstr "Видалити проект"
+
+msgid "Request Access"
+msgstr "Запит доступу"
+
+msgid "Revert this commit"
+msgstr "Скасувати цей комміт"
+
+msgid "Revert this merge request"
+msgstr "Скасувати цей запит на злиття"
+
+msgid "Save pipeline schedule"
+msgstr "Зберегти Розклад Конвеєра"
+
+msgid "Schedule a new pipeline"
+msgstr "Розклад нового конвеєра"
+
+msgid "Scheduling Pipelines"
+msgstr "Планування конвеєрів"
+
+msgid "Search branches and tags"
+msgstr "Пошук гілок та тегів"
+
+msgid "Select Archive Format"
+msgstr "Виберіть формат архіву"
+
+msgid "Select a timezone"
+msgstr "Вибрати часовий пояс"
+
+msgid "Select target branch"
+msgstr "Вибір цільової гілки"
+
+msgid "Set a password on your account to pull or push via %{protocol}."
+msgstr ""
+"Встановіть пароль свого облікового запису, щоб відправляти або отримувати "
+"код через %{protocol}."
+
+msgid "Set up CI"
+msgstr "Налаштування CI"
+
+msgid "Set up Koding"
+msgstr "Налаштування Koding"
+
+msgid "Set up auto deploy"
+msgstr "Налаштування автоматичне розгортання"
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr "встановити пароль"
+
+msgid "Showing %d event"
+msgid_plural "Showing %d events"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Source code"
+msgstr "Код"
+
+msgid "StarProject|Star"
+msgstr "Старт"
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr "Почати %{new_merge_request} з цих змін"
+
+msgid "Switch branch/tag"
+msgstr "тег"
+
+msgid "Tag"
+msgid_plural "Tags"
+msgstr[0] "Тег"
+msgstr[1] "Теги"
+msgstr[2] "Тегів"
+
+msgid "Tags"
+msgstr "Теги"
+
+msgid "Target Branch"
+msgstr "Цільова гілка"
+
+msgid ""
+"The coding stage shows the time from the first commit to creating the merge "
+"request. The data will automatically be added here once you create your "
+"first merge request."
+msgstr ""
+
+msgid "The collection of events added to the data gathered for that stage."
+msgstr ""
+
+msgid "The fork relationship has been removed."
+msgstr "Зв'язок форка видалена."
+
+msgid ""
+"The issue stage shows the time it takes from creating an issue to assigning "
+"the issue to a milestone, or add the issue to a list on your Issue Board. "
+"Begin creating issues to see data for this stage."
+msgstr ""
+"Етап випуску показує, скільки часу потрібно від створення проблеми до "
+"присвоєння випуску, або додавання проблеми в вашу дошку проблем. Почніть "
+"створювати проблеми, щоб переглядати дані для цього етапу."
+
+msgid "The phase of the development lifecycle."
+msgstr "Фаза життєвого циклу розробки."
+
+msgid ""
+"The pipelines schedule runs pipelines in the future, repeatedly, for "
+"specific branches or tags. Those scheduled pipelines will inherit limited "
+"project access based on their associated user."
+msgstr ""
+"Розклад конвеєрів запускає в майбутньому конвеєри, для певних гілок або "
+"тегів. Заплановані конвеєри успадковують обмеження на доступ до проекту на "
+"основі пов'язаного з ними користувача."
+
+msgid ""
+"The planning stage shows the time from the previous step to pushing your "
+"first commit. This time will be added automatically once you push your first "
+"commit."
+msgstr ""
+
+msgid ""
+"The production stage shows the total time it takes between creating an issue "
+"and deploying the code to production. The data will be automatically added "
+"once you have completed the full idea to production cycle."
+msgstr ""
+
+msgid "The project can be accessed by any logged in user."
+msgstr "Доступ до проекту можливий будь-яким зареєстрованим користувачем."
+
+msgid "The project can be accessed without any authentication."
+msgstr "Доступ до проекту можливий без будь-якої перевірки автентичності."
+
+msgid "The repository for this project does not exist."
+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 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 ""
+
+msgid ""
+"The testing stage shows the time GitLab CI takes to run every pipeline for "
+"the related merge request. The data will automatically be added after your "
+"first pipeline finishes running."
+msgstr ""
+
+msgid "The time taken by each data entry gathered by that stage."
+msgstr ""
+
+msgid ""
+"The value lying at the midpoint of a series of observed values. E.g., "
+"between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 ="
+" 6."
+msgstr ""
+
+msgid ""
+"This means you can not push code until you create an empty repository or "
+"import existing one."
+msgstr ""
+"Це означає, що ви не можете відправляти код, поки не створите порожній "
+"репозиторій або НЕ імпортуєте існуючий."
+
+msgid "Time before an issue gets scheduled"
+msgstr ""
+
+msgid "Time before an issue starts implementation"
+msgstr ""
+
+msgid "Time between merge request creation and merge/close"
+msgstr "Час між створенням запиту злиття і злиттям або закриттям"
+
+msgid "Time until first merge request"
+msgstr "Час до першого запиту на злиття"
+
+msgid "Timeago|%s days ago"
+msgstr "%s днів тому"
+
+msgid "Timeago|%s days remaining"
+msgstr "%s днів, що залишилися"
+
+msgid "Timeago|%s hours remaining"
+msgstr "%s годин, що залишилися"
+
+msgid "Timeago|%s minutes ago"
+msgstr "%s хвилин тому"
+
+msgid "Timeago|%s minutes remaining"
+msgstr "%s хвилини залишитися"
+
+msgid "Timeago|%s months ago"
+msgstr "%s місяців тому"
+
+msgid "Timeago|%s months remaining"
+msgstr "%s місяці, що залишилися"
+
+msgid "Timeago|%s seconds remaining"
+msgstr "%s секунд, що залишаються"
+
+msgid "Timeago|%s weeks ago"
+msgstr "%s тижнів тому"
+
+msgid "Timeago|%s weeks remaining"
+msgstr "%s тижнів залишилися"
+
+msgid "Timeago|%s years ago"
+msgstr "%s років тому"
+
+msgid "Timeago|%s years remaining"
+msgstr "%s роки, що залишилися"
+
+msgid "Timeago|1 day remaining"
+msgstr "Залишився 1 день"
+
+msgid "Timeago|1 hour remaining"
+msgstr "Залишилась 1 година"
+
+msgid "Timeago|1 minute remaining"
+msgstr "Залишилась 1 хвилина"
+
+msgid "Timeago|1 month remaining"
+msgstr "Залишився 1 місяць"
+
+msgid "Timeago|1 week remaining"
+msgstr "Залишився 1 тиждень"
+
+msgid "Timeago|1 year remaining"
+msgstr "Залишився 1 рік"
+
+msgid "Timeago|Past due"
+msgstr "Прострочені"
+
+msgid "Timeago|a day ago"
+msgstr "годин тому"
+
+msgid "Timeago|a month ago"
+msgstr "місяць тому"
+
+msgid "Timeago|a week ago"
+msgstr "тиждень тому"
+
+msgid "Timeago|a while"
+msgstr "деякий час назад"
+
+msgid "Timeago|a year ago"
+msgstr "рік тому"
+
+msgid "Timeago|about %s hours ago"
+msgstr "Близько %s годин тому"
+
+msgid "Timeago|about a minute ago"
+msgstr "Близько хвилини тому"
+
+msgid "Timeago|about an hour ago"
+msgstr "Близько години тому"
+
+msgid "Timeago|in %s days"
+msgstr "через %s днїв"
+
+msgid "Timeago|in %s hours"
+msgstr "через %s години"
+
+msgid "Timeago|in %s minutes"
+msgstr "через %s хвилини"
+
+msgid "Timeago|in %s months"
+msgstr "через %s місяців"
+
+msgid "Timeago|in %s seconds"
+msgstr "через %s секунд"
+
+msgid "Timeago|in %s weeks"
+msgstr "через %s тижні"
+
+msgid "Timeago|in %s years"
+msgstr "через %s років"
+
+msgid "Timeago|in 1 day"
+msgstr "через день"
+
+msgid "Timeago|in 1 hour"
+msgstr "через годину"
+
+msgid "Timeago|in 1 minute"
+msgstr "через хвилину"
+
+msgid "Timeago|in 1 month"
+msgstr "через місяць"
+
+msgid "Timeago|in 1 week"
+msgstr "через тиждень"
+
+msgid "Timeago|in 1 year"
+msgstr "через рік"
+
+msgid "Timeago|less than a minute ago"
+msgstr "менш хвилини тому"
+
+msgid "Time|hr"
+msgid_plural "Time|hrs"
+msgstr[0] "Година"
+msgstr[1] "Годині"
+msgstr[2] "Годин"
+
+msgid "Time|min"
+msgid_plural "Time|mins"
+msgstr[0] "хвилина"
+msgstr[1] "хвилині"
+msgstr[2] "хвилин"
+
+msgid "Time|s"
+msgstr "секунда"
+
+msgid "Total Time"
+msgstr "Загальний час"
+
+msgid "Total test time for all commits/merges"
+msgstr "Загальний час, щоб перевірити всі фіксації/злиття"
+
+msgid "Unstar"
+msgstr "Зняти позначку"
+
+msgid "Upload New File"
+msgstr "Завантажити новий файл"
+
+msgid "Upload file"
+msgstr "Завантажити файл"
+
+msgid "UploadLink|click to upload"
+msgstr "Натисніть, щоб завантажити"
+
+msgid "Use your global notification setting"
+msgstr "Використовуються глобальний налаштування повідомлень"
+
+msgid "View open merge request"
+msgstr "Перегляд відкритих запитів на злиття"
+
+msgid "VisibilityLevel|Internal"
+msgstr "Внутрішній"
+
+msgid "VisibilityLevel|Private"
+msgstr "Приватний"
+
+msgid "VisibilityLevel|Public"
+msgstr "Публічний"
+
+msgid "Want to see the data? Please ask an administrator for access."
+msgstr "Хочете побачити дані? Будь ласка, попросить у адміністратора доступ."
+
+msgid "We don't have enough data to show this stage."
+msgstr "Ми не маємо достатньо даних для показу цього етапу."
+
+msgid "Withdraw Access Request"
+msgstr "Скасувати запит доступу"
+
+msgid ""
+"You are going to remove %{project_name_with_namespace}.\n"
+"Removed project CANNOT be restored!\n"
+"Are you ABSOLUTELY sure?"
+msgstr ""
+"Ви хочете видалити %{project_name_with_namespace}.\n"
+"Видалений проект НЕ МОЖЕ бути відновлений!\n"
+"Ви АБСОЛЮТНО впевнені?"
+
+msgid ""
+"You are going to remove the fork relationship to source project "
+"%{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr ""
+"Ви збираєтеся видалити зв'язок з форка з вихідним проектом "
+"%{forked_from_project}. Ви АБСОЛЮТНО впевнені?"
+
+msgid ""
+"You are going to transfer %{project_name_with_namespace} to another owner. "
+"Are you ABSOLUTELY sure?"
+msgstr ""
+"Ви збираєтеся передати проект %{project_name_with_namespace} іншому власнику."
+" Ви АБСОЛЮТНО впевнені?"
+
+msgid "You can only add files when you are on a branch"
+msgstr "Ви можете додавати тільки файли, коли перебуваєте в гілці"
+
+msgid "You have reached your project limit"
+msgstr "Ви досягли обмеження в вашому проекті"
+
+msgid "You must sign in to star a project"
+msgstr "Необхідно увійти, щоб оцінити проект"
+
+msgid "You need permission."
+msgstr "Вам потрібен дозвіл"
+
+msgid "You will not get any notifications via email"
+msgstr "Ви не отримаєте ніяких повідомлень по електронній пошті"
+
+msgid "You will only receive notifications for the events you choose"
+msgstr "Ви будете отримувати повідомлення тільки про обрані вами події"
+
+msgid ""
+"You will only receive notifications for threads you have participated in"
+msgstr ""
+"Ви будете отримувати повідомлення тільки про тих темах, в яких ви брали "
+"участь"
+
+msgid "You will receive notifications for any activity"
+msgstr "Ви будете отримувати повідомлення про будь-які дії"
+
+msgid ""
+"You will receive notifications only for comments in which you were "
+"@mentioned"
+msgstr ""
+"Ви будете отримувати повідомлення тільки для коментарів, в яких ви були "
+"@згадані"
+
+msgid ""
+"You won't be able to pull or push project code via %{protocol} until you "
+"%{set_password_link} on your account"
+msgstr ""
+"Ви не зможете отримувати і відправляти код проекту через %{protocol} поки "
+"%{set_password_link} в ваш аккаунт"
+
+msgid ""
+"You won't be able to pull or push project code via SSH until you "
+"%{add_ssh_key_link} to your profile"
+msgstr ""
+"Ви не зможете отримувати і відправляти код проекту через SSH поки "
+"%{add_ssh_key_link} в ваш профіль."
+
+msgid "Your name"
+msgstr "Ваше ім'я"
+
+msgid "day"
+msgid_plural "days"
+msgstr[0] "день"
+msgstr[1] "дні"
+msgstr[2] "днів"
+
+msgid "new merge request"
+msgstr "Новий запит на злиття"
+
+msgid "notification emails"
+msgstr "Повідомлення електронною поштою"
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] "джерело"
+msgstr[1] "джерела"
+msgstr[2] "джерел"
+
diff --git a/locale/uk/gitlab.po.time_stamp b/locale/uk/gitlab.po.time_stamp
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/locale/uk/gitlab.po.time_stamp
diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po
index b7a88aadeb9..47b72d7be1a 100644
--- a/locale/zh_CN/gitlab.po
+++ b/locale/zh_CN/gitlab.po
@@ -4,11 +4,11 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-06-28 13:32+0200\n"
+"POT-Creation-Date: 2017-07-05 08:50-0500\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2017-07-10 09:58-0400\n"
+"PO-Revision-Date: 2017-07-12 06:23-0400\n"
"Last-Translator: Huang Tao <htve@outlook.com>\n"
"Language-Team: Chinese (China) (https://translate.zanata.org/project/view/GitLab)\n"
"Language: zh-CN\n"
@@ -621,6 +621,12 @@ msgstr "所有"
msgid "PipelineSchedules|Inactive"
msgstr "未启用"
+msgid "PipelineSchedules|Input variable key"
+msgstr "输入变量名"
+
+msgid "PipelineSchedules|Input variable value"
+msgstr "输入变量值"
+
msgid "PipelineSchedules|Next Run"
msgstr "下次运行时间"
@@ -630,12 +636,18 @@ msgstr "无"
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "为此流水线提供简短描述"
+msgid "PipelineSchedules|Remove variable row"
+msgstr "删除变量"
+
msgid "PipelineSchedules|Take ownership"
-msgstr "取得所有者"
+msgstr "取得所有权"
msgid "PipelineSchedules|Target"
msgstr "目标"
+msgid "PipelineSchedules|Variables"
+msgstr "变量"
+
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "自定义"
@@ -1086,6 +1098,14 @@ msgid "Withdraw Access Request"
msgstr "取消权限申请"
msgid ""
+"You are going to remove %{group_name}.\n"
+"Removed groups CANNOT be restored!\n"
+"Are you ABSOLUTELY sure?"
+msgstr "即将删除 %{group_name}。\n"
+"已删除的群组无法恢复!\n"
+"确定继续吗?"
+
+msgid ""
"You are going to remove %{project_name_with_namespace}.\n"
"Removed project CANNOT be restored!\n"
"Are you ABSOLUTELY sure?"
diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po
index f6add31db99..8a4e6da4ea9 100644
--- a/locale/zh_HK/gitlab.po
+++ b/locale/zh_HK/gitlab.po
@@ -3,13 +3,13 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-06-28 13:32+0200\n"
+"POT-Creation-Date: 2017-07-05 08:50-0500\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2017-07-06 11:26-0400\n"
+"PO-Revision-Date: 2017-07-12 06:32-0400\n"
"Last-Translator: Huang Tao <htve@outlook.com>\n"
-"Language-Team: Chinese (Hong Kong SAR China)\n"
+"Language-Team: Chinese (Hong Kong SAR China) (https://translate.zanata.org/project/view/GitLab)\n"
"Language: zh-HK\n"
"X-Generator: Zanata 3.9.6\n"
"Plural-Forms: nplurals=1; plural=0\n"
@@ -620,6 +620,12 @@ msgstr "所有"
msgid "PipelineSchedules|Inactive"
msgstr "未啟用"
+msgid "PipelineSchedules|Input variable key"
+msgstr "輸入變量名"
+
+msgid "PipelineSchedules|Input variable value"
+msgstr "輸入變量值"
+
msgid "PipelineSchedules|Next Run"
msgstr "下次運行時間"
@@ -629,12 +635,18 @@ msgstr "無"
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "為此流水線提供簡短描述"
+msgid "PipelineSchedules|Remove variable row"
+msgstr "刪除變量"
+
msgid "PipelineSchedules|Take ownership"
-msgstr "取得所有者"
+msgstr "取得所有權"
msgid "PipelineSchedules|Target"
msgstr "目標"
+msgid "PipelineSchedules|Variables"
+msgstr "變量"
+
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "自定義"
@@ -1085,6 +1097,14 @@ msgid "Withdraw Access Request"
msgstr "取消權限申请"
msgid ""
+"You are going to remove %{group_name}.\n"
+"Removed groups CANNOT be restored!\n"
+"Are you ABSOLUTELY sure?"
+msgstr "即將刪除 %{group_name}。\n"
+"已刪除的群組無法恢復!\n"
+"確定繼續嗎?"
+
+msgid ""
"You are going to remove %{project_name_with_namespace}.\n"
"Removed project CANNOT be restored!\n"
"Are you ABSOLUTELY sure?"
diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po
index e61cf0e5152..05173ed12c0 100644
--- a/locale/zh_TW/gitlab.po
+++ b/locale/zh_TW/gitlab.po
@@ -7,13 +7,13 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-06-28 13:32+0200\n"
+"POT-Creation-Date: 2017-07-05 08:50-0500\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2017-07-11 09:10-0400\n"
-"Last-Translator: Lin Jen-Shin <godfat@godfat.org>\n"
"Language-Team: Chinese (Taiwan) (https://translate.zanata.org/project/view/GitLab)\n"
+"PO-Revision-Date: 2017-07-20 09:50-0400\n"
+"Last-Translator: Lin Jen-Shin <godfat@godfat.org>\n"
"Language: zh-TW\n"
"X-Generator: Zanata 3.9.6\n"
"Plural-Forms: nplurals=1; plural=0\n"
@@ -624,6 +624,12 @@ msgstr "所有"
msgid "PipelineSchedules|Inactive"
msgstr "未啟用"
+msgid "PipelineSchedules|Input variable key"
+msgstr "變數名稱"
+
+msgid "PipelineSchedules|Input variable value"
+msgstr "變數值"
+
msgid "PipelineSchedules|Next Run"
msgstr "下次執行時間"
@@ -633,12 +639,18 @@ msgstr "無"
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "請簡單說明此流水線 (pipeline) "
+msgid "PipelineSchedules|Remove variable row"
+msgstr "刪除變數"
+
msgid "PipelineSchedules|Take ownership"
msgstr "取得所有權"
msgid "PipelineSchedules|Target"
msgstr "目標"
+msgid "PipelineSchedules|Variables"
+msgstr "變數"
+
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "自訂"
@@ -759,13 +771,13 @@ msgid "Revert this merge request"
msgstr "還原此合併請求 (merge request) "
msgid "Save pipeline schedule"
-msgstr "保存流水線 (pipeline) 排程"
+msgstr "儲存流水線 (pipeline) 排程"
msgid "Schedule a new pipeline"
msgstr "建立流水線 (pipeline) 排程"
msgid "Scheduling Pipelines"
-msgstr "流水線 (pipeline) 計劃"
+msgstr "流水線 (pipeline) 排程"
msgid "Search branches and tags"
msgstr "搜尋分支 (branch) 和標籤"
@@ -849,8 +861,7 @@ msgid ""
"specific branches or tags. Those scheduled pipelines will inherit limited "
"project access based on their associated user."
msgstr ""
-"在指定了特定分支 (branch) 或標籤後,此處的流水線 (pipeline) 排程會不斷地重複執行。\n"
-"流水線排程的存取權限與專案本身相同。"
+"在指定了特定分支 (branch) 或標籤後,此處的流水線 (pipeline) 排程會不斷地重複執行。流水線排程的存取權限與專案本身相同。"
msgid ""
"The planning stage shows the time from the previous step to pushing your "
@@ -1097,6 +1108,14 @@ msgid "Withdraw Access Request"
msgstr "取消權限申請"
msgid ""
+"You are going to remove %{group_name}.\n"
+"Removed groups CANNOT be restored!\n"
+"Are you ABSOLUTELY sure?"
+msgstr "即將要刪除 %{group_name}。\n"
+"被刪除的群組完全無法救回來喔!\n"
+"真的「100%確定」要這麼做嗎?"
+
+msgid ""
"You are going to remove %{project_name_with_namespace}.\n"
"Removed project CANNOT be restored!\n"
"Are you ABSOLUTELY sure?"
diff --git a/qa/qa.rb b/qa/qa.rb
index bdfb8237995..db9d8c42fde 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -48,7 +48,14 @@ module QA
module Main
autoload :Entry, 'qa/page/main/entry'
autoload :Menu, 'qa/page/main/menu'
- autoload :Groups, 'qa/page/main/groups'
+ end
+
+ module Dashboard
+ autoload :Groups, 'qa/page/dashboard/groups'
+ end
+
+ module Group
+ autoload :Show, 'qa/page/group/show'
end
module Project
diff --git a/qa/qa/page/main/groups.rb b/qa/qa/page/dashboard/groups.rb
index 169c5ebc967..3690f40dcfe 100644
--- a/qa/qa/page/main/groups.rb
+++ b/qa/qa/page/dashboard/groups.rb
@@ -1,9 +1,11 @@
module QA
module Page
- module Main
+ module Dashboard
class Groups < Page::Base
def prepare_test_namespace
- return if page.has_content?(Runtime::Namespace.name)
+ if page.has_content?(Runtime::Namespace.name)
+ return click_link(Runtime::Namespace.name)
+ end
click_on 'New group'
diff --git a/qa/qa/page/group/show.rb b/qa/qa/page/group/show.rb
new file mode 100644
index 00000000000..296c311d7c6
--- /dev/null
+++ b/qa/qa/page/group/show.rb
@@ -0,0 +1,11 @@
+module QA
+ module Page
+ module Group
+ class Show < Page::Base
+ def go_to_new_project
+ click_link 'New Project'
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb
index f7c2086d0dd..7ce4e9009f5 100644
--- a/qa/qa/page/main/menu.rb
+++ b/qa/qa/page/main/menu.rb
@@ -14,13 +14,6 @@ module QA
within_user_menu { click_link 'Admin area' }
end
- def go_to_new_project
- within_user_menu do
- find('.header-new-dropdown-toggle').click
- click_link('New project')
- end
- end
-
def sign_out
within_user_menu do
find('.header-user-dropdown-toggle').click
diff --git a/qa/qa/scenario/gitlab/project/create.rb b/qa/qa/scenario/gitlab/project/create.rb
index 99d0fc42a94..b860701c304 100644
--- a/qa/qa/scenario/gitlab/project/create.rb
+++ b/qa/qa/scenario/gitlab/project/create.rb
@@ -13,8 +13,8 @@ module QA
def perform
Page::Main::Menu.act { go_to_groups }
- Page::Main::Groups.act { prepare_test_namespace }
- Page::Main::Menu.act { go_to_new_project }
+ Page::Dashboard::Groups.act { prepare_test_namespace }
+ Page::Group::Show.act { go_to_new_project }
Page::Project::New.perform do |page|
page.choose_test_namespace
diff --git a/spec/controllers/dashboard/todos_controller_spec.rb b/spec/controllers/dashboard/todos_controller_spec.rb
index 085f3fd8543..4a48621abe1 100644
--- a/spec/controllers/dashboard/todos_controller_spec.rb
+++ b/spec/controllers/dashboard/todos_controller_spec.rb
@@ -12,6 +12,36 @@ describe Dashboard::TodosController do
end
describe 'GET #index' do
+ context 'project authorization' do
+ it 'renders 404 when user does not have read access on given project' do
+ unauthorized_project = create(:empty_project, :private)
+
+ get :index, project_id: unauthorized_project.id
+
+ expect(response).to have_http_status(404)
+ end
+
+ it 'renders 404 when given project does not exists' do
+ get :index, project_id: 999
+
+ expect(response).to have_http_status(404)
+ end
+
+ it 'renders 200 when filtering for "any project" todos' do
+ get :index, project_id: ''
+
+ expect(response).to have_http_status(200)
+ end
+
+ it 'renders 200 when user has access on given project' do
+ authorized_project = create(:empty_project, :public)
+
+ get :index, project_id: authorized_project.id
+
+ expect(response).to have_http_status(200)
+ end
+ end
+
context 'when using pagination' do
let(:last_page) { user.todos.page.total_pages }
let!(:issues) { create_list(:issue, 2, project: project, assignees: [user]) }
diff --git a/spec/controllers/metrics_controller_spec.rb b/spec/controllers/metrics_controller_spec.rb
index 8964d89b438..7b0976e3e67 100644
--- a/spec/controllers/metrics_controller_spec.rb
+++ b/spec/controllers/metrics_controller_spec.rb
@@ -12,7 +12,7 @@ describe MetricsController do
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
- stub_env('prometheus_multiproc_dir', metrics_multiproc_dir)
+ allow(Prometheus::Client.configuration).to receive(:multiprocess_files_dir).and_return(metrics_multiproc_dir)
allow(Gitlab::Metrics).to receive(:prometheus_metrics_enabled?).and_return(true)
allow(Settings.monitoring).to receive(:ip_whitelist).and_return([whitelisted_ip, whitelisted_ip_range])
end
diff --git a/spec/controllers/profiles/accounts_controller_spec.rb b/spec/controllers/profiles/accounts_controller_spec.rb
index 2f9d18e3a0e..d387aba227b 100644
--- a/spec/controllers/profiles/accounts_controller_spec.rb
+++ b/spec/controllers/profiles/accounts_controller_spec.rb
@@ -29,7 +29,7 @@ describe Profiles::AccountsController do
end
end
- [:twitter, :facebook, :google_oauth2, :gitlab, :github, :bitbucket, :crowd, :auth0].each do |provider|
+ [:twitter, :facebook, :google_oauth2, :gitlab, :github, :bitbucket, :crowd, :auth0, :authentiq].each do |provider|
describe "#{provider} provider" do
let(:user) { create(:omniauth_user, provider: provider.to_s) }
diff --git a/spec/controllers/projects/badges_controller_spec.rb b/spec/controllers/projects/badges_controller_spec.rb
new file mode 100644
index 00000000000..d68200164e4
--- /dev/null
+++ b/spec/controllers/projects/badges_controller_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe Projects::BadgesController do
+ let(:project) { pipeline.project }
+ let!(:pipeline) { create(:ci_empty_pipeline) }
+ let(:user) { create(:user) }
+
+ before do
+ project.add_master(user)
+ sign_in(user)
+ end
+
+ it 'requests the pipeline badge successfully' do
+ get_badge(:pipeline)
+
+ expect(response).to have_http_status(:ok)
+ end
+
+ it 'requests the coverage badge successfully' do
+ get_badge(:coverage)
+
+ expect(response).to have_http_status(:ok)
+ end
+
+ def get_badge(badge)
+ get badge, namespace_id: project.namespace.to_param, project_id: project, ref: pipeline.ref, format: :svg
+ end
+end
diff --git a/spec/controllers/projects/hooks_controller_spec.rb b/spec/controllers/projects/hooks_controller_spec.rb
new file mode 100644
index 00000000000..b93ab220f4d
--- /dev/null
+++ b/spec/controllers/projects/hooks_controller_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe Projects::HooksController do
+ let(:project) { create(:empty_project) }
+ let(:user) { create(:user) }
+
+ before do
+ project.team << [user, :master]
+ sign_in(user)
+ end
+
+ describe '#index' do
+ it 'redirects to settings/integrations page' do
+ get(:index, namespace_id: project.namespace, project_id: project)
+
+ expect(response).to redirect_to(
+ project_settings_integrations_path(project)
+ )
+ end
+ end
+end
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 22aad0b3225..18d0be3c103 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -7,14 +7,16 @@ describe Projects::IssuesController do
describe "GET #index" do
context 'external issue tracker' do
+ let!(:service) do
+ create(:custom_issue_tracker_service, project: project, title: 'Custom Issue Tracker', project_url: 'http://test.com')
+ end
+
it 'redirects to the external issue tracker' do
- external = double(project_path: 'https://example.com/project')
- allow(project).to receive(:external_issue_tracker).and_return(external)
controller.instance_variable_set(:@project, project)
get :index, namespace_id: project.namespace, project_id: project
- expect(response).to redirect_to('https://example.com/project')
+ expect(response).to redirect_to(service.issue_tracker_path)
end
end
@@ -139,19 +141,21 @@ describe Projects::IssuesController do
end
context 'external issue tracker' do
+ let!(:service) do
+ create(:custom_issue_tracker_service, project: project, title: 'Custom Issue Tracker', new_issue_url: 'http://test.com')
+ end
+
before do
sign_in(user)
project.team << [user, :developer]
end
it 'redirects to the external issue tracker' do
- external = double(new_issue_path: 'https://example.com/issues/new')
- allow(project).to receive(:external_issue_tracker).and_return(external)
controller.instance_variable_set(:@project, project)
get :new, namespace_id: project.namespace, project_id: project
- expect(response).to redirect_to('https://example.com/issues/new')
+ expect(response).to redirect_to('http://test.com')
end
end
end
@@ -512,6 +516,36 @@ describe Projects::IssuesController do
end
end
+ describe 'GET #realtime_changes' do
+ it_behaves_like 'restricted action', success: 200
+
+ def go(id:)
+ get :realtime_changes,
+ namespace_id: project.namespace.to_param,
+ project_id: project,
+ id: id
+ end
+
+ context 'when an issue was edited by a deleted user' do
+ let(:deleted_user) { create(:user) }
+
+ before do
+ project.team << [user, :developer]
+
+ issue.update!(last_edited_by: deleted_user, last_edited_at: Time.now)
+
+ deleted_user.destroy
+ sign_in(user)
+ end
+
+ it 'returns 200' do
+ go(id: issue.iid)
+
+ expect(response).to have_http_status(200)
+ end
+ end
+ end
+
describe 'GET #edit' do
it_behaves_like 'restricted action', success: 200
diff --git a/spec/controllers/projects/registry/tags_controller_spec.rb b/spec/controllers/projects/registry/tags_controller_spec.rb
new file mode 100644
index 00000000000..a823516830e
--- /dev/null
+++ b/spec/controllers/projects/registry/tags_controller_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+describe Projects::Registry::TagsController do
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project, :private) }
+
+ before do
+ sign_in(user)
+ stub_container_registry_config(enabled: true)
+ end
+
+ context 'when user has access to registry' do
+ before do
+ project.add_developer(user)
+ end
+
+ describe 'POST destroy' do
+ context 'when there is matching tag present' do
+ before do
+ stub_container_registry_tags(repository: /image/, tags: %w[rc1 test.])
+ end
+
+ let(:repository) do
+ create(:container_repository, name: 'image', project: project)
+ end
+
+ it 'makes it possible to delete regular tag' do
+ expect_any_instance_of(ContainerRegistry::Tag).to receive(:delete)
+
+ destroy_tag('rc1')
+ end
+
+ it 'makes it possible to delete a tag that ends with a dot' do
+ expect_any_instance_of(ContainerRegistry::Tag).to receive(:delete)
+
+ destroy_tag('test.')
+ end
+ end
+ end
+ end
+
+ def destroy_tag(name)
+ post :destroy, namespace_id: project.namespace,
+ project_id: project,
+ repository_id: repository,
+ id: name
+ end
+end
diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb
index 2b4e8723b48..a22fd8eaf9b 100644
--- a/spec/controllers/sessions_controller_spec.rb
+++ b/spec/controllers/sessions_controller_spec.rb
@@ -1,9 +1,11 @@
require 'spec_helper'
describe SessionsController do
+ include DeviseHelpers
+
describe '#new' do
before do
- @request.env['devise.mapping'] = Devise.mappings[:user]
+ set_devise_mapping(context: @request)
end
context 'when auto sign-in is enabled' do
@@ -34,7 +36,7 @@ describe SessionsController do
describe '#create' do
before do
- @request.env['devise.mapping'] = Devise.mappings[:user]
+ set_devise_mapping(context: @request)
end
context 'when using standard authentications' do
@@ -257,7 +259,7 @@ describe SessionsController do
describe '#new' do
before do
- @request.env['devise.mapping'] = Devise.mappings[:user]
+ set_devise_mapping(context: @request)
end
it 'redirects correctly for referer on same host with params' do
diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb
index 15416a89017..475ceda11fe 100644
--- a/spec/controllers/snippets_controller_spec.rb
+++ b/spec/controllers/snippets_controller_spec.rb
@@ -186,8 +186,8 @@ describe SnippetsController do
end
context 'when the snippet description contains a file' do
- let(:picture_file) { '/temp/secret56/picture.jpg' }
- let(:text_file) { '/temp/secret78/text.txt' }
+ let(:picture_file) { '/system/temp/secret56/picture.jpg' }
+ let(:text_file) { '/system/temp/secret78/text.txt' }
let(:description) do
"Description with picture: ![picture](/uploads#{picture_file}) and "\
"text: [text.txt](/uploads#{text_file})"
@@ -208,8 +208,8 @@ describe SnippetsController do
snippet = subject
expected_description = "Description with picture: "\
- "![picture](/uploads/personal_snippet/#{snippet.id}/secret56/picture.jpg) and "\
- "text: [text.txt](/uploads/personal_snippet/#{snippet.id}/secret78/text.txt)"
+ "![picture](/uploads/system/personal_snippet/#{snippet.id}/secret56/picture.jpg) and "\
+ "text: [text.txt](/uploads/system/personal_snippet/#{snippet.id}/secret78/text.txt)"
expect(snippet.description).to eq(expected_description)
end
diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb
index 01a0659479b..96f719e2b82 100644
--- a/spec/controllers/uploads_controller_spec.rb
+++ b/spec/controllers/uploads_controller_spec.rb
@@ -102,7 +102,7 @@ describe UploadsController do
subject
expect(response.body).to match '\"alt\":\"rails_sample\"'
- expect(response.body).to match "\"url\":\"/uploads/temp"
+ expect(response.body).to match "\"url\":\"/uploads/system/temp"
end
it 'does not create an Upload record' do
@@ -119,7 +119,7 @@ describe UploadsController do
subject
expect(response.body).to match '\"alt\":\"doc_sample.txt\"'
- expect(response.body).to match "\"url\":\"/uploads/temp"
+ expect(response.body).to match "\"url\":\"/uploads/system/temp"
end
it 'does not create an Upload record' do
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index a77f01ecb00..5bba1dec7db 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -84,6 +84,10 @@ FactoryGirl.define do
success
end
+ trait :retried do
+ retried true
+ end
+
trait :cancelable do
pending
end
@@ -106,7 +110,7 @@ FactoryGirl.define do
end
after(:build) do |build, evaluator|
- build.project = build.pipeline.project
+ build.project ||= build.pipeline.project
end
factory :ci_not_started_build do
diff --git a/spec/factories/project_hooks.rb b/spec/factories/project_hooks.rb
index cd754ea235f..d754e980931 100644
--- a/spec/factories/project_hooks.rb
+++ b/spec/factories/project_hooks.rb
@@ -2,6 +2,7 @@ FactoryGirl.define do
factory :project_hook do
url { generate(:url) }
enable_ssl_verification false
+ project factory: :empty_project
trait :token do
token { SecureRandom.hex(10) }
diff --git a/spec/factories/uploads.rb b/spec/factories/uploads.rb
index 1383420fb44..3222c41c3d8 100644
--- a/spec/factories/uploads.rb
+++ b/spec/factories/uploads.rb
@@ -1,7 +1,7 @@
FactoryGirl.define do
factory :upload do
model { build(:project) }
- path { "uploads/system/project/avatar/avatar.jpg" }
+ path { "uploads/-/system/project/avatar/avatar.jpg" }
size 100.kilobytes
uploader "AvatarUploader"
end
diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb
index 1e2cb8569ec..b9e361328df 100644
--- a/spec/features/admin/admin_appearance_spec.rb
+++ b/spec/features/admin/admin_appearance_spec.rb
@@ -63,11 +63,11 @@ feature 'Admin Appearance', feature: true do
end
def logo_selector
- '//img[@src^="/uploads/system/appearance/logo"]'
+ '//img[@src^="/uploads/-/system/appearance/logo"]'
end
def header_logo_selector
- '//img[@src^="/uploads/system/appearance/header_logo"]'
+ '//img[@src^="/uploads/-/system/appearance/header_logo"]'
end
def logo_fixture
diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb
index 1e675fc0ce7..9a438b65e68 100644
--- a/spec/features/admin/admin_hooks_spec.rb
+++ b/spec/features/admin/admin_hooks_spec.rb
@@ -74,11 +74,13 @@ describe 'Admin::Hooks', feature: true do
end
end
- describe 'Test' do
+ describe 'Test', js: true do
before do
WebMock.stub_request(:post, @system_hook.url)
visit admin_hooks_path
- click_link 'Test hook'
+
+ find('.hook-test-button.dropdown').click
+ click_link 'Push events'
end
it { expect(current_path).to eq(admin_hooks_path) }
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index b06e7e5037c..46bab3763cc 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -19,7 +19,7 @@ describe "Admin Runners" do
end
it 'has all necessary texts' do
- expect(page).to have_text "To register a new Runner"
+ expect(page).to have_text "How to setup"
expect(page).to have_text "Runners with last contact more than a minute ago: 1"
end
@@ -54,7 +54,7 @@ describe "Admin Runners" do
end
it 'has all necessary texts including no runner message' do
- expect(page).to have_text "To register a new Runner"
+ expect(page).to have_text "How to setup"
expect(page).to have_text "Runners with last contact more than a minute ago: 0"
expect(page).to have_text 'No runners found'
end
@@ -163,12 +163,11 @@ describe "Admin Runners" do
end
it 'has a registration token' do
- expect(page).to have_content("Registration token is #{token}")
- expect(page).to have_selector('#runners-token', text: token)
+ expect(page.find('#registration_token')).to have_content(token)
end
describe 'reload registration token' do
- let(:page_token) { find('#runners-token').text }
+ let(:page_token) { find('#registration_token').text }
before do
click_button 'Reset runners registration token'
diff --git a/spec/features/dashboard/activity_spec.rb b/spec/features/dashboard/activity_spec.rb
index ebfe7340eb7..a96270c9147 100644
--- a/spec/features/dashboard/activity_spec.rb
+++ b/spec/features/dashboard/activity_spec.rb
@@ -1,13 +1,162 @@
require 'spec_helper'
-RSpec.describe 'Dashboard Activity', feature: true do
+feature 'Dashboard > Activity' do
let(:user) { create(:user) }
before do
sign_in(user)
- visit activity_dashboard_path
end
- it_behaves_like "it has an RSS button with current_user's RSS token"
- it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
+ context 'rss' do
+ before do
+ visit activity_dashboard_path
+ end
+
+ it_behaves_like "it has an RSS button with current_user's RSS token"
+ it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
+ end
+
+ context 'event filters', :js do
+ let(:project) { create(:empty_project) }
+
+ let(:merge_request) do
+ create(:merge_request, author: user, source_project: project, target_project: project)
+ end
+
+ let(:push_event_data) do
+ {
+ before: Gitlab::Git::BLANK_SHA,
+ after: '0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e',
+ ref: 'refs/heads/new_design',
+ user_id: user.id,
+ user_name: user.name,
+ repository: {
+ name: project.name,
+ url: 'localhost/rubinius',
+ description: '',
+ homepage: 'localhost/rubinius',
+ private: true
+ }
+ }
+ end
+
+ let(:note) { create(:note, project: project, noteable: merge_request) }
+
+ let!(:push_event) do
+ create(:event, :pushed, data: push_event_data, project: project, author: user)
+ end
+
+ let!(:merged_event) do
+ create(:event, :merged, project: project, target: merge_request, author: user)
+ end
+
+ let!(:joined_event) do
+ create(:event, :joined, project: project, author: user)
+ end
+
+ let!(:closed_event) do
+ create(:event, :closed, project: project, target: merge_request, author: user)
+ end
+
+ let!(:comments_event) do
+ create(:event, :commented, project: project, target: note, author: user)
+ end
+
+ before do
+ project.add_master(user)
+
+ visit activity_dashboard_path
+ wait_for_requests
+ end
+
+ scenario 'user should see all events' do
+ within '.content_list' do
+ expect(page).to have_content('pushed new branch')
+ expect(page).to have_content('joined')
+ expect(page).to have_content('accepted')
+ expect(page).to have_content('closed')
+ expect(page).to have_content('commented on')
+ end
+ end
+
+ scenario 'user should see only pushed events' do
+ click_link('Push events')
+ wait_for_requests
+
+ within '.content_list' do
+ expect(page).to have_content('pushed new branch')
+ expect(page).not_to have_content('joined')
+ expect(page).not_to have_content('accepted')
+ expect(page).not_to have_content('closed')
+ expect(page).not_to have_content('commented on')
+ end
+ end
+
+ scenario 'user should see only merged events' do
+ click_link('Merge events')
+ wait_for_requests
+
+ within '.content_list' do
+ expect(page).not_to have_content('pushed new branch')
+ expect(page).not_to have_content('joined')
+ expect(page).to have_content('accepted')
+ expect(page).not_to have_content('closed')
+ expect(page).not_to have_content('commented on')
+ end
+ end
+
+ scenario 'user should see only issues events' do
+ click_link('Issue events')
+ wait_for_requests
+
+ within '.content_list' do
+ expect(page).not_to have_content('pushed new branch')
+ expect(page).not_to have_content('joined')
+ expect(page).not_to have_content('accepted')
+ expect(page).to have_content('closed')
+ expect(page).not_to have_content('commented on')
+ end
+ end
+
+ scenario 'user should see only comments events' do
+ click_link('Comments')
+ wait_for_requests
+
+ within '.content_list' do
+ expect(page).not_to have_content('pushed new branch')
+ expect(page).not_to have_content('joined')
+ expect(page).not_to have_content('accepted')
+ expect(page).not_to have_content('closed')
+ expect(page).to have_content('commented on')
+ end
+ end
+
+ scenario 'user should see only joined events' do
+ click_link('Team')
+ wait_for_requests
+
+ within '.content_list' do
+ expect(page).not_to have_content('pushed new branch')
+ expect(page).to have_content('joined')
+ expect(page).not_to have_content('accepted')
+ expect(page).not_to have_content('closed')
+ expect(page).not_to have_content('commented on')
+ end
+ end
+
+ scenario 'user see selected event after page reloading' do
+ click_link('Push events')
+ wait_for_requests
+ visit activity_dashboard_path
+ wait_for_requests
+
+ within '.content_list' do
+ expect(page).to have_content('pushed new branch')
+ expect(page).not_to have_content('joined')
+ expect(page).not_to have_content('accepted')
+ expect(page).not_to have_content('closed')
+ expect(page).not_to have_content('commented on')
+ end
+ end
+ end
end
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
index 54a01e837de..533df7a325c 100644
--- a/spec/features/dashboard/groups_list_spec.rb
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Dashboard Groups page', js: true, feature: true do
+feature 'Dashboard Groups page', :js do
let!(:user) { create :user }
let!(:group) { create(:group) }
let!(:nested_group) { create(:group, :nested) }
@@ -41,7 +41,7 @@ describe 'Dashboard Groups page', js: true, feature: true do
fill_in 'filter_groups', with: group.name
wait_for_requests
- fill_in 'filter_groups', with: ""
+ fill_in 'filter_groups', with: ''
wait_for_requests
expect(page).to have_content(group.full_name)
diff --git a/spec/features/dashboard_issues_spec.rb b/spec/features/dashboard/issues_filter_spec.rb
index f235fef1aa4..9b84f67b555 100644
--- a/spec/features/dashboard_issues_spec.rb
+++ b/spec/features/dashboard/issues_filter_spec.rb
@@ -1,21 +1,23 @@
require 'spec_helper'
-describe "Dashboard Issues filtering", feature: true, js: true do
+feature 'Dashboard Issues filtering', js: true do
+ include SortingHelper
+
let(:user) { create(:user) }
let(:project) { create(:empty_project) }
let(:milestone) { create(:milestone, project: project) }
- context 'filtering by milestone' do
- before do
- project.team << [user, :master]
- sign_in(user)
+ let!(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
+ let!(:issue2) { create(:issue, project: project, author: user, assignees: [user], milestone: milestone) }
- create(:issue, project: project, author: user, assignees: [user])
- create(:issue, project: project, author: user, assignees: [user], milestone: milestone)
+ before do
+ project.add_master(user)
+ sign_in(user)
- visit_issues
- end
+ visit_issues
+ end
+ context 'filtering by milestone' do
it 'shows all issues with no milestone' do
show_milestone_dropdown
@@ -62,6 +64,46 @@ describe "Dashboard Issues filtering", feature: true, js: true do
end
end
+ context 'filtering by label' do
+ let(:label) { create(:label, project: project) }
+ let!(:label_link) { create(:label_link, label: label, target: issue) }
+
+ it 'shows all issues without filter' do
+ page.within 'ul.content-list' do
+ expect(page).to have_content issue.title
+ expect(page).to have_content issue2.title
+ end
+ end
+
+ it 'shows all issues with the selected label' do
+ page.within '.labels-filter' do
+ find('.dropdown').click
+ click_link label.title
+ end
+
+ page.within 'ul.content-list' do
+ expect(page).to have_content issue.title
+ expect(page).not_to have_content issue2.title
+ end
+ end
+ end
+
+ context 'sorting' do
+ it 'shows sorted issues' do
+ sorting_by('Oldest updated')
+ visit_issues
+
+ expect(find('.issues-filters')).to have_content('Oldest updated')
+ end
+
+ it 'keeps sorting issues after visiting Projects Issues page' do
+ sorting_by('Oldest updated')
+ visit project_issues_path(project)
+
+ expect(find('.issues-filters')).to have_content('Oldest updated')
+ end
+ end
+
def show_milestone_dropdown
click_button 'Milestone'
expect(page).to have_selector('.dropdown-content', visible: true)
diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb
index 86ac24ea06e..69c1a2ed89a 100644
--- a/spec/features/dashboard/issues_spec.rb
+++ b/spec/features/dashboard/issues_spec.rb
@@ -62,7 +62,7 @@ RSpec.describe 'Dashboard Issues', feature: true do
it 'state filter tabs work' do
find('#state-closed').click
- expect(page).to have_current_path(issues_dashboard_url(assignee_id: current_user.id, scope: 'all', state: 'closed'), url: true)
+ expect(page).to have_current_path(issues_dashboard_url(assignee_id: current_user.id, state: 'closed'), url: true)
end
it_behaves_like "it has an RSS button with current_user's RSS token"
diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb
index bb1fb5b3feb..42d6fadc0c1 100644
--- a/spec/features/dashboard/merge_requests_spec.rb
+++ b/spec/features/dashboard/merge_requests_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
feature 'Dashboard Merge Requests' do
include FilterItemSelectHelper
+ include SortingHelper
let(:current_user) { create :user }
let(:project) { create(:empty_project) }
@@ -109,5 +110,21 @@ feature 'Dashboard Merge Requests' do
expect(page).to have_content(assigned_merge_request_from_fork.title)
expect(page).to have_content(other_merge_request.title)
end
+
+ it 'shows sorted merge requests' do
+ sorting_by('Oldest updated')
+
+ visit merge_requests_dashboard_path(assignee_id: current_user.id)
+
+ expect(find('.issues-filters')).to have_content('Oldest updated')
+ end
+
+ it 'keeps sorting merge requests after visiting Projects MR page' do
+ sorting_by('Oldest updated')
+
+ visit project_merge_requests_path(project)
+
+ expect(find('.issues-filters')).to have_content('Oldest updated')
+ end
end
end
diff --git a/spec/features/dashboard_milestones_spec.rb b/spec/features/dashboard/milestones_spec.rb
index 7a6a448d4c2..7a6a448d4c2 100644
--- a/spec/features/dashboard_milestones_spec.rb
+++ b/spec/features/dashboard/milestones_spec.rb
diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb
index bdba22fe9a9..abb9e5eef96 100644
--- a/spec/features/dashboard/projects_spec.rb
+++ b/spec/features/dashboard/projects_spec.rb
@@ -61,7 +61,7 @@ feature 'Dashboard Projects' do
end
end
- describe "with a pipeline", clean_gitlab_redis_shared_state: true do
+ describe 'with a pipeline', clean_gitlab_redis_shared_state: true do
let(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit.sha) }
before do
@@ -74,7 +74,50 @@ feature 'Dashboard Projects' do
it 'shows that the last pipeline passed' do
visit dashboard_projects_path
- expect(page).to have_xpath("//a[@href='#{pipelines_project_commit_path(project, project.commit)}']")
+ page.within('.controls') do
+ expect(page).to have_xpath("//a[@href='#{pipelines_project_commit_path(project, project.commit)}']")
+ expect(page).to have_css('.ci-status-link')
+ expect(page).to have_css('.ci-status-icon-success')
+ expect(page).to have_link('Commit: passed')
+ end
+ end
+ end
+
+ context 'last push widget' do
+ let(:push_event_data) do
+ {
+ before: Gitlab::Git::BLANK_SHA,
+ after: '0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e',
+ ref: 'refs/heads/feature',
+ user_id: user.id,
+ user_name: user.name,
+ repository: {
+ name: project.name,
+ url: 'localhost/rubinius',
+ description: '',
+ homepage: 'localhost/rubinius',
+ private: true
+ }
+ }
+ end
+ let!(:push_event) { create(:event, :pushed, data: push_event_data, project: project, author: user) }
+
+ before do
+ visit dashboard_projects_path
+ end
+
+ scenario 'shows "Create merge request" button' do
+ expect(page).to have_content 'You pushed to feature'
+
+ within('#content-body') do
+ find_link('Create merge request', visible: false).click
+ end
+
+ expect(page).to have_selector('.merge-request-form')
+ expect(current_path).to eq project_new_merge_request_path(project)
+ expect(find('#merge_request_target_project_id').value).to eq project.id.to_s
+ expect(find('input#merge_request_source_branch').value).to eq 'feature'
+ expect(find('input#merge_request_target_branch').value).to eq 'master'
end
end
end
diff --git a/spec/features/issues/issue_detail_spec.rb b/spec/features/issues/issue_detail_spec.rb
new file mode 100644
index 00000000000..e1c55d246ab
--- /dev/null
+++ b/spec/features/issues/issue_detail_spec.rb
@@ -0,0 +1,43 @@
+require 'rails_helper'
+
+feature 'Issue Detail', js: true, feature: true do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
+ let(:issue) { create(:issue, project: project, author: user) }
+
+ context 'when user displays the issue' do
+ before do
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ it 'shows the issue' do
+ page.within('.issuable-details') do
+ expect(find('h2')).to have_content(issue.title)
+ end
+ end
+ end
+
+ context 'when edited by a user who is later deleted' do
+ before do
+ sign_in(user)
+ visit project_issue_path(project, issue)
+ wait_for_requests
+
+ click_link 'Edit'
+ fill_in 'issue-title', with: 'issue title'
+ click_button 'Save'
+
+ visit profile_account_path
+ click_link 'Delete account'
+
+ visit project_issue_path(project, issue)
+ end
+
+ it 'shows the issue' do
+ page.within('.issuable-details') do
+ expect(find('h2')).to have_content(issue.reload.title)
+ end
+ end
+ end
+end
diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb
index 1cd1f016674..4740402dc01 100644
--- a/spec/features/issues/user_uses_slash_commands_spec.rb
+++ b/spec/features/issues/user_uses_slash_commands_spec.rb
@@ -21,6 +21,16 @@ feature 'Issues > User uses quick actions', feature: true, js: true do
wait_for_requests
end
+ describe 'time tracking' do
+ let(:issue) { create(:issue, project: project) }
+
+ before do
+ visit project_issue_path(project, issue)
+ end
+
+ it_behaves_like 'issuable time tracker'
+ end
+
describe 'adding a due date from note' do
let(:issue) { create(:issue, project: project) }
@@ -99,32 +109,6 @@ feature 'Issues > User uses quick actions', feature: true, js: true do
end
end
- describe 'Issuable time tracking' do
- let(:issue) { create(:issue, project: project) }
-
- before do
- project.team << [user, :developer]
- end
-
- context 'Issue' do
- before do
- visit project_issue_path(project, issue)
- end
-
- it_behaves_like 'issuable time tracker'
- end
-
- context 'Merge Request' do
- let(:merge_request) { create(:merge_request, source_project: project) }
-
- before do
- visit project_merge_request_path(project, merge_request)
- end
-
- it_behaves_like 'issuable time tracker'
- end
- end
-
describe 'toggling the WIP prefix from the title from note' do
let(:issue) { create(:issue, project: project) }
diff --git a/spec/features/merge_requests/filter_merge_requests_spec.rb b/spec/features/merge_requests/filter_merge_requests_spec.rb
index 2a161b83aa0..e8085ec36aa 100644
--- a/spec/features/merge_requests/filter_merge_requests_spec.rb
+++ b/spec/features/merge_requests/filter_merge_requests_spec.rb
@@ -132,19 +132,13 @@ describe 'Filter merge requests', feature: true do
end
end
- describe 'for assignee and label from issues#index' do
+ describe 'for assignee and label from mr#index' do
let(:search_query) { "assignee:@#{user.username} label:~#{label.title}" }
before do
- input_filtered_search("assignee:@#{user.username}")
-
- expect_mr_list_count(1)
- expect_tokens([{ name: 'assignee', value: "@#{user.username}" }])
- expect_filtered_search_input_empty
-
- input_filtered_search_keys("label:~#{label.title}")
+ input_filtered_search(search_query)
- expect_mr_list_count(1)
+ expect_mr_list_count(0)
end
context 'assignee and label', js: true do
diff --git a/spec/features/merge_requests/user_uses_slash_commands_spec.rb b/spec/features/merge_requests/user_uses_slash_commands_spec.rb
index 434f5a7c0ac..b2187e01bdb 100644
--- a/spec/features/merge_requests/user_uses_slash_commands_spec.rb
+++ b/spec/features/merge_requests/user_uses_slash_commands_spec.rb
@@ -24,6 +24,14 @@ feature 'Merge Requests > User uses quick actions', feature: true, js: true do
wait_for_requests
end
+ describe 'time tracking' do
+ before do
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ it_behaves_like 'issuable time tracker'
+ end
+
describe 'toggling the WIP prefix in the title from note' do
context 'when the current user can toggle the WIP prefix' do
it 'adds the WIP: prefix to the title' do
diff --git a/spec/features/oauth_login_spec.rb b/spec/features/oauth_login_spec.rb
index 42764e808e6..0064c9ef25e 100644
--- a/spec/features/oauth_login_spec.rb
+++ b/spec/features/oauth_login_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
feature 'OAuth Login', js: true do
+ include DeviseHelpers
+
def enter_code(code)
fill_in 'user_otp_attempt', with: code
click_button 'Verify code'
@@ -8,7 +10,7 @@ feature 'OAuth Login', js: true do
def stub_omniauth_config(provider)
OmniAuth.config.add_mock(provider, OmniAuth::AuthHash.new(provider: provider.to_s, uid: "12345"))
- Rails.application.env_config['devise.mapping'] = Devise.mappings[:user]
+ set_devise_mapping(context: Rails.application)
Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[provider]
end
diff --git a/spec/features/projects/badges/list_spec.rb b/spec/features/projects/badges/list_spec.rb
index 161d731f524..fd8e9232b02 100644
--- a/spec/features/projects/badges/list_spec.rb
+++ b/spec/features/projects/badges/list_spec.rb
@@ -10,16 +10,16 @@ feature 'list of badges' do
end
scenario 'user wants to see build status badge' do
- page.within('.build-status') do
- expect(page).to have_content 'build status'
+ page.within('.pipeline-status') do
+ expect(page).to have_content 'pipeline status'
expect(page).to have_content 'Markdown'
expect(page).to have_content 'HTML'
expect(page).to have_content 'AsciiDoc'
expect(page).to have_css('.highlight', count: 3)
- expect(page).to have_xpath("//img[@alt='build status']")
+ expect(page).to have_xpath("//img[@alt='pipeline status']")
page.within('.highlight', match: :first) do
- expect(page).to have_content 'badges/master/build.svg'
+ expect(page).to have_content 'badges/master/pipeline.svg'
end
end
end
@@ -40,14 +40,14 @@ feature 'list of badges' do
end
scenario 'user changes current ref of build status badge', js: true do
- page.within('.build-status') do
+ page.within('.pipeline-status') do
first('.js-project-refs-dropdown').click
page.within '.project-refs-form' do
click_link 'improve/awesome'
end
- expect(page).to have_content 'badges/improve/awesome/build.svg'
+ expect(page).to have_content 'badges/improve/awesome/pipeline.svg'
end
end
end
diff --git a/spec/features/projects/badges/pipeline_badge_spec.rb b/spec/features/projects/badges/pipeline_badge_spec.rb
new file mode 100644
index 00000000000..b83ea8f4eaa
--- /dev/null
+++ b/spec/features/projects/badges/pipeline_badge_spec.rb
@@ -0,0 +1,70 @@
+require 'spec_helper'
+
+feature 'Pipeline Badge' do
+ set(:project) { create(:project, :repository, :public) }
+ let(:ref) { project.default_branch }
+
+ # this can't be tested in the controller, as it bypasses the rails router
+ # and constructs a route based on the controller being tested
+ # Keep around until 10.0, see gitlab-org/gitlab-ce#35307
+ context 'when the deprecated badge is requested' do
+ it 'displays the badge' do
+ visit build_project_badges_path(project, ref: ref, format: :svg)
+
+ expect(page.status_code).to eq(200)
+ end
+ end
+
+ context 'when the project has a pipeline' do
+ let!(:pipeline) { create(:ci_empty_pipeline, project: project, ref: ref, sha: project.commit(ref).sha) }
+ let!(:job) { create(:ci_build, pipeline: pipeline) }
+
+ context 'when the pipeline was successfull' do
+ it 'displays so on the badge' do
+ job.success
+
+ visit pipeline_project_badges_path(project, ref: ref, format: :svg)
+
+ expect(page.status_code).to eq(200)
+ expect_badge('passed')
+ end
+ end
+
+ context 'when the pipeline failed' do
+ it 'shows displays so on the badge' do
+ job.drop
+
+ visit pipeline_project_badges_path(project, ref: ref, format: :svg)
+
+ expect(page.status_code).to eq(200)
+ expect_badge('failed')
+ end
+ end
+
+ context 'when the pipeline is running' do
+ it 'shows displays so on the badge' do
+ create(:ci_build, pipeline: pipeline, name: 'second build', status_event: 'run')
+
+ visit pipeline_project_badges_path(project, ref: ref, format: :svg)
+
+ expect(page.status_code).to eq(200)
+ expect_badge('running')
+ end
+ end
+
+ context 'when a new pipeline is created' do
+ it 'shows a fresh badge' do
+ visit pipeline_project_badges_path(project, ref: ref, format: :svg)
+
+ expect(page.status_code).to eq(200)
+ expect(page.response_headers['Cache-Control']).to include 'no-cache'
+ end
+ end
+
+ def expect_badge(status)
+ svg = Nokogiri::XML.parse(page.body)
+ expect(page.response_headers['Content-Type']).to include('image/svg+xml')
+ expect(svg.at(%Q{text:contains("#{status}")})).to be_truthy
+ end
+ end
+end
diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb
index 4fae324d8d5..d18cd3d6adc 100644
--- a/spec/features/projects/branches_spec.rb
+++ b/spec/features/projects/branches_spec.rb
@@ -24,7 +24,6 @@ describe 'Branches', feature: true do
repository.branches_sorted_by(:name).first(20).each do |branch|
expect(page).to have_content("#{branch.name}")
end
- expect(page).to have_content("Protected branches can be managed in project settings")
end
it 'sorts the branches by name' do
@@ -130,6 +129,14 @@ describe 'Branches', feature: true do
project.team << [user, :master]
end
+ describe 'Initial branches page' do
+ it 'shows description for admin' do
+ visit project_branches_path(project)
+
+ expect(page).to have_content("Protected branches can be managed in project settings")
+ end
+ end
+
describe 'Delete protected branch' do
before do
visit project_protected_branches_path(project)
diff --git a/spec/features/projects/issuable_counts_caching_spec.rb b/spec/features/projects/issuable_counts_caching_spec.rb
new file mode 100644
index 00000000000..703d1cbd327
--- /dev/null
+++ b/spec/features/projects/issuable_counts_caching_spec.rb
@@ -0,0 +1,132 @@
+require 'spec_helper'
+
+describe 'Issuable counts caching', :use_clean_rails_memory_store_caching do
+ let!(:member) { create(:user) }
+ let!(:member_2) { create(:user) }
+ let!(:non_member) { create(:user) }
+ let!(:project) { create(:empty_project, :public) }
+ let!(:open_issue) { create(:issue, project: project) }
+ let!(:confidential_issue) { create(:issue, :confidential, project: project, author: non_member) }
+ let!(:closed_issue) { create(:issue, :closed, project: project) }
+
+ before do
+ project.add_developer(member)
+ project.add_developer(member_2)
+ end
+
+ it 'caches issuable counts correctly for non-members' do
+ # We can't use expect_any_instance_of because that uses a single instance.
+ counts = 0
+
+ allow_any_instance_of(IssuesFinder).to receive(:count_by_state).and_wrap_original do |m, *args|
+ counts += 1
+
+ m.call(*args)
+ end
+
+ aggregate_failures 'only counts once on first load with no params, and caches for later loads' do
+ expect { visit project_issues_path(project) }
+ .to change { counts }.by(1)
+
+ expect { visit project_issues_path(project) }
+ .not_to change { counts }
+ end
+
+ aggregate_failures 'uses counts from cache on load from non-member' do
+ sign_in(non_member)
+
+ expect { visit project_issues_path(project) }
+ .not_to change { counts }
+
+ sign_out(non_member)
+ end
+
+ aggregate_failures 'does not use the same cache for a member' do
+ sign_in(member)
+
+ expect { visit project_issues_path(project) }
+ .to change { counts }.by(1)
+
+ sign_out(member)
+ end
+
+ aggregate_failures 'uses the same cache for all members' do
+ sign_in(member_2)
+
+ expect { visit project_issues_path(project) }
+ .not_to change { counts }
+
+ sign_out(member_2)
+ end
+
+ aggregate_failures 'shares caches when params are passed' do
+ expect { visit project_issues_path(project, author_username: non_member.username) }
+ .to change { counts }.by(1)
+
+ sign_in(member)
+
+ expect { visit project_issues_path(project, author_username: non_member.username) }
+ .to change { counts }.by(1)
+
+ sign_in(non_member)
+
+ expect { visit project_issues_path(project, author_username: non_member.username) }
+ .not_to change { counts }
+
+ sign_in(member_2)
+
+ expect { visit project_issues_path(project, author_username: non_member.username) }
+ .not_to change { counts }
+
+ sign_out(member_2)
+ end
+
+ aggregate_failures 'resets caches on issue close' do
+ Issues::CloseService.new(project, member).execute(open_issue)
+
+ expect { visit project_issues_path(project) }
+ .to change { counts }.by(1)
+
+ sign_in(member)
+
+ expect { visit project_issues_path(project) }
+ .to change { counts }.by(1)
+
+ sign_in(non_member)
+
+ expect { visit project_issues_path(project) }
+ .not_to change { counts }
+
+ sign_in(member_2)
+
+ expect { visit project_issues_path(project) }
+ .not_to change { counts }
+
+ sign_out(member_2)
+ end
+
+ aggregate_failures 'does not reset caches on issue update' do
+ Issues::UpdateService.new(project, member, title: 'new title').execute(open_issue)
+
+ expect { visit project_issues_path(project) }
+ .not_to change { counts }
+
+ sign_in(member)
+
+ expect { visit project_issues_path(project) }
+ .not_to change { counts }
+
+ sign_in(non_member)
+
+ expect { visit project_issues_path(project) }
+ .not_to change { counts }
+
+ sign_in(member_2)
+
+ expect { visit project_issues_path(project) }
+ .not_to change { counts }
+
+ sign_out(member_2)
+ end
+ end
+end
diff --git a/spec/features/projects/merge_request_button_spec.rb b/spec/features/projects/merge_request_button_spec.rb
index 12b4747602d..8cbd26551bc 100644
--- a/spec/features/projects/merge_request_button_spec.rb
+++ b/spec/features/projects/merge_request_button_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Merge Request button', feature: true do
+feature 'Merge Request button' do
shared_examples 'Merge request button only shown when allowed' do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
@@ -10,16 +10,14 @@ feature 'Merge Request button', feature: true do
it 'does not show Create merge request button' do
visit url
- within("#content-body") do
- expect(page).not_to have_link(label)
- end
+ expect(page).not_to have_link(label)
end
end
context 'logged in as developer' do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'shows Create merge request button' do
@@ -29,7 +27,7 @@ feature 'Merge Request button', feature: true do
visit url
- within("#content-body") do
+ within('#content-body') do
expect(page).to have_link(label, href: href)
end
end
@@ -42,7 +40,7 @@ feature 'Merge Request button', feature: true do
it 'does not show Create merge request button' do
visit url
- within("#content-body") do
+ within('#content-body') do
expect(page).not_to have_link(label)
end
end
@@ -57,7 +55,7 @@ feature 'Merge Request button', feature: true do
it 'does not show Create merge request button' do
visit url
- within("#content-body") do
+ within('#content-body') do
expect(page).not_to have_link(label)
end
end
diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb
index 033ccf06124..de6dd8fc8a6 100644
--- a/spec/features/projects/pipeline_schedules_spec.rb
+++ b/spec/features/projects/pipeline_schedules_spec.rb
@@ -70,6 +70,17 @@ feature 'Pipeline Schedules', :feature, js: true do
expect(first('.branch-name-cell').text).to eq('')
end
end
+
+ context 'when ref is empty' do
+ before do
+ pipeline_schedule.update_attribute(:ref, '')
+ visit_pipelines_schedules
+ end
+
+ it 'shows a list of the pipeline schedules with empty ref column' do
+ expect(first('.branch-name-cell').text).to eq('')
+ end
+ end
end
describe 'POST /projects/pipeline_schedules/new' do
@@ -128,6 +139,19 @@ feature 'Pipeline Schedules', :feature, js: true do
end
end
end
+
+ context 'when ref is empty' do
+ before do
+ pipeline_schedule.update_attribute(:ref, '')
+ edit_pipeline_schedule
+ end
+
+ it 'shows the pipeline schedule with default ref' do
+ page.within('.js-target-branch-dropdown') do
+ expect(first('.dropdown-toggle-text').text).to eq('master')
+ end
+ end
+ end
end
context 'when user creates a new pipeline schedule with variables' do
diff --git a/spec/features/projects/services/mattermost_slash_command_spec.rb b/spec/features/projects/services/mattermost_slash_command_spec.rb
index 584d3ed8f42..3319b0fedf3 100644
--- a/spec/features/projects/services/mattermost_slash_command_spec.rb
+++ b/spec/features/projects/services/mattermost_slash_command_spec.rb
@@ -159,7 +159,7 @@ feature 'Setup Mattermost slash commands', :feature, :js do
it 'shows the correct trigger url' do
value = find_field('request_url').value
- expect(value).to match("api/v3/projects/#{project.id}/services/mattermost_slash_commands/trigger")
+ expect(value).to match("api/v4/projects/#{project.id}/services/mattermost_slash_commands/trigger")
end
it 'shows a token placeholder' do
diff --git a/spec/features/projects/services/slack_slash_command_spec.rb b/spec/features/projects/services/slack_slash_command_spec.rb
index 4efe484262a..71f5a8d7a4e 100644
--- a/spec/features/projects/services/slack_slash_command_spec.rb
+++ b/spec/features/projects/services/slack_slash_command_spec.rb
@@ -40,6 +40,6 @@ feature 'Slack slash commands', feature: true do
it 'shows the correct trigger url' do
value = find_field('url').value
- expect(value).to match("api/v3/projects/#{project.id}/services/slack_slash_commands/trigger")
+ expect(value).to match("api/v4/projects/#{project.id}/services/slack_slash_commands/trigger")
end
end
diff --git a/spec/features/projects/settings/integration_settings_spec.rb b/spec/features/projects/settings/integration_settings_spec.rb
index 13313bfde24..6ae242af87f 100644
--- a/spec/features/projects/settings/integration_settings_spec.rb
+++ b/spec/features/projects/settings/integration_settings_spec.rb
@@ -36,14 +36,14 @@ feature 'Integration settings', feature: true do
expect(page.status_code).to eq(200)
expect(page).to have_content(hook.url)
expect(page).to have_content('SSL Verification: enabled')
- expect(page).to have_content('Push Events')
- expect(page).to have_content('Tag Push Events')
- expect(page).to have_content('Issues Events')
- expect(page).to have_content('Confidential Issues Events')
- expect(page).to have_content('Note Events')
- expect(page).to have_content('Merge Requests Events')
- expect(page).to have_content('Pipeline Events')
- expect(page).to have_content('Wiki Page Events')
+ expect(page).to have_content('Push events')
+ expect(page).to have_content('Tag push events')
+ expect(page).to have_content('Issues events')
+ expect(page).to have_content('Confidential issues events')
+ expect(page).to have_content('Note events')
+ expect(page).to have_content('Merge requests events')
+ expect(page).to have_content('Pipeline events')
+ expect(page).to have_content('Wiki page events')
end
scenario 'create webhook' do
@@ -58,8 +58,8 @@ feature 'Integration settings', feature: true do
expect(page).to have_content(url)
expect(page).to have_content('SSL Verification: enabled')
- expect(page).to have_content('Push Events')
- expect(page).to have_content('Tag Push Events')
+ expect(page).to have_content('Push events')
+ expect(page).to have_content('Tag push events')
expect(page).to have_content('Job events')
end
@@ -76,11 +76,12 @@ feature 'Integration settings', feature: true do
expect(page).to have_content(url)
end
- scenario 'test existing webhook' do
+ scenario 'test existing webhook', js: true do
WebMock.stub_request(:post, hook.url)
visit integrations_path
- click_link 'Test'
+ find('.hook-test-button.dropdown').click
+ click_link 'Push events'
expect(current_path).to eq(integrations_path)
end
diff --git a/spec/features/snippets/user_creates_snippet_spec.rb b/spec/features/snippets/user_creates_snippet_spec.rb
index 57dec14b480..698d3b5d3e3 100644
--- a/spec/features/snippets/user_creates_snippet_spec.rb
+++ b/spec/features/snippets/user_creates_snippet_spec.rb
@@ -41,7 +41,7 @@ feature 'User creates snippet', :js, feature: true do
expect(page).to have_content('My Snippet')
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
- expect(link).to match(%r{/uploads/temp/\h{32}/banana_sample\.gif\z})
+ expect(link).to match(%r{/uploads/system/temp/\h{32}/banana_sample\.gif\z})
visit(link)
expect(page.status_code).to eq(200)
@@ -59,7 +59,7 @@ feature 'User creates snippet', :js, feature: true do
wait_for_requests
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
- expect(link).to match(%r{/uploads/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z})
+ expect(link).to match(%r{/uploads/system/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z})
visit(link)
expect(page.status_code).to eq(200)
@@ -84,7 +84,7 @@ feature 'User creates snippet', :js, feature: true do
end
expect(page).to have_content('Hello World!')
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
- expect(link).to match(%r{/uploads/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z})
+ expect(link).to match(%r{/uploads/system/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z})
visit(link)
expect(page.status_code).to eq(200)
diff --git a/spec/features/snippets/user_edits_snippet_spec.rb b/spec/features/snippets/user_edits_snippet_spec.rb
index cff64423873..c9f9741b4bb 100644
--- a/spec/features/snippets/user_edits_snippet_spec.rb
+++ b/spec/features/snippets/user_edits_snippet_spec.rb
@@ -33,7 +33,7 @@ feature 'User edits snippet', :js, feature: true do
wait_for_requests
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
- expect(link).to match(%r{/uploads/personal_snippet/#{snippet.id}/\h{32}/banana_sample\.gif\z})
+ expect(link).to match(%r{/uploads/system/personal_snippet/#{snippet.id}/\h{32}/banana_sample\.gif\z})
end
it 'updates the snippet to make it internal' do
diff --git a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
index 32784de1613..5843f18d89f 100644
--- a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
+++ b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
@@ -18,7 +18,7 @@ feature 'User uploads avatar to group', feature: true do
visit group_path(group)
- expect(page).to have_selector(%Q(img[src$="/uploads/system/group/avatar/#{group.id}/dk.png"]))
+ expect(page).to have_selector(%Q(img[src$="/uploads/-/system/group/avatar/#{group.id}/dk.png"]))
# Cheating here to verify something that isn't user-facing, but is important
expect(group.reload.avatar.file).to exist
diff --git a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
index 82c356735b9..e8171dcaeb0 100644
--- a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
+++ b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
@@ -16,7 +16,7 @@ feature 'User uploads avatar to profile', feature: true do
visit user_path(user)
- expect(page).to have_selector(%Q(img[src$="/uploads/system/user/avatar/#{user.id}/dk.png"]))
+ expect(page).to have_selector(%Q(img[src$="/uploads/-/system/user/avatar/#{user.id}/dk.png"]))
# Cheating here to verify something that isn't user-facing, but is important
expect(user.reload.avatar.file).to exist
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index e0cad1da86a..f5e139685e8 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -59,13 +59,13 @@ describe ApplicationHelper do
describe 'project_icon' do
it 'returns an url for the avatar' do
project = create(:empty_project, avatar: File.open(uploaded_image_temp_path))
- avatar_url = "/uploads/system/project/avatar/#{project.id}/banana_sample.gif"
+ avatar_url = "/uploads/-/system/project/avatar/#{project.id}/banana_sample.gif"
expect(helper.project_icon(project.full_path).to_s)
.to eq "<img src=\"#{avatar_url}\" alt=\"Banana sample\" />"
allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host)
- avatar_url = "#{gitlab_host}/uploads/system/project/avatar/#{project.id}/banana_sample.gif"
+ avatar_url = "#{gitlab_host}/uploads/-/system/project/avatar/#{project.id}/banana_sample.gif"
expect(helper.project_icon(project.full_path).to_s)
.to eq "<img src=\"#{avatar_url}\" alt=\"Banana sample\" />"
@@ -88,7 +88,7 @@ describe ApplicationHelper do
context 'when there is a matching user' do
it 'returns a relative URL for the avatar' do
expect(helper.avatar_icon(user.email).to_s)
- .to eq("/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+ .to eq("/uploads/-/system/user/avatar/#{user.id}/banana_sample.gif")
end
context 'when an asset_host is set in the config' do
@@ -100,14 +100,14 @@ describe ApplicationHelper do
it 'returns an absolute URL on that asset host' do
expect(helper.avatar_icon(user.email, only_path: false).to_s)
- .to eq("#{asset_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+ .to eq("#{asset_host}/uploads/-/system/user/avatar/#{user.id}/banana_sample.gif")
end
end
context 'when only_path is set to false' do
it 'returns an absolute URL for the avatar' do
expect(helper.avatar_icon(user.email, only_path: false).to_s)
- .to eq("#{gitlab_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+ .to eq("#{gitlab_host}/uploads/-/system/user/avatar/#{user.id}/banana_sample.gif")
end
end
@@ -120,7 +120,7 @@ describe ApplicationHelper do
it 'returns a relative URL with the correct prefix' do
expect(helper.avatar_icon(user.email).to_s)
- .to eq("/gitlab/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+ .to eq("/gitlab/uploads/-/system/user/avatar/#{user.id}/banana_sample.gif")
end
end
end
@@ -138,14 +138,14 @@ describe ApplicationHelper do
context 'when only_path is true' do
it 'returns a relative URL for the avatar' do
expect(helper.avatar_icon(user, only_path: true).to_s)
- .to eq("/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+ .to eq("/uploads/-/system/user/avatar/#{user.id}/banana_sample.gif")
end
end
context 'when only_path is false' do
it 'returns an absolute URL for the avatar' do
expect(helper.avatar_icon(user, only_path: false).to_s)
- .to eq("#{gitlab_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+ .to eq("#{gitlab_host}/uploads/-/system/user/avatar/#{user.id}/banana_sample.gif")
end
end
end
diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb
index a0e1265efff..c94fedd615b 100644
--- a/spec/helpers/auth_helper_spec.rb
+++ b/spec/helpers/auth_helper_spec.rb
@@ -70,7 +70,7 @@ describe AuthHelper do
end
end
- [:twitter, :facebook, :google_oauth2, :gitlab, :github, :bitbucket, :crowd, :auth0].each do |provider|
+ [:twitter, :facebook, :google_oauth2, :gitlab, :github, :bitbucket, :crowd, :auth0, :authentiq].each do |provider|
it "returns false if the provider is #{provider}" do
expect(helper.unlink_allowed?(provider)).to be true
end
diff --git a/spec/helpers/emails_helper_spec.rb b/spec/helpers/emails_helper_spec.rb
index c68e4f56b05..2390c1f3e5d 100644
--- a/spec/helpers/emails_helper_spec.rb
+++ b/spec/helpers/emails_helper_spec.rb
@@ -52,7 +52,7 @@ describe EmailsHelper do
)
expect(header_logo).to eq(
- %{<img style="height: 50px" src="/uploads/system/appearance/header_logo/#{appearance.id}/dk.png" alt="Dk" />}
+ %{<img style="height: 50px" src="/uploads/-/system/appearance/header_logo/#{appearance.id}/dk.png" alt="Dk" />}
)
end
end
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index e3f9d9db9eb..3a246f10283 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -11,7 +11,7 @@ describe GroupsHelper do
group.avatar = fixture_file_upload(avatar_file_path)
group.save!
expect(group_icon(group.path).to_s)
- .to match("/uploads/system/group/avatar/#{group.id}/banana_sample.gif")
+ .to match("/uploads/-/system/group/avatar/#{group.id}/banana_sample.gif")
end
it 'gives default avatar_icon when no avatar is present' do
diff --git a/spec/helpers/hooks_helper_spec.rb b/spec/helpers/hooks_helper_spec.rb
new file mode 100644
index 00000000000..9f0004bf8cf
--- /dev/null
+++ b/spec/helpers/hooks_helper_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe HooksHelper do
+ let(:project) { create(:empty_project) }
+ let(:project_hook) { create(:project_hook, project: project) }
+ let(:system_hook) { create(:system_hook) }
+ let(:trigger) { 'push_events' }
+
+ describe '#link_to_test_hook' do
+ it 'returns project namespaced link' do
+ expect(helper.link_to_test_hook(project_hook, trigger))
+ .to include("href=\"#{test_project_hook_path(project, project_hook, trigger: trigger)}\"")
+ end
+
+ it 'returns admin namespaced link' do
+ expect(helper.link_to_test_hook(system_hook, trigger))
+ .to include("href=\"#{test_admin_hook_path(system_hook, trigger: trigger)}\"")
+ end
+ end
+end
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index b423a09873b..7789cfa3554 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -244,5 +244,25 @@ describe IssuablesHelper do
it { expect(helper.updated_at_by(unedited_issuable)).to eq({}) }
it { expect(helper.updated_at_by(edited_issuable)).to eq(edited_updated_at_by) }
+
+ context 'when updated by a deleted user' do
+ let(:edited_updated_at_by) do
+ {
+ updatedAt: edited_issuable.updated_at.to_time.iso8601,
+ updatedBy: {
+ name: User.ghost.name,
+ path: user_path(User.ghost)
+ }
+ }
+ end
+
+ before do
+ user.destroy
+ end
+
+ it 'returns "Ghost user" as edited_by' do
+ expect(helper.updated_at_by(edited_issuable.reload)).to eq(edited_updated_at_by)
+ end
+ end
end
end
diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb
index 95b4032616e..9aca3987657 100644
--- a/spec/helpers/page_layout_helper_spec.rb
+++ b/spec/helpers/page_layout_helper_spec.rb
@@ -60,7 +60,7 @@ describe PageLayoutHelper do
%w(project user group).each do |type|
context "with @#{type} assigned" do
it "uses #{type.titlecase} avatar if available" do
- object = double(avatar_url: 'http://example.com/uploads/system/avatar.png')
+ object = double(avatar_url: 'http://example.com/uploads/-/system/avatar.png')
assign(type, object)
expect(helper.page_image).to eq object.avatar_url
diff --git a/spec/javascripts/filtered_search/dropdown_user_spec.js b/spec/javascripts/filtered_search/dropdown_user_spec.js
index 0132f4b7c93..b3c9bca64cc 100644
--- a/spec/javascripts/filtered_search/dropdown_user_spec.js
+++ b/spec/javascripts/filtered_search/dropdown_user_spec.js
@@ -12,7 +12,9 @@ describe('Dropdown User', () => {
spyOn(gl.DropdownUser.prototype, 'getProjectId').and.callFake(() => {});
spyOn(gl.DropdownUtils, 'getSearchInput').and.callFake(() => {});
- dropdownUser = new gl.DropdownUser(null, null, null, gl.FilteredSearchTokenKeys);
+ dropdownUser = new gl.DropdownUser({
+ tokenKeys: gl.FilteredSearchTokenKeys,
+ });
});
it('should not return the double quote found in value', () => {
@@ -78,7 +80,10 @@ describe('Dropdown User', () => {
loadFixtures(fixtureTemplate);
authorFilterDropdownElement = document.querySelector('#js-dropdown-author');
const dummyInput = document.createElement('div');
- dropdown = new gl.DropdownUser(null, authorFilterDropdownElement, dummyInput);
+ dropdown = new gl.DropdownUser({
+ dropdown: authorFilterDropdownElement,
+ input: dummyInput,
+ });
});
const findCurrentUserElement = () => authorFilterDropdownElement.querySelector('.js-current-user');
diff --git a/spec/javascripts/fixtures/u2f.rb b/spec/javascripts/fixtures/u2f.rb
index c9c0b891237..e3d7986f2cf 100644
--- a/spec/javascripts/fixtures/u2f.rb
+++ b/spec/javascripts/fixtures/u2f.rb
@@ -10,10 +10,12 @@ context 'U2F' do
end
describe SessionsController, '(JavaScript fixtures)', type: :controller do
+ include DeviseHelpers
+
render_views
before do
- @request.env['devise.mapping'] = Devise.mappings[:user]
+ set_devise_mapping(context: @request)
end
it 'u2f/authenticate.html.raw' do |example|
diff --git a/spec/javascripts/vue_shared/components/commit_spec.js b/spec/javascripts/vue_shared/components/commit_spec.js
index 1c3188cdda2..d5754aaa9e7 100644
--- a/spec/javascripts/vue_shared/components/commit_spec.js
+++ b/spec/javascripts/vue_shared/components/commit_spec.js
@@ -22,7 +22,7 @@ describe('Commit component', () => {
shortSha: 'b7836edd',
title: 'Commit message',
author: {
- avatar_url: 'https://gitlab.com/uploads/system/user/avatar/300478/avatar.png',
+ avatar_url: 'https://gitlab.com/uploads/-/system/user/avatar/300478/avatar.png',
web_url: 'https://gitlab.com/jschatz1',
path: '/jschatz1',
username: 'jschatz1',
@@ -45,7 +45,7 @@ describe('Commit component', () => {
shortSha: 'b7836edd',
title: 'Commit message',
author: {
- avatar_url: 'https://gitlab.com/uploads/system/user/avatar/300478/avatar.png',
+ avatar_url: 'https://gitlab.com/uploads/-/system/user/avatar/300478/avatar.png',
web_url: 'https://gitlab.com/jschatz1',
path: '/jschatz1',
username: 'jschatz1',
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index ea79389e67e..ed571a2ba05 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -32,6 +32,28 @@ module Ci
end
end
+ describe 'retry entry' do
+ context 'when retry count is specified' do
+ let(:config) do
+ YAML.dump(rspec: { script: 'rspec', retry: 1 })
+ end
+
+ it 'includes retry count in build options attribute' do
+ expect(subject[:options]).to include(retry: 1)
+ end
+ end
+
+ context 'when retry count is not specified' do
+ let(:config) do
+ YAML.dump(rspec: { script: 'rspec' })
+ end
+
+ it 'does not persist retry count in the database' do
+ expect(subject[:options]).not_to have_key(:retry)
+ end
+ end
+ end
+
describe 'allow failure entry' do
context 'when job is a manual action' do
context 'when allow_failure is defined' do
diff --git a/spec/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder_spec.rb b/spec/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder_spec.rb
new file mode 100644
index 00000000000..a910fb105a5
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::MigrateSystemUploadsToNewFolder do
+ let(:migration) { described_class.new }
+
+ before do
+ allow(migration).to receive(:logger).and_return(Logger.new(nil))
+ end
+
+ describe '#perform' do
+ it 'renames the path of system-uploads', truncate: true do
+ upload = create(:upload, model: create(:empty_project), path: 'uploads/system/project/avatar.jpg')
+
+ migration.perform('uploads/system/', 'uploads/-/system/')
+
+ expect(upload.reload.path).to eq('uploads/-/system/project/avatar.jpg')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration_spec.rb b/spec/lib/gitlab/background_migration_spec.rb
index cfa59280139..4ad69aeba43 100644
--- a/spec/lib/gitlab/background_migration_spec.rb
+++ b/spec/lib/gitlab/background_migration_spec.rb
@@ -25,7 +25,7 @@ describe Gitlab::BackgroundMigration do
expect(queue[0]).to receive(:delete).and_return(true)
expect(described_class).to receive(:perform)
- .with('Foo', [10, 20], anything)
+ .with('Foo', [10, 20])
described_class.steal('Foo')
end
@@ -93,9 +93,9 @@ describe Gitlab::BackgroundMigration do
it 'steals from the scheduled sets queue first' do
Sidekiq::Testing.disable! do
expect(described_class).to receive(:perform)
- .with('Object', [1], anything).ordered
+ .with('Object', [1]).ordered
expect(described_class).to receive(:perform)
- .with('Object', [2], anything).ordered
+ .with('Object', [2]).ordered
BackgroundMigrationWorker.perform_async('Object', [2])
BackgroundMigrationWorker.perform_in(10.minutes, 'Object', [1])
diff --git a/spec/lib/gitlab/badge/build/metadata_spec.rb b/spec/lib/gitlab/badge/pipeline/metadata_spec.rb
index 9df96ea04eb..d537ce8803c 100644
--- a/spec/lib/gitlab/badge/build/metadata_spec.rb
+++ b/spec/lib/gitlab/badge/pipeline/metadata_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
require 'lib/gitlab/badge/shared/metadata'
-describe Gitlab::Badge::Build::Metadata do
+describe Gitlab::Badge::Pipeline::Metadata do
let(:badge) { double(project: create(:empty_project), ref: 'feature') }
let(:metadata) { described_class.new(badge) }
@@ -9,13 +9,13 @@ describe Gitlab::Badge::Build::Metadata do
describe '#title' do
it 'returns build status title' do
- expect(metadata.title).to eq 'build status'
+ expect(metadata.title).to eq 'pipeline status'
end
end
describe '#image_url' do
it 'returns valid url' do
- expect(metadata.image_url).to include 'badges/feature/build.svg'
+ expect(metadata.image_url).to include 'badges/feature/pipeline.svg'
end
end
diff --git a/spec/lib/gitlab/badge/build/status_spec.rb b/spec/lib/gitlab/badge/pipeline/status_spec.rb
index 6abf4ca46a9..dc835375c66 100644
--- a/spec/lib/gitlab/badge/build/status_spec.rb
+++ b/spec/lib/gitlab/badge/pipeline/status_spec.rb
@@ -1,36 +1,35 @@
require 'spec_helper'
-describe Gitlab::Badge::Build::Status do
+describe Gitlab::Badge::Pipeline::Status do
let(:project) { create(:project, :repository) }
let(:sha) { project.commit.sha }
let(:branch) { 'master' }
let(:badge) { described_class.new(project, branch) }
describe '#entity' do
- it 'always says build' do
- expect(badge.entity).to eq 'build'
+ it 'always says pipeline' do
+ expect(badge.entity).to eq 'pipeline'
end
end
describe '#template' do
it 'returns badge template' do
- expect(badge.template.key_text).to eq 'build'
+ expect(badge.template.key_text).to eq 'pipeline'
end
end
describe '#metadata' do
it 'returns badge metadata' do
- expect(badge.metadata.image_url)
- .to include 'badges/master/build.svg'
+ expect(badge.metadata.image_url).to include 'badges/master/pipeline.svg'
end
end
- context 'build exists' do
- let!(:build) { create_build(project, sha, branch) }
+ context 'pipeline exists' do
+ let!(:pipeline) { create_pipeline(project, sha, branch) }
- context 'build success' do
+ context 'pipeline success' do
before do
- build.success!
+ pipeline.success!
end
describe '#status' do
@@ -40,9 +39,9 @@ describe Gitlab::Badge::Build::Status do
end
end
- context 'build failed' do
+ context 'pipeline failed' do
before do
- build.drop!
+ pipeline.drop!
end
describe '#status' do
@@ -54,10 +53,10 @@ describe Gitlab::Badge::Build::Status do
context 'when outdated pipeline for given ref exists' do
before do
- build.success!
+ pipeline.success!
- old_build = create_build(project, '11eeffdd', branch)
- old_build.drop!
+ old_pipeline = create_pipeline(project, '11eeffdd', branch)
+ old_pipeline.drop!
end
it 'does not take outdated pipeline into account' do
@@ -67,10 +66,10 @@ describe Gitlab::Badge::Build::Status do
context 'when multiple pipelines exist for given sha' do
before do
- build.drop!
+ pipeline.drop!
- new_build = create_build(project, sha, branch)
- new_build.success!
+ new_pipeline = create_pipeline(project, sha, branch)
+ new_pipeline.success!
end
it 'does not take outdated pipeline into account' do
@@ -87,7 +86,7 @@ describe Gitlab::Badge::Build::Status do
end
end
- def create_build(project, sha, branch)
+ def create_pipeline(project, sha, branch)
pipeline = create(:ci_empty_pipeline,
project: project,
sha: sha,
diff --git a/spec/lib/gitlab/badge/build/template_spec.rb b/spec/lib/gitlab/badge/pipeline/template_spec.rb
index a7e21fb8bb1..20fa4f879c3 100644
--- a/spec/lib/gitlab/badge/build/template_spec.rb
+++ b/spec/lib/gitlab/badge/pipeline/template_spec.rb
@@ -1,28 +1,28 @@
require 'spec_helper'
-describe Gitlab::Badge::Build::Template do
- let(:badge) { double(entity: 'build', status: 'success') }
+describe Gitlab::Badge::Pipeline::Template do
+ let(:badge) { double(entity: 'pipeline', status: 'success') }
let(:template) { described_class.new(badge) }
describe '#key_text' do
- it 'is always says build' do
- expect(template.key_text).to eq 'build'
+ it 'is always says pipeline' do
+ expect(template.key_text).to eq 'pipeline'
end
end
describe '#value_text' do
it 'is status value' do
- expect(template.value_text).to eq 'success'
+ expect(template.value_text).to eq 'passed'
end
end
describe 'widths and text anchors' do
it 'has fixed width and text anchors' do
- expect(template.width).to eq 92
- expect(template.key_width).to eq 38
+ expect(template.width).to eq 116
+ expect(template.key_width).to eq 62
expect(template.value_width).to eq 54
- expect(template.key_text_anchor).to eq 19
- expect(template.value_text_anchor).to eq 65
+ expect(template.key_text_anchor).to eq 31
+ expect(template.value_text_anchor).to eq 89
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 c5cad887b64..6769f64f950 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -80,6 +80,45 @@ describe Gitlab::Ci::Config::Entry::Job do
expect(entry.errors).to include "job script can't be blank"
end
end
+
+ context 'when retry value is not correct' do
+ context 'when it is not a numeric value' do
+ let(:config) { { retry: true } }
+
+ it 'returns error about invalid type' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'job retry is not a number'
+ end
+ end
+
+ context 'when it is lower than zero' do
+ let(:config) { { retry: -1 } }
+
+ it 'returns error about value too low' do
+ expect(entry).not_to be_valid
+ expect(entry.errors)
+ .to include 'job retry must be greater than or equal to 0'
+ end
+ end
+
+ context 'when it is not an integer' do
+ let(:config) { { retry: 1.5 } }
+
+ it 'returns error about wrong value' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'job retry must be an integer'
+ end
+ end
+
+ context 'when the value is too high' do
+ let(:config) { { retry: 10 } }
+
+ it 'returns error about value too high' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'job retry must be less than or equal to 2'
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/trace/stream_spec.rb b/spec/lib/gitlab/ci/trace/stream_spec.rb
index bbb3f9912a3..8b925fd4e22 100644
--- a/spec/lib/gitlab/ci/trace/stream_spec.rb
+++ b/spec/lib/gitlab/ci/trace/stream_spec.rb
@@ -293,5 +293,41 @@ describe Gitlab::Ci::Trace::Stream do
it { is_expected.to eq("65") }
end
+
+ context 'malicious regexp' do
+ let(:data) { malicious_text }
+ let(:regex) { malicious_regexp }
+
+ include_examples 'malicious regexp'
+ end
+
+ context 'multi-line data with rooted regexp' do
+ let(:data) { "\n65%\n" }
+ let(:regex) { '^(\d+)\%$' }
+
+ it { is_expected.to eq('65') }
+ end
+
+ context 'empty regex' do
+ let(:data) { 'foo' }
+ let(:regex) { '' }
+
+ it 'skips processing' do
+ expect(stream).not_to receive(:read)
+
+ is_expected.to be_nil
+ end
+ end
+
+ context 'nil regex' do
+ let(:data) { 'foo' }
+ let(:regex) { nil }
+
+ it 'skips processing' do
+ expect(stream).not_to receive(:read)
+
+ is_expected.to be_nil
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/data_builder/wiki_page_spec.rb b/spec/lib/gitlab/data_builder/wiki_page_spec.rb
new file mode 100644
index 00000000000..a776d888c47
--- /dev/null
+++ b/spec/lib/gitlab/data_builder/wiki_page_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe Gitlab::DataBuilder::WikiPage do
+ let(:project) { create(:project, :repository) }
+ let(:wiki_page) { create(:wiki_page, wiki: project.wiki) }
+ let(:user) { create(:user) }
+
+ describe '.build' do
+ let(:data) { described_class.build(wiki_page, user, 'create') }
+
+ it { expect(data).to be_a(Hash) }
+ it { expect(data[:object_kind]).to eq('wiki_page') }
+ it { expect(data[:user]).to eq(user.hook_attrs) }
+ it { expect(data[:project]).to eq(project.hook_attrs) }
+ it { expect(data[:wiki]).to eq(project.wiki.hook_attrs) }
+
+ it { expect(data[:object_attributes]).to include(wiki_page.hook_attrs) }
+ it { expect(data[:object_attributes]).to include(url: Gitlab::UrlBuilder.build(wiki_page)) }
+ it { expect(data[:object_attributes]).to include(action: 'create') }
+ end
+end
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index 60de91324f0..730fdb112d9 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -91,7 +91,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
committer: committer
)
end
- let(:commit) { described_class.new(gitaly_commit) }
+ let(:commit) { described_class.new(Gitlab::GitalyClient::Commit.new(repository, gitaly_commit)) }
it { expect(commit.short_id).to eq(id[0..10]) }
it { expect(commit.id).to eq(id) }
@@ -290,69 +290,85 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
describe '.find_all' do
- it 'should return a return a collection of commits' do
- commits = described_class.find_all(repository)
+ shared_examples 'finding all commits' do
+ it 'should return a return a collection of commits' do
+ commits = described_class.find_all(repository)
- expect(commits).not_to be_empty
- expect(commits).to all( be_a_kind_of(Gitlab::Git::Commit) )
- end
-
- context 'while applying a sort order based on the `order` option' do
- it "allows ordering topologically (no parents shown before their children)" do
- expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_TOPO)
-
- described_class.find_all(repository, order: :topo)
+ expect(commits).to all( be_a_kind_of(Gitlab::Git::Commit) )
end
- it "allows ordering by date" do
- expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_DATE | Rugged::SORT_TOPO)
-
- described_class.find_all(repository, order: :date)
+ context 'max_count' do
+ subject do
+ commits = Gitlab::Git::Commit.find_all(
+ repository,
+ max_count: 50
+ )
+
+ commits.map(&:id)
+ end
+
+ it 'has 33 elements' do
+ expect(subject.size).to eq(33)
+ end
+
+ it 'includes the expected commits' do
+ expect(subject).to include(
+ SeedRepo::Commit::ID,
+ SeedRepo::Commit::PARENT_ID,
+ SeedRepo::FirstCommit::ID
+ )
+ end
end
- it "applies no sorting by default" do
- expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_NONE)
-
- described_class.find_all(repository)
+ context 'ref + max_count + skip' do
+ subject do
+ commits = Gitlab::Git::Commit.find_all(
+ repository,
+ ref: 'master',
+ max_count: 50,
+ skip: 1
+ )
+
+ commits.map(&:id)
+ end
+
+ it 'has 24 elements' do
+ expect(subject.size).to eq(24)
+ end
+
+ it 'includes the expected commits' do
+ expect(subject).to include(SeedRepo::Commit::ID, SeedRepo::FirstCommit::ID)
+ expect(subject).not_to include(SeedRepo::LastCommit::ID)
+ end
end
end
- context 'max_count' do
- subject do
- commits = Gitlab::Git::Commit.find_all(
- repository,
- max_count: 50
- )
+ context 'when Gitaly find_all_commits feature is enabled' do
+ it_behaves_like 'finding all commits'
+ end
- commits.map { |c| c.id }
- end
+ context 'when Gitaly find_all_commits feature is disabled', skip_gitaly_mock: true do
+ it_behaves_like 'finding all commits'
- it 'has 31 elements' do
- expect(subject.size).to eq(33)
- end
- it { is_expected.to include(SeedRepo::Commit::ID) }
- it { is_expected.to include(SeedRepo::Commit::PARENT_ID) }
- it { is_expected.to include(SeedRepo::FirstCommit::ID) }
- end
+ context 'while applying a sort order based on the `order` option' do
+ it "allows ordering topologically (no parents shown before their children)" do
+ expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_TOPO)
- context 'ref + max_count + skip' do
- subject do
- commits = Gitlab::Git::Commit.find_all(
- repository,
- ref: 'master',
- max_count: 50,
- skip: 1
- )
+ described_class.find_all(repository, order: :topo)
+ end
- commits.map { |c| c.id }
- end
+ it "allows ordering by date" do
+ expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_DATE | Rugged::SORT_TOPO)
+
+ described_class.find_all(repository, order: :date)
+ end
+
+ it "applies no sorting by default" do
+ expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_NONE)
- it 'has 23 elements' do
- expect(subject.size).to eq(24)
+ described_class.find_all(repository)
+ end
end
- it { is_expected.to include(SeedRepo::Commit::ID) }
- it { is_expected.to include(SeedRepo::FirstCommit::ID) }
- it { is_expected.not_to include(SeedRepo::LastCommit::ID) }
end
end
end
diff --git a/spec/lib/gitlab/git/diff_collection_spec.rb b/spec/lib/gitlab/git/diff_collection_spec.rb
index d20298fa139..0cfb210e390 100644
--- a/spec/lib/gitlab/git/diff_collection_spec.rb
+++ b/spec/lib/gitlab/git/diff_collection_spec.rb
@@ -484,6 +484,8 @@ describe Gitlab::Git::DiffCollection, seed_helper: true do
end
def each
+ return enum_for(:each) unless block_given?
+
loop do
break if @count.zero?
# It is critical to decrement before yielding. We may never reach the lines after 'yield'.
diff --git a/spec/lib/gitlab/git/tree_spec.rb b/spec/lib/gitlab/git/tree_spec.rb
index 4b76a43e6b5..98ddd3c3664 100644
--- a/spec/lib/gitlab/git/tree_spec.rb
+++ b/spec/lib/gitlab/git/tree_spec.rb
@@ -1,8 +1,9 @@
require "spec_helper"
describe Gitlab::Git::Tree, seed_helper: true do
+ let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH) }
+
context :repo do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH) }
let(:tree) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID) }
it { expect(tree).to be_kind_of Array }
@@ -74,4 +75,24 @@ describe Gitlab::Git::Tree, seed_helper: true do
it { expect(submodule.name).to eq('gitlab-shell') }
end
end
+
+ describe '#where' do
+ context 'with gitaly disabled' do
+ before do
+ allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false)
+ end
+
+ it 'calls #tree_entries_from_rugged' do
+ expect(described_class).to receive(:tree_entries_from_rugged)
+
+ described_class.where(repository, SeedRepo::Commit::ID, '/')
+ end
+ end
+
+ it 'gets the tree entries from GitalyClient' do
+ expect_any_instance_of(Gitlab::GitalyClient::CommitService).to receive(:tree_entries)
+
+ described_class.where(repository, SeedRepo::Commit::ID, '/')
+ end
+ end
end
diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
index 93affb12f2b..0868c793a33 100644
--- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
@@ -2,9 +2,13 @@ require 'spec_helper'
describe Gitlab::GitalyClient::CommitService do
let(:project) { create(:project, :repository) }
+ let(:storage_name) { project.repository_storage }
+ let(:relative_path) { project.path_with_namespace + '.git' }
let(:repository) { project.repository }
let(:repository_message) { repository.gitaly_repository }
- let(:commit) { project.commit('913c66a37b4a45b9769037c55c2d238bd0942d2e') }
+ let(:revision) { '913c66a37b4a45b9769037c55c2d238bd0942d2e' }
+ let(:commit) { project.commit(revision) }
+ let(:client) { described_class.new(repository) }
describe '#diff_from_parent' do
context 'when a commit has a parent' do
@@ -12,12 +16,15 @@ describe Gitlab::GitalyClient::CommitService do
request = Gitaly::CommitDiffRequest.new(
repository: repository_message,
left_commit_id: 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660',
- right_commit_id: commit.id
+ right_commit_id: commit.id,
+ collapse_diffs: true,
+ enforce_limits: true,
+ **Gitlab::Git::DiffCollection.collection_limits.to_h
)
expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_diff).with(request, kind_of(Hash))
- described_class.new(repository).diff_from_parent(commit)
+ client.diff_from_parent(commit)
end
end
@@ -27,27 +34,30 @@ describe Gitlab::GitalyClient::CommitService do
request = Gitaly::CommitDiffRequest.new(
repository: repository_message,
left_commit_id: '4b825dc642cb6eb9a060e54bf8d69288fbee4904',
- right_commit_id: initial_commit.id
+ right_commit_id: initial_commit.id,
+ collapse_diffs: true,
+ enforce_limits: true,
+ **Gitlab::Git::DiffCollection.collection_limits.to_h
)
expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_diff).with(request, kind_of(Hash))
- described_class.new(repository).diff_from_parent(initial_commit)
+ client.diff_from_parent(initial_commit)
end
end
it 'returns a Gitlab::Git::DiffCollection' do
- ret = described_class.new(repository).diff_from_parent(commit)
+ ret = client.diff_from_parent(commit)
expect(ret).to be_kind_of(Gitlab::Git::DiffCollection)
end
it 'passes options to Gitlab::Git::DiffCollection' do
- options = { max_files: 31, max_lines: 13 }
+ options = { max_files: 31, max_lines: 13, from_gitaly: true }
expect(Gitlab::Git::DiffCollection).to receive(:new).with(kind_of(Enumerable), options)
- described_class.new(repository).diff_from_parent(commit, options)
+ client.diff_from_parent(commit, options)
end
end
@@ -62,7 +72,7 @@ describe Gitlab::GitalyClient::CommitService do
expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_delta).with(request, kind_of(Hash)).and_return([])
- described_class.new(repository).commit_deltas(commit)
+ client.commit_deltas(commit)
end
end
@@ -77,7 +87,7 @@ describe Gitlab::GitalyClient::CommitService do
expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_delta).with(request, kind_of(Hash)).and_return([])
- described_class.new(repository).commit_deltas(initial_commit)
+ client.commit_deltas(initial_commit)
end
end
end
@@ -85,6 +95,7 @@ describe Gitlab::GitalyClient::CommitService do
describe '#between' do
let(:from) { 'master' }
let(:to) { '4b825dc642cb6eb9a060e54bf8d69288fbee4904' }
+
it 'sends an RPC request' do
request = Gitaly::CommitsBetweenRequest.new(
repository: repository_message, from: from, to: to
@@ -96,4 +107,17 @@ describe Gitlab::GitalyClient::CommitService do
described_class.new(repository).between(from, to)
end
end
+
+ describe '#tree_entries' do
+ let(:path) { '/' }
+
+ it 'sends a get_tree_entries message' do
+ expect_any_instance_of(Gitaly::CommitService::Stub)
+ .to receive(:get_tree_entries)
+ .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ .and_return([])
+
+ client.tree_entries(repository, revision, path)
+ end
+ end
end
diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb
index 51e2c3c38c6..251f82849bf 100644
--- a/spec/lib/gitlab/regex_spec.rb
+++ b/spec/lib/gitlab/regex_spec.rb
@@ -38,4 +38,15 @@ describe Gitlab::Regex, lib: true do
it { is_expected.not_to match('9foo') }
it { is_expected.not_to match('foo-') }
end
+
+ describe '.container_repository_name_regex' do
+ subject { described_class.container_repository_name_regex }
+
+ it { is_expected.to match('image') }
+ it { is_expected.to match('my/image') }
+ it { is_expected.to match('my/awesome/image-1') }
+ it { is_expected.to match('my/awesome/image.test') }
+ it { is_expected.not_to match('.my/image') }
+ it { is_expected.not_to match('my/image.') }
+ end
end
diff --git a/spec/lib/gitlab/route_map_spec.rb b/spec/lib/gitlab/route_map_spec.rb
index 21c00c6e5b8..e8feb21e4d7 100644
--- a/spec/lib/gitlab/route_map_spec.rb
+++ b/spec/lib/gitlab/route_map_spec.rb
@@ -55,6 +55,19 @@ describe Gitlab::RouteMap, lib: true do
end
describe '#public_path_for_source_path' do
+ context 'malicious regexp' do
+ include_examples 'malicious regexp'
+
+ subject do
+ map = described_class.new(<<-"MAP".strip_heredoc)
+ - source: '#{malicious_regexp}'
+ public: '/'
+ MAP
+
+ map.public_path_for_source_path(malicious_text)
+ end
+ end
+
subject do
described_class.new(<<-'MAP'.strip_heredoc)
# Team data
diff --git a/spec/lib/gitlab/untrusted_regexp_spec.rb b/spec/lib/gitlab/untrusted_regexp_spec.rb
new file mode 100644
index 00000000000..21d47b7897a
--- /dev/null
+++ b/spec/lib/gitlab/untrusted_regexp_spec.rb
@@ -0,0 +1,98 @@
+require 'spec_helper'
+
+describe Gitlab::UntrustedRegexp do
+ describe '#initialize' do
+ subject { described_class.new(pattern) }
+
+ context 'invalid regexp' do
+ let(:pattern) { '[' }
+
+ it { expect { subject }.to raise_error(RegexpError) }
+ end
+ end
+
+ describe '#replace_all' do
+ it 'replaces all instances of the match in a string' do
+ result = described_class.new('foo').replace_all('foo bar foo', 'oof')
+
+ expect(result).to eq('oof bar oof')
+ end
+ end
+
+ describe '#replace' do
+ it 'replaces the first instance of the match in a string' do
+ result = described_class.new('foo').replace('foo bar foo', 'oof')
+
+ expect(result).to eq('oof bar foo')
+ end
+ end
+
+ describe '#===' do
+ it 'returns true for a match' do
+ result = described_class.new('foo') === 'a foo here'
+
+ expect(result).to be_truthy
+ end
+
+ it 'returns false for no match' do
+ result = described_class.new('foo') === 'a bar here'
+
+ expect(result).to be_falsy
+ end
+ end
+
+ describe '#scan' do
+ subject { described_class.new(regexp).scan(text) }
+ context 'malicious regexp' do
+ let(:text) { malicious_text }
+ let(:regexp) { malicious_regexp }
+
+ include_examples 'malicious regexp'
+ end
+
+ context 'empty regexp' do
+ let(:regexp) { '' }
+ let(:text) { 'foo' }
+
+ it 'returns an array of empty matches' do
+ is_expected.to eq([''])
+ end
+ end
+
+ context 'empty capture group regexp' do
+ let(:regexp) { '()' }
+ let(:text) { 'foo' }
+
+ it 'returns an array of empty matches in an array' do
+ is_expected.to eq([['']])
+ end
+ end
+
+ context 'no capture group' do
+ let(:regexp) { '.+' }
+ let(:text) { 'foo' }
+
+ it 'returns the whole match' do
+ is_expected.to eq(['foo'])
+ end
+ end
+
+ context 'one capture group' do
+ let(:regexp) { '(f).+' }
+ let(:text) { 'foo' }
+
+ it 'returns the captured part' do
+ is_expected.to eq([%w[f]])
+ end
+ end
+
+ context 'two capture groups' do
+ let(:regexp) { '(f).(o)' }
+ let(:text) { 'foo' }
+
+ it 'returns the captured parts' do
+ is_expected.to eq([%w[f o]])
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index c6718827028..daf097f8d51 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -48,6 +48,7 @@ describe Gitlab::UsageData do
milestones
notes
projects
+ projects_imported_from_github
projects_prometheus_active
pages_domains
protected_branches
diff --git a/spec/migrations/clean_appearance_symlinks_spec.rb b/spec/migrations/clean_appearance_symlinks_spec.rb
new file mode 100644
index 00000000000..9225dc0d894
--- /dev/null
+++ b/spec/migrations/clean_appearance_symlinks_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20170613111224_clean_appearance_symlinks.rb')
+
+describe CleanAppearanceSymlinks do
+ let(:migration) { described_class.new }
+ let(:test_dir) { File.join(Rails.root, "tmp", "tests", "clean_appearance_test") }
+ let(:uploads_dir) { File.join(test_dir, "public", "uploads") }
+ let(:new_uploads_dir) { File.join(uploads_dir, "system") }
+ let(:original_path) { File.join(new_uploads_dir, 'appearance') }
+ let(:symlink_path) { File.join(uploads_dir, 'appearance') }
+
+ before do
+ FileUtils.remove_dir(test_dir) if File.directory?(test_dir)
+ FileUtils.mkdir_p(uploads_dir)
+ allow(migration).to receive(:base_directory).and_return(test_dir)
+ allow(migration).to receive(:say)
+ end
+
+ describe "#up" do
+ before do
+ FileUtils.mkdir_p(original_path)
+ FileUtils.ln_s(original_path, symlink_path)
+ end
+
+ it 'removes the symlink' do
+ migration.up
+
+ expect(File.symlink?(symlink_path)).to be(false)
+ end
+ end
+
+ describe '#down' do
+ before do
+ FileUtils.mkdir_p(File.join(original_path))
+ FileUtils.touch(File.join(original_path, 'dummy.file'))
+ end
+
+ it 'creates a symlink' do
+ expected_path = File.join(symlink_path, "dummy.file")
+ migration.down
+
+ expect(File.exist?(expected_path)).to be(true)
+ expect(File.symlink?(symlink_path)).to be(true)
+ end
+ end
+end
diff --git a/spec/migrations/clean_stage_id_reference_migration_spec.rb b/spec/migrations/clean_stage_id_reference_migration_spec.rb
new file mode 100644
index 00000000000..9a581df28a2
--- /dev/null
+++ b/spec/migrations/clean_stage_id_reference_migration_spec.rb
@@ -0,0 +1,34 @@
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20170710083355_clean_stage_id_reference_migration.rb')
+
+describe CleanStageIdReferenceMigration, :migration, :sidekiq, :redis do
+ let(:migration_class) { 'MigrateBuildStageIdReference' }
+ let(:migration) { spy('migration') }
+
+ before do
+ allow(Gitlab::BackgroundMigration.const_get(migration_class))
+ .to receive(:new).and_return(migration)
+ end
+
+ context 'when there are pending background migrations' do
+ it 'processes pending jobs synchronously' do
+ Sidekiq::Testing.disable! do
+ BackgroundMigrationWorker.perform_in(2.minutes, migration_class, [1, 1])
+ BackgroundMigrationWorker.perform_async(migration_class, [1, 1])
+
+ migrate!
+
+ expect(migration).to have_received(:perform).with(1, 1).twice
+ end
+ end
+ end
+ context 'when there are no background migrations pending' do
+ it 'does nothing' do
+ Sidekiq::Testing.disable! do
+ migrate!
+
+ expect(migration).not_to have_received(:perform)
+ end
+ end
+ end
+end
diff --git a/spec/migrations/cleanup_move_system_upload_folder_symlink_spec.rb b/spec/migrations/cleanup_move_system_upload_folder_symlink_spec.rb
new file mode 100644
index 00000000000..3a9fa8c7113
--- /dev/null
+++ b/spec/migrations/cleanup_move_system_upload_folder_symlink_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+require Rails.root.join("db", "post_migrate", "20170717111152_cleanup_move_system_upload_folder_symlink.rb")
+
+describe CleanupMoveSystemUploadFolderSymlink do
+ let(:migration) { described_class.new }
+ let(:test_base) { File.join(Rails.root, 'tmp', 'tests', 'move-system-upload-folder') }
+ let(:test_folder) { File.join(test_base, '-', 'system') }
+
+ before do
+ allow(migration).to receive(:base_directory).and_return(test_base)
+ FileUtils.rm_rf(test_base)
+ FileUtils.mkdir_p(test_folder)
+ allow(migration).to receive(:say)
+ end
+
+ describe '#up' do
+ before do
+ FileUtils.ln_s(test_folder, File.join(test_base, 'system'))
+ end
+
+ it 'removes the symlink' do
+ migration.up
+
+ expect(File.exist?(File.join(test_base, 'system'))).to be_falsey
+ end
+ end
+
+ describe '#down' do
+ it 'creates the symlink' do
+ migration.down
+
+ expect(File.symlink?(File.join(test_base, 'system'))).to be_truthy
+ end
+ end
+end
diff --git a/spec/migrations/move_personal_snippets_files_spec.rb b/spec/migrations/move_personal_snippets_files_spec.rb
new file mode 100644
index 00000000000..8505c7bf3e3
--- /dev/null
+++ b/spec/migrations/move_personal_snippets_files_spec.rb
@@ -0,0 +1,180 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20170612071012_move_personal_snippets_files.rb')
+
+describe MovePersonalSnippetsFiles do
+ let(:migration) { described_class.new }
+ let(:test_dir) { File.join(Rails.root, "tmp", "tests", "move_snippet_files_test") }
+ let(:uploads_dir) { File.join(test_dir, 'uploads') }
+ let(:new_uploads_dir) { File.join(uploads_dir, 'system') }
+
+ before do
+ allow(CarrierWave).to receive(:root).and_return(test_dir)
+ allow(migration).to receive(:base_directory).and_return(test_dir)
+ FileUtils.remove_dir(test_dir) if File.directory?(test_dir)
+ allow(migration).to receive(:say)
+ end
+
+ describe "#up" do
+ let(:snippet) do
+ snippet = create(:personal_snippet)
+ create_upload('picture.jpg', snippet)
+ snippet.update(description: markdown_linking_file('picture.jpg', snippet))
+ snippet
+ end
+
+ let(:snippet_with_missing_file) do
+ snippet = create(:snippet)
+ create_upload('picture.jpg', snippet, create_file: false)
+ snippet.update(description: markdown_linking_file('picture.jpg', snippet))
+ snippet
+ end
+
+ it 'moves the files' do
+ source_path = File.join(uploads_dir, model_file_path('picture.jpg', snippet))
+ destination_path = File.join(new_uploads_dir, model_file_path('picture.jpg', snippet))
+
+ migration.up
+
+ expect(File.exist?(source_path)).to be_falsy
+ expect(File.exist?(destination_path)).to be_truthy
+ end
+
+ describe 'updating the markdown' do
+ it 'includes the new path when the file exists' do
+ secret = "secret#{snippet.id}"
+ file_location = "/uploads/system/personal_snippet/#{snippet.id}/#{secret}/picture.jpg"
+
+ migration.up
+
+ expect(snippet.reload.description).to include(file_location)
+ end
+
+ it 'does not update the markdown when the file is missing' do
+ secret = "secret#{snippet_with_missing_file.id}"
+ file_location = "/uploads/personal_snippet/#{snippet_with_missing_file.id}/#{secret}/picture.jpg"
+
+ migration.up
+
+ expect(snippet_with_missing_file.reload.description).to include(file_location)
+ end
+
+ it 'updates the note markdown' do
+ secret = "secret#{snippet.id}"
+ file_location = "/uploads/system/personal_snippet/#{snippet.id}/#{secret}/picture.jpg"
+ markdown = markdown_linking_file('picture.jpg', snippet)
+ note = create(:note_on_personal_snippet, noteable: snippet, note: "with #{markdown}")
+
+ migration.up
+
+ expect(note.reload.note).to include(file_location)
+ end
+ end
+ end
+
+ describe "#down" do
+ let(:snippet) do
+ snippet = create(:personal_snippet)
+ create_upload('picture.jpg', snippet, in_new_path: true)
+ snippet.update(description: markdown_linking_file('picture.jpg', snippet, in_new_path: true))
+ snippet
+ end
+
+ let(:snippet_with_missing_file) do
+ snippet = create(:personal_snippet)
+ create_upload('picture.jpg', snippet, create_file: false, in_new_path: true)
+ snippet.update(description: markdown_linking_file('picture.jpg', snippet, in_new_path: true))
+ snippet
+ end
+
+ it 'moves the files' do
+ source_path = File.join(new_uploads_dir, model_file_path('picture.jpg', snippet))
+ destination_path = File.join(uploads_dir, model_file_path('picture.jpg', snippet))
+
+ migration.down
+
+ expect(File.exist?(source_path)).to be_falsey
+ expect(File.exist?(destination_path)).to be_truthy
+ end
+
+ describe 'updating the markdown' do
+ it 'includes the new path when the file exists' do
+ secret = "secret#{snippet.id}"
+ file_location = "/uploads/personal_snippet/#{snippet.id}/#{secret}/picture.jpg"
+
+ migration.down
+
+ expect(snippet.reload.description).to include(file_location)
+ end
+
+ it 'keeps the markdown as is when the file is missing' do
+ secret = "secret#{snippet_with_missing_file.id}"
+ file_location = "/uploads/system/personal_snippet/#{snippet_with_missing_file.id}/#{secret}/picture.jpg"
+
+ migration.down
+
+ expect(snippet_with_missing_file.reload.description).to include(file_location)
+ end
+
+ it 'updates the note markdown' do
+ markdown = markdown_linking_file('picture.jpg', snippet, in_new_path: true)
+ secret = "secret#{snippet.id}"
+ file_location = "/uploads/personal_snippet/#{snippet.id}/#{secret}/picture.jpg"
+ note = create(:note_on_personal_snippet, noteable: snippet, note: "with #{markdown}")
+
+ migration.down
+
+ expect(note.reload.note).to include(file_location)
+ end
+ end
+ end
+
+ describe '#update_markdown' do
+ it 'escapes sql in the snippet description' do
+ migration.instance_variable_set('@source_relative_location', '/uploads/personal_snippet')
+ migration.instance_variable_set('@destination_relative_location', '/uploads/system/personal_snippet')
+
+ secret = '123456789'
+ filename = 'hello.jpg'
+ snippet = create(:personal_snippet)
+
+ path_before = "/uploads/personal_snippet/#{snippet.id}/#{secret}/#{filename}"
+ path_after = "/uploads/system/personal_snippet/#{snippet.id}/#{secret}/#{filename}"
+ description_before = "Hello world; ![image](#{path_before})'; select * from users;"
+ description_after = "Hello world; ![image](#{path_after})'; select * from users;"
+
+ migration.update_markdown(snippet.id, secret, filename, description_before)
+
+ expect(snippet.reload.description).to eq(description_after)
+ end
+ end
+
+ def create_upload(filename, snippet, create_file: true, in_new_path: false)
+ secret = "secret#{snippet.id}"
+ absolute_path = if in_new_path
+ File.join(new_uploads_dir, model_file_path(filename, snippet))
+ else
+ File.join(uploads_dir, model_file_path(filename, snippet))
+ end
+
+ if create_file
+ FileUtils.mkdir_p(File.dirname(absolute_path))
+ FileUtils.touch(absolute_path)
+ end
+
+ create(:upload, model: snippet, path: "#{secret}/#{filename}", uploader: PersonalFileUploader)
+ end
+
+ def markdown_linking_file(filename, snippet, in_new_path: false)
+ markdown = "![#{filename.split('.')[0]}]"
+ markdown += '(/uploads'
+ markdown += '/system' if in_new_path
+ markdown += "/#{model_file_path(filename, snippet)})"
+ markdown
+ end
+
+ def model_file_path(filename, snippet)
+ secret = "secret#{snippet.id}"
+
+ File.join('personal_snippet', snippet.id.to_s, secret, filename)
+ end
+end
diff --git a/spec/migrations/move_system_upload_folder_spec.rb b/spec/migrations/move_system_upload_folder_spec.rb
new file mode 100644
index 00000000000..b622b4e9536
--- /dev/null
+++ b/spec/migrations/move_system_upload_folder_spec.rb
@@ -0,0 +1,62 @@
+require 'spec_helper'
+require Rails.root.join("db", "migrate", "20170717074009_move_system_upload_folder.rb")
+
+describe MoveSystemUploadFolder do
+ let(:migration) { described_class.new }
+ let(:test_base) { File.join(Rails.root, 'tmp', 'tests', 'move-system-upload-folder') }
+
+ before do
+ allow(migration).to receive(:base_directory).and_return(test_base)
+ FileUtils.rm_rf(test_base)
+ FileUtils.mkdir_p(test_base)
+ allow(migration).to receive(:say)
+ end
+
+ describe '#up' do
+ let(:test_folder) { File.join(test_base, 'system') }
+ let(:test_file) { File.join(test_folder, 'file') }
+
+ before do
+ FileUtils.mkdir_p(test_folder)
+ FileUtils.touch(test_file)
+ end
+
+ it 'moves the related folder' do
+ migration.up
+
+ expect(File.exist?(File.join(test_base, '-', 'system', 'file'))).to be_truthy
+ end
+
+ it 'creates a symlink linking making the new folder available on the old path' do
+ migration.up
+
+ expect(File.symlink?(File.join(test_base, 'system'))).to be_truthy
+ expect(File.exist?(File.join(test_base, 'system', 'file'))).to be_truthy
+ end
+ end
+
+ describe '#down' do
+ let(:test_folder) { File.join(test_base, '-', 'system') }
+ let(:test_file) { File.join(test_folder, 'file') }
+
+ before do
+ FileUtils.mkdir_p(test_folder)
+ FileUtils.touch(test_file)
+ end
+
+ it 'moves the system folder back to the old location' do
+ migration.down
+
+ expect(File.exist?(File.join(test_base, 'system', 'file'))).to be_truthy
+ end
+
+ it 'removes the symlink if it existed' do
+ FileUtils.ln_s(test_folder, File.join(test_base, 'system'))
+
+ migration.down
+
+ expect(File.directory?(File.join(test_base, 'system'))).to be_truthy
+ expect(File.symlink?(File.join(test_base, 'system'))).to be_falsey
+ end
+ end
+end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 154b6759f46..0b521d720f3 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -802,6 +802,47 @@ describe Ci::Build, :models do
end
end
+ describe 'build auto retry feature' do
+ describe '#retries_count' do
+ subject { create(:ci_build, name: 'test', pipeline: pipeline) }
+
+ context 'when build has been retried several times' do
+ before do
+ create(:ci_build, :retried, name: 'test', pipeline: pipeline)
+ create(:ci_build, :retried, name: 'test', pipeline: pipeline)
+ end
+
+ it 'reports a correct retry count value' do
+ expect(subject.retries_count).to eq 2
+ end
+ end
+
+ context 'when build has not been retried' do
+ it 'returns zero' do
+ expect(subject.retries_count).to eq 0
+ end
+ end
+ end
+
+ describe '#retries_max' do
+ context 'when max retries value is defined' do
+ subject { create(:ci_build, options: { retry: 1 }) }
+
+ it 'returns a number of configured max retries' do
+ expect(subject.retries_max).to eq 1
+ end
+ end
+
+ context 'when max retries value is not defined' do
+ subject { create(:ci_build) }
+
+ it 'returns zero' do
+ expect(subject.retries_max).to eq 0
+ end
+ end
+ end
+ end
+
describe '#keep_artifacts!' do
let(:build) { create(:ci_build, artifacts_expire_at: Time.now + 7.days) }
@@ -1583,7 +1624,7 @@ describe Ci::Build, :models do
end
end
- describe 'State transition: any => [:pending]' do
+ describe 'state transition: any => [:pending]' do
let(:build) { create(:ci_build, :created) }
it 'queues BuildQueueWorker' do
@@ -1592,4 +1633,35 @@ describe Ci::Build, :models do
build.enqueue
end
end
+
+ describe 'state transition when build fails' do
+ context 'when build is configured to be retried' do
+ subject { create(:ci_build, :running, options: { retry: 3 }) }
+
+ it 'retries builds and assigns a same user to it' do
+ expect(described_class).to receive(:retry)
+ .with(subject, subject.user)
+
+ subject.drop!
+ end
+ end
+
+ context 'when build is not configured to be retried' do
+ subject { create(:ci_build, :running) }
+
+ it 'does not retry build' do
+ expect(described_class).not_to receive(:retry)
+
+ subject.drop!
+ end
+
+ it 'does not count retries when not necessary' do
+ expect(described_class).not_to receive(:retry)
+ expect_any_instance_of(described_class)
+ .not_to receive(:retries_count)
+
+ subject.drop!
+ end
+ end
+ end
end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 066d7b9307f..770176451fe 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -189,7 +189,7 @@ describe Group, models: true do
let!(:group) { create(:group, :access_requestable, :with_avatar) }
let(:user) { create(:user) }
let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" }
- let(:avatar_path) { "/uploads/system/group/avatar/#{group.id}/dk.png" }
+ let(:avatar_path) { "/uploads/-/system/group/avatar/#{group.id}/dk.png" }
context 'when avatar file is uploaded' do
before do
diff --git a/spec/models/hooks/project_hook_spec.rb b/spec/models/hooks/project_hook_spec.rb
index 474ae62ccec..0af270014b5 100644
--- a/spec/models/hooks/project_hook_spec.rb
+++ b/spec/models/hooks/project_hook_spec.rb
@@ -1,10 +1,14 @@
require 'spec_helper'
describe ProjectHook, models: true do
- describe "Associations" do
+ describe 'associations' do
it { is_expected.to belong_to :project }
end
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:project) }
+ end
+
describe '.push_hooks' do
it 'returns hooks for push events only' do
hook = create(:project_hook, push_events: true)
diff --git a/spec/models/hooks/service_hook_spec.rb b/spec/models/hooks/service_hook_spec.rb
index 57454d2a773..8e871a41a8c 100644
--- a/spec/models/hooks/service_hook_spec.rb
+++ b/spec/models/hooks/service_hook_spec.rb
@@ -5,6 +5,10 @@ describe ServiceHook, models: true do
it { is_expected.to belong_to :service }
end
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:service) }
+ end
+
describe 'execute' do
let(:hook) { build(:service_hook) }
let(:data) { { key: 'value' } }
diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index 0d2b622132e..559778257fa 100644
--- a/spec/models/hooks/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -7,8 +7,7 @@ describe SystemHook, models: true do
it 'sets defined default parameters' do
attrs = {
push_events: false,
- repository_update_events: true,
- enable_ssl_verification: true
+ repository_update_events: true
}
expect(system_hook).to have_attributes(attrs)
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 89ea5ceda95..a4090b37f65 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -44,7 +44,7 @@ describe Namespace, models: true do
end
context "is case insensitive" do
- let(:group) { build(:group, path: "System") }
+ let(:group) { build(:group, path: "Groups") }
it { expect(group).not_to be_valid }
end
diff --git a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
index 6ee30e86495..d45e0a441d4 100644
--- a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
+++ b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
@@ -43,7 +43,7 @@ describe GitlabIssueTrackerService, models: true do
end
it 'gives the correct path' do
- expect(service.project_path).to eq("/gitlab/root/#{project.path_with_namespace}/issues")
+ expect(service.issue_tracker_path).to eq("/gitlab/root/#{project.path_with_namespace}/issues")
expect(service.new_issue_path).to eq("/gitlab/root/#{project.path_with_namespace}/issues/new")
expect(service.issue_path(432)).to eq("/gitlab/root/#{project.path_with_namespace}/issues/432")
end
diff --git a/spec/models/project_services/microsoft_teams_service_spec.rb b/spec/models/project_services/microsoft_teams_service_spec.rb
index bd50a2d1470..fb95c4cda35 100644
--- a/spec/models/project_services/microsoft_teams_service_spec.rb
+++ b/spec/models/project_services/microsoft_teams_service_spec.rb
@@ -112,7 +112,7 @@ describe MicrosoftTeamsService, models: true do
let(:wiki_page_sample_data) do
service = WikiPages::CreateService.new(project, user, opts)
wiki_page = service.execute
- service.hook_data(wiki_page, 'create')
+ Gitlab::DataBuilder::WikiPage.build(wiki_page, user, 'create')
end
it "calls Microsoft Teams API" do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index e636250c37d..fdcb011d685 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -309,10 +309,14 @@ describe Project, models: true do
end
describe 'delegation' do
- it { is_expected.to delegate_method(:add_guest).to(:team) }
- it { is_expected.to delegate_method(:add_reporter).to(:team) }
- it { is_expected.to delegate_method(:add_developer).to(:team) }
- it { is_expected.to delegate_method(:add_master).to(:team) }
+ [:add_guest, :add_reporter, :add_developer, :add_master, :add_user, :add_users].each do |method|
+ it { is_expected.to delegate_method(method).to(:team) }
+ end
+
+ it { is_expected.to delegate_method(:empty_repo?).to(:repository) }
+ it { is_expected.to delegate_method(:members).to(:team).with_prefix(true) }
+ it { is_expected.to delegate_method(:count).to(:forks).with_prefix(true) }
+ it { is_expected.to delegate_method(:name).to(:owner).with_prefix(true).with_arguments(allow_nil: true) }
end
describe '#to_reference' do
@@ -807,7 +811,7 @@ describe Project, models: true do
context 'when avatar file is uploaded' do
let(:project) { create(:empty_project, :with_avatar) }
- let(:avatar_path) { "/uploads/system/project/avatar/#{project.id}/dk.png" }
+ let(:avatar_path) { "/uploads/-/system/project/avatar/#{project.id}/dk.png" }
let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" }
it 'shows correct url' do
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 69f2570eec2..a1d6d7e6e0b 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -1028,7 +1028,7 @@ describe User, models: true do
context 'when avatar file is uploaded' do
let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" }
- let(:avatar_path) { "/uploads/system/user/avatar/#{user.id}/dk.png" }
+ let(:avatar_path) { "/uploads/-/system/user/avatar/#{user.id}/dk.png" }
it 'shows correct avatar url' do
expect(user.avatar_url).to eq(avatar_path)
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index fa704f23857..6dbde8bad31 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -442,7 +442,7 @@ describe API::Projects do
post api('/projects', user), project
project_id = json_response['id']
- expect(json_response['avatar_url']).to eq("http://localhost/uploads/system/project/avatar/#{project_id}/banana_sample.gif")
+ expect(json_response['avatar_url']).to eq("http://localhost/uploads/-/system/project/avatar/#{project_id}/banana_sample.gif")
end
it 'sets a project as allowing merge even if build fails' do
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index c969d08d0dd..49e815ee16c 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -69,6 +69,72 @@ describe Ci::API::Builds do
end
end
+ context 'when an old image syntax is used' do
+ before do
+ build.update!(options: { image: 'codeclimate' })
+ end
+
+ it 'starts a build' do
+ register_builds info: { platform: :darwin }
+
+ expect(response).to have_http_status(201)
+ expect(json_response["options"]).to eq({ "image" => "codeclimate" })
+ end
+ end
+
+ context 'when a new image syntax is used' do
+ before do
+ build.update!(options: { image: { name: 'codeclimate' } })
+ end
+
+ it 'starts a build' do
+ register_builds info: { platform: :darwin }
+
+ expect(response).to have_http_status(201)
+ expect(json_response["options"]).to eq({ "image" => "codeclimate" })
+ end
+ end
+
+ context 'when an old service syntax is used' do
+ before do
+ build.update!(options: { services: ['mysql'] })
+ end
+
+ it 'starts a build' do
+ register_builds info: { platform: :darwin }
+
+ expect(response).to have_http_status(201)
+ expect(json_response["options"]).to eq({ "services" => ["mysql"] })
+ end
+ end
+
+ context 'when a new service syntax is used' do
+ before do
+ build.update!(options: { services: [name: 'mysql'] })
+ end
+
+ it 'starts a build' do
+ register_builds info: { platform: :darwin }
+
+ expect(response).to have_http_status(201)
+ expect(json_response["options"]).to eq({ "services" => ["mysql"] })
+ end
+ end
+
+ context 'when no image or service is defined' do
+ before do
+ build.update!(options: {})
+ end
+
+ it 'starts a build' do
+ register_builds info: { platform: :darwin }
+
+ expect(response).to have_http_status(201)
+
+ expect(json_response["options"]).to be_empty
+ end
+ end
+
context 'when there is a pending build' do
it 'starts a build' do
register_builds info: { platform: :darwin }
diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb
index ebba28ba8ce..a927de952d0 100644
--- a/spec/requests/openid_connect_spec.rb
+++ b/spec/requests/openid_connect_spec.rb
@@ -79,7 +79,7 @@ describe 'OpenID Connect requests' do
'email_verified' => true,
'website' => 'https://example.com',
'profile' => 'http://localhost/alice',
- 'picture' => "http://localhost/uploads/system/user/avatar/#{user.id}/dk.png"
+ 'picture' => "http://localhost/uploads/-/system/user/avatar/#{user.id}/dk.png"
})
end
end
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 2f1c3c95e59..65314b688a4 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -609,4 +609,26 @@ describe 'project routing' do
expect(get('/gitlab/gitlabhq/pages/domains/my.domain.com')).to route_to('projects/pages_domains#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'my.domain.com')
end
end
+
+ describe Projects::Registry::TagsController, :routing do
+ describe '#destroy' do
+ it 'correctly routes to a destroy action' do
+ expect(delete('/gitlab/gitlabhq/registry/repository/1/tags/rc1'))
+ .to route_to('projects/registry/tags#destroy',
+ namespace_id: 'gitlab',
+ project_id: 'gitlabhq',
+ repository_id: '1',
+ id: 'rc1')
+ end
+
+ it 'takes registry tag name constrains into account' do
+ expect(delete('/gitlab/gitlabhq/registry/repository/1/tags/-rc1'))
+ .not_to route_to('projects/registry/tags#destroy',
+ namespace_id: 'gitlab',
+ project_id: 'gitlabhq',
+ repository_id: '1',
+ id: '-rc1')
+ end
+ end
+ end
end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index 77c07b71c68..ba07c01d43f 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -40,7 +40,7 @@ describe Ci::CreatePipelineService, :services do
it 'increments the prometheus counter' do
expect(Gitlab::Metrics).to receive(:counter)
- .with(:pipelines_created_count, "Pipelines created count")
+ .with(:pipelines_created_total, "Counter of pipelines created")
.and_call_original
pipeline
@@ -320,5 +320,19 @@ describe Ci::CreatePipelineService, :services do
end.not_to change { Environment.count }
end
end
+
+ context 'when builds with auto-retries are configured' do
+ before do
+ config = YAML.dump(rspec: { script: 'rspec', retry: 2 })
+ stub_ci_pipeline_yaml_file(config)
+ end
+
+ it 'correctly creates builds with auto-retry value configured' do
+ pipeline = execute_service
+
+ expect(pipeline).to be_persisted
+ expect(pipeline.builds.find_by(name: 'rspec').retries_max).to eq 2
+ end
+ end
end
end
diff --git a/spec/services/ci/process_pipeline_service_spec.rb b/spec/services/ci/process_pipeline_service_spec.rb
index efcaccc254e..0934833a4fa 100644
--- a/spec/services/ci/process_pipeline_service_spec.rb
+++ b/spec/services/ci/process_pipeline_service_spec.rb
@@ -463,6 +463,35 @@ describe Ci::ProcessPipelineService, '#execute', :services do
end
end
+ context 'when builds with auto-retries are configured' do
+ before do
+ create_build('build:1', stage_idx: 0, user: user, options: { retry: 2 })
+ create_build('test:1', stage_idx: 1, user: user, when: :on_failure)
+ create_build('test:2', stage_idx: 1, user: user, options: { retry: 1 })
+ end
+
+ it 'automatically retries builds in a valid order' do
+ expect(process_pipeline).to be_truthy
+
+ fail_running_or_pending
+
+ expect(builds_names).to eq %w[build:1 build:1]
+ expect(builds_statuses).to eq %w[failed pending]
+
+ succeed_running_or_pending
+
+ expect(builds_names).to eq %w[build:1 build:1 test:2]
+ expect(builds_statuses).to eq %w[failed success pending]
+
+ succeed_running_or_pending
+
+ expect(builds_names).to eq %w[build:1 build:1 test:2]
+ expect(builds_statuses).to eq %w[failed success success]
+
+ expect(pipeline.reload).to be_success
+ end
+ end
+
def process_pipeline
described_class.new(pipeline.project, user).execute(pipeline)
end
diff --git a/spec/services/projects/participants_service_spec.rb b/spec/services/projects/participants_service_spec.rb
index d75851134ee..3688f6d4e23 100644
--- a/spec/services/projects/participants_service_spec.rb
+++ b/spec/services/projects/participants_service_spec.rb
@@ -13,7 +13,7 @@ describe Projects::ParticipantsService, services: true do
groups = participants.groups
expect(groups.size).to eq 1
- expect(groups.first[:avatar_url]).to eq("/uploads/system/group/avatar/#{group.id}/dk.png")
+ expect(groups.first[:avatar_url]).to eq("/uploads/-/system/group/avatar/#{group.id}/dk.png")
end
it 'should return an url for the avatar with relative url' do
@@ -24,7 +24,7 @@ describe Projects::ParticipantsService, services: true do
groups = participants.groups
expect(groups.size).to eq 1
- expect(groups.first[:avatar_url]).to eq("/gitlab/uploads/system/group/avatar/#{group.id}/dk.png")
+ expect(groups.first[:avatar_url]).to eq("/gitlab/uploads/-/system/group/avatar/#{group.id}/dk.png")
end
end
end
diff --git a/spec/services/test_hook_service_spec.rb b/spec/services/test_hook_service_spec.rb
deleted file mode 100644
index f99fd8434c2..00000000000
--- a/spec/services/test_hook_service_spec.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-require 'spec_helper'
-
-describe TestHookService, services: true do
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
- let(:hook) { create(:project_hook, project: project) }
-
- describe '#execute' do
- it "executes successfully" do
- stub_request(:post, hook.url).to_return(status: 200)
- expect(TestHookService.new.execute(hook, user)).to be_truthy
- end
- end
-end
diff --git a/spec/services/test_hooks/project_service_spec.rb b/spec/services/test_hooks/project_service_spec.rb
new file mode 100644
index 00000000000..4218c15a3ce
--- /dev/null
+++ b/spec/services/test_hooks/project_service_spec.rb
@@ -0,0 +1,188 @@
+require 'spec_helper'
+
+describe TestHooks::ProjectService do
+ let(:current_user) { create(:user) }
+
+ describe '#execute' do
+ let(:project) { create(:project, :repository) }
+ let(:hook) { create(:project_hook, project: project) }
+ let(:service) { described_class.new(hook, current_user, trigger) }
+ let(:sample_data) { { data: 'sample' } }
+ let(:success_result) { { status: :success, http_status: 200, message: 'ok' } }
+
+ context 'hook with not implemented test' do
+ let(:trigger) { 'not_implemented_events' }
+
+ it 'returns error message' do
+ expect(hook).not_to receive(:execute)
+ expect(service.execute).to include({ status: :error, message: 'Testing not available for this hook' })
+ end
+ end
+
+ context 'push_events' do
+ let(:trigger) { 'push_events' }
+
+ it 'returns error message if not enough data' do
+ allow(project).to receive(:empty_repo?).and_return(true)
+
+ expect(hook).not_to receive(:execute)
+ expect(service.execute).to include({ status: :error, message: 'Ensure the project has at least one commit.' })
+ end
+
+ it 'executes hook' do
+ allow(project).to receive(:empty_repo?).and_return(false)
+ allow(Gitlab::DataBuilder::Push).to receive(:build_sample).and_return(sample_data)
+
+ expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result)
+ expect(service.execute).to include(success_result)
+ end
+ end
+
+ context 'tag_push_events' do
+ let(:trigger) { 'tag_push_events' }
+
+ it 'returns error message if not enough data' do
+ allow(project).to receive(:empty_repo?).and_return(true)
+
+ expect(hook).not_to receive(:execute)
+ expect(service.execute).to include({ status: :error, message: 'Ensure the project has at least one commit.' })
+ end
+
+ it 'executes hook' do
+ allow(project).to receive(:empty_repo?).and_return(false)
+ allow(Gitlab::DataBuilder::Push).to receive(:build_sample).and_return(sample_data)
+
+ expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result)
+ expect(service.execute).to include(success_result)
+ end
+ end
+
+ context 'note_events' do
+ let(:trigger) { 'note_events' }
+
+ it 'returns error message if not enough data' do
+ expect(hook).not_to receive(:execute)
+ expect(service.execute).to include({ status: :error, message: 'Ensure the project has notes.' })
+ end
+
+ it 'executes hook' do
+ allow(project).to receive(:notes).and_return([Note.new])
+ allow(Gitlab::DataBuilder::Note).to receive(:build).and_return(sample_data)
+
+ expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result)
+ expect(service.execute).to include(success_result)
+ end
+ end
+
+ context 'issues_events' do
+ let(:trigger) { 'issues_events' }
+ let(:issue) { build(:issue) }
+
+ it 'returns error message if not enough data' do
+ expect(hook).not_to receive(:execute)
+ expect(service.execute).to include({ status: :error, message: 'Ensure the project has issues.' })
+ end
+
+ it 'executes hook' do
+ allow(project).to receive(:issues).and_return([issue])
+ allow(issue).to receive(:to_hook_data).and_return(sample_data)
+
+ expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result)
+ expect(service.execute).to include(success_result)
+ end
+ end
+
+ context 'confidential_issues_events' do
+ let(:trigger) { 'confidential_issues_events' }
+ let(:issue) { build(:issue) }
+
+ it 'returns error message if not enough data' do
+ expect(hook).not_to receive(:execute)
+ expect(service.execute).to include({ status: :error, message: 'Ensure the project has issues.' })
+ end
+
+ it 'executes hook' do
+ allow(project).to receive(:issues).and_return([issue])
+ allow(issue).to receive(:to_hook_data).and_return(sample_data)
+
+ expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result)
+ expect(service.execute).to include(success_result)
+ end
+ end
+
+ context 'merge_requests_events' do
+ let(:trigger) { 'merge_requests_events' }
+
+ it 'returns error message if not enough data' do
+ expect(hook).not_to receive(:execute)
+ expect(service.execute).to include({ status: :error, message: 'Ensure the project has merge requests.' })
+ end
+
+ it 'executes hook' do
+ create(:merge_request, source_project: project)
+ allow_any_instance_of(MergeRequest).to receive(:to_hook_data).and_return(sample_data)
+
+ expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result)
+ expect(service.execute).to include(success_result)
+ end
+ end
+
+ context 'job_events' do
+ let(:trigger) { 'job_events' }
+
+ it 'returns error message if not enough data' do
+ expect(hook).not_to receive(:execute)
+ expect(service.execute).to include({ status: :error, message: 'Ensure the project has CI jobs.' })
+ end
+
+ it 'executes hook' do
+ create(:ci_build, project: project)
+ allow(Gitlab::DataBuilder::Build).to receive(:build).and_return(sample_data)
+
+ expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result)
+ expect(service.execute).to include(success_result)
+ end
+ end
+
+ context 'pipeline_events' do
+ let(:trigger) { 'pipeline_events' }
+
+ it 'returns error message if not enough data' do
+ expect(hook).not_to receive(:execute)
+ expect(service.execute).to include({ status: :error, message: 'Ensure the project has CI pipelines.' })
+ end
+
+ it 'executes hook' do
+ create(:ci_empty_pipeline, project: project)
+ allow(Gitlab::DataBuilder::Pipeline).to receive(:build).and_return(sample_data)
+
+ expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result)
+ expect(service.execute).to include(success_result)
+ end
+ end
+
+ context 'wiki_page_events' do
+ let(:trigger) { 'wiki_page_events' }
+
+ it 'returns error message if wiki disabled' do
+ allow(project).to receive(:wiki_enabled?).and_return(false)
+
+ expect(hook).not_to receive(:execute)
+ expect(service.execute).to include({ status: :error, message: 'Ensure the wiki is enabled and has pages.' })
+ end
+
+ it 'returns error message if not enough data' do
+ expect(hook).not_to receive(:execute)
+ expect(service.execute).to include({ status: :error, message: 'Ensure the wiki is enabled and has pages.' })
+ end
+
+ it 'executes hook' do
+ create(:wiki_page, wiki: project.wiki)
+ allow(Gitlab::DataBuilder::WikiPage).to receive(:build).and_return(sample_data)
+
+ expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result)
+ expect(service.execute).to include(success_result)
+ end
+ end
+ end
+end
diff --git a/spec/services/test_hooks/system_service_spec.rb b/spec/services/test_hooks/system_service_spec.rb
new file mode 100644
index 00000000000..00d89924766
--- /dev/null
+++ b/spec/services/test_hooks/system_service_spec.rb
@@ -0,0 +1,82 @@
+require 'spec_helper'
+
+describe TestHooks::SystemService do
+ let(:current_user) { create(:user) }
+
+ describe '#execute' do
+ let(:project) { create(:project, :repository) }
+ let(:hook) { create(:system_hook) }
+ let(:service) { described_class.new(hook, current_user, trigger) }
+ let(:sample_data) { { data: 'sample' }}
+ let(:success_result) { { status: :success, http_status: 200, message: 'ok' } }
+
+ before do
+ allow(Project).to receive(:first).and_return(project)
+ end
+
+ context 'hook with not implemented test' do
+ let(:trigger) { 'not_implemented_events' }
+
+ it 'returns error message' do
+ expect(hook).not_to receive(:execute)
+ expect(service.execute).to include({ status: :error, message: 'Testing not available for this hook' })
+ end
+ end
+
+ context 'push_events' do
+ let(:trigger) { 'push_events' }
+
+ it 'returns error message if not enough data' do
+ allow(project).to receive(:empty_repo?).and_return(true)
+
+ expect(hook).not_to receive(:execute)
+ expect(service.execute).to include({ status: :error, message: "Ensure project \"#{project.human_name}\" has commits." })
+ end
+
+ it 'executes hook' do
+ allow(project).to receive(:empty_repo?).and_return(false)
+ allow(Gitlab::DataBuilder::Push).to receive(:build_sample).and_return(sample_data)
+
+ expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result)
+ expect(service.execute).to include(success_result)
+ end
+ end
+
+ context 'tag_push_events' do
+ let(:trigger) { 'tag_push_events' }
+
+ it 'returns error message if not enough data' do
+ allow(project.repository).to receive(:tags).and_return([])
+
+ expect(hook).not_to receive(:execute)
+ expect(service.execute).to include({ status: :error, message: "Ensure project \"#{project.human_name}\" has tags." })
+ end
+
+ it 'executes hook' do
+ allow(project.repository).to receive(:tags).and_return(['tag'])
+ allow(Gitlab::DataBuilder::Push).to receive(:build_sample).and_return(sample_data)
+
+ expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result)
+ expect(service.execute).to include(success_result)
+ end
+ end
+
+ context 'repository_update_events' do
+ let(:trigger) { 'repository_update_events' }
+
+ it 'returns error message if not enough data' do
+ allow(project).to receive(:commit).and_return(nil)
+ expect(hook).not_to receive(:execute)
+ expect(service.execute).to include({ status: :error, message: "Ensure project \"#{project.human_name}\" has commits." })
+ end
+
+ it 'executes hook' do
+ allow(project).to receive(:empty_repo?).and_return(false)
+ allow(Gitlab::DataBuilder::Repository).to receive(:update).and_return(sample_data)
+
+ expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result)
+ expect(service.execute).to include(success_result)
+ end
+ end
+ end
+end
diff --git a/spec/services/users/migrate_to_ghost_user_service_spec.rb b/spec/services/users/migrate_to_ghost_user_service_spec.rb
index 9e1edf1ac30..e52ecd6d614 100644
--- a/spec/services/users/migrate_to_ghost_user_service_spec.rb
+++ b/spec/services/users/migrate_to_ghost_user_service_spec.rb
@@ -7,16 +7,32 @@ describe Users::MigrateToGhostUserService, services: true do
context "migrating a user's associated records to the ghost user" do
context 'issues' do
- include_examples "migrating a deleted user's associated records to the ghost user", Issue do
- let(:created_record) { create(:issue, project: project, author: user) }
- let(:assigned_record) { create(:issue, project: project, assignee: user) }
+ context 'deleted user is present as both author and edited_user' do
+ include_examples "migrating a deleted user's associated records to the ghost user", Issue, [:author, :last_edited_by] do
+ let(:created_record) do
+ create(:issue, project: project, author: user, last_edited_by: user)
+ end
+ end
+ end
+
+ context 'deleted user is present only as edited_user' do
+ include_examples "migrating a deleted user's associated records to the ghost user", Issue, [:last_edited_by] do
+ let(:created_record) { create(:issue, project: project, author: create(:user), last_edited_by: user) }
+ end
end
end
context 'merge requests' do
- include_examples "migrating a deleted user's associated records to the ghost user", MergeRequest do
- let(:created_record) { create(:merge_request, source_project: project, author: user, target_branch: "first") }
- let(:assigned_record) { create(:merge_request, source_project: project, assignee: user, target_branch: 'second') }
+ context 'deleted user is present as both author and merge_user' do
+ include_examples "migrating a deleted user's associated records to the ghost user", MergeRequest, [:author, :merge_user] do
+ let(:created_record) { create(:merge_request, source_project: project, author: user, merge_user: user, target_branch: "first") }
+ end
+ end
+
+ context 'deleted user is present only as both merge_user' do
+ include_examples "migrating a deleted user's associated records to the ghost user", MergeRequest, [:merge_user] do
+ let(:created_record) { create(:merge_request, source_project: project, merge_user: user, target_branch: "first") }
+ end
end
end
@@ -33,9 +49,8 @@ describe Users::MigrateToGhostUserService, services: true do
end
context 'award emoji' do
- include_examples "migrating a deleted user's associated records to the ghost user", AwardEmoji do
+ include_examples "migrating a deleted user's associated records to the ghost user", AwardEmoji, [:user] do
let(:created_record) { create(:award_emoji, user: user) }
- let(:author_alias) { :user }
context "when the awardable already has an award emoji of the same name assigned to the ghost user" do
let(:awardable) { create(:issue) }
diff --git a/spec/services/web_hook_service_spec.rb b/spec/services/web_hook_service_spec.rb
index b5abc46e80c..7ff37c22963 100644
--- a/spec/services/web_hook_service_spec.rb
+++ b/spec/services/web_hook_service_spec.rb
@@ -58,7 +58,7 @@ describe WebHookService, services: true do
exception = exception_class.new('Exception message')
WebMock.stub_request(:post, project_hook.url).to_raise(exception)
- expect(service_instance.execute).to eq([nil, exception.message])
+ expect(service_instance.execute).to eq({ status: :error, message: exception.message })
expect { service_instance.execute }.not_to raise_error
end
end
@@ -66,13 +66,13 @@ describe WebHookService, services: true do
it 'handles 200 status code' do
WebMock.stub_request(:post, project_hook.url).to_return(status: 200, body: 'Success')
- expect(service_instance.execute).to eq([200, 'Success'])
+ expect(service_instance.execute).to include({ status: :success, http_status: 200, message: 'Success' })
end
it 'handles 2xx status codes' do
WebMock.stub_request(:post, project_hook.url).to_return(status: 201, body: 'Success')
- expect(service_instance.execute).to eq([201, 'Success'])
+ expect(service_instance.execute).to include({ status: :success, http_status: 201, message: 'Success' })
end
context 'execution logging' do
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index b8ed1e18de0..5d5715b10ff 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -3,7 +3,6 @@ SimpleCovEnv.start!
ENV["RAILS_ENV"] ||= 'test'
ENV["IN_MEMORY_APPLICATION_SETTINGS"] = 'true'
-# ENV['prometheus_multiproc_dir'] = 'tmp/prometheus_multiproc_dir_test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
diff --git a/spec/support/devise_helpers.rb b/spec/support/devise_helpers.rb
new file mode 100644
index 00000000000..890a2d9d287
--- /dev/null
+++ b/spec/support/devise_helpers.rb
@@ -0,0 +1,14 @@
+module DeviseHelpers
+ # explicitly tells Devise which mapping to use
+ # this is needed when we are testing a Devise controller bypassing the router
+ def set_devise_mapping(context:)
+ env =
+ if context.respond_to?(:env_config)
+ context.env_config
+ elsif context.respond_to?(:env)
+ context.env
+ end
+
+ env['devise.mapping'] = Devise.mappings[:user] if env
+ end
+end
diff --git a/spec/support/login_helpers.rb b/spec/support/login_helpers.rb
index b410a652126..c714d1b08a6 100644
--- a/spec/support/login_helpers.rb
+++ b/spec/support/login_helpers.rb
@@ -1,4 +1,6 @@
module LoginHelpers
+ include DeviseHelpers
+
# Internal: Log in as a specific user or a new user of a specific role
#
# user_or_role - User object, or a role to create (e.g., :admin, :user)
@@ -62,7 +64,7 @@ module LoginHelpers
visit new_user_session_path
expect(page).to have_content('Sign in with')
- check 'Remember Me' if remember_me
+ check 'remember_me' if remember_me
click_link "oauth-login-#{provider}"
end
@@ -106,7 +108,7 @@ module LoginHelpers
end
def stub_omniauth_saml_config(messages)
- Rails.application.env_config['devise.mapping'] = Devise.mappings[:user]
+ set_devise_mapping(context: Rails.application)
Rails.application.routes.disable_clear_and_finalize = true
Rails.application.routes.draw do
post '/users/auth/saml' => 'omniauth_callbacks#saml'
diff --git a/spec/support/malicious_regexp_shared_examples.rb b/spec/support/malicious_regexp_shared_examples.rb
new file mode 100644
index 00000000000..ac5d22298bb
--- /dev/null
+++ b/spec/support/malicious_regexp_shared_examples.rb
@@ -0,0 +1,8 @@
+shared_examples 'malicious regexp' do
+ let(:malicious_text) { 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!' }
+ let(:malicious_regexp) { '(?i)^(([a-z])+.)+[A-Z]([a-z])+$' }
+
+ it 'takes under a second' do
+ expect { Timeout.timeout(1) { subject } }.not_to raise_error
+ end
+end
diff --git a/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb b/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb
index dcc562c684b..855051921f0 100644
--- a/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb
+++ b/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-shared_examples "migrating a deleted user's associated records to the ghost user" do |record_class|
+shared_examples "migrating a deleted user's associated records to the ghost user" do |record_class, fields|
record_class_name = record_class.to_s.titleize.downcase
let(:project) { create(:project) }
@@ -11,6 +11,7 @@ shared_examples "migrating a deleted user's associated records to the ghost user
context "for a #{record_class_name} the user has created" do
let!(:record) { created_record }
+ let(:migrated_fields) { fields || [:author] }
it "does not delete the #{record_class_name}" do
service.execute
@@ -18,22 +19,20 @@ shared_examples "migrating a deleted user's associated records to the ghost user
expect(record_class.find_by_id(record.id)).to be_present
end
- it "migrates the #{record_class_name} so that the 'Ghost User' is the #{record_class_name} owner" do
+ it "blocks the user before migrating #{record_class_name}s to the 'Ghost User'" do
service.execute
- migrated_record = record_class.find_by_id(record.id)
-
- if migrated_record.respond_to?(:author)
- expect(migrated_record.author).to eq(User.ghost)
- else
- expect(migrated_record.send(author_alias)).to eq(User.ghost)
- end
+ expect(user).to be_blocked
end
- it "blocks the user before migrating #{record_class_name}s to the 'Ghost User'" do
+ it 'migrates all associated fields to te "Ghost user"' do
service.execute
- expect(user).to be_blocked
+ migrated_record = record_class.find_by_id(record.id)
+
+ migrated_fields.each do |field|
+ expect(migrated_record.public_send(field)).to eq(User.ghost)
+ end
end
context "race conditions" do
diff --git a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb
index 66e598e2691..d5bc12f3bc5 100644
--- a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb
+++ b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb
@@ -5,7 +5,7 @@ shared_examples "protected branches > access control > CE" do
set_protected_branch_name('master')
- within('.new_protected_branch') do
+ within('.js-new-protected-branch') do
allowed_to_push_button = find(".js-allowed-to-push")
unless allowed_to_push_button.text == access_type_name
@@ -50,7 +50,7 @@ shared_examples "protected branches > access control > CE" do
set_protected_branch_name('master')
- within('.new_protected_branch') do
+ within('.js-new-protected-branch') do
allowed_to_merge_button = find(".js-allowed-to-merge")
unless allowed_to_merge_button.text == access_type_name
diff --git a/spec/support/slack_mattermost_notifications_shared_examples.rb b/spec/support/slack_mattermost_notifications_shared_examples.rb
index 044c09d5fde..6accf16bea4 100644
--- a/spec/support/slack_mattermost_notifications_shared_examples.rb
+++ b/spec/support/slack_mattermost_notifications_shared_examples.rb
@@ -78,7 +78,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
wiki_page_service = WikiPages::CreateService.new(project, user, opts)
@wiki_page = wiki_page_service.execute
- @wiki_page_sample_data = wiki_page_service.hook_data(@wiki_page, 'create')
+ @wiki_page_sample_data = Gitlab::DataBuilder::WikiPage.build(@wiki_page, user, 'create')
end
it "calls Slack/Mattermost API for push events" do
diff --git a/spec/support/sorting_helper.rb b/spec/support/sorting_helper.rb
new file mode 100644
index 00000000000..577518d726c
--- /dev/null
+++ b/spec/support/sorting_helper.rb
@@ -0,0 +1,18 @@
+# Helper allows you to sort items
+#
+# Params
+# value - value for sorting
+#
+# Usage:
+# include SortingHelper
+#
+# sorting_by('Oldest updated')
+#
+module SortingHelper
+ def sorting_by(value)
+ find('button.dropdown-toggle').click
+ page.within('.content ul.dropdown-menu.dropdown-menu-align-right li') do
+ click_link value
+ end
+ end
+end
diff --git a/spec/tasks/gitlab/task_helpers_spec.rb b/spec/tasks/gitlab/task_helpers_spec.rb
index 91cc684d032..d34617be474 100644
--- a/spec/tasks/gitlab/task_helpers_spec.rb
+++ b/spec/tasks/gitlab/task_helpers_spec.rb
@@ -20,7 +20,6 @@ describe Gitlab::TaskHelpers do
it 'checkout the version and reset to it' do
expect(subject).to receive(:checkout_version).with(tag, clone_path)
- expect(subject).to receive(:reset_to_version).with(tag, clone_path)
subject.checkout_or_clone_version(version: version, repo: repo, target_dir: clone_path)
end
@@ -31,7 +30,6 @@ describe Gitlab::TaskHelpers do
it 'checkout the version and reset to it with a branch name' do
expect(subject).to receive(:checkout_version).with(branch, clone_path)
- expect(subject).to receive(:reset_to_version).with(branch, clone_path)
subject.checkout_or_clone_version(version: version, repo: repo, target_dir: clone_path)
end
@@ -70,20 +68,11 @@ describe Gitlab::TaskHelpers do
describe '#checkout_version' do
it 'clones the repo in the target dir' do
expect(subject)
- .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} fetch --quiet])
+ .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} fetch --quiet origin #{tag}])
expect(subject)
- .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} checkout --quiet #{tag}])
+ .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} checkout -f --quiet FETCH_HEAD --])
subject.checkout_version(tag, clone_path)
end
end
-
- describe '#reset_to_version' do
- it 'resets --hard to the given version' do
- expect(subject)
- .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} reset --hard #{tag}])
-
- subject.reset_to_version(tag, clone_path)
- end
- end
end
diff --git a/spec/uploaders/attachment_uploader_spec.rb b/spec/uploaders/attachment_uploader_spec.rb
index d82dbe871d5..04ee6e9bfad 100644
--- a/spec/uploaders/attachment_uploader_spec.rb
+++ b/spec/uploaders/attachment_uploader_spec.rb
@@ -5,7 +5,7 @@ describe AttachmentUploader do
describe "#store_dir" do
it "stores in the system dir" do
- expect(uploader.store_dir).to start_with("uploads/system/user")
+ expect(uploader.store_dir).to start_with("uploads/-/system/user")
end
it "uses the old path when using object storage" do
diff --git a/spec/uploaders/avatar_uploader_spec.rb b/spec/uploaders/avatar_uploader_spec.rb
index 201fe6949aa..1dc574699d8 100644
--- a/spec/uploaders/avatar_uploader_spec.rb
+++ b/spec/uploaders/avatar_uploader_spec.rb
@@ -5,7 +5,7 @@ describe AvatarUploader do
describe "#store_dir" do
it "stores in the system dir" do
- expect(uploader.store_dir).to start_with("uploads/system/user")
+ expect(uploader.store_dir).to start_with("uploads/-/system/user")
end
it "uses the old path when using object storage" do
diff --git a/spec/uploaders/file_mover_spec.rb b/spec/uploaders/file_mover_spec.rb
index 896cb410ed5..d7c1b390f9a 100644
--- a/spec/uploaders/file_mover_spec.rb
+++ b/spec/uploaders/file_mover_spec.rb
@@ -4,11 +4,11 @@ describe FileMover do
let(:filename) { 'banana_sample.gif' }
let(:file) { fixture_file_upload(Rails.root.join('spec', 'fixtures', filename)) }
let(:temp_description) do
- 'test ![banana_sample](/uploads/temp/secret55/banana_sample.gif) same ![banana_sample]'\
- '(/uploads/temp/secret55/banana_sample.gif)'
+ 'test ![banana_sample](/uploads/system/temp/secret55/banana_sample.gif) same ![banana_sample]'\
+ '(/uploads/system/temp/secret55/banana_sample.gif)'
end
let(:temp_file_path) { File.join('secret55', filename).to_s }
- let(:file_path) { File.join('uploads', 'personal_snippet', snippet.id.to_s, 'secret55', filename).to_s }
+ let(:file_path) { File.join('uploads', 'system', 'personal_snippet', snippet.id.to_s, 'secret55', filename).to_s }
let(:snippet) { create(:personal_snippet, description: temp_description) }
@@ -28,8 +28,8 @@ describe FileMover do
expect(snippet.reload.description)
.to eq(
- "test ![banana_sample](/uploads/personal_snippet/#{snippet.id}/secret55/banana_sample.gif)"\
- " same ![banana_sample](/uploads/personal_snippet/#{snippet.id}/secret55/banana_sample.gif)"
+ "test ![banana_sample](/uploads/system/personal_snippet/#{snippet.id}/secret55/banana_sample.gif)"\
+ " same ![banana_sample](/uploads/system/personal_snippet/#{snippet.id}/secret55/banana_sample.gif)"
)
end
@@ -50,8 +50,8 @@ describe FileMover do
expect(snippet.reload.description)
.to eq(
- "test ![banana_sample](/uploads/temp/secret55/banana_sample.gif)"\
- " same ![banana_sample](/uploads/temp/secret55/banana_sample.gif)"
+ "test ![banana_sample](/uploads/system/temp/secret55/banana_sample.gif)"\
+ " same ![banana_sample](/uploads/system/temp/secret55/banana_sample.gif)"
)
end
diff --git a/spec/uploaders/personal_file_uploader_spec.rb b/spec/uploaders/personal_file_uploader_spec.rb
index fb92f2ae3ab..eb55e8ebd24 100644
--- a/spec/uploaders/personal_file_uploader_spec.rb
+++ b/spec/uploaders/personal_file_uploader_spec.rb
@@ -10,7 +10,7 @@ describe PersonalFileUploader do
dynamic_segment = "personal_snippet/#{snippet.id}"
- expect(described_class.absolute_path(upload)).to end_with("#{dynamic_segment}/secret/foo.jpg")
+ expect(described_class.absolute_path(upload)).to end_with("/system/#{dynamic_segment}/secret/foo.jpg")
end
end
@@ -19,7 +19,7 @@ describe PersonalFileUploader do
uploader = described_class.new(snippet, 'secret')
allow(uploader).to receive(:file).and_return(double(extension: 'txt', filename: 'file_name'))
- expected_url = "/uploads/personal_snippet/#{snippet.id}/secret/file_name"
+ expected_url = "/uploads/system/personal_snippet/#{snippet.id}/secret/file_name"
expect(uploader.to_h).to eq(
alt: 'file_name',