summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--CHANGELOG.md4
-rw-r--r--CONTRIBUTING.md65
-rw-r--r--Gemfile6
-rw-r--r--Gemfile.lock17
-rw-r--r--app/assets/javascripts/application.js5
-rw-r--r--app/assets/javascripts/boards/components/board_card.js2
-rw-r--r--app/assets/javascripts/boards/components/board_list.js.es617
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.js92
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.js.es664
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.js.es64
-rw-r--r--app/assets/javascripts/copy_as_gfm.js.es63
-rw-r--r--app/assets/javascripts/diff.js.es610
-rw-r--r--app/assets/javascripts/dispatcher.js.es66
-rw-r--r--app/assets/javascripts/environments/components/environment.js.es62
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_view.js.es62
-rw-r--r--app/assets/javascripts/environments/services/environments_service.js.es62
-rw-r--r--app/assets/javascripts/groups_list.js47
-rw-r--r--app/assets/javascripts/issuable/time_tracking/components/time_tracker.js.es64
-rw-r--r--app/assets/javascripts/issuable/time_tracking/time_tracking_bundle.js.es65
-rw-r--r--app/assets/javascripts/merge_request_widget/ci_bundle.js.es66
-rw-r--r--app/assets/javascripts/notes.js35
-rw-r--r--app/assets/javascripts/profile/gl_crop.js.es62
-rw-r--r--app/assets/javascripts/shortcuts_navigation.js6
-rw-r--r--app/assets/javascripts/user_callout.js4
-rw-r--r--app/assets/javascripts/vue_pipelines_index/pipelines.js.es617
-rw-r--r--app/assets/javascripts/vue_shared/components/table_pagination.js.es616
-rw-r--r--app/assets/stylesheets/framework/calendar.scss1
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss15
-rw-r--r--app/assets/stylesheets/framework/files.scss1
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss4
-rw-r--r--app/assets/stylesheets/highlight/dark.scss17
-rw-r--r--app/assets/stylesheets/highlight/monokai.scss17
-rw-r--r--app/assets/stylesheets/highlight/solarized_dark.scss17
-rw-r--r--app/assets/stylesheets/highlight/solarized_light.scss18
-rw-r--r--app/assets/stylesheets/highlight/white.scss18
-rw-r--r--app/assets/stylesheets/pages/diff.scss9
-rw-r--r--app/assets/stylesheets/pages/events.scss5
-rw-r--r--app/assets/stylesheets/pages/profile.scss3
-rw-r--r--app/assets/stylesheets/pages/tree.scss26
-rw-r--r--app/controllers/application_controller.rb21
-rw-r--r--app/controllers/ci/projects_controller.rb47
-rw-r--r--app/controllers/dashboard/groups_controller.rb14
-rw-r--r--app/controllers/explore/groups_controller.rb11
-rw-r--r--app/controllers/projects/blob_controller.rb2
-rw-r--r--app/controllers/projects/graphs_controller.rb31
-rw-r--r--app/controllers/projects/merge_requests_controller.rb28
-rw-r--r--app/controllers/projects/notes_controller.rb11
-rw-r--r--app/controllers/projects/pipelines_controller.rb11
-rw-r--r--app/controllers/projects_controller.rb2
-rw-r--r--app/controllers/root_controller.rb37
-rw-r--r--app/helpers/button_helper.rb2
-rw-r--r--app/helpers/explore_helper.rb14
-rw-r--r--app/helpers/issuables_helper.rb2
-rw-r--r--app/helpers/merge_requests_helper.rb2
-rw-r--r--app/helpers/milestones_helper.rb2
-rw-r--r--app/helpers/rss_helper.rb5
-rw-r--r--app/models/application_setting.rb17
-rw-r--r--app/models/ci/build.rb9
-rw-r--r--app/models/group.rb2
-rw-r--r--app/models/merge_request.rb12
-rw-r--r--app/models/note.rb4
-rw-r--r--app/models/project.rb7
-rw-r--r--app/models/project_wiki.rb2
-rw-r--r--app/models/snippet.rb2
-rw-r--r--app/models/user.rb36
-rw-r--r--app/serializers/merge_request_entity.rb2
-rw-r--r--app/serializers/pipeline_serializer.rb2
-rw-r--r--app/services/access_token_validation_service.rb8
-rw-r--r--app/services/ci/image_for_build_service.rb25
-rw-r--r--app/services/ci/register_build_service.rb36
-rw-r--r--app/services/commits/change_service.rb4
-rw-r--r--app/services/files/base_service.rb2
-rw-r--r--app/services/files/multi_service.rb2
-rw-r--r--app/services/files/update_service.rb2
-rw-r--r--app/services/issuable_base_service.rb9
-rw-r--r--app/services/issues/move_service.rb2
-rw-r--r--app/services/merge_requests/merge_when_pipeline_succeeds_service.rb18
-rw-r--r--app/services/merge_requests/refresh_service.rb6
-rw-r--r--app/services/merge_requests/resolve_service.rb3
-rw-r--r--app/services/notes/create_service.rb10
-rw-r--r--app/services/notification_service.rb2
-rw-r--r--app/services/projects/create_service.rb2
-rw-r--r--app/services/projects/destroy_service.rb2
-rw-r--r--app/services/projects/import_service.rb2
-rw-r--r--app/services/projects/transfer_service.rb2
-rw-r--r--app/services/slash_commands/interpret_service.rb19
-rw-r--r--app/services/system_hooks_service.rb4
-rw-r--r--app/services/system_note_service.rb4
-rw-r--r--app/services/todo_service.rb6
-rw-r--r--app/views/dashboard/_activities.html.haml7
-rw-r--r--app/views/dashboard/_groups_head.html.haml7
-rw-r--r--app/views/dashboard/activity.html.haml3
-rw-r--r--app/views/dashboard/groups/_groups.html.haml6
-rw-r--r--app/views/dashboard/groups/index.html.haml7
-rw-r--r--app/views/dashboard/issues.html.haml12
-rw-r--r--app/views/dashboard/projects/index.atom.builder2
-rw-r--r--app/views/dashboard/projects/index.html.haml3
-rw-r--r--app/views/explore/groups/_groups.html.haml6
-rw-r--r--app/views/explore/groups/index.html.haml38
-rw-r--r--app/views/groups/_activities.html.haml7
-rw-r--r--app/views/groups/activity.html.haml3
-rw-r--r--app/views/groups/issues.html.haml16
-rw-r--r--app/views/groups/show.atom.builder2
-rw-r--r--app/views/groups/show.html.haml3
-rw-r--r--app/views/help/_shortcuts.html.haml12
-rw-r--r--app/views/layouts/header/_default.html.haml2
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml6
-rw-r--r--app/views/layouts/nav/_project.html.haml27
-rw-r--r--app/views/projects/_activity.html.haml8
-rw-r--r--app/views/projects/_head.html.haml20
-rw-r--r--app/views/projects/_merge_request_merge_settings.html.haml4
-rw-r--r--app/views/projects/blob/_actions.html.haml9
-rw-r--r--app/views/projects/blob/_blob.html.haml13
-rw-r--r--app/views/projects/blob/diff.html.haml10
-rw-r--r--app/views/projects/boards/components/_board_list.html.haml22
-rw-r--r--app/views/projects/commits/_head.html.haml24
-rw-r--r--app/views/projects/commits/show.atom.builder2
-rw-r--r--app/views/projects/commits/show.html.haml10
-rw-r--r--app/views/projects/cycle_analytics/show.html.haml2
-rw-r--r--app/views/projects/graphs/_head.html.haml19
-rw-r--r--app/views/projects/graphs/charts.html.haml (renamed from app/views/projects/graphs/commits.html.haml)92
-rw-r--r--app/views/projects/graphs/ci.html.haml18
-rw-r--r--app/views/projects/graphs/languages.html.haml33
-rw-r--r--app/views/projects/graphs/show.html.haml7
-rw-r--r--app/views/projects/issues/_head.html.haml2
-rw-r--r--app/views/projects/issues/index.html.haml8
-rw-r--r--app/views/projects/merge_requests/cancel_merge_when_pipeline_succeeds.js.haml (renamed from app/views/projects/merge_requests/cancel_merge_when_build_succeeds.js.haml)0
-rw-r--r--app/views/projects/merge_requests/index.html.haml1
-rw-r--r--app/views/projects/merge_requests/merge.js.haml4
-rw-r--r--app/views/projects/merge_requests/widget/_open.html.haml4
-rw-r--r--app/views/projects/merge_requests/widget/open/_accept.html.haml8
-rw-r--r--app/views/projects/merge_requests/widget/open/_merge_when_pipeline_succeeds.html.haml (renamed from app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml)4
-rw-r--r--app/views/projects/milestones/show.html.haml5
-rw-r--r--app/views/projects/network/show.html.haml2
-rw-r--r--app/views/projects/pages/_use.html.haml4
-rw-r--r--app/views/projects/pipelines/_head.html.haml14
-rw-r--r--app/views/projects/pipelines/charts.html.haml21
-rw-r--r--app/views/projects/pipelines/charts/_build_times.haml (renamed from app/views/projects/graphs/ci/_build_times.haml)0
-rw-r--r--app/views/projects/pipelines/charts/_builds.haml (renamed from app/views/projects/graphs/ci/_builds.haml)0
-rw-r--r--app/views/projects/pipelines/charts/_overall.haml (renamed from app/views/projects/graphs/ci/_overall.haml)0
-rw-r--r--app/views/projects/show.atom.builder2
-rw-r--r--app/views/projects/show.html.haml6
-rw-r--r--app/views/projects/tree/show.html.haml3
-rw-r--r--app/views/shared/groups/_dropdown.html.haml18
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml32
-rw-r--r--app/views/users/show.html.haml13
-rw-r--r--changelogs/unreleased/22951-fix-todos-api-endpoint-error-for-commits.yml4
-rw-r--r--changelogs/unreleased/25437-just-emoji.yml4
-rw-r--r--changelogs/unreleased/26136-list-repository-tree-api-doc.yml4
-rw-r--r--changelogs/unreleased/2629-show-public-rss-feeds-to-anonymous-users.yml4
-rw-r--r--changelogs/unreleased/26348-cleanup-navigation-order.yml4
-rw-r--r--changelogs/unreleased/27142-api-replace-destroy-with-stop-environment.yml4
-rw-r--r--changelogs/unreleased/27501-api-use-visibility-everywhere.yml4
-rw-r--r--changelogs/unreleased/27778-a11y-sidebar.yml5
-rw-r--r--changelogs/unreleased/28257-issues-iids.yml4
-rw-r--r--changelogs/unreleased/28410-dropdown-styling.yml4
-rw-r--r--changelogs/unreleased/28609-fix-redirect-to-home-page-url.yml4
-rw-r--r--changelogs/unreleased/28696-improve-grammar-gitlab-flow-doc.yml4
-rw-r--r--changelogs/unreleased/28805-download-archive-with-branch-like-feature-xxxx-add-extra-directory-level.yml4
-rw-r--r--changelogs/unreleased/28807-search-for-milestone-by-title-in-rest-api.yml4
-rw-r--r--changelogs/unreleased/28837-remove-help-duplicate.yml4
-rw-r--r--changelogs/unreleased/28850-fix-broken-migration.yml4
-rw-r--r--changelogs/unreleased/28893-highlighted-diff-doesn-t-stay-highlighted-on-refresh.yml4
-rw-r--r--changelogs/unreleased/diff-make-obvious-cant-comment.yml4
-rw-r--r--changelogs/unreleased/dm-dont-copy-toolip.yml4
-rw-r--r--changelogs/unreleased/dm-group-reference-full-name.yml4
-rw-r--r--changelogs/unreleased/dz-dashboard-groups-search.yml4
-rw-r--r--changelogs/unreleased/expose-pagination-headers.yml4
-rw-r--r--changelogs/unreleased/fix-gb-remove-deprecated-ci-build-status-badge.yml4
-rw-r--r--changelogs/unreleased/fix-gb-update-commit-status-api.yml4
-rw-r--r--changelogs/unreleased/gitaly-post-receive.yml4
-rw-r--r--changelogs/unreleased/issue-descrpiption-spinner-off.yml4
-rw-r--r--changelogs/unreleased/long-file-name-overflow.yml4
-rw-r--r--changelogs/unreleased/remove-new-relic-gem.yml4
-rw-r--r--changelogs/unreleased/use-v3-api-on-frontend.yml4
-rw-r--r--changelogs/unreleased/user-calendar-border.yml4
-rw-r--r--config/application.rb4
-rw-r--r--config/initializers/8_gitaly.rb2
-rw-r--r--config/newrelic.yml16
-rw-r--r--config/routes/ci.rb8
-rw-r--r--config/routes/project.rb4
-rw-r--r--config/webpack.config.js1
-rw-r--r--db/migrate/20170217132157_rename_merge_when_build_succeeds.rb29
-rw-r--r--db/migrate/20170217151947_rename_only_allow_merge_if_build_succeeds.rb29
-rw-r--r--db/post_migrate/20170211073944_disable_invalid_service_templates.rb6
-rw-r--r--db/schema.rb6
-rw-r--r--doc/api/README.md18
-rw-r--r--doc/api/access_requests.md16
-rw-r--r--doc/api/award_emoji.md16
-rw-r--r--doc/api/boards.md12
-rw-r--r--doc/api/branches.md14
-rw-r--r--doc/api/broadcast_messages.md10
-rw-r--r--doc/api/build_triggers.md8
-rw-r--r--doc/api/build_variables.md10
-rw-r--r--doc/api/builds.md22
-rw-r--r--doc/api/ci/lint.md2
-rw-r--r--doc/api/commits.md18
-rw-r--r--doc/api/deploy_key_multiple_projects.md8
-rw-r--r--doc/api/deploy_keys.md12
-rw-r--r--doc/api/deployments.md4
-rw-r--r--doc/api/enviroments.md42
-rw-r--r--doc/api/groups.md28
-rw-r--r--doc/api/issues.md39
-rw-r--r--doc/api/labels.md12
-rw-r--r--doc/api/members.md20
-rw-r--r--doc/api/merge_requests.md50
-rw-r--r--doc/api/milestones.md8
-rw-r--r--doc/api/namespaces.md4
-rw-r--r--doc/api/notes.md6
-rw-r--r--doc/api/notification_settings.md12
-rw-r--r--doc/api/oauth2.md4
-rw-r--r--doc/api/pipelines.md10
-rw-r--r--doc/api/project_snippets.md16
-rw-r--r--doc/api/projects.md65
-rw-r--r--doc/api/repositories.md2
-rw-r--r--doc/api/repository_files.md8
-rw-r--r--doc/api/runners.md16
-rw-r--r--doc/api/session.md2
-rw-r--r--doc/api/settings.md21
-rw-r--r--doc/api/sidekiq_metrics.md8
-rw-r--r--doc/api/snippets.md29
-rw-r--r--doc/api/system_hooks.md8
-rw-r--r--doc/api/tags.md6
-rw-r--r--doc/api/templates/gitignores.md4
-rw-r--r--doc/api/templates/gitlab_ci_ymls.md4
-rw-r--r--doc/api/templates/licenses.md4
-rw-r--r--doc/api/todos.md12
-rw-r--r--doc/api/users.md2
-rw-r--r--doc/api/v3_to_v4.md8
-rw-r--r--doc/api/version.md2
-rw-r--r--doc/ci/triggers/README.md14
-rw-r--r--doc/ci/variables/README.md10
-rw-r--r--doc/development/doc_styleguide.md16
-rw-r--r--doc/development/ux_guide/img/karolina-plaskaty.pngbin0 -> 33498 bytes
-rw-r--r--doc/development/ux_guide/img/nazim-ramesh.pngbin0 -> 31163 bytes
-rw-r--r--doc/development/ux_guide/users.md4
-rw-r--r--doc/gitlab-basics/img/create_new_project_button.pngbin4196 -> 6978 bytes
-rw-r--r--doc/install/README.md37
-rw-r--r--doc/install/google_cloud_platform/img/change_admin_passwd_email.pngbin0 -> 7193 bytes
-rw-r--r--doc/install/google_cloud_platform/img/chrome_not_secure_page.pngbin0 -> 21705 bytes
-rw-r--r--doc/install/google_cloud_platform/img/gcp_gitlab_being_deployed.pngbin0 -> 23486 bytes
-rw-r--r--doc/install/google_cloud_platform/img/gcp_gitlab_overview.pngbin0 -> 42028 bytes
-rw-r--r--doc/install/google_cloud_platform/img/gcp_landing.pngbin0 -> 59912 bytes
-rw-r--r--doc/install/google_cloud_platform/img/gcp_launcher_console_home_page.pngbin0 -> 42090 bytes
-rw-r--r--doc/install/google_cloud_platform/img/gcp_search_for_gitlab.pngbin0 -> 7648 bytes
-rw-r--r--doc/install/google_cloud_platform/img/gitlab_deployed_page.pngbin0 -> 35573 bytes
-rw-r--r--doc/install/google_cloud_platform/img/gitlab_first_sign_in.pngbin0 -> 20054 bytes
-rw-r--r--doc/install/google_cloud_platform/img/gitlab_launch_button.pngbin0 -> 5198 bytes
-rw-r--r--doc/install/google_cloud_platform/img/new_gitlab_deployment_settings.pngbin0 -> 50014 bytes
-rw-r--r--doc/install/google_cloud_platform/img/ssh_via_button.pngbin0 -> 3062 bytes
-rw-r--r--doc/install/google_cloud_platform/index.md168
-rw-r--r--doc/install/installation.md7
-rw-r--r--doc/pages/README.md1
-rw-r--r--doc/pages/getting_started_part_one.md267
-rw-r--r--doc/pages/getting_started_part_three.md384
-rw-r--r--doc/pages/getting_started_part_two.md153
-rw-r--r--doc/pages/img/dns_cname_record_example.pngbin5004 -> 0 bytes
-rw-r--r--doc/pages/img/remove_fork_relashionship.pngbin13646 -> 0 bytes
-rw-r--r--doc/pages/img/setup_ci.pngbin10033 -> 0 bytes
-rw-r--r--doc/pages/index.md49
-rw-r--r--doc/raketasks/backup_restore.md34
-rw-r--r--doc/ssh/README.md93
-rw-r--r--doc/user/project/merge_requests/img/merge_when_build_succeeds_enable.pngbin39796 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_when_build_succeeds_only_if_succeeds_settings.pngbin12063 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_when_build_succeeds_status.pngbin48458 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_enable.pngbin0 -> 60346 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_msg.png (renamed from doc/user/project/merge_requests/img/merge_when_build_succeeds_only_if_succeeds_msg.png)bin5251 -> 5251 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_settings.pngbin0 -> 25783 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_status.pngbin0 -> 69953 bytes
-rw-r--r--doc/user/project/merge_requests/merge_when_pipeline_succeeds.md11
-rw-r--r--doc/user/project/pages/getting_started_part_four.md382
-rw-r--r--doc/user/project/pages/getting_started_part_one.md106
-rw-r--r--doc/user/project/pages/getting_started_part_three.md189
-rw-r--r--doc/user/project/pages/getting_started_part_two.md154
-rw-r--r--doc/user/project/pages/img/add_certificate_to_pages.png (renamed from doc/pages/img/add_certificate_to_pages.png)bin14608 -> 14608 bytes
-rw-r--r--doc/user/project/pages/img/choose_ci_template.png (renamed from doc/pages/img/choose_ci_template.png)bin23532 -> 23532 bytes
-rw-r--r--doc/user/project/pages/img/dns_a_record_example.png (renamed from doc/pages/img/dns_a_record_example.png)bin4709 -> 4709 bytes
-rw-r--r--doc/user/project/pages/img/dns_cname_record_example.pngbin0 -> 4983 bytes
-rw-r--r--doc/user/project/pages/img/pages_create_project.pngbin33597 -> 6063 bytes
-rw-r--r--doc/user/project/pages/img/pages_create_user_page.pngbin87071 -> 14435 bytes
-rw-r--r--doc/user/project/pages/img/pages_dns_details.pngbin34686 -> 5351 bytes
-rw-r--r--doc/user/project/pages/img/pages_multiple_domains.pngbin63716 -> 12936 bytes
-rw-r--r--doc/user/project/pages/img/pages_new_domain_button.pngbin51136 -> 8763 bytes
-rw-r--r--doc/user/project/pages/img/pages_remove.pngbin27259 -> 3810 bytes
-rw-r--r--doc/user/project/pages/img/pages_upload_cert.pngbin103730 -> 22907 bytes
-rw-r--r--doc/user/project/pages/img/remove_fork_relashionship.pngbin0 -> 13642 bytes
-rw-r--r--doc/user/project/pages/img/setup_ci.pngbin0 -> 10032 bytes
-rw-r--r--doc/user/project/pages/index.md497
-rw-r--r--doc/user/project/pages/introduction.md447
-rw-r--r--doc/user/project/slash_commands.md1
-rw-r--r--doc/workflow/gitlab_flow.md2
-rw-r--r--doc/workflow/shortcuts.md2
-rw-r--r--features/project/active_tab.feature6
-rw-r--r--features/project/graph.feature12
-rw-r--r--features/project/issues/award_emoji.feature2
-rw-r--r--features/project/shortcuts.feature14
-rw-r--r--features/steps/project/graph.rb4
-rw-r--r--features/steps/project/issues/award_emoji.rb4
-rw-r--r--features/steps/project/network_graph.rb2
-rw-r--r--features/steps/project/project_shortcuts.rb5
-rw-r--r--features/steps/shared/paths.rb2
-rw-r--r--features/steps/shared/project_tab.rb12
-rw-r--r--lib/api/api.rb2
-rw-r--r--lib/api/api_guard.rb11
-rw-r--r--lib/api/commit_statuses.rb9
-rw-r--r--lib/api/entities.rb22
-rw-r--r--lib/api/environments.rb17
-rw-r--r--lib/api/groups.rb6
-rw-r--r--lib/api/helpers.rb4
-rw-r--r--lib/api/internal.rb12
-rw-r--r--lib/api/issues.rb1
-rw-r--r--lib/api/merge_requests.rb10
-rw-r--r--lib/api/milestones.rb2
-rw-r--r--lib/api/project_snippets.rb16
-rw-r--r--lib/api/projects.rb16
-rw-r--r--lib/api/settings.rb10
-rw-r--r--lib/api/snippets.rb16
-rw-r--r--lib/api/v3/entities.rb141
-rw-r--r--lib/api/v3/environments.rb62
-rw-r--r--lib/api/v3/groups.rb147
-rw-r--r--lib/api/v3/merge_requests.rb28
-rw-r--r--lib/api/v3/projects.rb61
-rw-r--r--lib/api/v3/settings.rb137
-rw-r--r--lib/api/v3/snippets.rb138
-rw-r--r--lib/banzai/filter/user_reference_filter.rb2
-rw-r--r--lib/bitbucket/error/unauthorized.rb3
-rw-r--r--lib/ci/gitlab_ci_yaml_processor.rb2
-rw-r--r--lib/extracts_path.rb2
-rw-r--r--lib/gitlab/access.rb2
-rw-r--r--lib/gitlab/auth.rb2
-rw-r--r--lib/gitlab/ci/build/artifacts/metadata.rb2
-rw-r--r--lib/gitlab/ci/config/entry/factory.rb2
-rw-r--r--lib/gitlab/ci/config/entry/node.rb2
-rw-r--r--lib/gitlab/ci/config/loader.rb2
-rw-r--r--lib/gitlab/conflict/file.rb3
-rw-r--r--lib/gitlab/conflict/file_collection.rb3
-rw-r--r--lib/gitlab/conflict/parser.rb22
-rw-r--r--lib/gitlab/conflict/resolution_error.rb3
-rw-r--r--lib/gitlab/email/receiver.rb26
-rw-r--r--lib/gitlab/git/diff.rb2
-rw-r--r--lib/gitlab/git/repository.rb14
-rw-r--r--lib/gitlab/gitaly_client.rb29
-rw-r--r--lib/gitlab/gitaly_client/notifications.rb17
-rw-r--r--lib/gitlab/gon_helper.rb2
-rw-r--r--lib/gitlab/import_export/error.rb2
-rw-r--r--lib/gitlab/o_auth/user.rb2
-rw-r--r--lib/gitlab/route_map.rb2
-rw-r--r--lib/gitlab/serializer/pagination.rb2
-rw-r--r--lib/gitlab/shell.rb2
-rw-r--r--lib/gitlab/template/finders/repo_template_finder.rb2
-rw-r--r--lib/gitlab/update_path_error.rb2
-rw-r--r--lib/gitlab/visibility_level.rb39
-rw-r--r--lib/mattermost/client.rb2
-rw-r--r--lib/mattermost/error.rb2
-rw-r--r--lib/mattermost/session.rb2
-rw-r--r--package.json1
-rw-r--r--public/ci/build-canceled.svg1
-rw-r--r--public/ci/build-failed.svg1
-rw-r--r--public/ci/build-pending.svg1
-rw-r--r--public/ci/build-running.svg1
-rw-r--r--public/ci/build-skipped.svg1
-rw-r--r--public/ci/build-success.svg1
-rw-r--r--public/ci/build-unknown.svg1
-rw-r--r--rubocop/cop/custom_error_class.rb64
-rw-r--r--rubocop/rubocop.rb1
-rw-r--r--spec/controllers/ci/projects_controller_spec.rb74
-rw-r--r--spec/controllers/projects/graphs_controller_spec.rb20
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb37
-rw-r--r--spec/controllers/root_controller_spec.rb36
-rw-r--r--spec/factories/merge_requests.rb4
-rw-r--r--spec/factories/todos.rb14
-rw-r--r--spec/features/dashboard/activity_spec.rb11
-rw-r--r--spec/features/dashboard/groups_list_spec.rb47
-rw-r--r--spec/features/dashboard/issues_spec.rb3
-rw-r--r--spec/features/dashboard/projects_spec.rb10
-rw-r--r--spec/features/explore/groups_list_spec.rb46
-rw-r--r--spec/features/groups/activity_spec.rb26
-rw-r--r--spec/features/groups/issues_spec.rb18
-rw-r--r--spec/features/groups/show_spec.rb24
-rw-r--r--spec/features/issues/award_emoji_spec.rb25
-rw-r--r--spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb4
-rw-r--r--spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb8
-rw-r--r--spec/features/milestone_spec.rb2
-rw-r--r--spec/features/projects/activity/rss_spec.rb29
-rw-r--r--spec/features/projects/commit/rss_spec.rb27
-rw-r--r--spec/features/projects/guest_navigation_menu_spec.rb2
-rw-r--r--spec/features/projects/issues/rss_spec.rb31
-rw-r--r--spec/features/projects/main/rss_spec.rb25
-rw-r--r--spec/features/projects/tree/rss_spec.rb25
-rw-r--r--spec/features/users/rss_spec.rb22
-rw-r--r--spec/helpers/milestones_helper_spec.rb2
-rw-r--r--spec/helpers/rss_helper_spec.rb20
-rw-r--r--spec/javascripts/boards/board_card_spec.js2
-rw-r--r--spec/javascripts/boards/board_new_issue_spec.js191
-rw-r--r--spec/javascripts/fixtures/projects.json18
-rw-r--r--spec/javascripts/new_branch_spec.js1
-rw-r--r--spec/javascripts/user_callout_spec.js.es636
-rw-r--r--spec/javascripts/vue_shared/components/table_pagination_spec.js.es610
-rw-r--r--spec/lib/banzai/filter/user_reference_filter_spec.rb6
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb44
-rw-r--r--spec/lib/gitlab/gitaly_client/notifications_spec.rb20
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/import_export/project.json18
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml2
-rw-r--r--spec/models/ci/build_spec.rb14
-rw-r--r--spec/models/merge_request_spec.rb22
-rw-r--r--spec/requests/api/commit_statuses_spec.rb72
-rw-r--r--spec/requests/api/environments_spec.rb36
-rw-r--r--spec/requests/api/groups_spec.rb6
-rw-r--r--spec/requests/api/internal_spec.rb28
-rw-r--r--spec/requests/api/issues_spec.rb57
-rw-r--r--spec/requests/api/merge_requests_spec.rb6
-rw-r--r--spec/requests/api/milestones_spec.rb21
-rw-r--r--spec/requests/api/notes_spec.rb4
-rw-r--r--spec/requests/api/project_snippets_spec.rb16
-rw-r--r--spec/requests/api/projects_spec.rb85
-rw-r--r--spec/requests/api/settings_spec.rb7
-rw-r--r--spec/requests/api/snippets_spec.rb12
-rw-r--r--spec/requests/api/todos_spec.rb4
-rw-r--r--spec/requests/api/v3/environments_spec.rb126
-rw-r--r--spec/requests/api/v3/groups_spec.rb530
-rw-r--r--spec/requests/api/v3/notes_spec.rb4
-rw-r--r--spec/requests/api/v3/projects_spec.rb6
-rw-r--r--spec/requests/api/v3/settings_spec.rb65
-rw-r--r--spec/requests/api/v3/snippets_spec.rb187
-rw-r--r--spec/rubocop/cop/custom_error_class_spec.rb111
-rw-r--r--spec/services/ci/image_for_build_service_spec.rb50
-rw-r--r--spec/services/ci/process_pipeline_service_spec.rb2
-rw-r--r--spec/services/ci/register_build_service_spec.rb45
-rw-r--r--spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb14
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb4
-rw-r--r--spec/services/notes/create_service_spec.rb50
-rw-r--r--spec/services/notification_service_spec.rb12
-rw-r--r--spec/services/slash_commands/interpret_service_spec.rb39
-rw-r--r--spec/services/system_note_service_spec.rb8
-rw-r--r--spec/services/todo_service_spec.rb4
-rw-r--r--spec/support/features/rss_shared_examples.rb23
-rw-r--r--spec/support/matchers/gitaly_matchers.rb3
-rw-r--r--spec/support/project_features_apply_to_issuables_shared_examples.rb2
-rw-r--r--spec/views/ci/status/_badge.html.haml_spec.rb89
-rw-r--r--vendor/assets/javascripts/jquery.highlight.js53
-rw-r--r--yarn.lock4
443 files changed, 6415 insertions, 3144 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e7a279c828b..9006fef6a27 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -300,7 +300,7 @@ bundler:audit:
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
script:
- - "bundle exec bundle-audit check --update --ignore OSVDB-115941 CVE-2016-6316 CVE-2016-6317"
+ - "bundle exec bundle-audit check --update"
migration paths:
stage: test
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f279a57105c..e075de055e3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 8.17.2 (2017-03-01)
+
+- Expire all webpack assets after 8.17.1 included a badly compiled asset. !9602
+
## 8.17.1 (2017-02-28)
- Replace setInterval with setTimeout to prevent highly frequent requests. !9271 (Takuya Noguchi)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index eed63127d9f..3cbc826e6db 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,32 +1,48 @@
+## Contributor license agreement
+
+By submitting code as an individual you agree to the
+[individual contributor license agreement](doc/legal/individual_contributor_license_agreement.md).
+By submitting code as an entity you agree to the
+[corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md).
+
+_This notice should stay as the first item in the CONTRIBUTING.MD file._
+
+---
+
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+- [Contributor license agreement](#contributor-license-agreement)
- [Contribute to GitLab](#contribute-to-gitlab)
- - [Contributor license agreement](#contributor-license-agreement)
- - [Security vulnerability disclosure](#security-vulnerability-disclosure)
- - [Closing policy for issues and merge requests](#closing-policy-for-issues-and-merge-requests)
- - [Helping others](#helping-others)
- - [I want to contribute!](#i-want-to-contribute)
- - [Implement design & UI elements](#implement-design-ui-elements)
- - [Issue tracker](#issue-tracker)
- - [Feature proposals](#feature-proposals)
- - [Issue tracker guidelines](#issue-tracker-guidelines)
- - [Issue weight](#issue-weight)
- - [Regression issues](#regression-issues)
- - [Technical debt](#technical-debt)
- - [Stewardship](#stewardship)
- - [Merge requests](#merge-requests)
- - [Merge request guidelines](#merge-request-guidelines)
- - [Contribution acceptance criteria](#contribution-acceptance-criteria)
- - [Changes for Stable Releases](#changes-for-stable-releases)
- - [Definition of done](#definition-of-done)
- - [Style guides](#style-guides)
- - [Code of conduct](#code-of-conduct)
+- [Security vulnerability disclosure](#security-vulnerability-disclosure)
+- [Closing policy for issues and merge requests](#closing-policy-for-issues-and-merge-requests)
+- [Helping others](#helping-others)
+- [I want to contribute!](#i-want-to-contribute)
+- [Implement design & UI elements](#implement-design-ui-elements)
+- [Release retrospective and kickoff](#release-retrospective-and-kickoff)
+ - [Retrospective](#retrospective)
+ - [Kickoff](#kickoff)
+- [Issue tracker](#issue-tracker)
+ - [Feature proposals](#feature-proposals)
+ - [Issue tracker guidelines](#issue-tracker-guidelines)
+ - [Issue weight](#issue-weight)
+ - [Regression issues](#regression-issues)
+ - [Technical debt](#technical-debt)
+ - [Stewardship](#stewardship)
+- [Merge requests](#merge-requests)
+ - [Merge request guidelines](#merge-request-guidelines)
+ - [Contribution acceptance criteria](#contribution-acceptance-criteria)
+- [Changes for Stable Releases](#changes-for-stable-releases)
+- [Definition of done](#definition-of-done)
+- [Style guides](#style-guides)
+- [Code of conduct](#code-of-conduct)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
-# Contribute to GitLab
+---
+
+## Contribute to GitLab
Thank you for your interest in contributing to GitLab. This guide details how
to contribute to GitLab in a way that is efficient for everyone.
@@ -41,13 +57,6 @@ operates please see [the GitLab contributing process](PROCESS.md).
- [GitLab Inc engineers should refer to the engineering workflow document](https://about.gitlab.com/handbook/engineering/workflow/)
-## Contributor license agreement
-
-By submitting code as an individual you agree to the
-[individual contributor license agreement](doc/legal/individual_contributor_license_agreement.md).
-By submitting code as an entity you agree to the
-[corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md).
-
## Security vulnerability disclosure
Please report suspected security vulnerabilities in private to
diff --git a/Gemfile b/Gemfile
index 429454b9ed6..b21f563940d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -236,7 +236,6 @@ gem 'gemojione', '~> 3.0'
gem 'gon', '~> 6.1.0'
gem 'jquery-atwho-rails', '~> 1.3.2'
gem 'jquery-rails', '~> 4.1.0'
-gem 'jquery-ui-rails', '~> 5.0.0'
gem 'request_store', '~> 1.3'
gem 'select2-rails', '~> 3.5.9'
gem 'virtus', '~> 1.0.1'
@@ -329,8 +328,6 @@ group :test do
gem 'timecop', '~> 0.8.0'
end
-gem 'newrelic_rpm', '~> 3.16'
-
gem 'octokit', '~> 4.6.2'
gem 'mail_room', '~> 0.9.1'
@@ -352,3 +349,6 @@ gem 'health_check', '~> 2.2.0'
# System information
gem 'vmstat', '~> 2.3.0'
gem 'sys-filesystem', '~> 1.1.6'
+
+# Gitaly GRPC client
+gem 'gitaly', '~> 0.2.1'
diff --git a/Gemfile.lock b/Gemfile.lock
index 472cee510cb..2d7e6f6f0bf 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -245,6 +245,9 @@ GEM
json
get_process_mem (0.2.0)
gherkin-ruby (0.3.2)
+ gitaly (0.2.1)
+ google-protobuf (~> 3.1)
+ grpc (~> 1.0)
github-linguist (4.7.6)
charlock_holmes (~> 0.7.3)
escape_utils (~> 1.1.0)
@@ -296,6 +299,7 @@ GEM
multi_json (~> 1.10)
retriable (~> 1.4)
signet (~> 0.6)
+ google-protobuf (3.2.0)
googleauth (0.5.1)
faraday (~> 0.9)
jwt (~> 1.4)
@@ -317,6 +321,9 @@ GEM
grape-entity (0.6.0)
activesupport
multi_json (>= 1.3.2)
+ grpc (1.1.2)
+ google-protobuf (~> 3.1)
+ googleauth (~> 0.5.1)
haml (4.0.7)
tilt
haml_lint (0.21.0)
@@ -367,8 +374,6 @@ GEM
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
- jquery-ui-rails (5.0.5)
- railties (>= 3.2.16)
json (1.8.6)
json-schema (2.6.2)
addressable (~> 2.3.8)
@@ -427,7 +432,6 @@ GEM
net-ldap (0.12.1)
net-ssh (3.0.1)
netrc (0.11.0)
- newrelic_rpm (3.16.0.318)
nokogiri (1.6.8.1)
mini_portile2 (~> 2.1.0)
numerizer (0.1.1)
@@ -660,7 +664,7 @@ GEM
sexp_processor (~> 4.1)
rubyntlm (0.5.2)
rubypants (0.2.0)
- rubyzip (1.2.0)
+ rubyzip (1.2.1)
rufus-scheduler (3.1.10)
rugged (0.24.0)
safe_yaml (1.0.4)
@@ -878,6 +882,7 @@ DEPENDENCIES
fuubar (~> 2.0.0)
gemnasium-gitlab-service (~> 0.2)
gemojione (~> 3.0)
+ gitaly (~> 0.2.1)
github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.5.1)
@@ -899,7 +904,6 @@ DEPENDENCIES
jira-ruby (~> 1.1.2)
jquery-atwho-rails (~> 1.3.2)
jquery-rails (~> 4.1.0)
- jquery-ui-rails (~> 5.0.0)
json-schema (~> 2.6.2)
jwt (~> 1.5.6)
kaminari (~> 0.17.0)
@@ -915,7 +919,6 @@ DEPENDENCIES
mousetrap-rails (~> 1.4.6)
mysql2 (~> 0.3.16)
net-ssh (~> 3.0.1)
- newrelic_rpm (~> 3.16)
nokogiri (~> 1.6.7, >= 1.6.7.2)
oauth2 (~> 1.2.0)
octokit (~> 4.6.2)
@@ -1011,4 +1014,4 @@ DEPENDENCIES
wikicloth (= 0.8.1)
BUNDLED WITH
- 1.14.4
+ 1.14.5
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index c51860d1604..4c24d35b5bb 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -9,7 +9,6 @@
window.$ = window.jQuery = require('jquery');
require('jquery-ujs');
require('vendor/jquery.endless-scroll');
-require('vendor/jquery.highlight');
require('vendor/jquery.waitforimages');
require('vendor/jquery.caret');
require('vendor/jquery.atwho');
@@ -18,15 +17,11 @@ window.Cookies = require('js-cookie');
require('./autosave');
require('bootstrap/js/affix');
require('bootstrap/js/alert');
-require('bootstrap/js/button');
-require('bootstrap/js/collapse');
require('bootstrap/js/dropdown');
require('bootstrap/js/modal');
-require('bootstrap/js/scrollspy');
require('bootstrap/js/tab');
require('bootstrap/js/transition');
require('bootstrap/js/tooltip');
-require('bootstrap/js/popover');
require('select2/select2.js');
window.Pikaday = require('pikaday');
window._ = require('underscore');
diff --git a/app/assets/javascripts/boards/components/board_card.js b/app/assets/javascripts/boards/components/board_card.js
index 52f61d84517..795b3cf2ec0 100644
--- a/app/assets/javascripts/boards/components/board_card.js
+++ b/app/assets/javascripts/boards/components/board_card.js
@@ -3,7 +3,7 @@ require('./issue_card_inner');
const Store = gl.issueBoards.BoardsStore;
-module.exports = {
+export default {
name: 'BoardsIssueCard',
template: `
<li class="card"
diff --git a/app/assets/javascripts/boards/components/board_list.js.es6 b/app/assets/javascripts/boards/components/board_list.js.es6
index d92047cc0f8..2d52e96e7fb 100644
--- a/app/assets/javascripts/boards/components/board_list.js.es6
+++ b/app/assets/javascripts/boards/components/board_list.js.es6
@@ -2,8 +2,8 @@
/* global Vue */
/* global Sortable */
-const boardCard = require('./board_card');
-require('./board_new_issue');
+import boardNewIssue from './board_new_issue';
+import boardCard from './board_card';
(() => {
const Store = gl.issueBoards.BoardsStore;
@@ -15,7 +15,7 @@ require('./board_new_issue');
template: '#js-board-list-template',
components: {
boardCard,
- 'board-new-issue': gl.issueBoards.BoardNewIssue
+ boardNewIssue,
},
props: {
disabled: Boolean,
@@ -81,6 +81,12 @@ require('./board_new_issue');
});
}
},
+ toggleForm() {
+ this.showIssueForm = !this.showIssueForm;
+ },
+ },
+ created() {
+ gl.IssueBoardsApp.$on(`hide-issue-form-${this.list.id}`, this.toggleForm);
},
mounted () {
const options = gl.issueBoards.getBoardSortableDefaultOptions({
@@ -115,6 +121,9 @@ require('./board_new_issue');
this.loadNextPage();
}
};
- }
+ },
+ beforeDestroy() {
+ gl.IssueBoardsApp.$off(`hide-issue-form-${this.list.id}`, this.toggleForm);
+ },
});
})();
diff --git a/app/assets/javascripts/boards/components/board_new_issue.js b/app/assets/javascripts/boards/components/board_new_issue.js
new file mode 100644
index 00000000000..b88f59dd6d4
--- /dev/null
+++ b/app/assets/javascripts/boards/components/board_new_issue.js
@@ -0,0 +1,92 @@
+/* global ListIssue */
+const Store = gl.issueBoards.BoardsStore;
+
+export default {
+ name: 'BoardNewIssue',
+ props: {
+ list: Object,
+ },
+ data() {
+ return {
+ title: '',
+ error: false,
+ };
+ },
+ methods: {
+ submit(e) {
+ e.preventDefault();
+ if (this.title.trim() === '') return;
+
+ this.error = false;
+
+ const labels = this.list.label ? [this.list.label] : [];
+ const issue = new ListIssue({
+ title: this.title,
+ labels,
+ subscribed: true,
+ });
+
+ this.list.newIssue(issue)
+ .then(() => {
+ // Need this because our jQuery very kindly disables buttons on ALL form submissions
+ $(this.$refs.submitButton).enable();
+
+ Store.detail.issue = issue;
+ Store.detail.list = this.list;
+ })
+ .catch(() => {
+ // Need this because our jQuery very kindly disables buttons on ALL form submissions
+ $(this.$refs.submitButton).enable();
+
+ // Remove the issue
+ this.list.removeIssue(issue);
+
+ // Show error message
+ this.error = true;
+ });
+
+ this.cancel();
+ },
+ cancel() {
+ this.title = '';
+ gl.IssueBoardsApp.$emit(`hide-issue-form-${this.list.id}`);
+ },
+ },
+ mounted() {
+ this.$refs.input.focus();
+ },
+ template: `
+ <div class="card board-new-issue-form">
+ <form @submit="submit($event)">
+ <div class="flash-container"
+ v-if="error">
+ <div class="flash-alert">
+ An error occured. Please try again.
+ </div>
+ </div>
+ <label class="label-light"
+ :for="list.id + '-title'">
+ Title
+ </label>
+ <input class="form-control"
+ type="text"
+ v-model="title"
+ ref="input"
+ :id="list.id + '-title'" />
+ <div class="clearfix prepend-top-10">
+ <button class="btn btn-success pull-left"
+ type="submit"
+ :disabled="title === ''"
+ ref="submit-button">
+ Submit issue
+ </button>
+ <button class="btn btn-default pull-right"
+ type="button"
+ @click="cancel">
+ Cancel
+ </button>
+ </div>
+ </form>
+ </div>
+ `,
+};
diff --git a/app/assets/javascripts/boards/components/board_new_issue.js.es6 b/app/assets/javascripts/boards/components/board_new_issue.js.es6
deleted file mode 100644
index b5c14a198ba..00000000000
--- a/app/assets/javascripts/boards/components/board_new_issue.js.es6
+++ /dev/null
@@ -1,64 +0,0 @@
-/* eslint-disable comma-dangle, no-unused-vars */
-/* global Vue */
-/* global ListIssue */
-
-(() => {
- const Store = gl.issueBoards.BoardsStore;
-
- window.gl = window.gl || {};
-
- gl.issueBoards.BoardNewIssue = Vue.extend({
- props: {
- list: Object,
- },
- data() {
- return {
- title: '',
- error: false
- };
- },
- methods: {
- submit(e) {
- e.preventDefault();
- if (this.title.trim() === '') return;
-
- this.error = false;
-
- const labels = this.list.label ? [this.list.label] : [];
- const issue = new ListIssue({
- title: this.title,
- labels,
- subscribed: true
- });
-
- this.list.newIssue(issue)
- .then((data) => {
- // Need this because our jQuery very kindly disables buttons on ALL form submissions
- $(this.$refs.submitButton).enable();
-
- Store.detail.issue = issue;
- Store.detail.list = this.list;
- })
- .catch(() => {
- // Need this because our jQuery very kindly disables buttons on ALL form submissions
- $(this.$refs.submitButton).enable();
-
- // Remove the issue
- this.list.removeIssue(issue);
-
- // Show error message
- this.error = true;
- });
-
- this.cancel();
- },
- cancel() {
- this.title = '';
- this.$parent.showIssueForm = false;
- }
- },
- mounted() {
- this.$refs.input.focus();
- },
- });
-})();
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.js.es6 b/app/assets/javascripts/commit/pipelines/pipelines_table.js.es6
index 95e84710937..631ed34851c 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.js.es6
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.js.es6
@@ -64,7 +64,9 @@ const PipelineStore = require('./pipelines_store');
return pipelinesService.all()
.then(response => response.json())
.then((json) => {
- this.store.storePipelines(json);
+ // depending of the endpoint the response can either bring a `pipelines` key or not.
+ const pipelines = json.pipelines || json;
+ this.store.storePipelines(pipelines);
this.isLoading = false;
})
.catch(() => {
diff --git a/app/assets/javascripts/copy_as_gfm.js.es6 b/app/assets/javascripts/copy_as_gfm.js.es6
index 4bd537a6f28..2bc3d85fba4 100644
--- a/app/assets/javascripts/copy_as_gfm.js.es6
+++ b/app/assets/javascripts/copy_as_gfm.js.es6
@@ -25,6 +25,9 @@ require('./lib/utils/common_utils');
},
},
ReferenceFilter: {
+ '.tooltip'(el, text) {
+ return '';
+ },
'a.gfm:not([data-link=true])'(el, text) {
return el.dataset.original || text;
},
diff --git a/app/assets/javascripts/diff.js.es6 b/app/assets/javascripts/diff.js.es6
index ccccd0a36ff..6829e8aeaea 100644
--- a/app/assets/javascripts/diff.js.es6
+++ b/app/assets/javascripts/diff.js.es6
@@ -25,6 +25,10 @@ require('./lib/utils/url_utility');
isBound = true;
}
+ if (gl.utils.getLocationHash()) {
+ this.highlightSelectedLine();
+ }
+
this.openAnchoredDiff();
}
@@ -78,7 +82,7 @@ require('./lib/utils/url_utility');
if (nothingHereBlock.length) {
const clickTarget = $('.js-file-title, .click-to-expand', diffFile);
diffFile.data('singleFileDiff').toggleDiff(clickTarget, () => {
- this.highlighSelectedLine();
+ this.highlightSelectedLine();
if (cb) cb();
});
} else if (cb) {
@@ -94,7 +98,7 @@ require('./lib/utils/url_utility');
} else {
window.location.hash = hash;
}
- this.highlighSelectedLine();
+ this.highlightSelectedLine();
}
diffViewType() {
@@ -108,7 +112,7 @@ require('./lib/utils/url_utility');
return line.find('.diff-line-num').map((i, elm) => parseInt($(elm).data('linenumber'), 10));
}
- highlighSelectedLine() {
+ highlightSelectedLine() {
const hash = gl.utils.getLocationHash();
const $diffFiles = $('.diff-file');
$diffFiles.find('.hll').removeClass('hll');
diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6
index 0f678492d4c..fc25122aedc 100644
--- a/app/assets/javascripts/dispatcher.js.es6
+++ b/app/assets/javascripts/dispatcher.js.es6
@@ -35,6 +35,8 @@
/* global Labels */
/* global Shortcuts */
+import GroupsList from './groups_list';
+
const ShortcutsBlob = require('./shortcuts_blob');
const UserCallout = require('./user_callout');
@@ -96,6 +98,10 @@ const UserCallout = require('./user_callout');
case 'dashboard:todos:index':
new gl.Todos();
break;
+ case 'dashboard:groups:index':
+ case 'explore:groups:index':
+ new GroupsList();
+ break;
case 'projects:milestones:new':
case 'projects:milestones:edit':
case 'projects:milestones:update':
diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6
index 2fca61fdae0..2cb48dde628 100644
--- a/app/assets/javascripts/environments/components/environment.js.es6
+++ b/app/assets/javascripts/environments/components/environment.js.es6
@@ -75,7 +75,7 @@ module.exports = Vue.component('environment-component', {
this.isLoading = true;
- return service.all()
+ return service.get()
.then(resp => ({
headers: resp.headers,
body: resp.json(),
diff --git a/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 b/app/assets/javascripts/environments/folder/environments_folder_view.js.es6
index 53d52965758..2a9d0492d7a 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_view.js.es6
+++ b/app/assets/javascripts/environments/folder/environments_folder_view.js.es6
@@ -92,7 +92,7 @@ module.exports = Vue.component('environment-folder-view', {
this.isLoading = true;
- return service.all()
+ return service.get()
.then(resp => ({
headers: resp.headers,
body: resp.json(),
diff --git a/app/assets/javascripts/environments/services/environments_service.js.es6 b/app/assets/javascripts/environments/services/environments_service.js.es6
index 9cef335868e..effc6c4c838 100644
--- a/app/assets/javascripts/environments/services/environments_service.js.es6
+++ b/app/assets/javascripts/environments/services/environments_service.js.es6
@@ -5,7 +5,7 @@ class EnvironmentsService {
this.environments = Vue.resource(endpoint);
}
- all() {
+ get() {
return this.environments.get();
}
}
diff --git a/app/assets/javascripts/groups_list.js b/app/assets/javascripts/groups_list.js
new file mode 100644
index 00000000000..0ef81e49444
--- /dev/null
+++ b/app/assets/javascripts/groups_list.js
@@ -0,0 +1,47 @@
+/**
+ * Based on project list search.
+ * Makes search request for groups when user types a value in the search input.
+ * Updates the html content of the page with the received one.
+ */
+export default class GroupsList {
+ constructor() {
+ this.groupsListFilterElement = document.querySelector('.js-groups-list-filter');
+ this.groupsListHolderElement = document.querySelector('.js-groups-list-holder');
+
+ this.initSearch();
+ }
+
+ initSearch() {
+ this.debounceFilter = _.debounce(this.filterResults.bind(this), 500);
+
+ this.groupsListFilterElement.removeEventListener('input', this.debounceFilter);
+ this.groupsListFilterElement.addEventListener('input', this.debounceFilter);
+ }
+
+ filterResults() {
+ const form = document.querySelector('form#group-filter-form');
+ const groupFilterUrl = `${form.getAttribute('action')}?${$(form).serialize()}`;
+
+ $(this.groupsListHolderElement).fadeTo(250, 0.5);
+
+ return $.ajax({
+ url: form.getAttribute('action'),
+ data: $(form).serialize(),
+ type: 'GET',
+ dataType: 'json',
+ context: this,
+ complete() {
+ $(this.groupsListHolderElement).fadeTo(250, 1);
+ },
+ success(data) {
+ this.groupsListHolderElement.innerHTML = data.html;
+
+ // Change url so if user reload a page - search results are saved
+ return window.history.replaceState({
+ page: groupFilterUrl,
+
+ }, document.title, groupFilterUrl);
+ },
+ });
+ }
+}
diff --git a/app/assets/javascripts/issuable/time_tracking/components/time_tracker.js.es6 b/app/assets/javascripts/issuable/time_tracking/components/time_tracker.js.es6
index a2c83ff77ed..1fae2d62b14 100644
--- a/app/assets/javascripts/issuable/time_tracking/components/time_tracker.js.es6
+++ b/app/assets/javascripts/issuable/time_tracking/components/time_tracker.js.es6
@@ -77,12 +77,12 @@ require('./comparison_pane');
<div class='help-button pull-right'
v-if='!showHelpState'
@click='toggleHelpState(true)'>
- <i class='fa fa-question-circle'></i>
+ <i class='fa fa-question-circle' aria-hidden='true'></i>
</div>
<div class='close-help-button pull-right'
v-if='showHelpState'
@click='toggleHelpState(false)'>
- <i class='fa fa-close'></i>
+ <i class='fa fa-close' aria-hidden='true'></i>
</div>
</div>
<div class='time-tracking-content hide-collapsed'>
diff --git a/app/assets/javascripts/issuable/time_tracking/time_tracking_bundle.js.es6 b/app/assets/javascripts/issuable/time_tracking/time_tracking_bundle.js.es6
index 1ca01d3bdb9..958a0cc6d50 100644
--- a/app/assets/javascripts/issuable/time_tracking/time_tracking_bundle.js.es6
+++ b/app/assets/javascripts/issuable/time_tracking/time_tracking_bundle.js.es6
@@ -39,8 +39,9 @@ require('../../subbable_resource');
listenForSlashCommands() {
$(document).on('ajax:success', '.gfm-form', (e, data) => {
const subscribedCommands = ['spend_time', 'time_estimate'];
- const changedCommands = data.commands_changes;
-
+ const changedCommands = data.commands_changes
+ ? Object.keys(data.commands_changes)
+ : [];
if (changedCommands && _.intersection(subscribedCommands, changedCommands).length) {
this.fetchIssuable();
}
diff --git a/app/assets/javascripts/merge_request_widget/ci_bundle.js.es6 b/app/assets/javascripts/merge_request_widget/ci_bundle.js.es6
index 5840916846b..547dfa9e677 100644
--- a/app/assets/javascripts/merge_request_widget/ci_bundle.js.es6
+++ b/app/assets/javascripts/merge_request_widget/ci_bundle.js.es6
@@ -21,9 +21,9 @@
});
$(document)
- .off('click', '.merge_when_build_succeeds')
- .on('click', '.merge_when_build_succeeds', () => {
- $('#merge_when_build_succeeds').val('1');
+ .off('click', '.merge_when_pipeline_succeeds')
+ .on('click', '.merge_when_pipeline_succeeds', () => {
+ $('#merge_when_pipeline_succeeds').val('1');
});
$(document)
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 03504255bda..47fa0f2eb96 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -246,12 +246,21 @@ require('./task_list');
};
Notes.prototype.handleCreateChanges = function(note) {
+ var votesBlock;
if (typeof note === 'undefined') {
return;
}
- if (note.commands_changes && note.commands_changes.indexOf('merge') !== -1) {
- $.get(mrRefreshWidgetUrl);
+ if (note.commands_changes) {
+ if ('merge' in note.commands_changes) {
+ $.get(mrRefreshWidgetUrl);
+ }
+
+ if ('emoji_award' in note.commands_changes) {
+ votesBlock = $('.js-awards-block').eq(0);
+ gl.awardsHandler.addAwardToEmojiBar(votesBlock, note.commands_changes.emoji_award);
+ return gl.awardsHandler.scrollToAwards();
+ }
}
};
@@ -262,26 +271,16 @@ require('./task_list');
*/
Notes.prototype.renderNote = function(note) {
- var $notesList, votesBlock;
+ var $notesList;
if (!note.valid) {
- if (note.award) {
- new Flash('You have already awarded this emoji!', 'alert', this.parentTimeline);
- }
- else {
- if (note.errors.commands_only) {
- new Flash(note.errors.commands_only, 'notice', this.parentTimeline);
- this.refresh();
- }
+ if (note.errors.commands_only) {
+ new Flash(note.errors.commands_only, 'notice', this.parentTimeline);
+ this.refresh();
}
return;
}
- if (note.award) {
- votesBlock = $('.js-awards-block').eq(0);
- gl.awardsHandler.addAwardToEmojiBar(votesBlock, note.name);
- return gl.awardsHandler.scrollToAwards();
- // render note if it not present in loaded list
- // or skip if rendered
- } else if (this.isNewNote(note)) {
+
+ if (this.isNewNote(note)) {
this.note_ids.push(note.id);
$notesList = $('ul.main-notes-list');
$notesList.append(note.html).syntaxHighlight();
diff --git a/app/assets/javascripts/profile/gl_crop.js.es6 b/app/assets/javascripts/profile/gl_crop.js.es6
index 42e9847af91..192b1192d07 100644
--- a/app/assets/javascripts/profile/gl_crop.js.es6
+++ b/app/assets/javascripts/profile/gl_crop.js.es6
@@ -13,7 +13,7 @@
this.onPickImageClick = this.onPickImageClick.bind(this);
this.fileInput = $(input);
this.modalCropImg = _.isString(this.modalCropImg) ? $(this.modalCropImg) : this.modalCropImg;
- this.fileInput.attr('name', `${this.fileInput.attr('name')}-trigger`).attr('id', `this.fileInput.attr('id')-trigger`);
+ this.fileInput.attr('name', `${this.fileInput.attr('name')}-trigger`).attr('id', `${this.fileInput.attr('id')}-trigger`);
this.exportWidth = exportWidth;
this.exportHeight = exportHeight;
this.cropBoxWidth = cropBoxWidth;
diff --git a/app/assets/javascripts/shortcuts_navigation.js b/app/assets/javascripts/shortcuts_navigation.js
index 542cd586df0..73db8c10b99 100644
--- a/app/assets/javascripts/shortcuts_navigation.js
+++ b/app/assets/javascripts/shortcuts_navigation.js
@@ -16,9 +16,6 @@ require('./shortcuts');
Mousetrap.bind('g p', function() {
return ShortcutsNavigation.findAndFollowLink('.shortcuts-project');
});
- Mousetrap.bind('g e', function() {
- return ShortcutsNavigation.findAndFollowLink('.shortcuts-project-activity');
- });
Mousetrap.bind('g f', function() {
return ShortcutsNavigation.findAndFollowLink('.shortcuts-tree');
});
@@ -31,9 +28,6 @@ require('./shortcuts');
Mousetrap.bind('g n', function() {
return ShortcutsNavigation.findAndFollowLink('.shortcuts-network');
});
- Mousetrap.bind('g g', function() {
- return ShortcutsNavigation.findAndFollowLink('.shortcuts-graphs');
- });
Mousetrap.bind('g i', function() {
return ShortcutsNavigation.findAndFollowLink('.shortcuts-issues');
});
diff --git a/app/assets/javascripts/user_callout.js b/app/assets/javascripts/user_callout.js
index 74b869502a4..99419e85b20 100644
--- a/app/assets/javascripts/user_callout.js
+++ b/app/assets/javascripts/user_callout.js
@@ -43,6 +43,8 @@ class UserCallout {
this.userCalloutBody.append($template);
$template.find(closeButton).on('click', e => this.dismissCallout(e));
$template.find(userCalloutBtn).on('click', e => this.dismissCallout(e));
+ } else {
+ this.userCalloutBody.remove();
}
}
@@ -50,7 +52,7 @@ class UserCallout {
Cookies.set(USER_CALLOUT_COOKIE, 'true');
const $currentTarget = $(e.currentTarget);
if ($currentTarget.hasClass('close-user-callout')) {
- this.userCalloutBody.empty();
+ this.userCalloutBody.remove();
}
}
}
diff --git a/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 b/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6
index 9d78b02fcd5..601ef41e917 100644
--- a/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6
+++ b/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6
@@ -45,18 +45,15 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s
methods: {
/**
- * Changes the URL according to the pagination component.
+ * Will change the page number and update the URL.
*
- * If no scope is provided, 'all' is assumed.
- *
- * Pagination component sends "null" when no scope is provided.
- *
- * @param {Number} pagenum
- * @param {String} apiScope = 'all'
+ * @param {Number} pageNumber desired page to go to.
*/
- change(pagenum, apiScope) {
- if (!apiScope) apiScope = 'all';
- gl.utils.visitUrl(`?scope=${apiScope}&page=${pagenum}`);
+ change(pageNumber) {
+ const param = gl.utils.setParamInURL('page', pageNumber);
+
+ gl.utils.visitUrl(param);
+ return param;
},
},
template: `
diff --git a/app/assets/javascripts/vue_shared/components/table_pagination.js.es6 b/app/assets/javascripts/vue_shared/components/table_pagination.js.es6
index dca4530188e..8943b850a72 100644
--- a/app/assets/javascripts/vue_shared/components/table_pagination.js.es6
+++ b/app/assets/javascripts/vue_shared/components/table_pagination.js.es6
@@ -22,8 +22,8 @@ window.Vue = require('vue');
Here is an example `change` method:
- change(pagenum, apiScope) {
- gl.utils.visitUrl(`?scope=${apiScope}&p=${pagenum}`);
+ change(pagenum) {
+ gl.utils.visitUrl(`?page=${pagenum}`);
},
*/
@@ -56,8 +56,6 @@ window.Vue = require('vue');
},
methods: {
changePage(e) {
- const apiScope = gl.utils.getParameterByName('scope');
-
const text = e.target.innerText;
const { totalPages, nextPage, previousPage } = this.pageInfo;
@@ -65,19 +63,19 @@ window.Vue = require('vue');
case SPREAD:
break;
case LAST:
- this.change(totalPages, apiScope);
+ this.change(totalPages);
break;
case NEXT:
- this.change(nextPage, apiScope);
+ this.change(nextPage);
break;
case PREV:
- this.change(previousPage, apiScope);
+ this.change(previousPage);
break;
case FIRST:
- this.change(1, apiScope);
+ this.change(1);
break;
default:
- this.change(+text, apiScope);
+ this.change(+text);
break;
}
},
diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss
index fb8ea18d122..9a0f7a14e57 100644
--- a/app/assets/stylesheets/framework/calendar.scss
+++ b/app/assets/stylesheets/framework/calendar.scss
@@ -1,6 +1,7 @@
.calender-block {
padding-left: 0;
padding-right: 0;
+ border-top: 0;
direction: rtl;
@media (min-width: $screen-sm-min) and (max-width: $screen-md-max) {
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index ff31e7f7b3d..6e8a5cc688b 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -107,11 +107,12 @@
&.fa-spinner {
font-size: 16px;
- margin-top: -8px;
+ margin-top: -3px;
}
}
- .fa-chevron-down {
+ .fa-chevron-down,
+ .fa-spinner {
position: absolute;
top: 11px;
right: 8px;
@@ -192,6 +193,10 @@
&.is-focused {
background-color: $dropdown-link-hover-bg;
text-decoration: none;
+
+ .badge {
+ background-color: darken($row-hover, 5%);
+ }
}
&.dropdown-menu-empty-link {
@@ -228,6 +233,12 @@
padding: 5px 8px;
color: $gl-text-color-secondary;
}
+
+ .badge {
+ position: absolute;
+ right: 8px;
+ top: 5px;
+ }
}
.dropdown-menu-drop-up {
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index 30f242a35db..ffece53a093 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -271,6 +271,7 @@ span.idiff {
font-size: 13px;
line-height: 28px;
display: inline-block;
+ float: none;
}
}
}
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 8978e284f55..40e93032f59 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -73,10 +73,6 @@
right: $gutter_collapsed_width;
}
}
-
- &.with-overlay {
- padding-right: $gutter_collapsed_width;
- }
}
.right-sidebar {
diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss
index 9c76e58dfc8..09951fe3d3e 100644
--- a/app/assets/stylesheets/highlight/dark.scss
+++ b/app/assets/stylesheets/highlight/dark.scss
@@ -21,6 +21,7 @@ $dark-highlight-color: $black;
$dark-pre-hll-bg: #373b41;
$dark-hll-bg: #373b41;
$dark-over-bg: #9f9ab5;
+$dark-expanded-bg: #3e3e3e;
$dark-c: #969896;
$dark-err: #c66;
$dark-k: #b294bb;
@@ -155,6 +156,22 @@ $dark-il: #de935f;
.line_content.match {
@include dark-diff-match-line;
}
+
+ &:not(.diff-expanded) + .diff-expanded,
+ &.diff-expanded + .line_holder:not(.diff-expanded) {
+ > .diff-line-num,
+ > .line_content {
+ border-top: 1px solid $black;
+ }
+ }
+
+ &.diff-expanded {
+ > .diff-line-num,
+ > .line_content {
+ background: $dark-expanded-bg;
+ border-color: $dark-expanded-bg;
+ }
+ }
}
// highlight line via anchor
diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss
index 6488a099c74..b6a6d298adf 100644
--- a/app/assets/stylesheets/highlight/monokai.scss
+++ b/app/assets/stylesheets/highlight/monokai.scss
@@ -14,6 +14,7 @@ $monokai-line-empty-border: darken($monokai-line-empty-bg, 15%);
$monokai-diff-border: #808080;
$monokai-highlight-bg: #ffe792;
$monokai-over-bg: #9f9ab5;
+$monokai-expanded-bg: #3e3e3e;
$monokai-new-bg: rgba(166, 226, 46, 0.1);
$monokai-new-idiff: rgba(166, 226, 46, 0.15);
@@ -155,6 +156,22 @@ $monokai-gi: #a6e22e;
.line_content.match {
@include dark-diff-match-line;
}
+
+ &:not(.diff-expanded) + .diff-expanded,
+ &.diff-expanded + .line_holder:not(.diff-expanded) {
+ > .diff-line-num,
+ > .line_content {
+ border-top: 1px solid $black;
+ }
+ }
+
+ &.diff-expanded {
+ > .diff-line-num,
+ > .line_content {
+ background: $monokai-expanded-bg;
+ border-color: $monokai-expanded-bg;
+ }
+ }
}
// highlight line via anchor
diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss
index 00079cc2b5b..4f7a50dcb4f 100644
--- a/app/assets/stylesheets/highlight/solarized_dark.scss
+++ b/app/assets/stylesheets/highlight/solarized_dark.scss
@@ -18,6 +18,7 @@ $solarized-dark-line-color-old: #7a6c71;
$solarized-dark-highlight: #094554;
$solarized-dark-hll-bg: #174652;
$solarized-dark-over-bg: #9f9ab5;
+$solarized-dark-expanded-bg: #010d10;
$solarized-dark-c: #586e75;
$solarized-dark-err: #93a1a1;
$solarized-dark-g: #93a1a1;
@@ -159,6 +160,22 @@ $solarized-dark-il: #2aa198;
.line_content.match {
@include dark-diff-match-line;
}
+
+ &:not(.diff-expanded) + .diff-expanded,
+ &.diff-expanded + .line_holder:not(.diff-expanded) {
+ > .diff-line-num,
+ > .line_content {
+ border-top: 1px solid $black;
+ }
+ }
+
+ &.diff-expanded {
+ > .diff-line-num,
+ > .line_content {
+ background: $solarized-dark-expanded-bg;
+ border-color: $solarized-dark-expanded-bg;
+ }
+ }
}
// highlight line via anchor
diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss
index 2e3b68f1a58..6463fe96c1b 100644
--- a/app/assets/stylesheets/highlight/solarized_light.scss
+++ b/app/assets/stylesheets/highlight/solarized_light.scss
@@ -19,6 +19,8 @@ $solarized-light-line-color-old: #ad9186;
$solarized-light-highlight: #eee8d5;
$solarized-light-hll-bg: #ddd8c5;
$solarized-light-over-bg: #ded7fc;
+$solarized-light-expanded-border: #d2cdbd;
+$solarized-light-expanded-bg: #ece6d4;
$solarized-light-c: #93a1a1;
$solarized-light-err: #586e75;
$solarized-light-g: #586e75;
@@ -166,6 +168,22 @@ $solarized-light-il: #2aa198;
.line_content.match {
@include matchLine;
}
+
+ &:not(.diff-expanded) + .diff-expanded,
+ &.diff-expanded + .line_holder:not(.diff-expanded) {
+ > .diff-line-num,
+ > .line_content {
+ border-top: 1px solid $solarized-light-expanded-border;
+ }
+ }
+
+ &.diff-expanded {
+ > .diff-line-num,
+ > .line_content {
+ background: $solarized-light-expanded-bg;
+ border-color: $solarized-light-expanded-bg;
+ }
+ }
}
// highlight line via anchor
diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss
index 0eca30e570f..ab2018bfbca 100644
--- a/app/assets/stylesheets/highlight/white.scss
+++ b/app/assets/stylesheets/highlight/white.scss
@@ -8,6 +8,8 @@ $white-highlight: #fafe3d;
$white-pre-hll-bg: #f8eec7;
$white-hll-bg: #f8f8f8;
$white-over-bg: #ded7fc;
+$white-expanded-border: #e0e0e0;
+$white-expanded-bg: #f7f7f7;
$white-c: #998;
$white-err: #a61717;
$white-err-bg: #e3d2d2;
@@ -140,6 +142,22 @@ $white-gc-bg: #eaf2f5;
}
}
+ &:not(.diff-expanded) + .diff-expanded,
+ &.diff-expanded + .line_holder:not(.diff-expanded) {
+ > .diff-line-num,
+ > .line_content {
+ border-top: 1px solid $white-expanded-border;
+ }
+ }
+
+ &.diff-expanded {
+ > .diff-line-num,
+ > .line_content {
+ background: $white-expanded-bg;
+ border-color: $white-expanded-bg;
+ }
+ }
+
.line_content {
&.old {
background-color: $line-removed;
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 339cdcde480..5d0c247dea8 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -133,8 +133,13 @@
width: 35px;
font-weight: normal;
- &:hover {
- text-decoration: underline;
+ &[disabled] {
+ cursor: default;
+
+ &:hover,
+ &:active {
+ text-decoration: none;
+ }
}
}
}
diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss
index 5776d86983a..08398bb43a2 100644
--- a/app/assets/stylesheets/pages/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -155,7 +155,7 @@
@media (max-width: $screen-xs-max) {
.event-item {
- padding-left: $gl-padding;
+ padding-left: 0;
.event-title {
white-space: normal;
@@ -169,8 +169,7 @@
.event-body {
margin: 0;
- border-left: 2px solid $events-body-border;
- padding-left: 10px;
+ padding-left: 0;
}
.event-item-timestamp {
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index aad1a8986b0..1a983d8c9ef 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -279,7 +279,7 @@ table.u2f-registrations {
}
.user-callout {
- margin: 24px auto 0;
+ margin: 0 auto;
.bordered-box {
border: 1px solid $border-color;
@@ -287,6 +287,7 @@ table.u2f-registrations {
}
.landing {
+ margin-top: $gl-padding;
margin-bottom: $gl-padding;
.close {
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index e4487dbcb87..8d1063fc26f 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -178,3 +178,29 @@
margin-left: $btn-side-margin;
}
}
+
+.repo-charts {
+ .sub-header {
+ margin: 20px 0;
+ }
+
+ .sub-header-block.border-top {
+ margin-top: 20px;
+ padding: 0;
+ border-top: 1px solid $white-dark;
+ border-bottom: none;
+ }
+
+ .commit-stats li {
+ font-size: 16px;
+ }
+
+ .tree-ref-header {
+ margin-bottom: 20px;
+
+ h4 {
+ margin: 0;
+ line-height: 36px;
+ }
+ }
+}
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index e42e48f87d2..32484f810da 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -72,14 +72,6 @@ class ApplicationController < ActionController::Base
end
end
- def authenticate_user!(*args)
- if redirect_to_home_page_url?
- return redirect_to current_application_settings.home_page_url
- end
-
- super(*args)
- end
-
def log_exception(exception)
application_trace = ActionDispatch::ExceptionWrapper.new(env, exception).application_trace
application_trace.map!{ |t| " #{t}\n" }
@@ -287,19 +279,6 @@ class ApplicationController < ActionController::Base
session[:skip_tfa] && session[:skip_tfa] > Time.current
end
- def redirect_to_home_page_url?
- # If user is not signed-in and tries to access root_path - redirect him to landing page
- # Don't redirect to the default URL to prevent endless redirections
- return false unless current_application_settings.home_page_url.present?
-
- home_page_url = current_application_settings.home_page_url.chomp('/')
- root_urls = [Gitlab.config.gitlab['url'].chomp('/'), root_url.chomp('/')]
-
- return false if root_urls.include?(home_page_url)
-
- current_user.nil? && root_path == request.path
- end
-
# U2F (universal 2nd factor) devices need a unique identifier for the application
# to perform authentication.
# https://developers.yubico.com/U2F/App_ID.html
diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb
deleted file mode 100644
index ff297d6ff13..00000000000
--- a/app/controllers/ci/projects_controller.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-module Ci
- class ProjectsController < ::ApplicationController
- before_action :project
- before_action :no_cache, only: [:badge]
- before_action :authorize_read_project!, except: [:badge, :index]
- skip_before_action :authenticate_user!, only: [:badge]
- protect_from_forgery
-
- def index
- redirect_to root_path
- end
-
- def show
- # Temporary compatibility with CI badges pointing to CI project page
- redirect_to namespace_project_path(project.namespace, project)
- end
-
- # Project status badge
- # Image with build status for sha or ref
- #
- # This action in DEPRECATED, this is here only for backwards compatibility
- # with projects migrated from GitLab CI.
- #
- def badge
- return render_404 unless @project
-
- image = Ci::ImageForBuildService.new.execute(@project, params)
- send_file image.path, filename: image.name, disposition: 'inline', type: "image/svg+xml"
- end
-
- protected
-
- def project
- @project ||= Project.find_by(ci_id: params[:id].to_i)
- end
-
- def no_cache
- response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
- response.headers["Pragma"] = "no-cache"
- response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
- end
-
- def authorize_read_project!
- return access_denied! unless can?(current_user, :read_project, project)
- end
- end
-end
diff --git a/app/controllers/dashboard/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb
index 0b7cf8167f0..d03265e9f20 100644
--- a/app/controllers/dashboard/groups_controller.rb
+++ b/app/controllers/dashboard/groups_controller.rb
@@ -1,5 +1,17 @@
class Dashboard::GroupsController < Dashboard::ApplicationController
def index
- @group_members = current_user.group_members.includes(source: :route).page(params[:page])
+ @group_members = current_user.group_members.includes(source: :route).joins(:group)
+ @group_members = @group_members.merge(Group.search(params[:filter_groups])) if params[:filter_groups].present?
+ @group_members = @group_members.merge(Group.sort(@sort = params[:sort]))
+ @group_members = @group_members.page(params[:page])
+
+ respond_to do |format|
+ format.html
+ format.json do
+ render json: {
+ html: view_to_html_string("dashboard/groups/_groups", locals: { group_members: @group_members })
+ }
+ end
+ end
end
end
diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb
index a962f9a0937..68228c095da 100644
--- a/app/controllers/explore/groups_controller.rb
+++ b/app/controllers/explore/groups_controller.rb
@@ -1,8 +1,17 @@
class Explore::GroupsController < Explore::ApplicationController
def index
@groups = GroupsFinder.new.execute(current_user)
- @groups = @groups.search(params[:search]) if params[:search].present?
+ @groups = @groups.search(params[:filter_groups]) if params[:filter_groups].present?
@groups = @groups.sort(@sort = params[:sort])
@groups = @groups.page(params[:page])
+
+ respond_to do |format|
+ format.html
+ format.json do
+ render json: {
+ html: view_to_html_string("explore/groups/_groups", locals: { groups: @groups })
+ }
+ end
+ end
end
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 39ba815cfca..f9a5ef46786 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -5,7 +5,7 @@ class Projects::BlobController < Projects::ApplicationController
include ActionView::Helpers::SanitizeHelper
# Raised when given an invalid file path
- class InvalidPathError < StandardError; end
+ InvalidPathError = Class.new(StandardError)
before_action :require_non_empty_project, except: [:new, :create]
before_action :authorize_download_code!
diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb
index 923e7340e69..43fc0c39801 100644
--- a/app/controllers/projects/graphs_controller.rb
+++ b/app/controllers/projects/graphs_controller.rb
@@ -17,6 +17,25 @@ class Projects::GraphsController < Projects::ApplicationController
end
def commits
+ redirect_to action: 'charts'
+ end
+
+ def languages
+ redirect_to action: 'charts'
+ end
+
+ def charts
+ get_commits
+ get_languages
+ end
+
+ def ci
+ redirect_to charts_namespace_project_pipelines_path(@project.namespace, @project)
+ end
+
+ private
+
+ def get_commits
@commits = @project.repository.commits(@ref, limit: 2000, skip_merges: true)
@commits_graph = Gitlab::Graphs::Commits.new(@commits)
@commits_per_week_days = @commits_graph.commits_per_week_days
@@ -24,15 +43,7 @@ class Projects::GraphsController < Projects::ApplicationController
@commits_per_month = @commits_graph.commits_per_month
end
- def ci
- @charts = {}
- @charts[:week] = Ci::Charts::WeekChart.new(project)
- @charts[:month] = Ci::Charts::MonthChart.new(project)
- @charts[:year] = Ci::Charts::YearChart.new(project)
- @charts[:build_times] = Ci::Charts::BuildTime.new(project)
- end
-
- def languages
+ def get_languages
@languages = Linguist::Repository.new(@repository.rugged, @repository.rugged.head.target_id).languages
total = @languages.map(&:last).sum
@@ -52,8 +63,6 @@ class Projects::GraphsController < Projects::ApplicationController
end
end
- private
-
def fetch_graph
@commits = @project.repository.commits(@ref, limit: 6000, skip_merges: true)
@log = []
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 53f30a24312..76519022381 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -10,11 +10,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_action :module_enabled
before_action :merge_request, only: [
:edit, :update, :show, :diffs, :commits, :conflicts, :conflict_for_path, :pipelines, :merge, :merge_check,
- :ci_status, :ci_environments_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues
+ :ci_status, :ci_environments_status, :toggle_subscription, :cancel_merge_when_pipeline_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues
]
before_action :validates_merge_request, only: [:show, :diffs, :commits, :pipelines]
before_action :define_show_vars, only: [:show, :diffs, :commits, :conflicts, :conflict_for_path, :builds, :pipelines]
- before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check]
+ before_action :define_widget_vars, only: [:merge, :cancel_merge_when_pipeline_succeeds, :merge_check]
before_action :define_commit_vars, only: [:diffs]
before_action :define_diff_comment_vars, only: [:diffs]
before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds, :conflicts, :conflict_for_path, :pipelines]
@@ -245,9 +245,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
format.json do
define_pipelines_vars
- render json: PipelineSerializer
+ render json: {
+ pipelines: PipelineSerializer
.new(project: @project, user: @current_user)
.represent(@pipelines)
+ }
end
end
end
@@ -326,8 +328,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
render partial: "projects/merge_requests/widget/show.html.haml", layout: false
end
- def cancel_merge_when_build_succeeds
- unless @merge_request.can_cancel_merge_when_build_succeeds?(current_user)
+ def cancel_merge_when_pipeline_succeeds
+ unless @merge_request.can_cancel_merge_when_pipeline_succeeds?(current_user)
return access_denied!
end
@@ -339,9 +341,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def merge
return access_denied! unless @merge_request.can_be_merged_by?(current_user)
- # Disable the CI check if merge_when_build_succeeds is enabled since we have
+ # Disable the CI check if merge_when_pipeline_succeeds is enabled since we have
# to wait until CI completes to know
- unless @merge_request.mergeable?(skip_ci_check: merge_when_build_succeeds_active?)
+ unless @merge_request.mergeable?(skip_ci_check: merge_when_pipeline_succeeds_active?)
@status = :failed
return
end
@@ -353,7 +355,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.update(merge_error: nil)
- if params[:merge_when_build_succeeds].present?
+ if params[:merge_when_pipeline_succeeds].present?
unless @merge_request.head_pipeline
@status = :failed
return
@@ -364,7 +366,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
.new(@project, current_user, merge_params)
.execute(@merge_request)
- @status = :merge_when_build_succeeds
+ @status = :merge_when_pipeline_succeeds
elsif @merge_request.head_pipeline.success?
# This can be triggered when a user clicks the auto merge button while
# the tests finish at about the same time
@@ -381,8 +383,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def merge_widget_refresh
@status =
- if merge_request.merge_when_build_succeeds
- :merge_when_build_succeeds
+ if merge_request.merge_when_pipeline_succeeds
+ :merge_when_pipeline_succeeds
else
# Only MRs that can be merged end in this action
# MR can be already picked up for merge / merged already or can be waiting for worker to be picked up
@@ -672,8 +674,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.ensure_ref_fetched
end
- def merge_when_build_succeeds_active?
- params[:merge_when_build_succeeds].present? &&
+ def merge_when_pipeline_succeeds_active?
+ params[:merge_when_pipeline_succeeds].present? &&
@merge_request.head_pipeline && @merge_request.head_pipeline.active?
end
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index b033f7b5ea9..5cf3a7f593b 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -148,17 +148,10 @@ class Projects::NotesController < Projects::ApplicationController
def note_json(note)
attrs = {
- award: false,
id: note.id
}
- if note.is_a?(AwardEmoji)
- attrs.merge!(
- valid: note.valid?,
- award: true,
- name: note.name
- )
- elsif note.persisted?
+ if note.persisted?
Banzai::NoteRenderer.render([note], @project, current_user)
attrs.merge!(
@@ -198,7 +191,7 @@ class Projects::NotesController < Projects::ApplicationController
)
end
- attrs[:commands_changes] = note.commands_changes unless attrs[:award]
+ attrs[:commands_changes] = note.commands_changes
attrs
end
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index 8657bc4dfdc..718d9e86bea 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -1,9 +1,10 @@
class Projects::PipelinesController < Projects::ApplicationController
- before_action :pipeline, except: [:index, :new, :create]
+ before_action :pipeline, except: [:index, :new, :create, :charts]
before_action :commit, only: [:show, :builds]
before_action :authorize_read_pipeline!
before_action :authorize_create_pipeline!, only: [:new, :create]
before_action :authorize_update_pipeline!, only: [:retry, :cancel]
+ before_action :builds_enabled, only: :charts
def index
@scope = params[:scope]
@@ -92,6 +93,14 @@ class Projects::PipelinesController < Projects::ApplicationController
redirect_back_or_default default: namespace_project_pipelines_path(project.namespace, project)
end
+ def charts
+ @charts = {}
+ @charts[:week] = Ci::Charts::WeekChart.new(project)
+ @charts[:month] = Ci::Charts::MonthChart.new(project)
+ @charts[:year] = Ci::Charts::YearChart.new(project)
+ @charts[:build_times] = Ci::Charts::BuildTime.new(project)
+ end
+
private
def create_params
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index acca821782c..3e2015b7d5e 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -314,7 +314,7 @@ class ProjectsController < Projects::ApplicationController
:name,
:namespace_id,
:only_allow_merge_if_all_discussions_are_resolved,
- :only_allow_merge_if_build_succeeds,
+ :only_allow_merge_if_pipeline_succeeds,
:path,
:public_builds,
:request_access_enabled,
diff --git a/app/controllers/root_controller.rb b/app/controllers/root_controller.rb
index db2817fadf6..1b4545e4a49 100644
--- a/app/controllers/root_controller.rb
+++ b/app/controllers/root_controller.rb
@@ -8,7 +8,9 @@
# `DashboardController#show`, which is the default.
class RootController < Dashboard::ProjectsController
skip_before_action :authenticate_user!, only: [:index]
- before_action :redirect_to_custom_dashboard, only: [:index]
+
+ before_action :redirect_unlogged_user, if: -> { current_user.nil? }
+ before_action :redirect_logged_user, if: -> { current_user.present? }
def index
super
@@ -16,23 +18,38 @@ class RootController < Dashboard::ProjectsController
private
- def redirect_to_custom_dashboard
- return redirect_to new_user_session_path unless current_user
+ def redirect_unlogged_user
+ if redirect_to_home_page_url?
+ redirect_to(current_application_settings.home_page_url)
+ else
+ redirect_to(new_user_session_path)
+ end
+ end
+ def redirect_logged_user
case current_user.dashboard
when 'stars'
flash.keep
- redirect_to starred_dashboard_projects_path
+ redirect_to(starred_dashboard_projects_path)
when 'project_activity'
- redirect_to activity_dashboard_path
+ redirect_to(activity_dashboard_path)
when 'starred_project_activity'
- redirect_to activity_dashboard_path(filter: 'starred')
+ redirect_to(activity_dashboard_path(filter: 'starred'))
when 'groups'
- redirect_to dashboard_groups_path
+ redirect_to(dashboard_groups_path)
when 'todos'
- redirect_to dashboard_todos_path
- else
- return
+ redirect_to(dashboard_todos_path)
end
end
+
+ def redirect_to_home_page_url?
+ # If user is not signed-in and tries to access root_path - redirect him to landing page
+ # Don't redirect to the default URL to prevent endless redirections
+ return false unless current_application_settings.home_page_url.present?
+
+ home_page_url = current_application_settings.home_page_url.chomp('/')
+ root_urls = [Gitlab.config.gitlab['url'].chomp('/'), root_url.chomp('/')]
+
+ root_urls.exclude?(home_page_url)
+ end
end
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index 195094730aa..0b30471f2ae 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -19,7 +19,7 @@ module ButtonHelper
title = data[:title] || 'Copy to clipboard'
data = { toggle: 'tooltip', placement: 'bottom', container: 'body' }.merge(data)
content_tag :button,
- icon('clipboard'),
+ icon('clipboard', 'aria-hidden': 'true'),
class: "btn #{css_class}",
data: data,
type: :button,
diff --git a/app/helpers/explore_helper.rb b/app/helpers/explore_helper.rb
index 2b1f3825adc..bbcb52f7eaf 100644
--- a/app/helpers/explore_helper.rb
+++ b/app/helpers/explore_helper.rb
@@ -9,12 +9,20 @@ module ExploreHelper
}
options = exist_opts.merge(options)
- path = request.path
- path << "?#{options.to_param}"
- path
+ request_path_with_options(options)
+ end
+
+ def filter_groups_path(options = {})
+ request_path_with_options(options)
end
def explore_controller?
controller.class.name.split("::").first == "Explore"
end
+
+ private
+
+ def request_path_with_options(options = {})
+ request.path + "?#{options.to_param}"
+ end
end
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 860a665ae26..c2b399041c6 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -1,6 +1,6 @@
module IssuablesHelper
def sidebar_gutter_toggle_icon
- sidebar_gutter_collapsed? ? icon('angle-double-left') : icon('angle-double-right')
+ sidebar_gutter_collapsed? ? icon('angle-double-left', { 'aria-hidden': 'true' }) : icon('angle-double-right', { 'aria-hidden': 'true' })
end
def sidebar_gutter_collapsed_class
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index 7d8505d704e..38be073c8dc 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -146,7 +146,7 @@ module MergeRequestsHelper
def merge_params(merge_request)
{
- merge_when_build_succeeds: true,
+ merge_when_pipeline_succeeds: true,
should_remove_source_branch: true,
sha: merge_request.diff_head_sha
}.merge(merge_params_ee(merge_request))
diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb
index 729928ce1dd..7011e670cee 100644
--- a/app/helpers/milestones_helper.rb
+++ b/app/helpers/milestones_helper.rb
@@ -97,7 +97,7 @@ module MilestonesHelper
def milestone_date_range(milestone)
if milestone.start_date && milestone.due_date
- "#{milestone.start_date.to_s(:medium)} - #{milestone.due_date.to_s(:medium)}"
+ "#{milestone.start_date.to_s(:medium)}–#{milestone.due_date.to_s(:medium)}"
elsif milestone.due_date
if milestone.due_date.past?
"expired on #{milestone.due_date.to_s(:medium)}"
diff --git a/app/helpers/rss_helper.rb b/app/helpers/rss_helper.rb
new file mode 100644
index 00000000000..ea5d2932ef4
--- /dev/null
+++ b/app/helpers/rss_helper.rb
@@ -0,0 +1,5 @@
+module RssHelper
+ def rss_url_options
+ { format: :atom, private_token: current_user.try(:private_token) }
+ end
+end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index dc36c754438..255e8c4ff78 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -179,6 +179,7 @@ class ApplicationSetting < ActiveRecord::Base
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_projects_limit: Settings.gitlab['default_projects_limit'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
+ default_group_visibility: Settings.gitlab.default_projects_features['visibility_level'],
disabled_oauth_sign_in_sources: [],
domain_whitelist: Settings.gitlab['domain_whitelist'],
gravatar_enabled: Settings.gravatar['enabled'],
@@ -277,6 +278,22 @@ class ApplicationSetting < ActiveRecord::Base
self.repository_storages = [value]
end
+ def default_project_visibility=(level)
+ super(Gitlab::VisibilityLevel.level_value(level))
+ end
+
+ def default_snippet_visibility=(level)
+ super(Gitlab::VisibilityLevel.level_value(level))
+ end
+
+ def default_group_visibility=(level)
+ super(Gitlab::VisibilityLevel.level_value(level))
+ end
+
+ def restricted_visibility_levels=(levels)
+ super(levels.map { |level| Gitlab::VisibilityLevel.level_value(level) })
+ end
+
# Choose one of the available repository storage options. Currently all have
# equal weighting.
def pick_repository_storage
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 77aba91f65c..f2989eff22d 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -55,15 +55,6 @@ module Ci
pending.unstarted.order('created_at ASC').first
end
- def create_from(build)
- new_build = build.dup
- new_build.status = 'pending'
- new_build.runner_id = nil
- new_build.trigger_request_id = nil
- new_build.token = nil
- new_build.save
- end
-
def retry(build, current_user)
Ci::RetryBuildService
.new(build.project, current_user)
diff --git a/app/models/group.rb b/app/models/group.rb
index 240a17f1dc1..7d23f655225 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -93,7 +93,7 @@ class Group < Namespace
end
def visibility_level_field
- visibility_level
+ :visibility_level
end
def visibility_level_allowed_by_projects
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index d6e7ed87555..81bde54d5dc 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -97,7 +97,7 @@ class MergeRequest < ActiveRecord::Base
validates :source_branch, presence: true
validates :target_project, presence: true
validates :target_branch, presence: true
- validates :merge_user, presence: true, if: :merge_when_build_succeeds?, unless: :importing?
+ validates :merge_user, presence: true, if: :merge_when_pipeline_succeeds?, unless: :importing?
validate :validate_branches, unless: [:allow_broken, :importing?, :closed_without_fork?]
validate :validate_fork, unless: :closed_without_fork?
@@ -436,7 +436,7 @@ class MergeRequest < ActiveRecord::Base
true
end
- def can_cancel_merge_when_build_succeeds?(current_user)
+ def can_cancel_merge_when_pipeline_succeeds?(current_user)
can_be_merged_by?(current_user) || self.author == current_user
end
@@ -644,10 +644,10 @@ class MergeRequest < ActiveRecord::Base
message.join("\n\n")
end
- def reset_merge_when_build_succeeds
- return unless merge_when_build_succeeds?
+ def reset_merge_when_pipeline_succeeds
+ return unless merge_when_pipeline_succeeds?
- self.merge_when_build_succeeds = false
+ self.merge_when_pipeline_succeeds = false
self.merge_user = nil
if merge_params
merge_params.delete('should_remove_source_branch')
@@ -706,7 +706,7 @@ class MergeRequest < ActiveRecord::Base
end
def mergeable_ci_state?
- return true unless project.only_allow_merge_if_build_succeeds?
+ return true unless project.only_allow_merge_if_pipeline_succeeds?
!head_pipeline || head_pipeline.success? || head_pipeline.skipped?
end
diff --git a/app/models/note.rb b/app/models/note.rb
index d6d5396afa5..4c97e4a986c 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -231,10 +231,6 @@ class Note < ActiveRecord::Base
note =~ /\A#{Banzai::Filter::EmojiFilter.emoji_pattern}\s?\Z/
end
- def award_emoji_name
- note.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1]
- end
-
def to_ability_name
for_personal_snippet? ? 'personal_snippet' : noteable_type.underscore
end
diff --git a/app/models/project.rb b/app/models/project.rb
index e06fc30dc8a..1ac4a178a9b 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -19,7 +19,7 @@ class Project < ActiveRecord::Base
extend Gitlab::ConfigHelper
- class BoardLimitExceeded < StandardError; end
+ BoardLimitExceeded = Class.new(StandardError)
NUMBER_OF_PERMITTED_BOARDS = 1
UNKNOWN_IMPORT_URL = 'http://unknown.git'.freeze
@@ -113,6 +113,7 @@ class Project < ActiveRecord::Base
has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project
has_one :external_wiki_service, dependent: :destroy
has_one :kubernetes_service, dependent: :destroy, inverse_of: :project
+ has_one :mock_ci_service, dependent: :destroy
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
has_one :forked_from_project, through: :forked_project_link
@@ -334,7 +335,7 @@ class Project < ActiveRecord::Base
end
def search_by_visibility(level)
- where(visibility_level: Gitlab::VisibilityLevel.const_get(level.upcase))
+ where(visibility_level: Gitlab::VisibilityLevel.string_options[level])
end
def search_by_title(query)
@@ -1003,7 +1004,7 @@ class Project < ActiveRecord::Base
end
def visibility_level_field
- visibility_level
+ :visibility_level
end
def archive!
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index 9891f5edf41..539b31780b3 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -7,7 +7,7 @@ class ProjectWiki
'AsciiDoc' => :asciidoc
}.freeze unless defined?(MARKUPS)
- class CouldNotCreateWikiError < StandardError; end
+ CouldNotCreateWikiError = Class.new(StandardError)
# Returns a string describing what went wrong after
# an operation fails.
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index 2665a7249a3..dbd564e5e7d 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -120,7 +120,7 @@ class Snippet < ActiveRecord::Base
end
def visibility_level_field
- visibility_level
+ :visibility_level
end
def no_highlighting?
diff --git a/app/models/user.rb b/app/models/user.rb
index 6fb5ac4a4ef..8443594c055 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -346,7 +346,11 @@ class User < ActiveRecord::Base
# Return (create if necessary) the ghost user. The ghost user
# owns records previously belonging to deleted users.
def ghost
- User.find_by_ghost(true) || create_ghost_user
+ unique_internal(where(ghost: true), 'ghost', 'ghost%s@example.com') 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.state = :blocked
+ u.name = 'Ghost User'
+ end
end
end
@@ -1017,10 +1021,14 @@ class User < ActiveRecord::Base
end
end
- def self.create_ghost_user
- # Since we only want a single ghost user in an instance, we use an
+ def self.unique_internal(scope, username, email_pattern, &b)
+ scope.first || create_unique_internal(scope, username, email_pattern, &b)
+ end
+
+ def self.create_unique_internal(scope, username, email_pattern, &creation_block)
+ # Since we only want a single one of these in an instance, we use an
# exclusive lease to ensure than this block is never run concurrently.
- lease_key = "ghost_user_creation"
+ lease_key = "user:unique_internal:#{username}"
lease = Gitlab::ExclusiveLease.new(lease_key, timeout: 1.minute.to_i)
until uuid = lease.try_obtain
@@ -1029,25 +1037,25 @@ class User < ActiveRecord::Base
sleep(1)
end
- # Recheck if a ghost user is already present. One might have been
+ # Recheck if the user is already present. One might have been
# added between the time we last checked (first line of this method)
# and the time we acquired the lock.
- ghost_user = User.find_by_ghost(true)
- return ghost_user if ghost_user.present?
+ existing_user = uncached { scope.first }
+ return existing_user if existing_user.present?
uniquify = Uniquify.new
- username = uniquify.string("ghost") { |s| User.find_by_username(s) }
+ username = uniquify.string(username) { |s| User.find_by_username(s) }
- email = uniquify.string(-> (n) { "ghost#{n}@example.com" }) do |s|
+ email = uniquify.string(-> (n) { Kernel.sprintf(email_pattern, n) }) do |s|
User.find_by_email(s)
end
- bio = 'This is a "Ghost User", created to hold all issues authored by users that have since been deleted. This user cannot be removed.'
-
- User.create(
- username: username, password: Devise.friendly_token, bio: bio,
- email: email, name: "Ghost User", state: :blocked, ghost: true
+ scope.create(
+ username: username,
+ password: Devise.friendly_token,
+ email: email,
+ &creation_block
)
ensure
Gitlab::ExclusiveLease.cancel(lease_key, uuid)
diff --git a/app/serializers/merge_request_entity.rb b/app/serializers/merge_request_entity.rb
index 7445298c714..5f80ab397a9 100644
--- a/app/serializers/merge_request_entity.rb
+++ b/app/serializers/merge_request_entity.rb
@@ -6,7 +6,7 @@ class MergeRequestEntity < IssuableEntity
expose :merge_params
expose :merge_status
expose :merge_user_id
- expose :merge_when_build_succeeds
+ expose :merge_when_pipeline_succeeds
expose :source_branch
expose :source_project_id
expose :target_branch
diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb
index 2bc6cf3266e..ab2d3d5a3ec 100644
--- a/app/serializers/pipeline_serializer.rb
+++ b/app/serializers/pipeline_serializer.rb
@@ -1,5 +1,5 @@
class PipelineSerializer < BaseSerializer
- class InvalidResourceError < StandardError; end
+ InvalidResourceError = Class.new(StandardError)
entity PipelineEntity
diff --git a/app/services/access_token_validation_service.rb b/app/services/access_token_validation_service.rb
index ddaaed90e5b..b2a543daa00 100644
--- a/app/services/access_token_validation_service.rb
+++ b/app/services/access_token_validation_service.rb
@@ -1,10 +1,16 @@
-AccessTokenValidationService = Struct.new(:token) do
+class AccessTokenValidationService
# Results:
VALID = :valid
EXPIRED = :expired
REVOKED = :revoked
INSUFFICIENT_SCOPE = :insufficient_scope
+ attr_reader :token
+
+ def initialize(token)
+ @token = token
+ end
+
def validate(scopes: [])
if token.expired?
return EXPIRED
diff --git a/app/services/ci/image_for_build_service.rb b/app/services/ci/image_for_build_service.rb
deleted file mode 100644
index 240ddabec36..00000000000
--- a/app/services/ci/image_for_build_service.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-module Ci
- class ImageForBuildService
- def execute(project, opts)
- ref = opts[:ref]
- sha = opts[:sha] || ref_sha(project, ref)
- pipelines = project.pipelines.where(sha: sha)
-
- image_name = image_for_status(pipelines.latest_status(ref))
- image_path = Rails.root.join('public/ci', image_name)
-
- OpenStruct.new(path: image_path, name: image_name)
- end
-
- private
-
- def ref_sha(project, ref)
- project.commit(ref).try(:sha) if ref
- end
-
- def image_for_status(status)
- status ||= 'unknown'
- 'build-' + status + ".svg"
- end
- end
-end
diff --git a/app/services/ci/register_build_service.rb b/app/services/ci/register_build_service.rb
index 6f03bf2be13..5b52a0425de 100644
--- a/app/services/ci/register_build_service.rb
+++ b/app/services/ci/register_build_service.rb
@@ -20,21 +20,33 @@ module Ci
builds_for_specific_runner
end
- build = builds.find do |build|
- runner.can_pick?(build)
- end
+ valid = true
- if build
- # In case when 2 runners try to assign the same build, second runner will be declined
- # with StateMachines::InvalidTransition or StaleObjectError when doing run! or save method.
- build.runner_id = runner.id
- build.run!
- end
+ builds.find do |build|
+ next unless runner.can_pick?(build)
+
+ begin
+ # In case when 2 runners try to assign the same build, second runner will be declined
+ # with StateMachines::InvalidTransition or StaleObjectError when doing run! or save method.
+ build.runner_id = runner.id
+ build.run!
- Result.new(build, true)
+ return Result.new(build, true)
+ rescue StateMachines::InvalidTransition, ActiveRecord::StaleObjectError
+ # We are looping to find another build that is not conflicting
+ # It also indicates that this build can be picked and passed to runner.
+ # If we don't do it, basically a bunch of runners would be competing for a build
+ # and thus we will generate a lot of 409. This will increase
+ # the number of generated requests, also will reduce significantly
+ # how many builds can be picked by runner in a unit of time.
+ # In case we hit the concurrency-access lock,
+ # we still have to return 409 in the end,
+ # to make sure that this is properly handled by runner.
+ valid = false
+ end
+ end
- rescue StateMachines::InvalidTransition, ActiveRecord::StaleObjectError
- Result.new(build, false)
+ Result.new(nil, valid)
end
private
diff --git a/app/services/commits/change_service.rb b/app/services/commits/change_service.rb
index 25e22f14e60..8a9bcd2d053 100644
--- a/app/services/commits/change_service.rb
+++ b/app/services/commits/change_service.rb
@@ -1,7 +1,7 @@
module Commits
class ChangeService < ::BaseService
- class ValidationError < StandardError; end
- class ChangeError < StandardError; end
+ ValidationError = Class.new(StandardError)
+ ChangeError = Class.new(StandardError)
def execute
@start_project = params[:start_project] || @project
diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb
index 0a25f56d24c..31869c2f01e 100644
--- a/app/services/files/base_service.rb
+++ b/app/services/files/base_service.rb
@@ -1,6 +1,6 @@
module Files
class BaseService < ::BaseService
- class ValidationError < StandardError; end
+ ValidationError = Class.new(StandardError)
def execute
@start_project = params[:start_project] || @project
diff --git a/app/services/files/multi_service.rb b/app/services/files/multi_service.rb
index 0609c6219e7..700f9f4f6f0 100644
--- a/app/services/files/multi_service.rb
+++ b/app/services/files/multi_service.rb
@@ -1,6 +1,6 @@
module Files
class MultiService < Files::BaseService
- class FileChangedError < StandardError; end
+ FileChangedError = Class.new(StandardError)
ACTIONS = %w[create update delete move].freeze
diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb
index 54e1aaf3f67..fbbab97632e 100644
--- a/app/services/files/update_service.rb
+++ b/app/services/files/update_service.rb
@@ -1,6 +1,6 @@
module Files
class UpdateService < Files::BaseService
- class FileChangedError < StandardError; end
+ FileChangedError = Class.new(StandardError)
def commit
repository.update_file(current_user, @file_path, @file_content,
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 9500faf2862..b618c3e038e 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -203,6 +203,7 @@ class IssuableBaseService < BaseService
change_state(issuable)
change_subscription(issuable)
change_todo(issuable)
+ toggle_award(issuable)
filter_params(issuable)
old_labels = issuable.labels.to_a
old_mentioned_users = issuable.mentioned_users.to_a
@@ -263,6 +264,14 @@ class IssuableBaseService < BaseService
end
end
+ def toggle_award(issuable)
+ award = params.delete(:emoji_award)
+ if award
+ todo_service.new_award_emoji(issuable, current_user)
+ issuable.toggle_award_emoji(award, current_user)
+ end
+ end
+
def has_changes?(issuable, old_labels: [])
valid_attrs = [:title, :description, :assignee_id, :milestone_id, :target_branch]
diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb
index a2a5f57d069..711f4035c55 100644
--- a/app/services/issues/move_service.rb
+++ b/app/services/issues/move_service.rb
@@ -1,6 +1,6 @@
module Issues
class MoveService < Issues::BaseService
- class MoveError < StandardError; end
+ MoveError = Class.new(StandardError)
def execute(issue, new_project)
@old_issue = issue
diff --git a/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb b/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb
index 5081dd5a0c4..aed5287940e 100644
--- a/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb
+++ b/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb
@@ -1,18 +1,18 @@
module MergeRequests
class MergeWhenPipelineSucceedsService < MergeRequests::BaseService
- # Marks the passed `merge_request` to be merged when the build succeeds or
+ # Marks the passed `merge_request` to be merged when the pipeline succeeds or
# updates the params for the automatic merge
def execute(merge_request)
merge_request.merge_params.merge!(params)
# The service is also called when the merge params are updated.
- already_approved = merge_request.merge_when_build_succeeds?
+ already_approved = merge_request.merge_when_pipeline_succeeds?
unless already_approved
- merge_request.merge_when_build_succeeds = true
- merge_request.merge_user = @current_user
+ merge_request.merge_when_pipeline_succeeds = true
+ merge_request.merge_user = @current_user
- SystemNoteService.merge_when_build_succeeds(merge_request, @project, @current_user, merge_request.diff_head_commit)
+ SystemNoteService.merge_when_pipeline_succeeds(merge_request, @project, @current_user, merge_request.diff_head_commit)
end
merge_request.save
@@ -23,7 +23,7 @@ module MergeRequests
return unless pipeline.success?
pipeline_merge_requests(pipeline) do |merge_request|
- next unless merge_request.merge_when_build_succeeds?
+ next unless merge_request.merge_when_pipeline_succeeds?
unless merge_request.mergeable?
todo_service.merge_request_became_unmergeable(merge_request)
@@ -36,9 +36,9 @@ module MergeRequests
# Cancels the automatic merge
def cancel(merge_request)
- if merge_request.merge_when_build_succeeds? && merge_request.open?
- merge_request.reset_merge_when_build_succeeds
- SystemNoteService.cancel_merge_when_build_succeeds(merge_request, @project, @current_user)
+ if merge_request.merge_when_pipeline_succeeds? && merge_request.open?
+ merge_request.reset_merge_when_pipeline_succeeds
+ SystemNoteService.cancel_merge_when_pipeline_succeeds(merge_request, @project, @current_user)
success
else
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index 581d18032e6..1131d6f4913 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -11,7 +11,7 @@ module MergeRequests
# empty diff during a manual merge
close_merge_requests
reload_merge_requests
- reset_merge_when_build_succeeds
+ reset_merge_when_pipeline_succeeds
mark_pending_todos_done
cache_merge_requests_closing_issues
@@ -78,8 +78,8 @@ module MergeRequests
end
end
- def reset_merge_when_build_succeeds
- merge_requests_for_source_branch.each(&:reset_merge_when_build_succeeds)
+ def reset_merge_when_pipeline_succeeds
+ merge_requests_for_source_branch.each(&:reset_merge_when_pipeline_succeeds)
end
def mark_pending_todos_done
diff --git a/app/services/merge_requests/resolve_service.rb b/app/services/merge_requests/resolve_service.rb
index d22a1d3e0ad..82cd89d9a0b 100644
--- a/app/services/merge_requests/resolve_service.rb
+++ b/app/services/merge_requests/resolve_service.rb
@@ -1,7 +1,6 @@
module MergeRequests
class ResolveService < MergeRequests::BaseService
- class MissingFiles < Gitlab::Conflict::ResolutionError
- end
+ MissingFiles = Class.new(Gitlab::Conflict::ResolutionError)
attr_accessor :conflicts, :rugged, :merge_index, :merge_request
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index b4f8b33d564..61d66a26932 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -8,14 +8,6 @@ module Notes
note.author = current_user
note.system = false
- if note.award_emoji?
- noteable = note.noteable
- if noteable.user_can_award?(current_user, note.award_emoji_name)
- todo_service.new_award_emoji(noteable, current_user)
- return noteable.create_award_emoji(note.award_emoji_name, current_user)
- end
- end
-
# We execute commands (extracted from `params[:note]`) on the noteable
# **before** we save the note because if the note consists of commands
# only, there is no need be create a note!
@@ -48,7 +40,7 @@ module Notes
note.errors.add(:commands_only, 'Commands applied')
end
- note.commands_changes = command_params.keys
+ note.commands_changes = command_params
end
note
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 3734e3c4253..fbad85d310e 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -135,7 +135,7 @@ class NotificationService
merge_request.target_project,
current_user,
:merged_merge_request_email,
- skip_current_user: !merge_request.merge_when_build_succeeds?
+ skip_current_user: !merge_request.merge_when_pipeline_succeeds?
)
end
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index 6dc3d8c2416..fbdaa455651 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -12,7 +12,7 @@ module Projects
@project = Project.new(params)
# Make sure that the user is allowed to use the specified visibility level
- unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level])
+ unless Gitlab::VisibilityLevel.allowed_for?(current_user, @project.visibility_level)
deny_visibility_level(@project)
return @project
end
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index 2e06826c311..a7142d5950e 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -2,7 +2,7 @@ module Projects
class DestroyService < BaseService
include Gitlab::ShellAdapter
- class DestroyError < StandardError; end
+ DestroyError = Class.new(StandardError)
DELETED_FLAG = '+deleted'.freeze
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index cd230528743..1c5a549feb9 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -2,7 +2,7 @@ module Projects
class ImportService < BaseService
include Gitlab::ShellAdapter
- class Error < StandardError; end
+ Error = Class.new(StandardError)
def execute
add_repository_to_project unless project.gitlab_project_import?
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index 20dfbddc823..da6e6acd4a7 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -9,7 +9,7 @@
module Projects
class TransferService < BaseService
include Gitlab::ShellAdapter
- class TransferError < StandardError; end
+ TransferError = Class.new(StandardError)
def execute(new_namespace)
if allowed_transfer?(current_user, project, new_namespace)
diff --git a/app/services/slash_commands/interpret_service.rb b/app/services/slash_commands/interpret_service.rb
index 3e0a85cf059..595653ea58a 100644
--- a/app/services/slash_commands/interpret_service.rb
+++ b/app/services/slash_commands/interpret_service.rb
@@ -59,7 +59,7 @@ module SlashCommands
@updates[:state_event] = 'reopen'
end
- desc 'Merge (when build succeeds)'
+ desc 'Merge (when the pipeline succeeds)'
condition do
last_diff_sha = params && params[:merge_request_diff_head_sha]
issuable.is_a?(MergeRequest) &&
@@ -255,6 +255,18 @@ module SlashCommands
@updates[:wip_event] = issuable.work_in_progress? ? 'unwip' : 'wip'
end
+ desc 'Toggle emoji reward'
+ params ':emoji:'
+ condition do
+ issuable.persisted?
+ end
+ command :award do |emoji|
+ name = award_emoji_name(emoji)
+ if name && issuable.user_can_award?(current_user, name)
+ @updates[:emoji_award] = name
+ end
+ end
+
desc 'Set time estimate'
params '<1w 3d 2h 14m>'
condition do
@@ -329,5 +341,10 @@ module SlashCommands
ext.references(type)
end
+
+ def award_emoji_name(emoji)
+ match = emoji.match(Banzai::Filter::EmojiFilter.emoji_pattern)
+ match[1] if match
+ end
end
end
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index 9b6dd013e3a..868fa7b3f21 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -84,7 +84,7 @@ class SystemHooksService
project_id: model.id,
owner_name: owner.name,
owner_email: owner.respond_to?(:email) ? owner.email : "",
- project_visibility: Project.visibility_levels.key(model.visibility_level_field).downcase
+ project_visibility: Project.visibility_levels.key(model.visibility_level_value).downcase
}
end
@@ -101,7 +101,7 @@ class SystemHooksService
user_email: model.user.email,
user_id: model.user.id,
access_level: model.human_access,
- project_visibility: Project.visibility_levels.key(project.visibility_level_field).downcase
+ project_visibility: Project.visibility_levels.key(project.visibility_level_value).downcase
}
end
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 55b548a12f9..db6a092d8fc 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -187,14 +187,14 @@ module SystemNoteService
end
# Called when 'merge when pipeline succeeds' is executed
- def merge_when_build_succeeds(noteable, project, author, last_commit)
+ def merge_when_pipeline_succeeds(noteable, project, author, last_commit)
body = "enabled an automatic merge when the pipeline for #{last_commit.to_reference(project)} succeeds"
create_note(noteable: noteable, project: project, author: author, note: body)
end
# Called when 'merge when pipeline succeeds' is canceled
- def cancel_merge_when_build_succeeds(noteable, project, author)
+ def cancel_merge_when_pipeline_succeeds(noteable, project, author)
body = 'canceled the automatic merge'
create_note(noteable: noteable, project: project, author: author, note: body)
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index ad86b4f9f42..8787a1c93a9 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -103,7 +103,7 @@ class TodoService
#
def merge_request_build_failed(merge_request)
create_build_failed_todo(merge_request, merge_request.author)
- create_build_failed_todo(merge_request, merge_request.merge_user) if merge_request.merge_when_build_succeeds?
+ create_build_failed_todo(merge_request, merge_request.merge_user) if merge_request.merge_when_pipeline_succeeds?
end
# When a new commit is pushed to a merge request we should:
@@ -121,7 +121,7 @@ class TodoService
#
def merge_request_build_retried(merge_request)
mark_pending_todos_as_done(merge_request, merge_request.author)
- mark_pending_todos_as_done(merge_request, merge_request.merge_user) if merge_request.merge_when_build_succeeds?
+ mark_pending_todos_as_done(merge_request, merge_request.merge_user) if merge_request.merge_when_pipeline_succeeds?
end
# When a merge request could not be automatically merged due to its unmergeable state we should:
@@ -129,7 +129,7 @@ class TodoService
# * create a todo for a merge_user
#
def merge_request_became_unmergeable(merge_request)
- create_unmergeable_todo(merge_request, merge_request.merge_user) if merge_request.merge_when_build_succeeds?
+ create_unmergeable_todo(merge_request, merge_request.merge_user) if merge_request.merge_when_pipeline_succeeds?
end
# When create a note we should:
diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml
index 0dbb0ca6958..89d991abe54 100644
--- a/app/views/dashboard/_activities.html.haml
+++ b/app/views/dashboard/_activities.html.haml
@@ -2,10 +2,9 @@
= render "events/event_last_push", event: @last_push
.nav-block
- - if current_user
- .controls
- = link_to dashboard_projects_path(:atom, { private_token: current_user.private_token }), class: 'btn rss-btn has-tooltip', title: 'Subscribe' do
- %i.fa.fa-rss
+ .controls
+ = link_to dashboard_projects_path(rss_url_options), class: 'btn rss-btn has-tooltip', title: 'Subscribe' do
+ %i.fa.fa-rss
= render 'shared/event_filter'
.content_list
diff --git a/app/views/dashboard/_groups_head.html.haml b/app/views/dashboard/_groups_head.html.haml
index 23c145ebbb4..c6d5937a3c3 100644
--- a/app/views/dashboard/_groups_head.html.haml
+++ b/app/views/dashboard/_groups_head.html.haml
@@ -6,7 +6,10 @@
= nav_link(page: explore_groups_path) do
= link_to explore_groups_path, title: 'Explore groups' do
Explore Groups
- - if current_user.can_create_group?
- .nav-controls
+ .nav-controls
+ = form_tag request.path, method: :get, class: 'group-filter-form', id: 'group-filter-form' do |f|
+ = search_field_tag :filter_groups, params[:filter_groups], placeholder: 'Filter by name...', class: 'group-filter-form-field form-control input-short js-groups-list-filter', spellcheck: false, id: 'group-filter-form-field', tabindex: "2"
+ = render 'shared/groups/dropdown'
+ - if current_user.can_create_group?
= link_to new_group_path, class: "btn btn-new" do
New Group
diff --git a/app/views/dashboard/activity.html.haml b/app/views/dashboard/activity.html.haml
index aa57df14c23..190ad4b40a5 100644
--- a/app/views/dashboard/activity.html.haml
+++ b/app/views/dashboard/activity.html.haml
@@ -1,6 +1,5 @@
= content_for :meta_tags do
- - if current_user
- = auto_discovery_link_tag(:atom, dashboard_projects_url(format: :atom, private_token: current_user.private_token), title: "All activity")
+ = auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
- page_title "Activity"
- header_title "Activity", activity_dashboard_path
diff --git a/app/views/dashboard/groups/_groups.html.haml b/app/views/dashboard/groups/_groups.html.haml
new file mode 100644
index 00000000000..6c3bf1a2b3b
--- /dev/null
+++ b/app/views/dashboard/groups/_groups.html.haml
@@ -0,0 +1,6 @@
+.js-groups-list-holder
+ %ul.content-list
+ - @group_members.each do |group_member|
+ = render 'shared/groups/group', group: group_member.group, group_member: group_member
+
+ = paginate @group_members, theme: 'gitlab'
diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml
index 1a679c51774..73ab2c95ff9 100644
--- a/app/views/dashboard/groups/index.html.haml
+++ b/app/views/dashboard/groups/index.html.haml
@@ -5,9 +5,4 @@
- if @group_members.empty?
= render 'empty_state'
- else
- %ul.content-list
- - @group_members.each do |group_member|
- - group = group_member.group
- = render 'shared/groups/group', group: group, group_member: group_member
-
- = paginate @group_members, theme: 'gitlab'
+ = render 'groups'
diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml
index 653052f7c54..9a4e423f896 100644
--- a/app/views/dashboard/issues.html.haml
+++ b/app/views/dashboard/issues.html.haml
@@ -1,17 +1,15 @@
- page_title "Issues"
- header_title "Issues", issues_dashboard_path(assignee_id: current_user.id)
= content_for :meta_tags do
- - if current_user
- = auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{current_user.name} issues")
+ = auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{current_user.name} issues")
.top-area
= render 'shared/issuable/nav', type: :issues
.nav-controls
- - if current_user
- = link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn' do
- = icon('rss')
- %span.icon-label
- Subscribe
+ = link_to params.merge(rss_url_options), class: 'btn' do
+ = icon('rss')
+ %span.icon-label
+ Subscribe
= render 'shared/new_project_item_select', path: 'issues/new', label: "New Issue"
= render 'shared/issuable/filter', type: :issues
diff --git a/app/views/dashboard/projects/index.atom.builder b/app/views/dashboard/projects/index.atom.builder
index fb5be63b472..13f7a8ddcec 100644
--- a/app/views/dashboard/projects/index.atom.builder
+++ b/app/views/dashboard/projects/index.atom.builder
@@ -1,7 +1,7 @@
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "Activity"
- xml.link href: dashboard_projects_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
+ xml.link href: dashboard_projects_url(rss_url_options), rel: "self", type: "application/atom+xml"
xml.link href: dashboard_projects_url, rel: "alternate", type: "text/html"
xml.id dashboard_projects_url
xml.updated @events[0].updated_at.xmlschema if @events[0]
diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml
index b82b933c3ad..f0adbad8412 100644
--- a/app/views/dashboard/projects/index.html.haml
+++ b/app/views/dashboard/projects/index.html.haml
@@ -1,6 +1,5 @@
= content_for :meta_tags do
- - if current_user
- = auto_discovery_link_tag(:atom, dashboard_projects_url(format: :atom, private_token: current_user.private_token), title: "All activity")
+ = auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
- page_title "Projects"
- header_title "Projects", dashboard_projects_path
diff --git a/app/views/explore/groups/_groups.html.haml b/app/views/explore/groups/_groups.html.haml
new file mode 100644
index 00000000000..794c6d1d170
--- /dev/null
+++ b/app/views/explore/groups/_groups.html.haml
@@ -0,0 +1,6 @@
+.js-groups-list-holder
+ %ul.content-list
+ - @groups.each do |group|
+ = render 'shared/groups/group', group: group
+
+ = paginate @groups, theme: 'gitlab'
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index 73cf6e87eb4..7f1bacc91cb 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -6,40 +6,10 @@
- else
= render 'explore/head'
-.row-content-block.clearfix
- .pull-left
- = form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f|
- = hidden_field_tag :sort, @sort
- .form-group
- = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "groups_search", spellcheck: false
- .form-group
- = button_tag 'Search', class: "btn btn-default"
-
- .pull-right
- .dropdown.inline
- %button.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
- %span.light
- - if @sort.present?
- = sort_options_hash[@sort]
- - else
- = sort_title_recently_created
- = icon('chevron-down')
- %ul.dropdown-menu.dropdown-menu-align-right
- %li
- = link_to explore_groups_path(sort: sort_value_recently_created) do
- = sort_title_recently_created
- = link_to explore_groups_path(sort: sort_value_oldest_created) do
- = sort_title_oldest_created
- = link_to explore_groups_path(sort: sort_value_recently_updated) do
- = sort_title_recently_updated
- = link_to explore_groups_path(sort: sort_value_oldest_updated) do
- = sort_title_oldest_updated
-
-%ul.content-list
- - @groups.each do |group|
- = render 'shared/groups/group', group: group
- - unless @groups.present?
- .nothing-here-block No public groups
+- if @groups.present?
+ = render 'groups'
+- else
+ .nothing-here-block No public groups
= paginate @groups, theme: "gitlab"
diff --git a/app/views/groups/_activities.html.haml b/app/views/groups/_activities.html.haml
index c442cf056c3..d7851c79990 100644
--- a/app/views/groups/_activities.html.haml
+++ b/app/views/groups/_activities.html.haml
@@ -2,10 +2,9 @@
= render "events/event_last_push", event: @last_push
.nav-block
- - if current_user
- .controls
- = link_to group_path(@group, format: :atom, private_token: current_user.private_token), class: 'btn rss-btn has-tooltip' , title: 'Subscribe' do
- %i.fa.fa-rss
+ .controls
+ = link_to group_path(@group, rss_url_options), class: 'btn rss-btn has-tooltip' , title: 'Subscribe' do
+ %i.fa.fa-rss
= render 'shared/event_filter'
.content_list
diff --git a/app/views/groups/activity.html.haml b/app/views/groups/activity.html.haml
index d7375b23524..3969e56f937 100644
--- a/app/views/groups/activity.html.haml
+++ b/app/views/groups/activity.html.haml
@@ -1,6 +1,5 @@
= content_for :meta_tags do
- - if current_user
- = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity")
+ = auto_discovery_link_tag(:atom, group_url(@group, rss_url_options), title: "#{@group.name} activity")
- page_title "Activity"
= render 'groups/head'
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index 939bddf3fe9..f4c17dc2d16 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -1,19 +1,17 @@
- page_title "Issues"
= render "head_issues"
= content_for :meta_tags do
- - if current_user
- = auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{@group.name} issues")
+ = auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{@group.name} issues")
- if group_issues(@group).exists?
.top-area
= render 'shared/issuable/nav', type: :issues
- - if current_user
- .nav-controls
- = link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn' do
- = icon('rss')
- %span.icon-label
- Subscribe
- = render 'shared/new_project_item_select', path: 'issues/new', label: "New Issue"
+ .nav-controls
+ = link_to params.merge(rss_url_options), class: 'btn' do
+ = icon('rss')
+ %span.icon-label
+ Subscribe
+ = render 'shared/new_project_item_select', path: 'issues/new', label: "New Issue"
= render 'shared/issuable/filter', type: :issues
diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder
index b68bf444d27..914091dfd15 100644
--- a/app/views/groups/show.atom.builder
+++ b/app/views/groups/show.atom.builder
@@ -1,7 +1,7 @@
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "#{@group.name} activity"
- xml.link href: group_url(@group, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
+ xml.link href: group_url(@group, rss_url_options), rel: "self", type: "application/atom+xml"
xml.link href: group_url(@group), rel: "alternate", type: "text/html"
xml.id group_url(@group)
xml.updated @events[0].updated_at.xmlschema if @events[0]
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 3d7b469660a..8f0f2708194 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -1,8 +1,7 @@
- @no_container = true
= content_for :meta_tags do
- - if current_user
- = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity")
+ = auto_discovery_link_tag(:atom, group_url(@group, rss_url_options), title: "#{@group.name} activity")
= render 'groups/head'
= render 'groups/home_panel'
diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml
index 705e20112fa..5d1369c2010 100644
--- a/app/views/help/_shortcuts.html.haml
+++ b/app/views/help/_shortcuts.html.haml
@@ -131,12 +131,6 @@
%tr
%td.shortcut
.key g
- .key e
- %td
- Go to the project's activity feed
- %tr
- %td.shortcut
- .key g
.key f
%td
Go to files
@@ -161,12 +155,6 @@
%tr
%td.shortcut
.key g
- .key g
- %td
- Go to graphs
- %tr
- %td.shortcut
- .key g
.key i
%td
Go to issues
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index c28661c2351..555ec8ad079 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -55,8 +55,6 @@
= link_to "Profile", current_user, class: 'profile-link', aria: { label: "Profile" }, data: { user: current_user.username }
%li
= link_to "Settings", profile_path, aria: { label: "Settings" }
- %li
- = link_to "Help", help_path, aria: { label: "Help" }
%li.divider
%li
= link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link", aria: { label: "Sign out" }
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 5d4178f03d7..15285ee32a3 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -24,16 +24,16 @@
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
%span
Issues
- (#{number_with_delimiter(cached_assigned_issuables_count(current_user, :issues, :opened))})
+ .badge= number_with_delimiter(cached_assigned_issuables_count(current_user, :issues, :opened))
= nav_link(path: 'dashboard#merge_requests') do
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
%span
Merge Requests
- (#{number_with_delimiter(cached_assigned_issuables_count(current_user, :merge_requests, :opened))})
+ .badge= number_with_delimiter(cached_assigned_issuables_count(current_user, :merge_requests, :opened))
= nav_link(controller: 'dashboard/snippets') do
= link_to dashboard_snippets_path, title: 'Snippets' do
%span
Snippets
%li.divider
%li
- = link_to "About GitLab CE", help_path, title: 'About GitLab CE', class: 'about-gitlab'
+ = link_to "Help", help_path, title: 'About GitLab CE', class: 'about-gitlab'
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 7883823b21e..2335d467389 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -21,40 +21,23 @@
.fade-right
= icon('angle-right')
%ul.nav-links.scrolling-tabs
- = nav_link(path: 'projects#show', html_options: {class: 'home'}) do
+ = nav_link(path: ['projects#show', 'projects#activity', 'cycle_analytics#show'], html_options: { class: 'home' }) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
%span
Project
- = nav_link(path: 'projects#activity') do
- = link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do
- %span
- Activity
-
- if project_nav_tab? :files
- = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare repositories tags branches releases network)) do
+ = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare repositories tags branches releases graphs network)) do
= link_to project_files_path(@project), title: 'Repository', class: 'shortcuts-tree' do
%span
Repository
- - if project_nav_tab? :pipelines
- = nav_link(controller: [:pipelines, :builds, :environments, :cycle_analytics]) do
- = link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
- %span
- Pipelines
-
- if project_nav_tab? :container_registry
= nav_link(controller: %w(container_registry)) do
= link_to project_container_registry_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do
%span
Registry
- - if project_nav_tab? :graphs
- = nav_link(controller: %w(graphs)) do
- = link_to namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Graphs', class: 'shortcuts-graphs' do
- %span
- Graphs
-
- if project_nav_tab? :issues
= nav_link(controller: [:issues, :labels, :milestones, :boards]) do
= link_to namespace_project_issues_path(@project.namespace, @project), title: 'Issues', class: 'shortcuts-issues' do
@@ -70,6 +53,12 @@
Merge Requests
%span.badge.count.merge_counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count)
+ - if project_nav_tab? :pipelines
+ = nav_link(controller: [:pipelines, :builds, :environments]) do
+ = link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
+ %span
+ Pipelines
+
- if project_nav_tab? :wiki
= nav_link(controller: :wikis) do
= link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do
diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml
index 4268337fd6d..fb990dd9592 100644
--- a/app/views/projects/_activity.html.haml
+++ b/app/views/projects/_activity.html.haml
@@ -1,11 +1,11 @@
- @no_container = true
+= render "projects/head"
%div{ class: container_class }
.nav-block.activity-filter-block
- - if current_user
- .controls
- = link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "Subscribe", class: 'btn rss-btn has-tooltip' do
- = icon('rss')
+ .controls
+ = link_to namespace_project_path(@project.namespace, @project, rss_url_options), title: "Subscribe", class: 'btn rss-btn has-tooltip' do
+ = icon('rss')
= render 'shared/event_filter'
diff --git a/app/views/projects/_head.html.haml b/app/views/projects/_head.html.haml
new file mode 100644
index 00000000000..db08b77c8e0
--- /dev/null
+++ b/app/views/projects/_head.html.haml
@@ -0,0 +1,20 @@
+= content_for :sub_nav do
+ .scrolling-tabs-container.sub-nav-scroll
+ = render 'shared/nav_scroll'
+ .nav-links.sub-nav.scrolling-tabs
+ %ul{ class: container_class }
+ = nav_link(path: 'projects#show') do
+ = link_to project_path(@project), title: 'Project home', class: 'shortcuts-project' do
+ %span
+ Home
+
+ = nav_link(path: 'projects#activity') do
+ = link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do
+ %span
+ Activity
+
+ - if can?(current_user, :read_cycle_analytics, @project)
+ = nav_link(path: 'cycle_analytics#show') do
+ = link_to project_cycle_analytics_path(@project), title: 'Cycle Analytics', class: 'shortcuts-project-cycle-analytics' do
+ %span
+ Cycle Analytics
diff --git a/app/views/projects/_merge_request_merge_settings.html.haml b/app/views/projects/_merge_request_merge_settings.html.haml
index 27d25a6b682..188198c47d5 100644
--- a/app/views/projects/_merge_request_merge_settings.html.haml
+++ b/app/views/projects/_merge_request_merge_settings.html.haml
@@ -2,8 +2,8 @@
.form-group
.checkbox.builds-feature
- = form.label :only_allow_merge_if_build_succeeds do
- = form.check_box :only_allow_merge_if_build_succeeds
+ = form.label :only_allow_merge_if_pipeline_succeeds do
+ = form.check_box :only_allow_merge_if_pipeline_succeeds
%strong Only allow merge requests to be merged if the pipeline succeeds
%br
%span.descr
diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml
index 7b9cfbbd067..c44d8fcd430 100644
--- a/app/views/projects/blob/_actions.html.haml
+++ b/app/views/projects/blob/_actions.html.haml
@@ -1,7 +1,8 @@
-.btn-group
- = view_on_environment_button(@commit.sha, @path, @environment) if @environment
+- if @environment
+ .btn-group<
+ = view_on_environment_button(@commit.sha, @path, @environment)
-.btn-group.tree-btn-group
+.btn-group{ role: "group" }<
= link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id),
class: 'btn btn-sm', target: '_blank'
-# only show normal/blame view links for text files
@@ -18,7 +19,7 @@
tree_join(@commit.sha, @path)), class: 'btn btn-sm js-data-file-blob-permalink-url'
- if current_user
- .btn-group{ role: "group" }
+ .btn-group{ role: "group" }<
- if blob_text_viewable?(@blob)
= edit_blob_link
= replace_blob_link
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index 19fa4c78501..41a7191302d 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -24,12 +24,13 @@
#blob-content-holder.blob-content-holder
%article.file-holder
- .js-file-title.file-title
- = blob_icon blob.mode, blob.name
- %strong
- = blob.name
- %small
- = number_to_human_size(blob_size(blob))
+ .js-file-title.file-title-flex-parent
+ .file-header-content
+ = blob_icon blob.mode, blob.name
+ %strong.file-title-name
+ = blob.name
+ %small
+ = number_to_human_size(blob_size(blob))
.file-actions.hidden-xs
= render "actions"
= render blob, blob: blob
diff --git a/app/views/projects/blob/diff.html.haml b/app/views/projects/blob/diff.html.haml
index c48d9dd982c..d1d448f0d4c 100644
--- a/app/views/projects/blob/diff.html.haml
+++ b/app/views/projects/blob/diff.html.haml
@@ -9,20 +9,20 @@
- line_old = line_new - @form.offset
- line_content = capture do
%td.line_content.noteable_line{ class: line_class }==#{' ' * @form.indent}#{line}
- %tr.line_holder{ id: line_old, class: line_class }
+ %tr.line_holder.diff-expanded{ id: line_old, class: line_class }
- case diff_view
- when :inline
%td.old_line.diff-line-num{ data: { linenumber: line_old } }
- %a{ href: "##{line_old}", data: { linenumber: line_old } }
+ %a{ href: "#", data: { linenumber: line_old }, disabled: true }
%td.new_line.diff-line-num{ data: { linenumber: line_new } }
- %a{ href: "##{line_new}", data: { linenumber: line_new } }
+ %a{ href: "#", data: { linenumber: line_new }, disabled: true }
= line_content
- when :parallel
%td.old_line.diff-line-num{ data: { linenumber: line_old } }
- %a{ href: "##{line_old}", data: { linenumber: line_old } }
+ %a{ href: "##{line_old}", data: { linenumber: line_old }, disabled: true }
= line_content
%td.new_line.diff-line-num{ data: { linenumber: line_new } }
- %a{ href: "##{line_new}", data: { linenumber: line_new } }
+ %a{ href: "##{line_new}", data: { linenumber: line_new }, disabled: true }
= line_content
- if @form.unfold? && @form.bottom? && @form.to < @blob.lines.size
diff --git a/app/views/projects/boards/components/_board_list.html.haml b/app/views/projects/boards/components/_board_list.html.haml
index f413a5e94c1..0993e880da9 100644
--- a/app/views/projects/boards/components/_board_list.html.haml
+++ b/app/views/projects/boards/components/_board_list.html.haml
@@ -2,28 +2,8 @@
.board-list-loading.text-center{ "v-if" => "loading" }
= icon("spinner spin")
- if can? current_user, :create_issue, @project
- %board-new-issue{ "inline-template" => true,
- ":list" => "list",
+ %board-new-issue{ ":list" => "list",
"v-if" => 'list.type !== "done" && showIssueForm' }
- .card.board-new-issue-form
- %form{ "@submit" => "submit($event)" }
- .flash-container{ "v-if" => "error" }
- .flash-alert
- An error occured. Please try again.
- %label.label-light{ ":for" => 'list.id + "-title"' }
- Title
- %input.form-control{ type: "text",
- "v-model" => "title",
- "ref" => "input",
- ":id" => 'list.id + "-title"' }
- .clearfix.prepend-top-10
- %button.btn.btn-success.pull-left{ type: "submit",
- ":disabled" => 'title === ""',
- "ref" => "submit-button" }
- Submit issue
- %button.btn.btn-default.pull-right{ type: "button",
- "@click" => "cancel" }
- Cancel
%ul.board-list{ "ref" => "list",
"v-show" => "!loading",
":data-board" => "list.id",
diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml
index 80763ce67ca..dd6797f10c0 100644
--- a/app/views/projects/commits/_head.html.haml
+++ b/app/views/projects/commits/_head.html.haml
@@ -11,14 +11,6 @@
= link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
Commits
- = nav_link(controller: %w(network)) do
- = link_to namespace_project_network_path(@project.namespace, @project, current_ref) do
- Network
-
- = nav_link(controller: :compare) do
- = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: current_ref) do
- Compare
-
= nav_link(html_options: {class: branches_tab_class}) do
= link_to namespace_project_branches_path(@project.namespace, @project) do
Branches
@@ -26,3 +18,19 @@
= nav_link(controller: [:tags, :releases]) do
= link_to namespace_project_tags_path(@project.namespace, @project) do
Tags
+
+ = nav_link(path: 'graphs#show') do
+ = link_to namespace_project_graph_path(@project.namespace, @project, current_ref) do
+ Contributors
+
+ = nav_link(controller: %w(network)) do
+ = link_to namespace_project_network_path(@project.namespace, @project, current_ref) do
+ Graph
+
+ = nav_link(controller: :compare) do
+ = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: current_ref) do
+ Compare
+
+ = nav_link(path: 'graphs#charts') do
+ = link_to charts_namespace_project_graph_path(@project.namespace, @project, current_ref) do
+ Charts
diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder
index 30bb7412073..2f0b6e39800 100644
--- a/app/views/projects/commits/show.atom.builder
+++ b/app/views/projects/commits/show.atom.builder
@@ -1,7 +1,7 @@
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "#{@project.name}:#{@ref} commits"
- xml.link href: namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
+ xml.link href: namespace_project_commits_url(@project.namespace, @project, @ref, rss_url_options), rel: "self", type: "application/atom+xml"
xml.link href: namespace_project_commits_url(@project.namespace, @project, @ref), rel: "alternate", type: "text/html"
xml.id namespace_project_commits_url(@project.namespace, @project, @ref)
xml.updated @commits.first.committed_date.xmlschema if @commits.any?
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index 08cb8a04413..38dbf2ac10b 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -2,8 +2,7 @@
- page_title "Commits", @ref
= content_for :meta_tags do
- - if current_user
- = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits")
+ = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits")
= content_for :sub_nav do
= render "head"
@@ -27,10 +26,9 @@
.control
= form_tag(namespace_project_commits_path(@project.namespace, @project, @id), method: :get, class: 'commits-search-form') do
= search_field_tag :search, params[:search], { placeholder: 'Filter by commit message', id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false }
- - if current_user && current_user.private_token
- .control
- = link_to namespace_project_commits_path(@project.namespace, @project, @ref, { format: :atom, private_token: current_user.private_token }), title: "Commits Feed", class: 'btn' do
- = icon("rss")
+ .control
+ = link_to namespace_project_commits_path(@project.namespace, @project, @ref, rss_url_options), title: "Commits Feed", class: 'btn' do
+ = icon("rss")
%div{ id: dom_id(@project) }
%ol#commits-list.list-unstyled.content_list
diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml
index 5405ff16bea..be17f2c764e 100644
--- a/app/views/projects/cycle_analytics/show.html.haml
+++ b/app/views/projects/cycle_analytics/show.html.haml
@@ -3,7 +3,7 @@
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('cycle_analytics')
-= render "projects/pipelines/head"
+= render "projects/head"
#cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } }
- if @cycle_analytics_no_data
diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml
deleted file mode 100644
index 67018aaa2ac..00000000000
--- a/app/views/projects/graphs/_head.html.haml
+++ /dev/null
@@ -1,19 +0,0 @@
-= content_for :sub_nav do
- .scrolling-tabs-container.sub-nav-scroll
- = render 'shared/nav_scroll'
- .nav-links.sub-nav.scrolling-tabs
- %ul{ class: (container_class) }
-
- - content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('lib_chart')
- = page_specific_javascript_bundle_tag('graphs')
- = nav_link(action: :show) do
- = link_to 'Contributors', namespace_project_graph_path
- = nav_link(action: :commits) do
- = link_to 'Commits', commits_namespace_project_graph_path
- = nav_link(action: :languages) do
- = link_to 'Languages', languages_namespace_project_graph_path
- - if @project.feature_available?(:builds, current_user)
- = nav_link(action: :ci) do
- = link_to ci_namespace_project_graph_path do
- Continuous Integration
diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/charts.html.haml
index c8a82f7bca3..d3bce45e974 100644
--- a/app/views/projects/graphs/commits.html.haml
+++ b/app/views/projects/graphs/charts.html.haml
@@ -1,38 +1,58 @@
- @no_container = true
-- page_title "Commits", "Graphs"
-= render 'head'
+- page_title "Charts"
+- content_for :page_specific_javascripts do
+ = page_specific_javascript_bundle_tag('lib_chart')
+ = page_specific_javascript_bundle_tag('graphs')
+= render "projects/commits/head"
-%div{ class: container_class }
- .sub-header-block
- .tree-ref-holder
- = render 'shared/ref_switcher', destination: 'graphs_commits'
- %ul.breadcrumb.repo-breadcrumb
- = commits_breadcrumbs
+.repo-charts{ class: container_class }
+ %h4.sub-header
+ Programming languages used in this repository
- %p.lead
- Commit statistics for
- %strong= @ref
- #{@commits_graph.start_date.strftime('%b %d')} - #{@commits_graph.end_date.strftime('%b %d')}
+ .row
+ .col-md-4
+ %ul.bordered-list
+ - @languages.each do |language|
+ %li
+ %span{ style: "color: #{language[:color]}" }
+ = icon('circle')
+ &nbsp;
+ = language[:label]
+ .pull-right
+ = language[:value]
+ \%
+ .col-md-8
+ %canvas#languages-chart{ height: 400 }
+
+.repo-charts{ class: container_class }
+ .sub-header-block.border-top
+
+ .row.tree-ref-header
+ .col-md-6
+ %h4
+ Commit statistics for
+ %strong= @ref
+ #{@commits_graph.start_date.strftime('%b %d')} - #{@commits_graph.end_date.strftime('%b %d')}
+
+ .col-md-6
+ .tree-ref-container
+ .tree-ref-holder
+ = render 'shared/ref_switcher', destination: 'graphs_commits'
+ %ul.breadcrumb.repo-breadcrumb
+ = commits_breadcrumbs
.row
.col-md-6
- %ul
+ %ul.commit-stats
%li
- %p.lead
- %strong= @commits_graph.commits.size
- commits during
- %strong= @commits_graph.duration
- days
+ Total:
+ %strong #{@commits_graph.commits.size} commits
%li
- %p.lead
- Average
- %strong= @commits_graph.commit_per_day
- commits per day
+ Average per day:
+ %strong #{@commits_graph.commit_per_day} commits
%li
- %p.lead
- Contributed by
- %strong= @commits_graph.authors
- authors
+ Authors:
+ %strong= @commits_graph.authors
.col-md-6
%div
%p.slead
@@ -40,15 +60,18 @@
%canvas#month-chart
.row
.col-md-6
- %div
- %p.slead
- Commits per day hour (UTC)
- %canvas#hour-chart
.col-md-6
%div
%p.slead
Commits per weekday
%canvas#weekday-chart
+ .row
+ .col-md-6
+ .col-md-6
+ %div
+ %p.slead
+ Commits per day hour (UTC)
+ %canvas#hour-chart
:javascript
var responsiveChart = function (selector, data) {
@@ -93,3 +116,12 @@
var monthData = chartData(#{@commits_per_month.keys.to_json}, #{@commits_per_month.values.to_json});
responsiveChart($('#month-chart'), monthData);
+
+ var data = #{@languages.to_json};
+ var ctx = $("#languages-chart").get(0).getContext("2d");
+ var options = {
+ scaleOverlay: true,
+ responsive: true,
+ maintainAspectRatio: false
+ }
+ var myPieChart = new Chart(ctx).Pie(data, options);
diff --git a/app/views/projects/graphs/ci.html.haml b/app/views/projects/graphs/ci.html.haml
deleted file mode 100644
index 6be4273b6ab..00000000000
--- a/app/views/projects/graphs/ci.html.haml
+++ /dev/null
@@ -1,18 +0,0 @@
-- @no_container = true
-- page_title "Continuous Integration", "Graphs"
-= render 'head'
-
-%div{ class: container_class }
- .sub-header-block
- .oneline
- A collection of graphs for Continuous Integration
-
- #charts.ci-charts
- .row
- .col-md-6
- = render 'projects/graphs/ci/overall'
- .col-md-6
- = render 'projects/graphs/ci/build_times'
-
- %hr
- = render 'projects/graphs/ci/builds'
diff --git a/app/views/projects/graphs/languages.html.haml b/app/views/projects/graphs/languages.html.haml
deleted file mode 100644
index fcfcae0be20..00000000000
--- a/app/views/projects/graphs/languages.html.haml
+++ /dev/null
@@ -1,33 +0,0 @@
-- @no_container = true
-- page_title "Languages", "Graphs"
-= render 'head'
-
-%div{ class: container_class }
- .sub-header-block
- .oneline
- Programming languages used in this repository
-
- .row
- .col-md-8
- %canvas#languages-chart{ height: 400 }
- .col-md-4
- %ul.bordered-list
- - @languages.each do |language|
- %li
- %span{ style: "color: #{language[:color]}" }
- = icon('circle')
- &nbsp;
- = language[:label]
- .pull-right
- = language[:value]
- \%
-
-:javascript
- var data = #{@languages.to_json};
- var ctx = $("#languages-chart").get(0).getContext("2d");
- var options = {
- scaleOverlay: true,
- responsive: true,
- maintainAspectRatio: false
- }
- var myPieChart = new Chart(ctx).Pie(data, options);
diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml
index 5ebb939a109..d89dfe31e47 100644
--- a/app/views/projects/graphs/show.html.haml
+++ b/app/views/projects/graphs/show.html.haml
@@ -1,6 +1,9 @@
- @no_container = true
-- page_title "Contributors", "Graphs"
-= render 'head'
+- page_title "Contributors"
+- content_for :page_specific_javascripts do
+ = page_specific_javascript_bundle_tag('lib_chart')
+ = page_specific_javascript_bundle_tag('graphs')
+= render 'projects/commits/head'
%div{ class: container_class }
.sub-header-block
diff --git a/app/views/projects/issues/_head.html.haml b/app/views/projects/issues/_head.html.haml
index 4825820c4d9..7a188cb6445 100644
--- a/app/views/projects/issues/_head.html.haml
+++ b/app/views/projects/issues/_head.html.haml
@@ -7,7 +7,7 @@
= nav_link(controller: :issues) do
= link_to namespace_project_issues_path(@project.namespace, @project), title: 'Issues' do
%span
- Issues
+ List
= nav_link(controller: :boards) do
= link_to namespace_project_boards_path(@project.namespace, @project), title: 'Board' do
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index 8ea1a3a45e1..7b7d7b1e00e 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -10,17 +10,15 @@
= page_specific_javascript_bundle_tag('filtered_search')
= content_for :meta_tags do
- - if current_user
- = auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{@project.name} issues")
+ = auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{@project.name} issues")
- if project_issues(@project).exists?
%div{ class: (container_class) }
.top-area
= render 'shared/issuable/nav', type: :issues
.nav-controls
- - if current_user
- = link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn append-right-10 has-tooltip', title: 'Subscribe' do
- = icon('rss')
+ = link_to params.merge(rss_url_options), class: 'btn append-right-10 has-tooltip', title: 'Subscribe' do
+ = icon('rss')
- if can? current_user, :create_issue, @project
= link_to new_namespace_project_issue_path(@project.namespace,
@project,
diff --git a/app/views/projects/merge_requests/cancel_merge_when_build_succeeds.js.haml b/app/views/projects/merge_requests/cancel_merge_when_pipeline_succeeds.js.haml
index eab5be488b5..eab5be488b5 100644
--- a/app/views/projects/merge_requests/cancel_merge_when_build_succeeds.js.haml
+++ b/app/views/projects/merge_requests/cancel_merge_when_pipeline_succeeds.js.haml
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index 83e6c026ba7..8a96c8dacf6 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -2,7 +2,6 @@
- @bulk_edit = can?(current_user, :admin_merge_request, @project)
- page_title "Merge Requests"
-= render "projects/issues/head"
= render 'projects/last_push'
- content_for :page_specific_javascripts do
diff --git a/app/views/projects/merge_requests/merge.js.haml b/app/views/projects/merge_requests/merge.js.haml
index 84b6c9ebc5c..f0a23bec5e7 100644
--- a/app/views/projects/merge_requests/merge.js.haml
+++ b/app/views/projects/merge_requests/merge.js.haml
@@ -2,9 +2,9 @@
- when :success
:plain
merge_request_widget.mergeInProgress(#{params[:should_remove_source_branch] == '1'});
-- when :merge_when_build_succeeds
+- when :merge_when_pipeline_succeeds
:plain
- $('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/merge_when_build_succeeds'))}");
+ $('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/merge_when_pipeline_succeeds'))}");
- when :sha_mismatch
:plain
$('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/sha_mismatch'))}");
diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml
index c0d6ab669b8..f0ccc4e00fd 100644
--- a/app/views/projects/merge_requests/widget/_open.html.haml
+++ b/app/views/projects/merge_requests/widget/_open.html.haml
@@ -19,8 +19,8 @@
= render 'projects/merge_requests/widget/open/conflicts'
- elsif @merge_request.work_in_progress?
= render 'projects/merge_requests/widget/open/wip'
- - elsif @merge_request.merge_when_build_succeeds?
- = render 'projects/merge_requests/widget/open/merge_when_build_succeeds'
+ - elsif @merge_request.merge_when_pipeline_succeeds?
+ = render 'projects/merge_requests/widget/open/merge_when_pipeline_succeeds'
- elsif !@merge_request.can_be_merged_by?(current_user)
= render 'projects/merge_requests/widget/open/not_allowed'
- elsif !@merge_request.mergeable_ci_state? && (@pipeline.failed? || @pipeline.canceled?)
diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml
index b730ced4214..1fa987bf537 100644
--- a/app/views/projects/merge_requests/widget/open/_accept.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml
@@ -11,16 +11,16 @@
.accept-action
- if @pipeline && @pipeline.active?
%span.btn-group
- = button_tag class: "btn btn-create js-merge-button merge_when_build_succeeds" do
+ = button_tag class: "btn btn-create js-merge-button merge_when_pipeline_succeeds" do
Merge When Pipeline Succeeds
- - unless @project.only_allow_merge_if_build_succeeds?
+ - unless @project.only_allow_merge_if_pipeline_succeeds?
= button_tag class: "btn btn-success dropdown-toggle", 'data-toggle' => 'dropdown' do
= icon('caret-down')
%span.sr-only
Select Merge Moment
%ul.js-merge-dropdown.dropdown-menu.dropdown-menu-right{ role: 'menu' }
%li
- = link_to "#", class: "merge_when_build_succeeds" do
+ = link_to "#", class: "merge_when_pipeline_succeeds" do
= icon('check fw')
Merge When Pipeline Succeeds
%li
@@ -49,4 +49,4 @@
text: @merge_request.merge_commit_message,
rows: 14, hint: true
- = hidden_field_tag :merge_when_build_succeeds, "", autocomplete: "off"
+ = hidden_field_tag :merge_when_pipeline_succeeds, "", autocomplete: "off"
diff --git a/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml b/app/views/projects/merge_requests/widget/open/_merge_when_pipeline_succeeds.html.haml
index cf7abf3756c..40a683d3fbd 100644
--- a/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_merge_when_pipeline_succeeds.html.haml
@@ -15,7 +15,7 @@
The source branch will not be removed.
- remove_source_branch_button = !@merge_request.remove_source_branch? && @merge_request.can_remove_source_branch?(current_user) && @merge_request.merge_user == current_user
- - user_can_cancel_automatic_merge = @merge_request.can_cancel_merge_when_build_succeeds?(current_user)
+ - user_can_cancel_automatic_merge = @merge_request.can_cancel_merge_when_pipeline_succeeds?(current_user)
- if remove_source_branch_button || user_can_cancel_automatic_merge
.clearfix.prepend-top-10
- if remove_source_branch_button
@@ -24,5 +24,5 @@
Remove Source Branch When Merged
- if user_can_cancel_automatic_merge
- = link_to cancel_merge_when_build_succeeds_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request), remote: true, method: :post, class: "btn btn-grouped btn-sm" do
+ = link_to cancel_merge_when_pipeline_succeeds_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request), remote: true, method: :post, class: "btn btn-grouped btn-sm" do
Cancel Automatic Merge
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index 06a31698ee6..a216d59bc74 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -19,10 +19,9 @@
Open
.header-text-content
%span.identifier
- Milestone ##{@milestone.iid}
+ %strong
+ Milestone %#{@milestone.iid}
- if @milestone.due_date || @milestone.start_date
- %span.creator
- &middot;
= milestone_date_range(@milestone)
.milestone-buttons
- if can?(current_user, :admin_milestone, @project)
diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml
index b88eef65cef..a4a24a217d3 100644
--- a/app/views/projects/network/show.html.haml
+++ b/app/views/projects/network/show.html.haml
@@ -1,4 +1,4 @@
-- page_title "Network", @ref
+- page_title "Graph", @ref
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/raphael.js')
= page_specific_javascript_bundle_tag('network')
diff --git a/app/views/projects/pages/_use.html.haml b/app/views/projects/pages/_use.html.haml
index 9db46f0b1fc..e442e6e9a09 100644
--- a/app/views/projects/pages/_use.html.haml
+++ b/app/views/projects/pages/_use.html.haml
@@ -5,4 +5,6 @@
.panel-body
%p
Learn how to upload your static site and have it served by
- GitLab by following the #{link_to "documentation on GitLab Pages", "http://doc.gitlab.com/ee/pages/README.html", target: :blank}.
+ GitLab by following the
+ = succeed '.' do
+ = link_to 'documentation on GitLab Pages', help_page_path('user/project/pages/index.md'), target: '_blank'
diff --git a/app/views/projects/pipelines/_head.html.haml b/app/views/projects/pipelines/_head.html.haml
index 721a9b6beb5..a5acb7ac4a5 100644
--- a/app/views/projects/pipelines/_head.html.haml
+++ b/app/views/projects/pipelines/_head.html.haml
@@ -4,25 +4,25 @@
.nav-links.sub-nav.scrolling-tabs{ class: ('build' if local_assigns.fetch(:build_subnav, false)) }
%ul{ class: (container_class) }
- if project_nav_tab? :pipelines
- = nav_link(controller: :pipelines) do
+ = nav_link(path: 'pipelines#index', controller: :pipelines) do
= link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
%span
Pipelines
- if project_nav_tab? :builds
- = nav_link(controller: %w(builds)) do
+ = nav_link(path: 'builds#index', controller: :builds) do
= link_to project_builds_path(@project), title: 'Jobs', class: 'shortcuts-builds' do
%span
Jobs
- if project_nav_tab? :environments
- = nav_link(controller: %w(environments)) do
+ = nav_link(path: 'environments#index', controller: :environments) do
= link_to project_environments_path(@project), title: 'Environments', class: 'shortcuts-environments' do
%span
Environments
- - if can?(current_user, :read_cycle_analytics, @project)
- = nav_link(controller: %w(cycle_analytics)) do
- = link_to project_cycle_analytics_path(@project), title: 'Cycle Analytics' do
+ - if @project.feature_available?(:builds, current_user) && !@project.empty_repo?
+ = nav_link(path: 'pipelines#charts') do
+ = link_to charts_namespace_project_pipelines_path(@project.namespace, @project), title: 'Charts', class: 'shortcuts-pipelines-charts' do
%span
- Cycle Analytics
+ Charts
diff --git a/app/views/projects/pipelines/charts.html.haml b/app/views/projects/pipelines/charts.html.haml
new file mode 100644
index 00000000000..8ffdfa1a2cf
--- /dev/null
+++ b/app/views/projects/pipelines/charts.html.haml
@@ -0,0 +1,21 @@
+- @no_container = true
+- page_title "Charts", "Pipelines"
+- content_for :page_specific_javascripts do
+ = page_specific_javascript_bundle_tag('lib_chart')
+ = page_specific_javascript_bundle_tag('graphs')
+= render 'head'
+
+%div{ class: container_class }
+ .sub-header-block
+ .oneline
+ A collection of graphs for Continuous Integration
+
+ #charts.ci-charts
+ .row
+ .col-md-6
+ = render 'projects/pipelines/charts/overall'
+ .col-md-6
+ = render 'projects/pipelines/charts/build_times'
+
+ %hr
+ = render 'projects/pipelines/charts/builds'
diff --git a/app/views/projects/graphs/ci/_build_times.haml b/app/views/projects/pipelines/charts/_build_times.haml
index bb0975a9535..bb0975a9535 100644
--- a/app/views/projects/graphs/ci/_build_times.haml
+++ b/app/views/projects/pipelines/charts/_build_times.haml
diff --git a/app/views/projects/graphs/ci/_builds.haml b/app/views/projects/pipelines/charts/_builds.haml
index b6f453b9736..b6f453b9736 100644
--- a/app/views/projects/graphs/ci/_builds.haml
+++ b/app/views/projects/pipelines/charts/_builds.haml
diff --git a/app/views/projects/graphs/ci/_overall.haml b/app/views/projects/pipelines/charts/_overall.haml
index edc4f7b079f..edc4f7b079f 100644
--- a/app/views/projects/graphs/ci/_overall.haml
+++ b/app/views/projects/pipelines/charts/_overall.haml
diff --git a/app/views/projects/show.atom.builder b/app/views/projects/show.atom.builder
index 11310d5e1e1..5c7f2e315f0 100644
--- a/app/views/projects/show.atom.builder
+++ b/app/views/projects/show.atom.builder
@@ -1,7 +1,7 @@
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "#{@project.name} activity"
- xml.link href: namespace_project_url(@project.namespace, @project, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
+ xml.link href: namespace_project_url(@project.namespace, @project, rss_url_options), rel: "self", type: "application/atom+xml"
xml.link href: namespace_project_url(@project.namespace, @project), rel: "alternate", type: "text/html"
xml.id namespace_project_url(@project.namespace, @project)
xml.updated @events[0].updated_at.xmlschema if @events[0]
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 80d4081dd7b..30d185e6556 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -1,15 +1,15 @@
- @no_container = true
= content_for :meta_tags do
- - if current_user
- = auto_discovery_link_tag(:atom, namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "#{@project.name} activity")
+ = auto_discovery_link_tag(:atom, namespace_project_path(@project.namespace, @project, rss_url_options), title: "#{@project.name} activity")
= content_for :flash_message do
- if current_user && can?(current_user, :download_code, @project)
= render 'shared/no_ssh'
= render 'shared/no_password'
-= render 'projects/last_push'
+= render "projects/head"
+= render "projects/last_push"
= render "home_panel"
- if current_user && can?(current_user, :download_code, @project)
diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml
index 9864be3562a..a2a26039220 100644
--- a/app/views/projects/tree/show.html.haml
+++ b/app/views/projects/tree/show.html.haml
@@ -2,8 +2,7 @@
- page_title @path.presence || "Files", @ref
= content_for :meta_tags do
- - if current_user
- = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits")
+ = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits")
= render "projects/commits/head"
= render 'projects/last_push'
diff --git a/app/views/shared/groups/_dropdown.html.haml b/app/views/shared/groups/_dropdown.html.haml
new file mode 100644
index 00000000000..37589b634fa
--- /dev/null
+++ b/app/views/shared/groups/_dropdown.html.haml
@@ -0,0 +1,18 @@
+.dropdown.inline
+ %button.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
+ %span.light
+ - if @sort.present?
+ = sort_options_hash[@sort]
+ - else
+ = sort_title_recently_created
+ = icon('chevron-down')
+ %ul.dropdown-menu.dropdown-menu-align-right
+ %li
+ = link_to filter_groups_path(sort: sort_value_recently_created) do
+ = sort_title_recently_created
+ = link_to filter_groups_path(sort: sort_value_oldest_created) do
+ = sort_title_oldest_created
+ = link_to filter_groups_path(sort: sort_value_recently_updated) do
+ = sort_title_recently_updated
+ = link_to filter_groups_path(sort: sort_value_oldest_updated) do
+ = sort_title_oldest_updated
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 1c8ff591171..37a0c63e514 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -9,16 +9,16 @@
- if current_user
%span.issuable-header-text.hide-collapsed.pull-left
Todo
- %a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", aria: { label: "Toggle sidebar" } }
+ %a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", "aria-label" => "Toggle sidebar" }
= sidebar_gutter_toggle_icon
- if current_user
- %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", aria: { label: (todo.nil? ? "Add todo" : "Mark done") }, data: { todo_text: "Add todo", mark_text: "Mark done", issuable_id: issuable.id, issuable_type: issuable.class.name.underscore, url: namespace_project_todos_path(@project.namespace, @project), delete_path: (dashboard_todo_path(todo) if todo) } }
+ %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", "aria-label" => (todo.nil? ? "Add todo" : "Mark done"), data: { todo_text: "Add todo", mark_text: "Mark done", issuable_id: issuable.id, issuable_type: issuable.class.name.underscore, url: namespace_project_todos_path(@project.namespace, @project), delete_path: (dashboard_todo_path(todo) if todo) } }
%span.js-issuable-todo-text
- if todo
Mark done
- else
Add todo
- = icon('spin spinner', class: 'hidden js-issuable-todo-loading')
+ = icon('spin spinner', class: 'hidden js-issuable-todo-loading', 'aria-hidden': 'true')
= form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: { class: 'issuable-context-form inline-update js-issuable-update' } do |f|
.block.assignee
@@ -26,10 +26,10 @@
- if issuable.assignee
= link_to_member(@project, issuable.assignee, size: 24)
- else
- = icon('user')
+ = icon('user', 'aria-hidden': 'true')
.title.hide-collapsed
Assignee
- = icon('spinner spin', class: 'block-loading')
+ = icon('spinner spin', class: 'block-loading', 'aria-hidden': 'true')
- if can_edit_issuable
= link_to 'Edit', '#', class: 'edit-link pull-right'
.value.hide-collapsed
@@ -37,7 +37,7 @@
= link_to_member(@project, issuable.assignee, size: 32, extra_class: 'bold') do
- if issuable.instance_of?(MergeRequest) && !issuable.can_be_merged_by?(issuable.assignee)
%span.pull-right.cannot-be-merged{ data: { toggle: 'tooltip', placement: 'left' }, title: 'Not allowed to merge' }
- = icon('exclamation-triangle')
+ = icon('exclamation-triangle', 'aria-hidden': 'true')
%span.username
= issuable.assignee.to_reference
- else
@@ -54,7 +54,7 @@
.block.milestone
.sidebar-collapsed-icon
- = icon('clock-o')
+ = icon('clock-o', 'aria-hidden': 'true')
%span
- if issuable.milestone
%span.has-tooltip{ title: milestone_remaining_days(issuable.milestone), data: { container: 'body', html: 1, placement: 'left' } }
@@ -63,7 +63,7 @@
None
.title.hide-collapsed
Milestone
- = icon('spinner spin', class: 'block-loading')
+ = icon('spinner spin', class: 'block-loading', 'aria-hidden': 'true')
- if can_edit_issuable
= link_to 'Edit', '#', class: 'edit-link pull-right'
.value.hide-collapsed
@@ -81,16 +81,16 @@
// Fallback while content is loading
.title.hide-collapsed
Time tracking
- = icon('spinner spin')
+ = icon('spinner spin', 'aria-hidden': 'true')
- if issuable.has_attribute?(:due_date)
.block.due_date
.sidebar-collapsed-icon
- = icon('calendar')
+ = icon('calendar', 'aria-hidden': 'true')
%span.js-due-date-sidebar-value
= issuable.due_date.try(:to_s, :medium) || 'None'
.title.hide-collapsed
Due date
- = icon('spinner spin', class: 'block-loading')
+ = icon('spinner spin', class: 'block-loading', 'aria-hidden': 'true')
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
= link_to 'Edit', '#', class: 'edit-link pull-right'
.value.hide-collapsed
@@ -110,7 +110,7 @@
.dropdown
%button.dropdown-menu-toggle.js-due-date-select{ type: 'button', data: { toggle: 'dropdown', field_name: "#{issuable.to_ability_name}[due_date]", ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable) } }
%span.dropdown-toggle-text Due date
- = icon('chevron-down')
+ = icon('chevron-down', 'aria-hidden': 'true')
.dropdown-menu.dropdown-menu-due-date
= dropdown_title('Due date')
= dropdown_content do
@@ -120,12 +120,12 @@
- selected_labels = issuable.labels
.block.labels
.sidebar-collapsed-icon.js-sidebar-labels-tooltip{ title: issuable_labels_tooltip(issuable.labels_array), data: { placement: "left", container: "body" } }
- = icon('tags')
+ = icon('tags', 'aria-hidden': 'true')
%span
= selected_labels.size
.title.hide-collapsed
Labels
- = icon('spinner spin', class: 'block-loading')
+ = icon('spinner spin', class: 'block-loading', 'aria-hidden': 'true')
- if can_edit_issuable
= link_to 'Edit', '#', class: 'edit-link pull-right'
.value.issuable-show-labels.hide-collapsed{ class: ("has-labels" if selected_labels.any?) }
@@ -141,7 +141,7 @@
%button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{ type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (namespace_project_labels_path(@project.namespace, @project, :json) if @project) } }
%span.dropdown-toggle-text{ class: ("is-default" if selected_labels.empty?) }
= multi_label_name(selected_labels, "Labels")
- = icon('chevron-down')
+ = icon('chevron-down', 'aria-hidden': 'true')
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
= render partial: "shared/issuable/label_page_default"
- if can? current_user, :admin_label, @project and @project
@@ -152,7 +152,7 @@
- subscribed = issuable.subscribed?(current_user, @project)
.block.light.subscription{ data: { url: toggle_subscription_path(issuable) } }
.sidebar-collapsed-icon
- = icon('rss')
+ = icon('rss', 'aria-hidden': 'true')
%span.issuable-header-text.hide-collapsed.pull-left
Notifications
- subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed'
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index c130f3d9e17..af091f9ab88 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -24,13 +24,12 @@
= link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn btn-gray',
title: 'Report abuse', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= icon('exclamation-circle')
- - if current_user
- = link_to user_path(@user, :atom, { private_token: current_user.private_token }), class: 'btn btn-gray' do
- = icon('rss')
- - if current_user.admin?
- = link_to [:admin, @user], class: 'btn btn-gray', title: 'View user in admin area',
- data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
- = icon('users')
+ = link_to user_path(@user, rss_url_options), class: 'btn btn-gray' do
+ = icon('rss')
+ - if current_user && current_user.admin?
+ = link_to [:admin, @user], class: 'btn btn-gray', title: 'View user in admin area',
+ data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = icon('users')
.profile-header
.avatar-holder
diff --git a/changelogs/unreleased/22951-fix-todos-api-endpoint-error-for-commits.yml b/changelogs/unreleased/22951-fix-todos-api-endpoint-error-for-commits.yml
new file mode 100644
index 00000000000..a53e7d77c16
--- /dev/null
+++ b/changelogs/unreleased/22951-fix-todos-api-endpoint-error-for-commits.yml
@@ -0,0 +1,4 @@
+---
+title: Add spec for todo with target_type Commit
+merge_request: 9351
+author: George Andrinopoulos
diff --git a/changelogs/unreleased/25437-just-emoji.yml b/changelogs/unreleased/25437-just-emoji.yml
new file mode 100644
index 00000000000..ceb81a47f2d
--- /dev/null
+++ b/changelogs/unreleased/25437-just-emoji.yml
@@ -0,0 +1,4 @@
+---
+title: Introduce /award slash command; Allow posting of just an emoji in comment
+merge_request: 9382
+author: mhasbini
diff --git a/changelogs/unreleased/26136-list-repository-tree-api-doc.yml b/changelogs/unreleased/26136-list-repository-tree-api-doc.yml
new file mode 100644
index 00000000000..85d8bc6ca8a
--- /dev/null
+++ b/changelogs/unreleased/26136-list-repository-tree-api-doc.yml
@@ -0,0 +1,4 @@
+---
+title: Make documentation of list repository tree API call more detailed
+merge_request: 9532
+author: Marius Kleiner
diff --git a/changelogs/unreleased/2629-show-public-rss-feeds-to-anonymous-users.yml b/changelogs/unreleased/2629-show-public-rss-feeds-to-anonymous-users.yml
new file mode 100644
index 00000000000..6ee8e5724bc
--- /dev/null
+++ b/changelogs/unreleased/2629-show-public-rss-feeds-to-anonymous-users.yml
@@ -0,0 +1,4 @@
+---
+title: Show public RSS feeds to anonymous users
+merge_request: 9596
+author: Michael Kozono
diff --git a/changelogs/unreleased/26348-cleanup-navigation-order.yml b/changelogs/unreleased/26348-cleanup-navigation-order.yml
new file mode 100644
index 00000000000..d5324f9e025
--- /dev/null
+++ b/changelogs/unreleased/26348-cleanup-navigation-order.yml
@@ -0,0 +1,4 @@
+---
+title: Clean-up Project navigation order
+merge_request: 9272
+author:
diff --git a/changelogs/unreleased/27142-api-replace-destroy-with-stop-environment.yml b/changelogs/unreleased/27142-api-replace-destroy-with-stop-environment.yml
new file mode 100644
index 00000000000..ee236310a71
--- /dev/null
+++ b/changelogs/unreleased/27142-api-replace-destroy-with-stop-environment.yml
@@ -0,0 +1,4 @@
+---
+title: API: Add environment stop action
+merge_request: 8808
+author:
diff --git a/changelogs/unreleased/27501-api-use-visibility-everywhere.yml b/changelogs/unreleased/27501-api-use-visibility-everywhere.yml
new file mode 100644
index 00000000000..f1b70687878
--- /dev/null
+++ b/changelogs/unreleased/27501-api-use-visibility-everywhere.yml
@@ -0,0 +1,4 @@
+---
+title: "API: Use `visibility` as string parameter everywhere"
+merge_request: 9337
+author:
diff --git a/changelogs/unreleased/27778-a11y-sidebar.yml b/changelogs/unreleased/27778-a11y-sidebar.yml
new file mode 100644
index 00000000000..fb37d7fdb35
--- /dev/null
+++ b/changelogs/unreleased/27778-a11y-sidebar.yml
@@ -0,0 +1,5 @@
+---
+title: Improves a11y in sidebar by adding aria-hidden attributes in i tags and by
+ fixing two broken aria-hidden attributes
+merge_request:
+author:
diff --git a/changelogs/unreleased/28257-issues-iids.yml b/changelogs/unreleased/28257-issues-iids.yml
new file mode 100644
index 00000000000..0a85504a8de
--- /dev/null
+++ b/changelogs/unreleased/28257-issues-iids.yml
@@ -0,0 +1,4 @@
+---
+title: API issues - support filtering by iids
+merge_request:
+author:
diff --git a/changelogs/unreleased/28410-dropdown-styling.yml b/changelogs/unreleased/28410-dropdown-styling.yml
new file mode 100644
index 00000000000..2a7af1dd6e8
--- /dev/null
+++ b/changelogs/unreleased/28410-dropdown-styling.yml
@@ -0,0 +1,4 @@
+---
+title: Add badges to global dropdown
+merge_request:
+author:
diff --git a/changelogs/unreleased/28609-fix-redirect-to-home-page-url.yml b/changelogs/unreleased/28609-fix-redirect-to-home-page-url.yml
new file mode 100644
index 00000000000..baf832d4495
--- /dev/null
+++ b/changelogs/unreleased/28609-fix-redirect-to-home-page-url.yml
@@ -0,0 +1,4 @@
+---
+title: Fix the redirect to custom home page URL
+merge_request: 9518
+author:
diff --git a/changelogs/unreleased/28696-improve-grammar-gitlab-flow-doc.yml b/changelogs/unreleased/28696-improve-grammar-gitlab-flow-doc.yml
new file mode 100644
index 00000000000..e38e5d0db5b
--- /dev/null
+++ b/changelogs/unreleased/28696-improve-grammar-gitlab-flow-doc.yml
@@ -0,0 +1,4 @@
+---
+title: Improve grammar in GitLab flow documentation
+merge_request: 9552
+author: infogrind
diff --git a/changelogs/unreleased/28805-download-archive-with-branch-like-feature-xxxx-add-extra-directory-level.yml b/changelogs/unreleased/28805-download-archive-with-branch-like-feature-xxxx-add-extra-directory-level.yml
new file mode 100644
index 00000000000..38ff6b97b2b
--- /dev/null
+++ b/changelogs/unreleased/28805-download-archive-with-branch-like-feature-xxxx-add-extra-directory-level.yml
@@ -0,0 +1,4 @@
+---
+title: Ensure archive download is only one directory deep
+merge_request: 9616
+author:
diff --git a/changelogs/unreleased/28807-search-for-milestone-by-title-in-rest-api.yml b/changelogs/unreleased/28807-search-for-milestone-by-title-in-rest-api.yml
new file mode 100644
index 00000000000..0016253e32e
--- /dev/null
+++ b/changelogs/unreleased/28807-search-for-milestone-by-title-in-rest-api.yml
@@ -0,0 +1,4 @@
+---
+title: Enable filtering milestones by search criteria in the API
+merge_request: 9606
+author:
diff --git a/changelogs/unreleased/28837-remove-help-duplicate.yml b/changelogs/unreleased/28837-remove-help-duplicate.yml
new file mode 100644
index 00000000000..b1001245663
--- /dev/null
+++ b/changelogs/unreleased/28837-remove-help-duplicate.yml
@@ -0,0 +1,4 @@
+---
+title: Remove help link from right dropdown
+merge_request:
+author:
diff --git a/changelogs/unreleased/28850-fix-broken-migration.yml b/changelogs/unreleased/28850-fix-broken-migration.yml
new file mode 100644
index 00000000000..7f59a7708bc
--- /dev/null
+++ b/changelogs/unreleased/28850-fix-broken-migration.yml
@@ -0,0 +1,4 @@
+---
+title: Fix broken migration when upgrading straight to 8.17.1
+merge_request: 9613
+author:
diff --git a/changelogs/unreleased/28893-highlighted-diff-doesn-t-stay-highlighted-on-refresh.yml b/changelogs/unreleased/28893-highlighted-diff-doesn-t-stay-highlighted-on-refresh.yml
new file mode 100644
index 00000000000..9ba33af010c
--- /dev/null
+++ b/changelogs/unreleased/28893-highlighted-diff-doesn-t-stay-highlighted-on-refresh.yml
@@ -0,0 +1,4 @@
+---
+title: Highlight line number if specified on diff pages when page loads
+merge_request: 9664
+author:
diff --git a/changelogs/unreleased/diff-make-obvious-cant-comment.yml b/changelogs/unreleased/diff-make-obvious-cant-comment.yml
new file mode 100644
index 00000000000..2cb95947939
--- /dev/null
+++ b/changelogs/unreleased/diff-make-obvious-cant-comment.yml
@@ -0,0 +1,4 @@
+---
+title: Visually show expanded diff lines cant have comments
+merge_request:
+author:
diff --git a/changelogs/unreleased/dm-dont-copy-toolip.yml b/changelogs/unreleased/dm-dont-copy-toolip.yml
new file mode 100644
index 00000000000..2b134da66ab
--- /dev/null
+++ b/changelogs/unreleased/dm-dont-copy-toolip.yml
@@ -0,0 +1,4 @@
+---
+title: Don't copy tooltip when copying GFM
+merge_request:
+author:
diff --git a/changelogs/unreleased/dm-group-reference-full-name.yml b/changelogs/unreleased/dm-group-reference-full-name.yml
new file mode 100644
index 00000000000..f445d955529
--- /dev/null
+++ b/changelogs/unreleased/dm-group-reference-full-name.yml
@@ -0,0 +1,4 @@
+---
+title: Use full group name in GFM group reference title
+merge_request:
+author:
diff --git a/changelogs/unreleased/dz-dashboard-groups-search.yml b/changelogs/unreleased/dz-dashboard-groups-search.yml
new file mode 100644
index 00000000000..c473cba774d
--- /dev/null
+++ b/changelogs/unreleased/dz-dashboard-groups-search.yml
@@ -0,0 +1,4 @@
+---
+title: Add filter and sorting to dashboard groups page
+merge_request: 9619
+author:
diff --git a/changelogs/unreleased/expose-pagination-headers.yml b/changelogs/unreleased/expose-pagination-headers.yml
new file mode 100644
index 00000000000..1b4cd43fa06
--- /dev/null
+++ b/changelogs/unreleased/expose-pagination-headers.yml
@@ -0,0 +1,4 @@
+---
+title: 'CORS: Whitelist pagination headers'
+merge_request: 9651
+author: Robert Schilling
diff --git a/changelogs/unreleased/fix-gb-remove-deprecated-ci-build-status-badge.yml b/changelogs/unreleased/fix-gb-remove-deprecated-ci-build-status-badge.yml
new file mode 100644
index 00000000000..71ff768a190
--- /dev/null
+++ b/changelogs/unreleased/fix-gb-remove-deprecated-ci-build-status-badge.yml
@@ -0,0 +1,4 @@
+---
+title: Remove deprecated build status badge and related services
+merge_request: 9620
+author:
diff --git a/changelogs/unreleased/fix-gb-update-commit-status-api.yml b/changelogs/unreleased/fix-gb-update-commit-status-api.yml
new file mode 100644
index 00000000000..aa4fcba4e89
--- /dev/null
+++ b/changelogs/unreleased/fix-gb-update-commit-status-api.yml
@@ -0,0 +1,4 @@
+---
+title: Fix updaing commit status when using optional attributes
+merge_request: 9618
+author:
diff --git a/changelogs/unreleased/gitaly-post-receive.yml b/changelogs/unreleased/gitaly-post-receive.yml
new file mode 100644
index 00000000000..cf206e39084
--- /dev/null
+++ b/changelogs/unreleased/gitaly-post-receive.yml
@@ -0,0 +1,4 @@
+---
+title: Add internal API to notify Gitaly of post receive
+merge_request: 8983
+author:
diff --git a/changelogs/unreleased/issue-descrpiption-spinner-off.yml b/changelogs/unreleased/issue-descrpiption-spinner-off.yml
new file mode 100644
index 00000000000..87104d09804
--- /dev/null
+++ b/changelogs/unreleased/issue-descrpiption-spinner-off.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed loading spinner position on issue template toggle
+merge_request:
+author:
diff --git a/changelogs/unreleased/long-file-name-overflow.yml b/changelogs/unreleased/long-file-name-overflow.yml
new file mode 100644
index 00000000000..7ccf05491e1
--- /dev/null
+++ b/changelogs/unreleased/long-file-name-overflow.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed long file names overflowing under action buttons
+merge_request:
+author:
diff --git a/changelogs/unreleased/remove-new-relic-gem.yml b/changelogs/unreleased/remove-new-relic-gem.yml
new file mode 100644
index 00000000000..b15ecd3e4e7
--- /dev/null
+++ b/changelogs/unreleased/remove-new-relic-gem.yml
@@ -0,0 +1,4 @@
+---
+title: Remove the newrelic gem
+merge_request: 9622
+author: Robert Schilling
diff --git a/changelogs/unreleased/use-v3-api-on-frontend.yml b/changelogs/unreleased/use-v3-api-on-frontend.yml
new file mode 100644
index 00000000000..467ad3c8276
--- /dev/null
+++ b/changelogs/unreleased/use-v3-api-on-frontend.yml
@@ -0,0 +1,4 @@
+---
+title: Make projects dropdown only show projects you are a member of
+merge_request: 9614
+author:
diff --git a/changelogs/unreleased/user-calendar-border.yml b/changelogs/unreleased/user-calendar-border.yml
new file mode 100644
index 00000000000..8ebcca83256
--- /dev/null
+++ b/changelogs/unreleased/user-calendar-border.yml
@@ -0,0 +1,4 @@
+---
+title: Removed top border from user contribution calendar
+merge_request:
+author:
diff --git a/config/application.rb b/config/application.rb
index 9088d3c432b..45f3b20d214 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -120,7 +120,7 @@ module Gitlab
credentials: true,
headers: :any,
methods: :any,
- expose: ['Link']
+ expose: ['Link', 'X-Total', 'X-Total-Pages', 'X-Per-Page', 'X-Page', 'X-Next-Page', 'X-Prev-Page']
end
# Cross-origin requests must not have the session cookie available
@@ -130,7 +130,7 @@ module Gitlab
credentials: false,
headers: :any,
methods: :any,
- expose: ['Link']
+ expose: ['Link', 'X-Total', 'X-Total-Pages', 'X-Per-Page', 'X-Page', 'X-Next-Page', 'X-Prev-Page']
end
end
diff --git a/config/initializers/8_gitaly.rb b/config/initializers/8_gitaly.rb
new file mode 100644
index 00000000000..07dd30f0a24
--- /dev/null
+++ b/config/initializers/8_gitaly.rb
@@ -0,0 +1,2 @@
+# Make sure we initialize a Gitaly channel before Sidekiq starts multi-threaded execution.
+Gitlab::GitalyClient.channel unless Rails.env.test?
diff --git a/config/newrelic.yml b/config/newrelic.yml
deleted file mode 100644
index 9ef922a38d9..00000000000
--- a/config/newrelic.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-# New Relic configuration file
-#
-# This file is here to make sure the New Relic gem stays
-# quiet by default.
-#
-# To enable and configure New Relic, please use
-# environment variables, e.g. NEW_RELIC_ENABLED=true
-
-production:
- enabled: false
-
-development:
- enabled: false
-
-test:
- enabled: false
diff --git a/config/routes/ci.rb b/config/routes/ci.rb
index 47a049d5b20..8d23aa8fbf6 100644
--- a/config/routes/ci.rb
+++ b/config/routes/ci.rb
@@ -5,11 +5,5 @@ namespace :ci do
resource :lint, only: [:show, :create]
- resources :projects, only: [:index, :show] do
- member do
- get :status, to: 'projects#badge'
- end
- end
-
- root to: 'projects#index'
+ root to: redirect('/')
end
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 84f123ff717..2703bf4ab46 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -58,6 +58,7 @@ constraints(ProjectUrlConstrainer.new) do
resources :graphs, only: [:show], constraints: { id: Gitlab::Regex.git_reference_regex } do
member do
+ get :charts
get :commits
get :ci
get :languages
@@ -100,7 +101,7 @@ constraints(ProjectUrlConstrainer.new) do
get :merge_check
post :merge
get :merge_widget_refresh
- post :cancel_merge_when_build_succeeds
+ post :cancel_merge_when_pipeline_succeeds
get :ci_status
get :ci_environments_status
post :toggle_subscription
@@ -140,6 +141,7 @@ constraints(ProjectUrlConstrainer.new) do
resources :pipelines, only: [:index, :new, :create, :show] do
collection do
resource :pipelines_settings, path: 'settings', only: [:show, :update]
+ get :charts
end
member do
diff --git a/config/webpack.config.js b/config/webpack.config.js
index d178106d179..13273902b0e 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -28,6 +28,7 @@ var config = {
environments_folder: './environments/folder/environments_folder_bundle.js',
filtered_search: './filtered_search/filtered_search_bundle.js',
graphs: './graphs/graphs_bundle.js',
+ groups_list: './groups_list.js',
issuable: './issuable/issuable_bundle.js',
merge_conflicts: './merge_conflicts/merge_conflicts_bundle.js',
merge_request_widget: './merge_request_widget/ci_bundle.js',
diff --git a/db/migrate/20170217132157_rename_merge_when_build_succeeds.rb b/db/migrate/20170217132157_rename_merge_when_build_succeeds.rb
new file mode 100644
index 00000000000..9011526565d
--- /dev/null
+++ b/db/migrate/20170217132157_rename_merge_when_build_succeeds.rb
@@ -0,0 +1,29 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class RenameMergeWhenBuildSucceeds < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = true
+
+ # When a migration requires downtime you **must** uncomment the following
+ # constant and define a short and easy to understand explanation as to why the
+ # migration requires downtime.
+ DOWNTIME_REASON = 'Renaming the column merge_when_build_succeeds'
+
+ # When using the methods "add_concurrent_index" or "add_column_with_default"
+ # you must disable the use of transactions as these methods can not run in an
+ # existing transaction. When using "add_concurrent_index" make sure that this
+ # method is the _only_ method called in the migration, any other changes
+ # should go in a separate migration. This ensures that upon failure _only_ the
+ # index creation fails and can be retried or reverted easily.
+ #
+ # To disable transactions uncomment the following line and remove these
+ # comments:
+ # disable_ddl_transaction!
+
+ def change
+ rename_column :merge_requests, :merge_when_build_succeeds, :merge_when_pipeline_succeeds
+ end
+end
diff --git a/db/migrate/20170217151947_rename_only_allow_merge_if_build_succeeds.rb b/db/migrate/20170217151947_rename_only_allow_merge_if_build_succeeds.rb
new file mode 100644
index 00000000000..b2b68ff72d1
--- /dev/null
+++ b/db/migrate/20170217151947_rename_only_allow_merge_if_build_succeeds.rb
@@ -0,0 +1,29 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class RenameOnlyAllowMergeIfBuildSucceeds < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = true
+
+ # When a migration requires downtime you **must** uncomment the following
+ # constant and define a short and easy to understand explanation as to why the
+ # migration requires downtime.
+ DOWNTIME_REASON = 'Renaming the column only_allow_merge_if_build_succeeds'
+
+ # When using the methods "add_concurrent_index" or "add_column_with_default"
+ # you must disable the use of transactions as these methods can not run in an
+ # existing transaction. When using "add_concurrent_index" make sure that this
+ # method is the _only_ method called in the migration, any other changes
+ # should go in a separate migration. This ensures that upon failure _only_ the
+ # index creation fails and can be retried or reverted easily.
+ #
+ # To disable transactions uncomment the following line and remove these
+ # comments:
+ # disable_ddl_transaction!
+
+ def change
+ rename_column :projects, :only_allow_merge_if_build_succeeds, :only_allow_merge_if_pipeline_succeeds
+ end
+end
diff --git a/db/post_migrate/20170211073944_disable_invalid_service_templates.rb b/db/post_migrate/20170211073944_disable_invalid_service_templates.rb
index 84954b1ef64..603efc43782 100644
--- a/db/post_migrate/20170211073944_disable_invalid_service_templates.rb
+++ b/db/post_migrate/20170211073944_disable_invalid_service_templates.rb
@@ -1,10 +1,8 @@
class DisableInvalidServiceTemplates < ActiveRecord::Migration
DOWNTIME = false
- unless defined?(Service)
- class Service < ActiveRecord::Base
- self.inheritance_column = nil
- end
+ class Service < ActiveRecord::Base
+ self.inheritance_column = nil
end
def up
diff --git a/db/schema.rb b/db/schema.rb
index 1d94368f66e..cd5aa339269 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: 20170216141440) do
+ActiveRecord::Schema.define(version: 20170217151947) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -689,7 +689,7 @@ ActiveRecord::Schema.define(version: 20170216141440) do
t.integer "updated_by_id"
t.text "merge_error"
t.text "merge_params"
- t.boolean "merge_when_build_succeeds", default: false, null: false
+ t.boolean "merge_when_pipeline_succeeds", default: false, null: false
t.integer "merge_user_id"
t.string "merge_commit_sha"
t.datetime "deleted_at"
@@ -972,7 +972,7 @@ ActiveRecord::Schema.define(version: 20170216141440) do
t.boolean "last_repository_check_failed"
t.datetime "last_repository_check_at"
t.boolean "container_registry_enabled"
- t.boolean "only_allow_merge_if_build_succeeds", default: false, null: false
+ t.boolean "only_allow_merge_if_pipeline_succeeds", default: false, null: false
t.boolean "has_external_issue_tracker"
t.string "repository_storage", default: "default", null: false
t.boolean "request_access_enabled", default: false, null: false
diff --git a/doc/api/README.md b/doc/api/README.md
index 1c3b2ad0fbc..3399e2bb5f6 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -89,7 +89,7 @@ You can use an OAuth 2 token to authenticate with the API by passing it either i
Example of using the OAuth2 token in the header:
```shell
-curl --header "Authorization: Bearer OAUTH-TOKEN" https://gitlab.example.com/api/v3/projects
+curl --header "Authorization: Bearer OAUTH-TOKEN" https://gitlab.example.com/api/v4/projects
```
Read more about [GitLab as an OAuth2 client](oauth2.md).
@@ -127,13 +127,13 @@ is defined in [`lib/api.rb`][lib-api-url].
Example of a valid API request:
```shell
-GET https://gitlab.example.com/api/v3/projects?private_token=9koXpg98eAheJpvBs5tK
+GET https://gitlab.example.com/api/v4/projects?private_token=9koXpg98eAheJpvBs5tK
```
Example of a valid API request using cURL and authentication via header:
```shell
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects"
```
The API uses JSON to serialize data. You don't need to specify `.json` at the
@@ -159,7 +159,7 @@ The following table shows the possible return codes for API requests.
| Return values | Description |
| ------------- | ----------- |
| `200 OK` | The `GET`, `PUT` or `DELETE` request was successful, the resource(s) itself is returned as JSON. |
-| `204 OK` | The server has successfully fulfilled the request and that there is no additional content to send in the response payload body. |
+| `204 No Content` | The server has successfully fulfilled the request and that there is no additional content to send in the response payload body. |
| `201 Created` | The `POST` request was successful and the resource is returned as JSON. |
| `304 Not Modified` | Indicates that the resource has not been modified since the last request. |
| `400 Bad Request` | A required attribute of the API request is missing, e.g., the title of an issue is not given. |
@@ -207,7 +207,7 @@ GET /projects?private_token=9koXpg98eAheJpvBs5tK&sudo=username
```
```shell
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "SUDO: username" "https://gitlab.example.com/api/v3/projects"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "SUDO: username" "https://gitlab.example.com/api/v4/projects"
```
Example of a valid API call and a request using cURL with sudo request,
@@ -218,7 +218,7 @@ GET /projects?private_token=9koXpg98eAheJpvBs5tK&sudo=23
```
```shell
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "SUDO: 23" "https://gitlab.example.com/api/v3/projects"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "SUDO: 23" "https://gitlab.example.com/api/v4/projects"
```
## Pagination
@@ -234,7 +234,7 @@ resources you can pass the following parameters:
In the example below, we list 50 [namespaces](namespaces.md) per page.
```bash
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/namespaces?per_page=50
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/namespaces?per_page=50
```
### Pagination Link header
@@ -248,7 +248,7 @@ and we request the second page (`page=2`) of [comments](notes.md) of the issue
with ID `8` which belongs to the project with ID `8`:
```bash
-curl --head --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/8/issues/8/notes?per_page=3&page=2
+curl --head --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/8/issues/8/notes?per_page=3&page=2
```
The response will then be:
@@ -259,7 +259,7 @@ Cache-Control: no-cache
Content-Length: 1103
Content-Type: application/json
Date: Mon, 18 Jan 2016 09:43:18 GMT
-Link: <https://gitlab.example.com/api/v3/projects/8/issues/8/notes?page=1&per_page=3>; rel="prev", <https://gitlab.example.com/api/v3/projects/8/issues/8/notes?page=3&per_page=3>; rel="next", <https://gitlab.example.com/api/v3/projects/8/issues/8/notes?page=1&per_page=3>; rel="first", <https://gitlab.example.com/api/v3/projects/8/issues/8/notes?page=3&per_page=3>; rel="last"
+Link: <https://gitlab.example.com/api/v4/projects/8/issues/8/notes?page=1&per_page=3>; rel="prev", <https://gitlab.example.com/api/v4/projects/8/issues/8/notes?page=3&per_page=3>; rel="next", <https://gitlab.example.com/api/v4/projects/8/issues/8/notes?page=1&per_page=3>; rel="first", <https://gitlab.example.com/api/v4/projects/8/issues/8/notes?page=3&per_page=3>; rel="last"
Status: 200 OK
Vary: Origin
X-Next-Page: 3
diff --git a/doc/api/access_requests.md b/doc/api/access_requests.md
index dee3e384080..96b8d654c58 100644
--- a/doc/api/access_requests.md
+++ b/doc/api/access_requests.md
@@ -28,8 +28,8 @@ GET /projects/:id/access_requests
| `id` | integer/string | yes | The group/project ID or path |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/:id/access_requests
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/:id/access_requests
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/:id/access_requests
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:id/access_requests
```
Example response:
@@ -69,8 +69,8 @@ POST /projects/:id/access_requests
| `id` | integer/string | yes | The group/project ID or path |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/:id/access_requests
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/:id/access_requests
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/:id/access_requests
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:id/access_requests
```
Example response:
@@ -102,8 +102,8 @@ PUT /projects/:id/access_requests/:user_id/approve
| `access_level` | integer | no | A valid access level (defaults: `30`, developer access level) |
```bash
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/:id/access_requests/:user_id/approve?access_level=20
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/:id/access_requests/:user_id/approve?access_level=20
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/:id/access_requests/:user_id/approve?access_level=20
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:id/access_requests/:user_id/approve?access_level=20
```
Example response:
@@ -134,6 +134,6 @@ DELETE /projects/:id/access_requests/:user_id
| `user_id` | integer | yes | The user ID of the access requester |
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/:id/access_requests/:user_id
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/:id/access_requests/:user_id
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/:id/access_requests/:user_id
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:id/access_requests/:user_id
```
diff --git a/doc/api/award_emoji.md b/doc/api/award_emoji.md
index c6fd8c5fa53..3470f8ce497 100644
--- a/doc/api/award_emoji.md
+++ b/doc/api/award_emoji.md
@@ -27,7 +27,7 @@ Parameters:
| `awardable_id` | integer | yes | The ID of an awardable |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/issues/80/award_emoji
```
Example Response:
@@ -88,7 +88,7 @@ Parameters:
| `award_id` | integer | yes | The ID of the award emoji |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji/1
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/issues/80/award_emoji/1
```
Example Response:
@@ -131,7 +131,7 @@ Parameters:
| `name` | string | yes | The name of the emoji, without colons |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji?name=blowfish
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/issues/80/award_emoji?name=blowfish
```
Example Response:
@@ -175,7 +175,7 @@ Parameters:
| `award_id` | integer | yes | The ID of a award_emoji |
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji/344
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/issues/80/award_emoji/344
```
## Award Emoji on Notes
@@ -201,7 +201,7 @@ Parameters:
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/notes/1/award_emoji
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/issues/80/notes/1/award_emoji
```
Example Response:
@@ -243,7 +243,7 @@ Parameters:
| `award_id` | integer | yes | The ID of the award emoji |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/notes/1/award_emoji/2
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/issues/80/notes/1/award_emoji/2
```
Example Response:
@@ -283,7 +283,7 @@ Parameters:
| `name` | string | yes | The name of the emoji, without colons |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/notes/1/award_emoji?name=rocket
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/issues/80/notes/1/award_emoji?name=rocket
```
Example Response:
@@ -326,7 +326,7 @@ Parameters:
| `award_id` | integer | yes | The ID of a award_emoji |
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji/345
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/issues/80/award_emoji/345
```
[ce-4575]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4575
diff --git a/doc/api/boards.md b/doc/api/boards.md
index f80b98f960b..a74e82335eb 100644
--- a/doc/api/boards.md
+++ b/doc/api/boards.md
@@ -18,7 +18,7 @@ GET /projects/:id/boards
| `id` | integer | yes | The ID of a project |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/:id/boards
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:id/boards
```
Example response:
@@ -75,7 +75,7 @@ GET /projects/:id/boards/:board_id/lists
| `board_id` | integer | yes | The ID of a board |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/boards/1/lists
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/boards/1/lists
```
Example response:
@@ -127,7 +127,7 @@ GET /projects/:id/boards/:board_id/lists/:list_id
| `list_id`| integer | yes | The ID of a board's list |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/boards/1/lists/1
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/boards/1/lists/1
```
Example response:
@@ -159,7 +159,7 @@ POST /projects/:id/boards/:board_id/lists
| `label_id` | integer | yes | The ID of a label |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/boards/1/lists?label_id=5
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/boards/1/lists?label_id=5
```
Example response:
@@ -192,7 +192,7 @@ PUT /projects/:id/boards/:board_id/lists/:list_id
| `position` | integer | yes | The position of the list |
```bash
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/boards/1/lists/1?position=2
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/boards/1/lists/1?position=2
```
Example response:
@@ -224,5 +224,5 @@ DELETE /projects/:id/boards/:board_id/lists/:list_id
| `list_id` | integer | yes | The ID of a board's list |
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/boards/1/lists/1
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/boards/1/lists/1
```
diff --git a/doc/api/branches.md b/doc/api/branches.md
index f29a8518945..83705106160 100644
--- a/doc/api/branches.md
+++ b/doc/api/branches.md
@@ -13,7 +13,7 @@ GET /projects/:id/repository/branches
| `id` | integer | yes | The ID of a project |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/repository/branches
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/repository/branches
```
Example response:
@@ -60,7 +60,7 @@ GET /projects/:id/repository/branches/:branch
| `branch` | string | yes | The name of the branch |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/repository/branches/master
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/repository/branches/master
```
Example response:
@@ -101,7 +101,7 @@ PUT /projects/:id/repository/branches/:branch/protect
```
```bash
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/repository/branches/master/protect?developers_can_push=true&developers_can_merge=true
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/repository/branches/master/protect?developers_can_push=true&developers_can_merge=true
```
| Attribute | Type | Required | Description |
@@ -149,7 +149,7 @@ PUT /projects/:id/repository/branches/:branch/unprotect
```
```bash
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/repository/branches/master/unprotect
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/repository/branches/master/unprotect
```
| Attribute | Type | Required | Description |
@@ -197,7 +197,7 @@ POST /projects/:id/repository/branches
| `ref` | string | yes | The branch name or commit SHA to create branch from |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/branches?branch=newbranch&ref=master"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/repository/branches?branch=newbranch&ref=master"
```
Example response:
@@ -241,7 +241,7 @@ DELETE /projects/:id/repository/branches/:branch
In case of an error, an explaining message is provided.
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/branches/newbranch"
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/repository/branches/newbranch"
```
## Delete merged branches
@@ -258,5 +258,5 @@ DELETE /projects/:id/repository/merged_branches
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/merged_branches"
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/repository/merged_branches"
```
diff --git a/doc/api/broadcast_messages.md b/doc/api/broadcast_messages.md
index fecfb142ab1..ad254e3515e 100644
--- a/doc/api/broadcast_messages.md
+++ b/doc/api/broadcast_messages.md
@@ -13,7 +13,7 @@ GET /broadcast_messages
```
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/broadcast_messages
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/broadcast_messages
```
Example response:
@@ -43,7 +43,7 @@ GET /broadcast_messages/:id
| `id` | integer | yes | Broadcast message ID |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/broadcast_messages/1
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/broadcast_messages/1
```
Example response:
@@ -75,7 +75,7 @@ POST /broadcast_messages
| `font` | string | no | Foreground color hex code |
```bash
-curl --data "message=Deploy in progress&color=#cecece" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/broadcast_messages
+curl --data "message=Deploy in progress&color=#cecece" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/broadcast_messages
```
Example response:
@@ -108,7 +108,7 @@ PUT /broadcast_messages/:id
| `font` | string | no | Foreground color hex code |
```bash
-curl --request PUT --data "message=Update message&color=#000" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/broadcast_messages/1
+curl --request PUT --data "message=Update message&color=#000" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/broadcast_messages/1
```
Example response:
@@ -136,5 +136,5 @@ DELETE /broadcast_messages/:id
| `id` | integer | yes | Broadcast message ID |
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/broadcast_messages/1
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/broadcast_messages/1
```
diff --git a/doc/api/build_triggers.md b/doc/api/build_triggers.md
index 6adefe8c58c..28befba69d6 100644
--- a/doc/api/build_triggers.md
+++ b/doc/api/build_triggers.md
@@ -15,7 +15,7 @@ GET /projects/:id/triggers
| `id` | integer | yes | The ID of a project |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/triggers"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/triggers"
```
```json
@@ -51,7 +51,7 @@ GET /projects/:id/triggers/:token
| `token` | string | yes | The `token` of a trigger |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/triggers/7b9148c158980bbd9bcea92c17522d"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/triggers/7b9148c158980bbd9bcea92c17522d"
```
```json
@@ -77,7 +77,7 @@ POST /projects/:id/triggers
| `id` | integer | yes | The ID of a project |
```
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/triggers"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/triggers"
```
```json
@@ -104,5 +104,5 @@ DELETE /projects/:id/triggers/:token
| `token` | string | yes | The `token` of a trigger |
```
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/triggers/7b9148c158980bbd9bcea92c17522d"
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/triggers/7b9148c158980bbd9bcea92c17522d"
```
diff --git a/doc/api/build_variables.md b/doc/api/build_variables.md
index c21d5ab2787..1c26e9b33ab 100644
--- a/doc/api/build_variables.md
+++ b/doc/api/build_variables.md
@@ -13,7 +13,7 @@ GET /projects/:id/variables
| `id` | integer | yes | The ID of a project |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/variables"
```
```json
@@ -43,7 +43,7 @@ GET /projects/:id/variables/:key
| `key` | string | yes | The `key` of a variable |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables/TEST_VARIABLE_1"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/variables/TEST_VARIABLE_1"
```
```json
@@ -68,7 +68,7 @@ POST /projects/:id/variables
| `value` | string | yes | The `value` of a variable |
```
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables" --form "key=NEW_VARIABLE" --form "value=new value"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/variables" --form "key=NEW_VARIABLE" --form "value=new value"
```
```json
@@ -93,7 +93,7 @@ PUT /projects/:id/variables/:key
| `value` | string | yes | The `value` of a variable |
```
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables/NEW_VARIABLE" --form "value=updated value"
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/variables/NEW_VARIABLE" --form "value=updated value"
```
```json
@@ -117,5 +117,5 @@ DELETE /projects/:id/variables/:key
| `key` | string | yes | The `key` of a variable |
```
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables/VARIABLE_1"
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/variables/VARIABLE_1"
```
diff --git a/doc/api/builds.md b/doc/api/builds.md
index bca2f9e44ef..84214e4708f 100644
--- a/doc/api/builds.md
+++ b/doc/api/builds.md
@@ -14,7 +14,7 @@ GET /projects/:id/builds
| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`; showing all builds if none provided |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v3/projects/1/builds?scope%5B0%5D=pending&scope%5B1%5D=running'
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/1/builds?scope%5B0%5D=pending&scope%5B1%5D=running'
```
Example of response
@@ -135,7 +135,7 @@ GET /projects/:id/repository/commits/:sha/builds
| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`; showing all builds if none provided |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v3/projects/1/repository/commits/0ff3ae198f8601a285adcf5c0fff204ee6fba5fd/builds?scope%5B0%5D=pending&scope%5B1%5D=running'
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/1/repository/commits/0ff3ae198f8601a285adcf5c0fff204ee6fba5fd/builds?scope%5B0%5D=pending&scope%5B1%5D=running'
```
Example of response
@@ -233,7 +233,7 @@ GET /projects/:id/builds/:build_id
| `build_id` | integer | yes | The ID of a build |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/8"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/builds/8"
```
Example of response
@@ -301,7 +301,7 @@ GET /projects/:id/builds/:build_id/artifacts
| `build_id` | integer | yes | The ID of a build |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/8/artifacts"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/builds/8/artifacts"
```
Response:
@@ -335,7 +335,7 @@ Parameters
Example request:
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/artifacts/master/download?job=test"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/builds/artifacts/master/download?job=test"
```
Example response:
@@ -361,7 +361,7 @@ GET /projects/:id/builds/:build_id/trace
| build_id | integer | yes | The ID of a build |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/8/trace"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/builds/8/trace"
```
Response:
@@ -385,7 +385,7 @@ POST /projects/:id/builds/:build_id/cancel
| `build_id` | integer | yes | The ID of a build |
```
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/1/cancel"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/builds/1/cancel"
```
Example of response
@@ -431,7 +431,7 @@ POST /projects/:id/builds/:build_id/retry
| `build_id` | integer | yes | The ID of a build |
```
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/1/retry"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/builds/1/retry"
```
Example of response
@@ -481,7 +481,7 @@ Parameters
Example of request
```
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/1/erase"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/builds/1/erase"
```
Example of response
@@ -531,7 +531,7 @@ Parameters
Example request:
```
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/1/artifacts/keep"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/builds/1/artifacts/keep"
```
Example response:
@@ -577,7 +577,7 @@ POST /projects/:id/builds/:build_id/play
| `build_id` | integer | yes | The ID of a build |
```
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/1/play"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/builds/1/play"
```
Example of response
diff --git a/doc/api/ci/lint.md b/doc/api/ci/lint.md
index 0c96b3ee335..74def207816 100644
--- a/doc/api/ci/lint.md
+++ b/doc/api/ci/lint.md
@@ -13,7 +13,7 @@ POST ci/lint
| `content` | string | yes | the .gitlab-ci.yaml content|
```bash
-curl --header "Content-Type: application/json" https://gitlab.example.com/api/v3/ci/lint --data '{"content": "{ \"image\": \"ruby:2.1\", \"services\": [\"postgres\"], \"before_script\": [\"gem install bundler\", \"bundle install\", \"bundle exec rake db:create\"], \"variables\": {\"DB_NAME\": \"postgres\"}, \"types\": [\"test\", \"deploy\", \"notify\"], \"rspec\": { \"script\": \"rake spec\", \"tags\": [\"ruby\", \"postgres\"], \"only\": [\"branches\"]}}"}'
+curl --header "Content-Type: application/json" https://gitlab.example.com/api/v4/ci/lint --data '{"content": "{ \"image\": \"ruby:2.1\", \"services\": [\"postgres\"], \"before_script\": [\"gem install bundler\", \"bundle install\", \"bundle exec rake db:create\"], \"variables\": {\"DB_NAME\": \"postgres\"}, \"types\": [\"test\", \"deploy\", \"notify\"], \"rspec\": { \"script\": \"rake spec\", \"tags\": [\"ruby\", \"postgres\"], \"only\": [\"branches\"]}}"}'
```
Be sure to copy paste the exact contents of `.gitlab-ci.yml` as YAML is very picky about indentation and spaces.
diff --git a/doc/api/commits.md b/doc/api/commits.md
index 18bc2873678..24c402346b1 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -16,7 +16,7 @@ GET /projects/:id/repository/commits
| `until` | string | no | Only commits before or on this date will be returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/commits"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/repository/commits"
```
Example response:
@@ -114,7 +114,7 @@ PAYLOAD=$(cat << 'JSON'
}
JSON
)
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "Content-Type: application/json" --data "$PAYLOAD" https://gitlab.example.com/api/v3/projects/1/repository/commits
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "Content-Type: application/json" --data "$PAYLOAD" https://gitlab.example.com/api/v4/projects/1/repository/commits
```
Example response:
@@ -159,7 +159,7 @@ Parameters:
| `sha` | string | yes | The commit hash or name of a repository branch or tag |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/commits/master
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/repository/commits/master
```
Example response:
@@ -208,7 +208,7 @@ Parameters:
| `branch` | string | yes | The name of the branch |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "branch=master" "https://gitlab.example.com/api/v3/projects/5/repository/commits/master/cherry_pick"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "branch=master" "https://gitlab.example.com/api/v4/projects/5/repository/commits/master/cherry_pick"
```
Example response:
@@ -249,7 +249,7 @@ Parameters:
| `sha` | string | yes | The commit hash or name of a repository branch or tag |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/commits/master/diff"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/repository/commits/master/diff"
```
Example response:
@@ -285,7 +285,7 @@ Parameters:
| `sha` | string | yes | The commit hash or name of a repository branch or tag |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/commits/master/comments"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/repository/commits/master/comments"
```
Example response:
@@ -338,7 +338,7 @@ POST /projects/:id/repository/commits/:sha/comments
| `line_type` | string | no | The line type. Takes `new` or `old` as arguments |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "note=Nice picture man\!" --form "path=dudeism.md" --form "line=11" --form "line_type=new" https://gitlab.example.com/api/v3/projects/17/repository/commits/18f3e63d05582537db6d183d9d557be09e1f90c8/comments
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "note=Nice picture man\!" --form "path=dudeism.md" --form "line=11" --form "line_type=new" https://gitlab.example.com/api/v4/projects/17/repository/commits/18f3e63d05582537db6d183d9d557be09e1f90c8/comments
```
Example response:
@@ -383,7 +383,7 @@ GET /projects/:id/repository/commits/:sha/statuses
| `all` | boolean | no | Return all statuses, not only the latest ones
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/17/repository/commits/18f3e63d05582537db6d183d9d557be09e1f90c8/statuses
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/17/repository/commits/18f3e63d05582537db6d183d9d557be09e1f90c8/statuses
```
Example response:
@@ -459,7 +459,7 @@ POST /projects/:id/statuses/:sha
| `coverage` | float | no | The total code coverage
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/17/statuses/18f3e63d05582537db6d183d9d557be09e1f90c8?state=success"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/17/statuses/18f3e63d05582537db6d183d9d557be09e1f90c8?state=success"
```
Example response:
diff --git a/doc/api/deploy_key_multiple_projects.md b/doc/api/deploy_key_multiple_projects.md
index 73cb4b7ea8c..f94dbfa4059 100644
--- a/doc/api/deploy_key_multiple_projects.md
+++ b/doc/api/deploy_key_multiple_projects.md
@@ -7,16 +7,16 @@ First, find the ID of the projects you're interested in, by either listing all
projects:
```
-curl --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' https://gitlab.example.com/api/v3/projects
+curl --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' https://gitlab.example.com/api/v4/projects
```
Or finding the ID of a group and then listing all projects in that group:
```
-curl --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' https://gitlab.example.com/api/v3/groups
+curl --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' https://gitlab.example.com/api/v4/groups
# For group 1234:
-curl --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' https://gitlab.example.com/api/v3/groups/1234
+curl --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' https://gitlab.example.com/api/v4/groups/1234
```
With those IDs, add the same deploy key to all:
@@ -24,6 +24,6 @@ With those IDs, add the same deploy key to all:
```
for project_id in 321 456 987; do
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "Content-Type: application/json" \
- --data '{"title": "my key", "key": "ssh-rsa AAAA..."}' https://gitlab.example.com/api/v3/projects/${project_id}/deploy_keys
+ --data '{"title": "my key", "key": "ssh-rsa AAAA..."}' https://gitlab.example.com/api/v4/projects/${project_id}/deploy_keys
done
```
diff --git a/doc/api/deploy_keys.md b/doc/api/deploy_keys.md
index d03d94cb867..f051f55ac3e 100644
--- a/doc/api/deploy_keys.md
+++ b/doc/api/deploy_keys.md
@@ -9,7 +9,7 @@ GET /deploy_keys
```
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/deploy_keys"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/deploy_keys"
```
Example response:
@@ -46,7 +46,7 @@ GET /projects/:id/deploy_keys
| `id` | integer | yes | The ID of the project |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/deploy_keys"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/deploy_keys"
```
Example response:
@@ -86,7 +86,7 @@ Parameters:
| `key_id` | integer | yes | The ID of the deploy key |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/deploy_keys/11"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/deploy_keys/11"
```
Example response:
@@ -120,7 +120,7 @@ POST /projects/:id/deploy_keys
| `can_push` | boolean | no | Can deploy key push to the project's repository |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "Content-Type: application/json" --data '{"title": "My deploy key", "key": "ssh-rsa AAAA...", "can_push": "true"}' "https://gitlab.example.com/api/v3/projects/5/deploy_keys/"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "Content-Type: application/json" --data '{"title": "My deploy key", "key": "ssh-rsa AAAA...", "can_push": "true"}' "https://gitlab.example.com/api/v4/projects/5/deploy_keys/"
```
Example response:
@@ -149,7 +149,7 @@ DELETE /projects/:id/deploy_keys/:key_id
| `key_id` | integer | yes | The ID of the deploy key |
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/deploy_keys/13"
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/deploy_keys/13"
```
## Enable a deploy key
@@ -157,7 +157,7 @@ curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gi
Enables a deploy key for a project so this can be used. Returns the enabled key, with a status code 201 when successful.
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/deploy_keys/13/enable
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/deploy_keys/13/enable
```
| Attribute | Type | Required | Description |
diff --git a/doc/api/deployments.md b/doc/api/deployments.md
index 3d95c4cde60..76e18c8a9bd 100644
--- a/doc/api/deployments.md
+++ b/doc/api/deployments.md
@@ -13,7 +13,7 @@ GET /projects/:id/deployments
| `id` | integer | yes | The ID of a project |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/deployments"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/deployments"
```
Example of response
@@ -151,7 +151,7 @@ GET /projects/:id/deployments/:deployment_id
| `deployment_id` | integer | yes | The ID of the deployment |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/deployments/1"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/deployments/1"
```
Example of response
diff --git a/doc/api/enviroments.md b/doc/api/enviroments.md
index e510f723e26..3f0a8d989f9 100644
--- a/doc/api/enviroments.md
+++ b/doc/api/enviroments.md
@@ -13,7 +13,7 @@ GET /projects/:id/environments
| `id` | integer | yes | The ID of the project |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/1/environments
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/environments
```
Example response:
@@ -33,7 +33,7 @@ Example response:
Creates a new environment with the given name and external_url.
-It returns 201 if the environment was successfully created, 400 for wrong parameters.
+It returns `201` if the environment was successfully created, `400` for wrong parameters.
```
POST /projects/:id/environment
@@ -46,7 +46,7 @@ POST /projects/:id/environment
| `external_url` | string | no | Place to link to for this environment |
```bash
-curl --data "name=deploy&external_url=https://deploy.example.gitlab.com" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/environments"
+curl --data "name=deploy&external_url=https://deploy.example.gitlab.com" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/environments"
```
Example response:
@@ -64,7 +64,7 @@ Example response:
Updates an existing environment's name and/or external_url.
-It returns 200 if the environment was successfully updated. In case of an error, a status code 400 is returned.
+It returns `200` if the environment was successfully updated. In case of an error, a status code `400` is returned.
```
PUT /projects/:id/environments/:environments_id
@@ -78,7 +78,7 @@ PUT /projects/:id/environments/:environments_id
| `external_url` | string | no | The new external_url |
```bash
-curl --request PUT --data "name=staging&external_url=https://staging.example.gitlab.com" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/environments/1"
+curl --request PUT --data "name=staging&external_url=https://staging.example.gitlab.com" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/environments/1"
```
Example response:
@@ -94,7 +94,7 @@ Example response:
## Delete an environment
-It returns 200 if the environment was successfully deleted, and 404 if the environment does not exist.
+It returns `200` if the environment was successfully deleted, and `404` if the environment does not exist.
```
DELETE /projects/:id/environments/:environment_id
@@ -106,5 +106,33 @@ DELETE /projects/:id/environments/:environment_id
| `environment_id` | integer | yes | The ID of the environment |
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/environments/1"
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/environments/1"
+```
+
+## Stop an environment
+
+It returns `200` if the environment was successfully stopped, and `404` if the environment does not exist.
+
+```
+POST /projects/:id/environments/:environment_id/stop
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ------- | -------- | --------------------- |
+| `id` | integer | yes | The ID of the project |
+| `environment_id` | integer | yes | The ID of the environment |
+
+```bash
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/environments/1/stop"
+```
+
+Example response:
+
+```json
+{
+ "id": 1,
+ "name": "deploy",
+ "slug": "deploy",
+ "external_url": "https://deploy.example.gitlab.com"
+}
```
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 39adb5be502..80c08096dea 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -27,7 +27,7 @@ GET /groups
"name": "Foobar Group",
"path": "foo-bar",
"description": "An interesting group",
- "visibility_level": 20,
+ "visibility": "public",
"lfs_enabled": true,
"avatar_url": "http://localhost:3000/uploads/group/avatar/1/foo.jpg",
"web_url": "http://localhost:3000/groups/foo-bar",
@@ -72,9 +72,8 @@ Example response:
"description": "foo",
"default_branch": "master",
"tag_list": [],
- "public": false,
"archived": false,
- "visibility_level": 10,
+ "visibility": "internal",
"ssh_url_to_repo": "git@gitlab.example.com/html5-boilerplate.git",
"http_url_to_repo": "http://gitlab.example.com/h5bp/html5-boilerplate.git",
"web_url": "http://gitlab.example.com/h5bp/html5-boilerplate",
@@ -123,7 +122,7 @@ Parameters:
| `id` | integer/string | yes | The ID or path of a group |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/4
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/4
```
Example response:
@@ -134,7 +133,7 @@ Example response:
"name": "Twitter",
"path": "twitter",
"description": "Aliquid qui quis dignissimos distinctio ut commodi voluptas est.",
- "visibility_level": 20,
+ "visibility": "public",
"avatar_url": null,
"web_url": "https://gitlab.example.com/groups/twitter",
"request_access_enabled": false,
@@ -147,9 +146,8 @@ Example response:
"description": "Voluptas veniam qui et beatae voluptas doloremque explicabo facilis.",
"default_branch": "master",
"tag_list": [],
- "public": true,
"archived": false,
- "visibility_level": 20,
+ "visibility": "public",
"ssh_url_to_repo": "git@gitlab.example.com:twitter/typeahead-js.git",
"http_url_to_repo": "https://gitlab.example.com/twitter/typeahead-js.git",
"web_url": "https://gitlab.example.com/twitter/typeahead-js",
@@ -186,9 +184,8 @@ Example response:
"description": "Aspernatur omnis repudiandae qui voluptatibus eaque.",
"default_branch": "master",
"tag_list": [],
- "public": false,
"archived": false,
- "visibility_level": 10,
+ "visibility": "internal",
"ssh_url_to_repo": "git@gitlab.example.com:twitter/flight.git",
"http_url_to_repo": "https://gitlab.example.com/twitter/flight.git",
"web_url": "https://gitlab.example.com/twitter/flight",
@@ -227,9 +224,8 @@ Example response:
"description": "Velit eveniet provident fugiat saepe eligendi autem.",
"default_branch": "master",
"tag_list": [],
- "public": false,
"archived": false,
- "visibility_level": 0,
+ "visibility": "private",
"ssh_url_to_repo": "git@gitlab.example.com:h5bp/html5-boilerplate.git",
"http_url_to_repo": "https://gitlab.example.com/h5bp/html5-boilerplate.git",
"web_url": "https://gitlab.example.com/h5bp/html5-boilerplate",
@@ -288,7 +284,7 @@ Parameters:
- `name` (required) - The name of the group
- `path` (required) - The path of the group
- `description` (optional) - The group's description
-- `visibility_level` (optional) - The group's visibility. 0 for private, 10 for internal, 20 for public.
+- `visibility` (optional) - The group's visibility. Can be `private`, `internal`, or `public`.
- `lfs_enabled` (optional) - Enable/disable Large File Storage (LFS) for the projects in this group
- `request_access_enabled` (optional) - Allow users to request member access.
- `parent_id` (optional) - The parent group id for creating nested group.
@@ -320,12 +316,12 @@ PUT /groups/:id
| `name` | string | no | The name of the group |
| `path` | string | no | The path of the group |
| `description` | string | no | The description of the group |
-| `visibility_level` | integer | no | The visibility level of the group. 0 for private, 10 for internal, 20 for public. |
+| `visibility` | string | no | The visibility level of the group. Can be `private`, `internal`, or `public`. |
| `lfs_enabled` (optional) | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group |
| `request_access_enabled` | boolean | no | Allow users to request member access. |
```bash
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/groups/5?name=Experimental"
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/5?name=Experimental"
```
@@ -337,7 +333,7 @@ Example response:
"name": "Experimental",
"path": "h5bp",
"description": "foo",
- "visibility_level": 10,
+ "visibility": "internal",
"avatar_url": null,
"web_url": "http://gitlab.example.com/groups/h5bp",
"request_access_enabled": false,
@@ -352,7 +348,7 @@ Example response:
"tag_list": [],
"public": false,
"archived": false,
- "visibility_level": 10,
+ "visibility": "internal",
"ssh_url_to_repo": "git@gitlab.example.com/html5-boilerplate.git",
"http_url_to_repo": "http://gitlab.example.com/h5bp/html5-boilerplate.git",
"web_url": "http://gitlab.example.com/h5bp/html5-boilerplate",
diff --git a/doc/api/issues.md b/doc/api/issues.md
index b6798fba0ae..51ce08c873e 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -25,6 +25,7 @@ GET /issues?labels=foo,bar
GET /issues?labels=foo,bar&state=opened
GET /issues?milestone=1.0.0
GET /issues?milestone=1.0.0&state=opened
+GET /issues?iids[]=42&iids[]=43
```
| Attribute | Type | Required | Description |
@@ -32,11 +33,12 @@ GET /issues?milestone=1.0.0&state=opened
| `state` | string | no | Return all issues or just those that are `opened` or `closed`|
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned |
| `milestone` | string| no | The milestone title |
+| `iids` | Array[integer] | no | Return only the issues having the given `iid` |
| `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/issues
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/issues
```
Example response:
@@ -102,6 +104,7 @@ GET /groups/:id/issues?labels=foo,bar
GET /groups/:id/issues?labels=foo,bar&state=opened
GET /groups/:id/issues?milestone=1.0.0
GET /groups/:id/issues?milestone=1.0.0&state=opened
+GET /groups/:id/issues?iids[]=42&iids[]=43
```
| Attribute | Type | Required | Description |
@@ -109,13 +112,14 @@ GET /groups/:id/issues?milestone=1.0.0&state=opened
| `id` | integer | yes | The ID of a group |
| `state` | string | no | Return all issues or just those that are `opened` or `closed`|
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned |
+| `iids` | Array[integer] | no | Return only the issues having the given `iid` |
| `milestone` | string| no | The milestone title |
| `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/4/issues
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/4/issues
```
Example response:
@@ -181,12 +185,13 @@ GET /projects/:id/issues?labels=foo,bar
GET /projects/:id/issues?labels=foo,bar&state=opened
GET /projects/:id/issues?milestone=1.0.0
GET /projects/:id/issues?milestone=1.0.0&state=opened
+GET /projects/:id/issues?iids[]=42&iids[]=43
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project |
-| `iid` | integer | no | Return the issue having the given `iid` |
+| `iids` | Array[integer] | no | Return only the milestone having the given `iid` |
| `state` | string | no | Return all issues or just those that are `opened` or `closed`|
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned |
| `milestone` | string| no | The milestone title |
@@ -195,7 +200,7 @@ GET /projects/:id/issues?milestone=1.0.0&state=opened
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/issues
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/4/issues
```
Example response:
@@ -262,7 +267,7 @@ GET /projects/:id/issues/:issue_id
| `issue_id`| integer | yes | The ID of a project's issue |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/issues/41
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/4/issues/41
```
Example response:
@@ -335,7 +340,7 @@ POST /projects/:id/issues
| `merge_request_for_resolving_discussions` | integer | no | The IID of a merge request in which to resolve all issues. This will fill the issue with a default description and mark all discussions as resolved. When passing a description or title, these values will take precedence over the default values. |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/issues?title=Issues%20with%20auth&labels=bug
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/4/issues?title=Issues%20with%20auth&labels=bug
```
Example response:
@@ -395,7 +400,7 @@ PUT /projects/:id/issues/:issue_id
| `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` |
```bash
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/issues/85?state_event=close
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/4/issues/85?state_event=close
```
Example response:
@@ -445,7 +450,7 @@ DELETE /projects/:id/issues/:issue_id
| `issue_id` | integer | yes | The ID of a project's issue |
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/issues/85
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/4/issues/85
```
## Move an issue
@@ -468,7 +473,7 @@ POST /projects/:id/issues/:issue_id/move
| `to_project_id` | integer | yes | The ID of the new project |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/issues/85/move
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/4/issues/85/move
```
Example response:
@@ -523,7 +528,7 @@ POST /projects/:id/issues/:issue_id/subscribe
| `issue_id` | integer | yes | The ID of a project's issue |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/subscribe
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/subscribe
```
Example response:
@@ -578,7 +583,7 @@ POST /projects/:id/issues/:issue_id/unsubscribe
| `issue_id` | integer | yes | The ID of a project's issue |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/unsubscribe
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/unsubscribe
```
## Create a todo
@@ -597,7 +602,7 @@ POST /projects/:id/issues/:issue_id/todo
| `issue_id` | integer | yes | The ID of a project's issue |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/todo
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/todo
```
Example response:
@@ -689,7 +694,7 @@ POST /projects/:id/issues/:issue_id/time_estimate
| `duration` | string | yes | The duration in human format. e.g: 3h30m |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/time_estimate?duration=3h30m
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/time_estimate?duration=3h30m
```
Example response:
@@ -717,7 +722,7 @@ POST /projects/:id/issues/:issue_id/reset_time_estimate
| `issue_id` | integer | yes | The ID of a project's issue |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/reset_time_estimate
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/reset_time_estimate
```
Example response:
@@ -746,7 +751,7 @@ POST /projects/:id/issues/:issue_id/add_spent_time
| `duration` | string | yes | The duration in human format. e.g: 3h30m |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/add_spent_time?duration=1h
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/add_spent_time?duration=1h
```
Example response:
@@ -774,7 +779,7 @@ POST /projects/:id/issues/:issue_id/reset_spent_time
| `issue_id` | integer | yes | The ID of a project's issue |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/reset_spent_time
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/reset_spent_time
```
Example response:
@@ -800,7 +805,7 @@ GET /projects/:id/issues/:issue_id/time_stats
| `issue_id` | integer | yes | The ID of a project's issue |
```bash
-curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/time_stats
+curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/time_stats
```
Example response:
diff --git a/doc/api/labels.md b/doc/api/labels.md
index 85bd9647a7b..e8c220f6809 100644
--- a/doc/api/labels.md
+++ b/doc/api/labels.md
@@ -13,7 +13,7 @@ GET /projects/:id/labels
| `id` | integer | yes | The ID of the project |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/1/labels
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/labels
```
Example response:
@@ -95,7 +95,7 @@ POST /projects/:id/labels
| `priority` | integer | no | The priority of the label. Must be greater or equal than zero or `null` to remove the priority. |
```bash
-curl --data "name=feature&color=#5843AD" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/labels"
+curl --data "name=feature&color=#5843AD" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/labels"
```
Example response:
@@ -128,7 +128,7 @@ DELETE /projects/:id/labels
| `name` | string | yes | The name of the label |
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/labels?name=bug"
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/labels?name=bug"
```
## Edit an existing label
@@ -151,7 +151,7 @@ PUT /projects/:id/labels
```bash
-curl --request PUT --data "name=documentation&new_name=docs&color=#8E44AD&description=Documentation" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/labels"
+curl --request PUT --data "name=documentation&new_name=docs&color=#8E44AD&description=Documentation" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/labels"
```
Example response:
@@ -186,7 +186,7 @@ POST /projects/:id/labels/:label_id/subscribe
| `label_id` | integer or string | yes | The ID or title of a project's label |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/labels/1/subscribe
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/labels/1/subscribe
```
Example response:
@@ -221,5 +221,5 @@ POST /projects/:id/labels/:label_id/unsubscribe
| `label_id` | integer or string | yes | The ID or title of a project's label |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/labels/1/unsubscribe
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/labels/1/unsubscribe
```
diff --git a/doc/api/members.md b/doc/api/members.md
index 5dcb2a5f60a..fe46f8f84bc 100644
--- a/doc/api/members.md
+++ b/doc/api/members.md
@@ -27,8 +27,8 @@ GET /projects/:id/members
| `query` | string | no | A query string to search for members |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/:id/members
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/:id/members
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/:id/members
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:id/members
```
Example response:
@@ -69,8 +69,8 @@ GET /projects/:id/members/:user_id
| `user_id` | integer | yes | The user ID of the member |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/:id/members/:user_id
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/:id/members/:user_id
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/:id/members/:user_id
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:id/members/:user_id
```
Example response:
@@ -104,8 +104,8 @@ POST /projects/:id/members
| `expires_at` | string | no | A date string in the format YEAR-MONTH-DAY |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "user_id=1&access_level=30" https://gitlab.example.com/api/v3/groups/:id/members
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "user_id=1&access_level=30" https://gitlab.example.com/api/v3/projects/:id/members
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "user_id=1&access_level=30" https://gitlab.example.com/api/v4/groups/:id/members
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "user_id=1&access_level=30" https://gitlab.example.com/api/v4/projects/:id/members
```
Example response:
@@ -138,8 +138,8 @@ PUT /projects/:id/members/:user_id
| `expires_at` | string | no | A date string in the format YEAR-MONTH-DAY |
```bash
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/:id/members/:user_id?access_level=40
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/:id/members/:user_id?access_level=40
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/:id/members/:user_id?access_level=40
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:id/members/:user_id?access_level=40
```
Example response:
@@ -170,6 +170,6 @@ DELETE /projects/:id/members/:user_id
| `user_id` | integer | yes | The user ID of the member |
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/:id/members/:user_id
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/:id/members/:user_id
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/:id/members/:user_id
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:id/members/:user_id
```
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index ea30a163a12..e178d5c1629 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -65,7 +65,7 @@ Parameters:
"updated_at": "2015-02-02T19:49:26.013Z",
"due_date": null
},
- "merge_when_build_succeeds": true,
+ "merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : false,
"sha": "8888888888888888888888888888888888888888",
@@ -134,7 +134,7 @@ Parameters:
"updated_at": "2015-02-02T19:49:26.013Z",
"due_date": null
},
- "merge_when_build_succeeds": true,
+ "merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
@@ -239,7 +239,7 @@ Parameters:
"updated_at": "2015-02-02T19:49:26.013Z",
"due_date": null
},
- "merge_when_build_succeeds": true,
+ "merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
@@ -326,7 +326,7 @@ POST /projects/:id/merge_requests
"updated_at": "2015-02-02T19:49:26.013Z",
"due_date": null
},
- "merge_when_build_succeeds": true,
+ "merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
@@ -403,7 +403,7 @@ Must include at least one non-required attribute from above.
"updated_at": "2015-02-02T19:49:26.013Z",
"due_date": null
},
- "merge_when_build_succeeds": true,
+ "merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
@@ -429,7 +429,7 @@ DELETE /projects/:id/merge_requests/:merge_request_id
| `merge_request_id` | integer | yes | The ID of a project's merge request |
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/merge_requests/85
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/4/merge_requests/85
```
## Accept MR
@@ -455,7 +455,7 @@ Parameters:
- `merge_request_id` (required) - ID of MR
- `merge_commit_message` (optional) - Custom merge commit message
- `should_remove_source_branch` (optional) - if `true` removes the source branch
-- `merge_when_build_succeeds` (optional) - if `true` the MR is merged when the build succeeds
+- `merge_when_pipeline_succeeds` (optional) - if `true` the MR is merged when the pipeline succeeds
- `sha` (optional) - if present, then this SHA must match the HEAD of the source branch, otherwise the merge will fail
```json
@@ -501,7 +501,7 @@ Parameters:
"updated_at": "2015-02-02T19:49:26.013Z",
"due_date": null
},
- "merge_when_build_succeeds": true,
+ "merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
@@ -519,9 +519,9 @@ If you don't have permissions to accept this merge request - you'll get a `401`
If the merge request is already merged or closed - you get `405` and error message 'Method Not Allowed'
-In case the merge request is not set to be merged when the build succeeds, you'll also get a `406` error.
+In case the merge request is not set to be merged when the pipeline succeeds, you'll also get a `406` error.
```
-PUT /projects/:id/merge_requests/:merge_request_id/cancel_merge_when_build_succeeds
+PUT /projects/:id/merge_requests/:merge_request_id/cancel_merge_when_pipeline_succeeds
```
Parameters:
@@ -571,7 +571,7 @@ Parameters:
"updated_at": "2015-02-02T19:49:26.013Z",
"due_date": null
},
- "merge_when_build_succeeds": true,
+ "merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
@@ -601,7 +601,7 @@ GET /projects/:id/merge_requests/:merge_request_id/closes_issues
| `merge_request_id` | integer | yes | The ID of the merge request |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/76/merge_requests/1/closes_issues
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/76/merge_requests/1/closes_issues
```
Example response when the GitLab issue tracker is used:
@@ -676,7 +676,7 @@ POST /projects/:id/merge_requests/:merge_request_id/subscribe
| `merge_request_id` | integer | yes | The ID of the merge request |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/17/subscribe
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/17/subscribe
```
Example response:
@@ -726,7 +726,7 @@ Example response:
"updated_at": "2016-04-05T21:41:40.905Z",
"due_date": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_status": "cannot_be_merged",
"subscribed": true,
"sha": "8888888888888888888888888888888888888888",
@@ -750,7 +750,7 @@ POST /projects/:id/merge_requests/:merge_request_id/unsubscribe
| `merge_request_id` | integer | yes | The ID of the merge request |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/17/unsubscribe
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/17/unsubscribe
```
Example response:
@@ -800,7 +800,7 @@ Example response:
"updated_at": "2016-04-05T21:41:40.905Z",
"due_date": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_status": "cannot_be_merged",
"subscribed": false,
"sha": "8888888888888888888888888888888888888888",
@@ -824,7 +824,7 @@ POST /projects/:id/merge_requests/:merge_request_id/todo
| `merge_request_id` | integer | yes | The ID of the merge request |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/27/todo
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/27/todo
```
Example response:
@@ -893,7 +893,7 @@ Example response:
"updated_at": "2016-06-17T07:47:33.840Z",
"due_date": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_status": "unchecked",
"subscribed": true,
"sha": "8888888888888888888888888888888888888888",
@@ -924,7 +924,7 @@ GET /projects/:id/merge_requests/:merge_request_id/versions
| `merge_request_id` | integer | yes | The ID of the merge request |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/1/merge_requests/1/versions
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/merge_requests/1/versions
```
Example response:
@@ -966,7 +966,7 @@ GET /projects/:id/merge_requests/:merge_request_id/versions/:version_id
| `version_id` | integer | yes | The ID of the merge request diff version |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/1/merge_requests/1/versions/1
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/merge_requests/1/versions/1
```
Example response:
@@ -1033,7 +1033,7 @@ POST /projects/:id/merge_requests/:merge_request_id/time_estimate
| `duration` | string | yes | The duration in human format. e.g: 3h30m |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/93/time_estimate?duration=3h30m
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/93/time_estimate?duration=3h30m
```
Example response:
@@ -1061,7 +1061,7 @@ POST /projects/:id/merge_requests/:merge_request_id/reset_time_estimate
| `merge_request_id` | integer | yes | The ID of a project's merge_request |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/93/reset_time_estimate
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/93/reset_time_estimate
```
Example response:
@@ -1090,7 +1090,7 @@ POST /projects/:id/merge_requests/:merge_request_id/add_spent_time
| `duration` | string | yes | The duration in human format. e.g: 3h30m |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/93/add_spent_time?duration=1h
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/93/add_spent_time?duration=1h
```
Example response:
@@ -1118,7 +1118,7 @@ POST /projects/:id/merge_requests/:merge_request_id/reset_spent_time
| `merge_request_id` | integer | yes | The ID of a project's merge_request |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/93/reset_spent_time
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/93/reset_spent_time
```
Example response:
@@ -1144,7 +1144,7 @@ GET /projects/:id/merge_requests/:merge_request_id/time_stats
| `merge_request_id` | integer | yes | The ID of a project's merge request |
```bash
-curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/93/time_stats
+curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/93/time_stats
```
Example response:
diff --git a/doc/api/milestones.md b/doc/api/milestones.md
index bf7dcc008e9..9439db84e9b 100644
--- a/doc/api/milestones.md
+++ b/doc/api/milestones.md
@@ -10,6 +10,7 @@ GET /projects/:id/milestones?iid=42
GET /projects/:id/milestones?iid[]=42&iid[]=43
GET /projects/:id/milestones?state=active
GET /projects/:id/milestones?state=closed
+GET /projects/:id/milestones?search=version
```
Parameters:
@@ -18,10 +19,11 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project |
| `iid` | Array[integer] | optional | Return only the milestone having the given `iid` |
-| `state` | string | optional | Return only `active` or `closed` milestones` |
+| `state` | string | optional | Return only `active` or `closed` milestones` |
+| `search` | string | optional | Return only milestones with a title or description matching the provided string |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/milestones
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/milestones
```
Example Response:
@@ -115,4 +117,4 @@ GET /projects/:id/milestones/:milestone_id/merge_requests
Parameters:
- `id` (required) - The ID of a project
-- `milestone_id` (required) - The ID of a project milestone \ No newline at end of file
+- `milestone_id` (required) - The ID of a project milestone
diff --git a/doc/api/namespaces.md b/doc/api/namespaces.md
index 1d97b5de688..eef06d5f324 100644
--- a/doc/api/namespaces.md
+++ b/doc/api/namespaces.md
@@ -19,7 +19,7 @@ GET /namespaces
Example request:
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/namespaces
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/namespaces
```
Example response:
@@ -60,7 +60,7 @@ GET /namespaces?search=foobar
Example request:
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/namespaces?search=twitter
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/namespaces?search=twitter
```
Example response:
diff --git a/doc/api/notes.md b/doc/api/notes.md
index 7dc1fd930de..6ef06b2c2e9 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -120,7 +120,7 @@ Parameters:
| `note_id` | integer | yes | The ID of a note |
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/11/notes/636
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/11/notes/636
```
## Snippets
@@ -218,7 +218,7 @@ Parameters:
| `note_id` | integer | yes | The ID of a note |
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/snippets/52/notes/1659
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/snippets/52/notes/1659
```
## Merge Requests
@@ -319,5 +319,5 @@ Parameters:
| `note_id` | integer | yes | The ID of a note |
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/7/notes/1602
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/7/notes/1602
```
diff --git a/doc/api/notification_settings.md b/doc/api/notification_settings.md
index aea1c12a392..43047917f77 100644
--- a/doc/api/notification_settings.md
+++ b/doc/api/notification_settings.md
@@ -41,7 +41,7 @@ GET /notification_settings
```
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/notification_settings
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/notification_settings
```
Example response:
@@ -62,7 +62,7 @@ PUT /notification_settings
```
```bash
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/notification_settings?level=watch
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/notification_settings?level=watch
```
| Attribute | Type | Required | Description |
@@ -101,8 +101,8 @@ GET /projects/:id/notification_settings
```
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/5/notification_settings
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/8/notification_settings
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/5/notification_settings
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/8/notification_settings
```
| Attribute | Type | Required | Description |
@@ -127,8 +127,8 @@ PUT /projects/:id/notification_settings
```
```bash
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/5/notification_settings?level=watch
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/8/notification_settings?level=custom&new_note=true
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/5/notification_settings?level=watch
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/8/notification_settings?level=custom&new_note=true
```
| Attribute | Type | Required | Description |
diff --git a/doc/api/oauth2.md b/doc/api/oauth2.md
index eab532af594..46fe64d382e 100644
--- a/doc/api/oauth2.md
+++ b/doc/api/oauth2.md
@@ -77,13 +77,13 @@ You can now make requests to the API with the access token returned.
The access token allows you to make requests to the API on a behalf of a user.
```
-GET https://gitlab.example.com/api/v3/user?access_token=OAUTH-TOKEN
+GET https://gitlab.example.com/api/v4/user?access_token=OAUTH-TOKEN
```
Or you can put the token to the Authorization header:
```
-curl --header "Authorization: Bearer OAUTH-TOKEN" https://gitlab.example.com/api/v3/user
+curl --header "Authorization: Bearer OAUTH-TOKEN" https://gitlab.example.com/api/v4/user
```
## Resource Owner Password Credentials
diff --git a/doc/api/pipelines.md b/doc/api/pipelines.md
index f3c9827f742..9d6f3ea41d9 100644
--- a/doc/api/pipelines.md
+++ b/doc/api/pipelines.md
@@ -13,7 +13,7 @@ GET /projects/:id/pipelines
| `id` | integer | yes | The ID of a project |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/pipelines"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipelines"
```
Example of response
@@ -85,7 +85,7 @@ GET /projects/:id/pipelines/:pipeline_id
| `pipeline_id` | integer | yes | The ID of a pipeline |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/pipeline/46"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipeline/46"
```
Example of response
@@ -131,7 +131,7 @@ POST /projects/:id/pipeline
| `ref` | string | yes | Reference to commit |
```
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/pipeline?ref=master"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipeline?ref=master"
```
Example of response
@@ -177,7 +177,7 @@ POST /projects/:id/pipelines/:pipeline_id/retry
| `pipeline_id` | integer | yes | The ID of a pipeline |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/pipelines/46/retry"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipelines/46/retry"
```
Response:
@@ -223,7 +223,7 @@ POST /projects/:id/pipelines/:pipeline_id/cancel
| `pipeline_id` | integer | yes | The ID of a pipeline |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/pipelines/46/cancel"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipelines/46/cancel"
```
Response:
diff --git a/doc/api/project_snippets.md b/doc/api/project_snippets.md
index 404876f6237..4f6f561b83e 100644
--- a/doc/api/project_snippets.md
+++ b/doc/api/project_snippets.md
@@ -3,15 +3,15 @@
### Snippet visibility level
Snippets in GitLab can be either private, internal or public.
-You can set it with the `visibility_level` field in the snippet.
+You can set it with the `visibility` field in the snippet.
Constants for snippet visibility levels are:
-| Visibility | visibility_level | Description |
-| ---------- | ---------------- | ----------- |
-| Private | `0` | The snippet is visible only the snippet creator |
-| Internal | `10` | The snippet is visible for any logged in user |
-| Public | `20` | The snippet can be accessed without any authentication |
+| visibility | Description |
+| ---------- | ----------- |
+| `private` | The snippet is visible only the snippet creator |
+| `internal` | The snippet is visible for any logged in user |
+| `public` | The snippet can be accessed without any authentication |
## List snippets
@@ -71,7 +71,7 @@ Parameters:
- `title` (required) - The title of a snippet
- `file_name` (required) - The name of a snippet file
- `code` (required) - The content of a snippet
-- `visibility_level` (required) - The snippet's visibility
+- `visibility` (required) - The snippet's visibility
## Update snippet
@@ -88,7 +88,7 @@ Parameters:
- `title` (optional) - The title of a snippet
- `file_name` (optional) - The name of a snippet file
- `code` (optional) - The content of a snippet
-- `visibility_level` (optional) - The snippet's visibility
+- `visibility` (optional) - The snippet's visibility
## Delete snippet
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 5de23908b42..f6eabc1f663 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -4,17 +4,17 @@
### Project visibility level
Project in GitLab has be either private, internal or public.
-You can determine it by `visibility_level` field in project.
+You can determine it by `visibility` field in project.
Constants for project visibility levels are next:
-* Private. `visibility_level` is `0`.
+* `private`:
Project access must be granted explicitly for each user.
-* Internal. `visibility_level` is `10`.
+* `internal`:
The project can be cloned by any logged in user.
-* Public. `visibility_level` is `20`.
+* `public`:
The project can be cloned without any authentication.
@@ -45,8 +45,7 @@ Parameters:
"id": 4,
"description": null,
"default_branch": "master",
- "public": false,
- "visibility_level": 0,
+ "visibility": "private",
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",
"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",
"web_url": "http://example.com/diaspora/diaspora-client",
@@ -88,7 +87,7 @@ Parameters:
"runners_token": "b8547b1dc37721d05889db52fa2f02",
"public_builds": true,
"shared_with_groups": [],
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
"request_access_enabled": false
},
@@ -96,8 +95,7 @@ Parameters:
"id": 6,
"description": null,
"default_branch": "master",
- "public": false,
- "visibility_level": 0,
+ "visibility": "private",
"ssh_url_to_repo": "git@example.com:brightbox/puppet.git",
"http_url_to_repo": "http://example.com/brightbox/puppet.git",
"web_url": "http://example.com/brightbox/puppet",
@@ -149,7 +147,7 @@ Parameters:
"runners_token": "b8547b1dc37721d05889db52fa2f02",
"public_builds": true,
"shared_with_groups": [],
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
"request_access_enabled": false
}
@@ -177,8 +175,7 @@ Parameters:
"id": 3,
"description": null,
"default_branch": "master",
- "public": false,
- "visibility_level": 0,
+ "visibility": "private",
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git",
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
"web_url": "http://example.com/diaspora/diaspora-project-site",
@@ -241,7 +238,7 @@ Parameters:
"group_access_level": 10
}
],
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
"request_access_enabled": false
}
@@ -446,10 +443,10 @@ Parameters:
| `snippets_enabled` | boolean | no | Enable snippets for this project |
| `container_registry_enabled` | boolean | no | Enable container registry for this project |
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
-| `visibility_level` | integer | no | See [project visibility level](#project-visibility-level) |
+| `visibility` | String | no | See [project visibility level](#project-visibility-level) |
| `import_url` | string | no | URL to import repository from |
| `public_builds` | boolean | no | If `true`, builds can be viewed by non-project-members |
-| `only_allow_merge_if_build_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
+| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
| `only_allow_merge_if_all_discussions_are_resolved` | boolean | no | Set whether merge requests can only be merged when all the discussions are resolved |
| `lfs_enabled` | boolean | no | Enable LFS |
| `request_access_enabled` | boolean | no | Allow users to request member access |
@@ -479,10 +476,10 @@ Parameters:
| `snippets_enabled` | boolean | no | Enable snippets for this project |
| `container_registry_enabled` | boolean | no | Enable container registry for this project |
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
-| `visibility_level` | integer | no | See [project visibility level](#project-visibility-level) |
+| `visibility` | string | no | See [project visibility level](#project-visibility-level) |
| `import_url` | string | no | URL to import repository from |
| `public_builds` | boolean | no | If `true`, builds can be viewed by non-project-members |
-| `only_allow_merge_if_build_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
+| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
| `only_allow_merge_if_all_discussions_are_resolved` | boolean | no | Set whether merge requests can only be merged when all the discussions are resolved |
| `lfs_enabled` | boolean | no | Enable LFS |
| `request_access_enabled` | boolean | no | Allow users to request member access |
@@ -511,10 +508,10 @@ Parameters:
| `snippets_enabled` | boolean | no | Enable snippets for this project |
| `container_registry_enabled` | boolean | no | Enable container registry for this project |
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
-| `visibility_level` | integer | no | See [project visibility level](#project-visibility-level) |
+| `visibility` | string | no | See [project visibility level](#project-visibility-level) |
| `import_url` | string | no | URL to import repository from |
| `public_builds` | boolean | no | If `true`, builds can be viewed by non-project-members |
-| `only_allow_merge_if_build_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
+| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
| `only_allow_merge_if_all_discussions_are_resolved` | boolean | no | Set whether merge requests can only be merged when all the discussions are resolved |
| `lfs_enabled` | boolean | no | Enable LFS |
| `request_access_enabled` | boolean | no | Allow users to request member access |
@@ -549,7 +546,7 @@ Parameters:
| `id` | integer/string | yes | The ID or NAMESPACE/PROJECT_NAME of the project |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/star"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/star"
```
Example response:
@@ -559,8 +556,7 @@ Example response:
"id": 3,
"description": null,
"default_branch": "master",
- "public": false,
- "visibility_level": 10,
+ "visibility": "internal",
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git",
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
"web_url": "http://example.com/diaspora/diaspora-project-site",
@@ -596,7 +592,7 @@ Example response:
"star_count": 1,
"public_builds": true,
"shared_with_groups": [],
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
"request_access_enabled": false
}
@@ -615,7 +611,7 @@ POST /projects/:id/unstar
| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/unstar"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/unstar"
```
Example response:
@@ -625,8 +621,7 @@ Example response:
"id": 3,
"description": null,
"default_branch": "master",
- "public": false,
- "visibility_level": 10,
+ "visibility": "internal",
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git",
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
"web_url": "http://example.com/diaspora/diaspora-project-site",
@@ -662,7 +657,7 @@ Example response:
"star_count": 0,
"public_builds": true,
"shared_with_groups": [],
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
"request_access_enabled": false
}
@@ -682,7 +677,7 @@ POST /projects/:id/archive
| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/archive"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/archive"
```
Example response:
@@ -692,8 +687,7 @@ Example response:
"id": 3,
"description": null,
"default_branch": "master",
- "public": false,
- "visibility_level": 0,
+ "visibility": "private",
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git",
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
"web_url": "http://example.com/diaspora/diaspora-project-site",
@@ -745,7 +739,7 @@ Example response:
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
"public_builds": true,
"shared_with_groups": [],
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
"request_access_enabled": false
}
@@ -765,7 +759,7 @@ POST /projects/:id/unarchive
| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/unarchive"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/unarchive"
```
Example response:
@@ -775,8 +769,7 @@ Example response:
"id": 3,
"description": null,
"default_branch": "master",
- "public": false,
- "visibility_level": 0,
+ "visibility": "private",
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git",
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
"web_url": "http://example.com/diaspora/diaspora-project-site",
@@ -828,7 +821,7 @@ Example response:
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
"public_builds": true,
"shared_with_groups": [],
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
"request_access_enabled": false
}
@@ -914,7 +907,7 @@ Parameters:
| `group_id` | integer | yes | The ID of the group |
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/share/17
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/share/17
```
## Hooks
diff --git a/doc/api/repositories.md b/doc/api/repositories.md
index 727617f1ecc..ddd11bb2a14 100644
--- a/doc/api/repositories.md
+++ b/doc/api/repositories.md
@@ -5,6 +5,8 @@
Get a list of repository files and directories in a project. This endpoint can
be accessed without authentication if the repository is publicly accessible.
+This command provides essentially the same functionality as the `git ls-tree` command. For more information, see the section _Tree Objects_ in the [Git internals documentation](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects/#_tree_objects).
+
```
GET /projects/:id/repository/tree
```
diff --git a/doc/api/repository_files.md b/doc/api/repository_files.md
index 677e209ccd9..ec56d0efa1c 100644
--- a/doc/api/repository_files.md
+++ b/doc/api/repository_files.md
@@ -15,7 +15,7 @@ GET /projects/:id/repository/files
```
```bash
-curl --request GET --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v3/projects/13083/repository/files?file_path=app/models/key.rb&ref=master'
+curl --request GET --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/files?file_path=app/models/key.rb&ref=master'
```
Example response:
@@ -46,7 +46,7 @@ POST /projects/:id/repository/files
```
```bash
-curl --request POST --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v3/projects/13083/repository/files?file_path=app/project.rb&branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&content=some%20content&commit_message=create%20a%20new%20file'
+curl --request POST --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/files?file_path=app/project.rb&branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&content=some%20content&commit_message=create%20a%20new%20file'
```
Example response:
@@ -75,7 +75,7 @@ PUT /projects/:id/repository/files
```
```bash
-curl --request PUT --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v3/projects/13083/repository/files?file_path=app/project.rb&branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&content=some%20other%20content&commit_message=update%20file'
+curl --request PUT --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/files?file_path=app/project.rb&branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&content=some%20other%20content&commit_message=update%20file'
```
Example response:
@@ -113,7 +113,7 @@ DELETE /projects/:id/repository/files
```
```bash
-curl --request DELETE --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v3/projects/13083/repository/files?file_path=app/project.rb&branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&commit_message=delete%20file'
+curl --request DELETE --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/files?file_path=app/project.rb&branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&commit_message=delete%20file'
```
Example response:
diff --git a/doc/api/runners.md b/doc/api/runners.md
index 27d8e7640b2..46f882ce937 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -18,7 +18,7 @@ GET /runners?scope=active
| `scope` | string | no | The scope of specific runners to show, one of: `active`, `paused`, `online`; showing all runners if none provided |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/runners"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/runners"
```
Example response:
@@ -57,7 +57,7 @@ GET /runners/all?scope=online
| `scope` | string | no | The scope of runners to show, one of: `specific`, `shared`, `active`, `paused`, `online`; showing all runners if none provided |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/runners/all"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/runners/all"
```
Example response:
@@ -108,7 +108,7 @@ GET /runners/:id
| `id` | integer | yes | The ID of a runner |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/runners/6"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/runners/6"
```
Example response:
@@ -158,7 +158,7 @@ PUT /runners/:id
| `tag_list` | array | no | The list of tags for a runner; put array of tags, that should be finally assigned to a runner |
```
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/runners/6" --form "description=test-1-20150125-test" --form "tag_list=ruby,mysql,tag1,tag2"
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/runners/6" --form "description=test-1-20150125-test" --form "tag_list=ruby,mysql,tag1,tag2"
```
Example response:
@@ -207,7 +207,7 @@ DELETE /runners/:id
| `id` | integer | yes | The ID of a runner |
```
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/runners/6"
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/runners/6"
```
## List project's runners
@@ -225,7 +225,7 @@ GET /projects/:id/runners
| `id` | integer | yes | The ID of a project |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/9/runners"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/9/runners"
```
Example response:
@@ -263,7 +263,7 @@ POST /projects/:id/runners
| `runner_id` | integer | yes | The ID of a runner |
```
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/9/runners" --form "runner_id=9"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/9/runners" --form "runner_id=9"
```
Example response:
@@ -294,5 +294,5 @@ DELETE /projects/:id/runners/:runner_id
| `runner_id` | integer | yes | The ID of a runner |
```
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/9/runners/9"
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/9/runners/9"
```
diff --git a/doc/api/session.md b/doc/api/session.md
index d7809716fbe..056cc32597c 100644
--- a/doc/api/session.md
+++ b/doc/api/session.md
@@ -21,7 +21,7 @@ POST /session
| `password` | string | yes | The password of the user |
```bash
-curl --request POST "https://gitlab.example.com/api/v3/session?login=john_smith&password=strongpassw0rd"
+curl --request POST "https://gitlab.example.com/api/v4/session?login=john_smith&password=strongpassw0rd"
```
Example response:
diff --git a/doc/api/settings.md b/doc/api/settings.md
index ca6b9347877..38a37cd920c 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -13,7 +13,7 @@ GET /application/settings
```
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/application/settings
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/application/settings
```
Example response:
@@ -32,12 +32,13 @@ Example response:
"updated_at" : "2016-01-04T15:44:55.176Z",
"session_expire_delay" : 10080,
"home_page_url" : null,
- "default_snippet_visibility" : 0,
+ "default_snippet_visibility" : "private",
"domain_whitelist" : [],
"domain_blacklist_enabled" : false,
"domain_blacklist" : [],
"created_at" : "2016-01-04T15:44:55.176Z",
- "default_project_visibility" : 0,
+ "default_project_visibility" : "private",
+ "default_group_visibility" : "private",
"gravatar_enabled" : true,
"sign_in_text" : null,
"container_registry_token_expire_delay": 5,
@@ -66,11 +67,12 @@ PUT /application/settings
| `sign_in_text` | string | no | Text on login page |
| `home_page_url` | string | no | Redirect to this URL when not logged in |
| `default_branch_protection` | integer | no | Determine if developers can push to master. Can take `0` _(not protected, both developers and masters can push new commits, force push or delete the branch)_, `1` _(partially protected, developers can push new commits, but cannot force push or delete the branch, masters can do anything)_ or `2` _(fully protected, developers cannot push new commits, force push or delete the branch, masters can do anything)_ as a parameter. Default is `2`. |
-| `restricted_visibility_levels` | array of integers | no | Selected levels cannot be used by non-admin users for projects or snippets. Can take `0` _(Private)_, `1` _(Internal)_ and `2` _(Public)_ as a parameter. Default is null which means there is no restriction. |
+| `restricted_visibility_levels` | array of strings | no | Selected levels cannot be used by non-admin users for projects or snippets. Can take `private`, `internal` and `public` as a parameter. Default is null which means there is no restriction. |
| `max_attachment_size` | integer | no | Limit attachment size in MB |
| `session_expire_delay` | integer | no | Session duration in minutes. GitLab restart is required to apply changes |
-| `default_project_visibility` | integer | no | What visibility level new projects receive. Can take `0` _(Private)_, `1` _(Internal)_ and `2` _(Public)_ as a parameter. Default is `0`.|
-| `default_snippet_visibility` | integer | no | What visibility level new snippets receive. Can take `0` _(Private)_, `1` _(Internal)_ and `2` _(Public)_ as a parameter. Default is `0`.|
+| `default_project_visibility` | string | no | What visibility level new projects receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`.|
+| `default_snippet_visibility` | string | no | What visibility level new snippets receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`.|
+| `default_group_visibility` | string | no | What visibility level new groups receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`.|
| `domain_whitelist` | array of strings | no | Force people to use only corporate emails for sign-up. Default is null, meaning there is no restriction. |
| `domain_blacklist_enabled` | boolean | no | Enable/disable the `domain_blacklist` |
| `domain_blacklist` | array of strings | yes (if `domain_blacklist_enabled` is `true`) | People trying to sign-up with emails from this domain will not be allowed to do so. |
@@ -88,7 +90,7 @@ PUT /application/settings
| `terminal_max_session_time` | integer | no | Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time. |
```bash
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/application/settings?signup_enabled=false&default_project_visibility=1
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/application/settings?signup_enabled=false&default_project_visibility=internal
```
Example response:
@@ -108,8 +110,9 @@ Example response:
"restricted_visibility_levels": [],
"max_attachment_size": 10,
"session_expire_delay": 10080,
- "default_project_visibility": 1,
- "default_snippet_visibility": 0,
+ "default_project_visibility": "internal",
+ "default_snippet_visibility": "private",
+ "default_group_visibility": "private",
"domain_whitelist": [],
"domain_blacklist_enabled" : false,
"domain_blacklist" : [],
diff --git a/doc/api/sidekiq_metrics.md b/doc/api/sidekiq_metrics.md
index 1ae732d40d6..ea10a26bcd0 100644
--- a/doc/api/sidekiq_metrics.md
+++ b/doc/api/sidekiq_metrics.md
@@ -15,7 +15,7 @@ GET /sidekiq/queue_metrics
```
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/sidekiq/queue_metrics
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/sidekiq/queue_metrics
```
Example response:
@@ -40,7 +40,7 @@ GET /sidekiq/process_metrics
```
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/sidekiq/process_metrics
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/sidekiq/process_metrics
```
Example response:
@@ -82,7 +82,7 @@ GET /sidekiq/job_stats
```
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/sidekiq/job_stats
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/sidekiq/job_stats
```
Example response:
@@ -106,7 +106,7 @@ GET /sidekiq/compound_metrics
```
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/sidekiq/compound_metrics
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/sidekiq/compound_metrics
```
Example response:
diff --git a/doc/api/snippets.md b/doc/api/snippets.md
index 5a5dc162ffe..e09d930698e 100644
--- a/doc/api/snippets.md
+++ b/doc/api/snippets.md
@@ -5,15 +5,15 @@
### Snippet visibility level
Snippets in GitLab can be either private, internal, or public.
-You can set it with the `visibility_level` field in the snippet.
+You can set it with the `visibility` field in the snippet.
Constants for snippet visibility levels are:
-| Visibility | Visibility level | Description |
-| ---------- | ---------------- | ----------- |
-| Private | `0` | The snippet is visible only to the snippet creator |
-| Internal | `10` | The snippet is visible for any logged in user |
-| Public | `20` | The snippet can be accessed without any authentication |
+| Visibility | Description |
+| ---------- | ----------- |
+| `private` | The snippet is visible only to the snippet creator |
+| `internal` | The snippet is visible for any logged in user |
+| `public` | The snippet can be accessed without any authentication |
## List snippets
@@ -38,7 +38,7 @@ Parameters:
| `id` | Integer | yes | The ID of a snippet |
``` bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/snippets/1
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/snippets/1
```
Example response:
@@ -78,11 +78,11 @@ Parameters:
| `title` | String | yes | The title of a snippet |
| `file_name` | String | yes | The name of a snippet file |
| `content` | String | yes | The content of a snippet |
-| `visibility_level` | Integer | yes | The snippet's visibility |
+| `visibility` | String | yes | The snippet's visibility |
``` bash
-curl --request POST --data '{"title": "This is a snippet", "content": "Hello world", "file_name": "test.txt", "visibility_level": 10 }' --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/snippets
+curl --request POST --data '{"title": "This is a snippet", "content": "Hello world", "file_name": "test.txt", "visibility": "internal" }' --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/snippets
```
Example response:
@@ -123,11 +123,11 @@ Parameters:
| `title` | String | no | The title of a snippet |
| `file_name` | String | no | The name of a snippet file |
| `content` | String | no | The content of a snippet |
-| `visibility_level` | Integer | no | The snippet's visibility |
+| `visibility` | String | no | The snippet's visibility |
``` bash
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data '{"title": "foo", "content": "bar"}' https://gitlab.example.com/api/v3/snippets/1
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data '{"title": "foo", "content": "bar"}' https://gitlab.example.com/api/v4/snippets/1
```
Example response:
@@ -154,7 +154,7 @@ Example response:
## Delete snippet
-Deletes an existing snippet.
+Deletes an existing snippet.
```
DELETE /snippets/:id
@@ -168,7 +168,7 @@ Parameters:
```
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/snippets/1"
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/snippets/1"
```
upon successful delete a `204 No content` HTTP code shall be expected, with no data,
@@ -186,7 +186,7 @@ GET /snippets/public
| `page` | Integer | no | the page to retrieve |
``` bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/snippets/public?per_page=2&page=1
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/snippets/public?per_page=2&page=1
```
Example response:
@@ -229,4 +229,3 @@ Example response:
}
]
```
-
diff --git a/doc/api/system_hooks.md b/doc/api/system_hooks.md
index a9edff799ac..bad380794c1 100644
--- a/doc/api/system_hooks.md
+++ b/doc/api/system_hooks.md
@@ -20,7 +20,7 @@ GET /hooks
Example request:
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/hooks
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/hooks
```
Example response:
@@ -59,7 +59,7 @@ POST /hooks
Example request:
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/hooks?url=https://gitlab.example.com/hook"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/hooks?url=https://gitlab.example.com/hook"
```
Example response:
@@ -90,7 +90,7 @@ GET /hooks/:id
Example request:
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/hooks/2
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/hooks/2
```
Example response:
@@ -123,5 +123,5 @@ DELETE /hooks/:id
Example request:
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/hooks/2
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/hooks/2
```
diff --git a/doc/api/tags.md b/doc/api/tags.md
index abeb4bfb40e..bf350f024f5 100644
--- a/doc/api/tags.md
+++ b/doc/api/tags.md
@@ -26,7 +26,7 @@ Parameters:
"committer_email": "jack@example.com",
"id": "2695effb5807a22ff3d138d593fd856244e155e7",
"message": "Initial commit",
- "parents_ids": [
+ "parent_ids": [
"2a4b78934375d7f53875269ffd4f45fd83a84ebe"
]
},
@@ -57,7 +57,7 @@ Parameters:
| `tag_name` | string | yes | The name of the tag |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/repository/tags/v1.0.0
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/repository/tags/v1.0.0
```
Example Response:
@@ -110,7 +110,7 @@ Parameters:
"committer_email": "jack@example.com",
"id": "2695effb5807a22ff3d138d593fd856244e155e7",
"message": "Initial commit",
- "parents_ids": [
+ "parent_ids": [
"2a4b78934375d7f53875269ffd4f45fd83a84ebe"
]
},
diff --git a/doc/api/templates/gitignores.md b/doc/api/templates/gitignores.md
index 8235be92b12..3f2f4ed54e0 100644
--- a/doc/api/templates/gitignores.md
+++ b/doc/api/templates/gitignores.md
@@ -9,7 +9,7 @@ GET /templates/gitignores
```
```bash
-curl https://gitlab.example.com/api/v3/templates/gitignores
+curl https://gitlab.example.com/api/v4/templates/gitignores
```
Example response:
@@ -566,7 +566,7 @@ GET /templates/gitignores/:key
| `key` | string | yes | The key of the gitignore template |
```bash
-curl https://gitlab.example.com/api/v3/templates/gitignores/Ruby
+curl https://gitlab.example.com/api/v4/templates/gitignores/Ruby
```
Example response:
diff --git a/doc/api/templates/gitlab_ci_ymls.md b/doc/api/templates/gitlab_ci_ymls.md
index e120016fbe6..27e8973da58 100644
--- a/doc/api/templates/gitlab_ci_ymls.md
+++ b/doc/api/templates/gitlab_ci_ymls.md
@@ -9,7 +9,7 @@ GET /templates/gitlab_ci_ymls
```
```bash
-curl https://gitlab.example.com/api/v3/templates/gitlab_ci_ymls
+curl https://gitlab.example.com/api/v4/templates/gitlab_ci_ymls
```
Example response:
@@ -107,7 +107,7 @@ GET /templates/gitlab_ci_ymls/:key
| `key` | string | yes | The key of the GitLab CI YML template |
```bash
-curl https://gitlab.example.com/api/v3/templates/gitlab_ci_ymls/Ruby
+curl https://gitlab.example.com/api/v4/templates/gitlab_ci_ymls/Ruby
```
Example response:
diff --git a/doc/api/templates/licenses.md b/doc/api/templates/licenses.md
index ae7218cf1bd..33018f0c53f 100644
--- a/doc/api/templates/licenses.md
+++ b/doc/api/templates/licenses.md
@@ -13,7 +13,7 @@ GET /templates/licenses
| `popular` | boolean | no | If passed, returns only popular licenses |
```bash
-curl https://gitlab.example.com/api/v3/templates/licenses?popular=1
+curl https://gitlab.example.com/api/v4/templates/licenses?popular=1
```
Example response:
@@ -116,7 +116,7 @@ If you omit the `fullname` parameter but authenticate your request, the name of
the authenticated user will be used to replace the copyright holder placeholder.
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/templates/licenses/mit?project=My+Cool+Project
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/templates/licenses/mit?project=My+Cool+Project
```
Example response:
diff --git a/doc/api/todos.md b/doc/api/todos.md
index a2fbbc7e1f8..77667a57195 100644
--- a/doc/api/todos.md
+++ b/doc/api/todos.md
@@ -22,7 +22,7 @@ Parameters:
| `type` | string | no | The type of a todo. Can be either `Issue` or `MergeRequest` |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/todos
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/todos
```
Example Response:
@@ -92,7 +92,7 @@ Example Response:
"updated_at": "2016-06-17T07:47:34.163Z",
"due_date": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_status": "cannot_be_merged",
"subscribed": true,
"user_notes_count": 7
@@ -165,7 +165,7 @@ Example Response:
"updated_at": "2016-06-17T07:47:34.163Z",
"due_date": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_status": "cannot_be_merged",
"subscribed": true,
"user_notes_count": 7
@@ -194,7 +194,7 @@ Parameters:
| `id` | integer | yes | The ID of a todo |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/todos/130/mark_as_done
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/todos/130/mark_as_done
```
Example Response:
@@ -263,7 +263,7 @@ Example Response:
"updated_at": "2016-06-17T07:47:34.163Z",
"due_date": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_status": "cannot_be_merged",
"subscribed": true,
"user_notes_count": 7
@@ -284,7 +284,7 @@ POST /todos/mark_as_done
```
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/todos/donmark_as_donee
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/todos/donmark_as_donee
```
diff --git a/doc/api/users.md b/doc/api/users.md
index d14548e8bbb..95f6bcfccb6 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -699,7 +699,7 @@ Parameters:
| `id` | integer | yes | The ID of the user |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/users/:id/events
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/users/:id/events
```
Example response:
diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md
index c178e224cc5..1b51b9200cd 100644
--- a/doc/api/v3_to_v4.md
+++ b/doc/api/v3_to_v4.md
@@ -30,6 +30,7 @@ changes are in V4:
- Moved `DELETE /todos` to `POST /todos/mark_as_done` and `DELETE /todos/:todo_id` to `POST /todos/:todo_id/mark_as_done` [!9410](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9410)
- Endpoints `/projects/owned`, `/projects/visible`, `/projects/starred` & `/projects/all` are consolidated into `/projects` using query parameters [!8962](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8962)
- Return pagination headers for all endpoints that return an array [!8606](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8606)
+- Added `POST /environments/:environment_id/stop` to stop an environment [!8808](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8808)
- Removed `DELETE projects/:id/deploy_keys/:key_id/disable`. Use `DELETE projects/:id/deploy_keys/:key_id` instead [!9366](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9366)
- Moved `PUT /users/:id/(block|unblock)` to `POST /users/:id/(block|unblock)` [!9371](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9371)
- Make subscription API more RESTful. Use `post ":project_id/:subscribable_type/:subscribable_id/subscribe"` to subscribe and `post ":project_id/:subscribable_type/:subscribable_id/unsubscribe"` to unsubscribe from a resource. [!9325](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9325)
@@ -38,8 +39,15 @@ changes are in V4:
- POST `:id/repository/branches`
- POST `:id/repository/commits`
- POST/PUT/DELETE `:id/repository/files`
+- Renamed `merge when build succeeds` to merge `when pipeline succeeds parameters` on the following endpoints: [!9335](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/)
+ - PUT `projects/:id/merge_requests/:merge_request_id/merge`
+ - POST `projects/:id/merge_requests/:merge_request_id/cancel_merge_when_pipeline_succeeds`
+ - POST `projects`
+ - POST `projects/user/:user_id`
+ - PUT `projects/:id`
- Renamed `branch_name` to `branch` on DELETE `id/repository/branches/:branch` response [!8936](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8936)
- Remove `public` param from create and edit actions of projects [!8736](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8736)
+- Use `visibility` as string parameter everywhere [!9337](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9337)
- Notes do not return deprecated field `upvote` and `downvote` [!9384](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9384)
- Return HTTP status code `400` for all validation errors when creating or updating a member instead of sometimes `422` error. [!9523](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9523)
- Remove `GET /groups/owned`. Use `GET /groups?owned=true` instead [!9505](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9505)
diff --git a/doc/api/version.md b/doc/api/version.md
index 287d17cf97f..8b2a5b51bc5 100644
--- a/doc/api/version.md
+++ b/doc/api/version.md
@@ -10,7 +10,7 @@ GET /version
```
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/version
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/version
```
Example response:
diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md
index 740edba1f59..1ad9621c8a0 100644
--- a/doc/ci/triggers/README.md
+++ b/doc/ci/triggers/README.md
@@ -71,7 +71,7 @@ To trigger a job from webhook of another project you need to add the following
webhook url for Push and Tag push events:
```
-https://gitlab.example.com/api/v3/projects/:id/ref/:ref/trigger/builds?token=TOKEN
+https://gitlab.example.com/api/v4/projects/:id/ref/:ref/trigger/builds?token=TOKEN
```
> **Note**:
@@ -105,7 +105,7 @@ Using cURL you can trigger a rebuild with minimal effort, for example:
curl --request POST \
--form token=TOKEN \
--form ref=master \
- https://gitlab.example.com/api/v3/projects/9/trigger/builds
+ https://gitlab.example.com/api/v4/projects/9/trigger/builds
```
In this case, the project with ID `9` will get rebuilt on `master` branch.
@@ -114,7 +114,7 @@ Alternatively, you can pass the `token` and `ref` arguments in the query string:
```bash
curl --request POST \
- "https://gitlab.example.com/api/v3/projects/9/trigger/builds?token=TOKEN&ref=master"
+ "https://gitlab.example.com/api/v4/projects/9/trigger/builds?token=TOKEN&ref=master"
```
### Triggering a job within `.gitlab-ci.yml`
@@ -128,7 +128,7 @@ need to add in project's A `.gitlab-ci.yml`:
build_docs:
stage: deploy
script:
- - "curl --request POST --form token=TOKEN --form ref=master https://gitlab.example.com/api/v3/projects/9/trigger/builds"
+ - "curl --request POST --form token=TOKEN --form ref=master https://gitlab.example.com/api/v4/projects/9/trigger/builds"
only:
- tags
```
@@ -187,7 +187,7 @@ curl --request POST \
--form token=TOKEN \
--form ref=master \
--form "variables[UPLOAD_TO_S3]=true" \
- https://gitlab.example.com/api/v3/projects/9/trigger/builds
+ https://gitlab.example.com/api/v4/projects/9/trigger/builds
```
### Using webhook to trigger job
@@ -195,7 +195,7 @@ curl --request POST \
You can add the following webhook to another project in order to trigger a job:
```
-https://gitlab.example.com/api/v3/projects/9/ref/master/trigger/builds?token=TOKEN&variables[UPLOAD_TO_S3]=true
+https://gitlab.example.com/api/v4/projects/9/ref/master/trigger/builds?token=TOKEN&variables[UPLOAD_TO_S3]=true
```
### Using cron to trigger nightly jobs
@@ -205,7 +205,7 @@ in conjunction with cron. The example below triggers a job on the `master`
branch of project with ID `9` every night at `00:30`:
```bash
-30 0 * * * curl --request POST --form token=TOKEN --form ref=master https://gitlab.example.com/api/v3/projects/9/trigger/builds
+30 0 * * * curl --request POST --form token=TOKEN --form ref=master https://gitlab.example.com/api/v4/projects/9/trigger/builds
```
[ci-229]: https://gitlab.com/gitlab-org/gitlab-ci/merge_requests/229
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 620d4744685..04c0af44237 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -131,6 +131,16 @@ job_name:
variables: []
```
+You are able to use other variables inside your variable definition (or escape them with `$$`):
+
+```yaml
+variables:
+ LS_CMD: 'ls $FLAGS $$TMP_DIR'
+ FLAGS: '-al'
+script:
+ - 'eval $LS_CMD' # will execute 'ls -al $TMP_DIR'
+```
+
## Secret variables
>**Notes:**
diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md
index fc948a7a116..9bed441c131 100644
--- a/doc/development/doc_styleguide.md
+++ b/doc/development/doc_styleguide.md
@@ -444,7 +444,7 @@ Rendered example:
### cURL commands
-- Use `https://gitlab.example.com/api/v3/` as an endpoint.
+- Use `https://gitlab.example.com/api/v4/` as an endpoint.
- Wherever needed use this private token: `9koXpg98eAheJpvBs5tK`.
- Always put the request first. `GET` is the default so you don't have to
include it.
@@ -468,7 +468,7 @@ Below is a set of [cURL][] examples that you can use in the API documentation.
Get the details of a group:
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/gitlab-org
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/gitlab-org
```
#### cURL example with parameters passed in the URL
@@ -476,7 +476,7 @@ curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/a
Create a new project under the authenticated user's namespace:
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects?name=foo"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects?name=foo"
```
#### Post data using cURL's --data
@@ -486,7 +486,7 @@ cURL's `--data` option. The example below will create a new project `foo` under
the authenticated user's namespace.
```bash
-curl --data "name=foo" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects"
+curl --data "name=foo" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects"
```
#### Post data using JSON content
@@ -495,7 +495,7 @@ curl --data "name=foo" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://g
and double quotes.
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "Content-Type: application/json" --data '{"path": "my-group", "name": "My group"}' https://gitlab.example.com/api/v3/groups
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "Content-Type: application/json" --data '{"path": "my-group", "name": "My group"}' https://gitlab.example.com/api/v4/groups
```
#### Post data using form-data
@@ -504,7 +504,7 @@ Instead of using JSON or urlencode you can use multipart/form-data which
properly handles data encoding:
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "title=ssh-key" --form "key=ssh-rsa AAAAB3NzaC1yc2EA..." https://gitlab.example.com/api/v3/users/25/keys
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "title=ssh-key" --form "key=ssh-rsa AAAAB3NzaC1yc2EA..." https://gitlab.example.com/api/v4/users/25/keys
```
The above example is run by and administrator and will add an SSH public key
@@ -518,7 +518,7 @@ contains spaces in its title. Observe how spaces are escaped using the `%20`
ASCII code.
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/42/issues?title=Hello%20Dude"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/42/issues?title=Hello%20Dude"
```
Use `%2F` for slashes (`/`).
@@ -530,7 +530,7 @@ restrict the sign-up e-mail domains of a GitLab instance to `*.example.com` and
`example.net`, you would do something like this:
```bash
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "domain_whitelist[]=*.example.com" --data "domain_whitelist[]=example.net" https://gitlab.example.com/api/v3/application/settings
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "domain_whitelist[]=*.example.com" --data "domain_whitelist[]=example.net" https://gitlab.example.com/api/v4/application/settings
```
[cURL]: http://curl.haxx.se/ "cURL website"
diff --git a/doc/development/ux_guide/img/karolina-plaskaty.png b/doc/development/ux_guide/img/karolina-plaskaty.png
new file mode 100644
index 00000000000..2e356c99762
--- /dev/null
+++ b/doc/development/ux_guide/img/karolina-plaskaty.png
Binary files differ
diff --git a/doc/development/ux_guide/img/nazim-ramesh.png b/doc/development/ux_guide/img/nazim-ramesh.png
new file mode 100644
index 00000000000..01ba0391630
--- /dev/null
+++ b/doc/development/ux_guide/img/nazim-ramesh.png
Binary files differ
diff --git a/doc/development/ux_guide/users.md b/doc/development/ux_guide/users.md
index 137154e24f3..cbd7c17de41 100644
--- a/doc/development/ux_guide/users.md
+++ b/doc/development/ux_guide/users.md
@@ -14,7 +14,7 @@
### Nazim Ramesh
- Small to medium size organisations using GitLab CE
-<img src="img/steven-lyons.png" width="300px">
+<img src="img/nazim-ramesh.png" width="300px">
#### Demographics
@@ -131,7 +131,7 @@ James and his team use CI quite heavily for several projects. Whilst they’ve w
- Would like to use GitLab at work
- Working for a medium to large size organisation
-<img src="img/harry-robison.png" width="300px">
+<img src="img/karolina-plaskaty.png" width="300px">
#### Demographics
diff --git a/doc/gitlab-basics/img/create_new_project_button.png b/doc/gitlab-basics/img/create_new_project_button.png
index a19f0e57b56..8d7a69e55ed 100644
--- a/doc/gitlab-basics/img/create_new_project_button.png
+++ b/doc/gitlab-basics/img/create_new_project_button.png
Binary files differ
diff --git a/doc/install/README.md b/doc/install/README.md
index 2d2fd8cb380..d35709266e4 100644
--- a/doc/install/README.md
+++ b/doc/install/README.md
@@ -1,9 +1,32 @@
# Installation
-- [Installation](installation.md)
-- [Requirements](requirements.md)
-- [Structure](structure.md)
-- [Database MySQL](database_mysql.md)
-- [Digital Ocean and Docker](digitaloceandocker.md)
-- [Docker](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/docker)
-- [All installation methods](https://about.gitlab.com/installation/)
+GitLab can be installed via various ways. Check the [installation methods][methods]
+for an overview.
+
+## Requirements
+
+Before installing GitLab, make sure to check the [requirements documentation](requirements.md)
+which includes useful information on the supported Operating Systems as well as
+the hardware requirements.
+
+## Installation methods
+
+- [Installation using the Omnibus packages](https://about.gitlab.com/downloads/) -
+ Install GitLab using our official deb/rpm repositories. This is the
+ recommended way.
+- [Installation from source](installation.md) - Install GitLab from source.
+ Useful for unsupported systems like *BSD. For an overview of the directory
+ structure, read the [structure documentation](structure.md).
+- [Docker](https://docs.gitlab.com/omnibus/docker/) - Install GitLab using Docker.
+- [Installation on Google Cloud Platform](google_cloud_platform/index.md) - Install
+ GitLab on Google Cloud Platform using our official image.
+- [Digital Ocean and Docker](digitaloceandocker.md) - Install GitLab quickly
+ on DigitalOcean using Docker.
+
+## Database
+
+While the recommended database is PostgreSQL, we provide information to install
+GitLab using MySQL. Check the [MySQL documentation](database_mysql.md) for more
+information.
+
+[methods]: https://about.gitlab.com/installation/
diff --git a/doc/install/google_cloud_platform/img/change_admin_passwd_email.png b/doc/install/google_cloud_platform/img/change_admin_passwd_email.png
new file mode 100644
index 00000000000..1ffe14f60ff
--- /dev/null
+++ b/doc/install/google_cloud_platform/img/change_admin_passwd_email.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/chrome_not_secure_page.png b/doc/install/google_cloud_platform/img/chrome_not_secure_page.png
new file mode 100644
index 00000000000..e732066908f
--- /dev/null
+++ b/doc/install/google_cloud_platform/img/chrome_not_secure_page.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/gcp_gitlab_being_deployed.png b/doc/install/google_cloud_platform/img/gcp_gitlab_being_deployed.png
new file mode 100644
index 00000000000..2a1859da6e3
--- /dev/null
+++ b/doc/install/google_cloud_platform/img/gcp_gitlab_being_deployed.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/gcp_gitlab_overview.png b/doc/install/google_cloud_platform/img/gcp_gitlab_overview.png
new file mode 100644
index 00000000000..1c4c870dbc9
--- /dev/null
+++ b/doc/install/google_cloud_platform/img/gcp_gitlab_overview.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/gcp_landing.png b/doc/install/google_cloud_platform/img/gcp_landing.png
new file mode 100644
index 00000000000..6398d247ba0
--- /dev/null
+++ b/doc/install/google_cloud_platform/img/gcp_landing.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/gcp_launcher_console_home_page.png b/doc/install/google_cloud_platform/img/gcp_launcher_console_home_page.png
new file mode 100644
index 00000000000..f492888ea4a
--- /dev/null
+++ b/doc/install/google_cloud_platform/img/gcp_launcher_console_home_page.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/gcp_search_for_gitlab.png b/doc/install/google_cloud_platform/img/gcp_search_for_gitlab.png
new file mode 100644
index 00000000000..b38af3966e2
--- /dev/null
+++ b/doc/install/google_cloud_platform/img/gcp_search_for_gitlab.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/gitlab_deployed_page.png b/doc/install/google_cloud_platform/img/gitlab_deployed_page.png
new file mode 100644
index 00000000000..fef9ae45f32
--- /dev/null
+++ b/doc/install/google_cloud_platform/img/gitlab_deployed_page.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/gitlab_first_sign_in.png b/doc/install/google_cloud_platform/img/gitlab_first_sign_in.png
new file mode 100644
index 00000000000..381c0fe48a5
--- /dev/null
+++ b/doc/install/google_cloud_platform/img/gitlab_first_sign_in.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/gitlab_launch_button.png b/doc/install/google_cloud_platform/img/gitlab_launch_button.png
new file mode 100644
index 00000000000..50f66f66118
--- /dev/null
+++ b/doc/install/google_cloud_platform/img/gitlab_launch_button.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/new_gitlab_deployment_settings.png b/doc/install/google_cloud_platform/img/new_gitlab_deployment_settings.png
new file mode 100644
index 00000000000..00060841619
--- /dev/null
+++ b/doc/install/google_cloud_platform/img/new_gitlab_deployment_settings.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/ssh_via_button.png b/doc/install/google_cloud_platform/img/ssh_via_button.png
new file mode 100644
index 00000000000..26106f159ad
--- /dev/null
+++ b/doc/install/google_cloud_platform/img/ssh_via_button.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/index.md b/doc/install/google_cloud_platform/index.md
new file mode 100644
index 00000000000..26506111548
--- /dev/null
+++ b/doc/install/google_cloud_platform/index.md
@@ -0,0 +1,168 @@
+# Installing GitLab on Google Cloud Platform
+
+![GCP landing page](img/gcp_landing.png)
+
+The fastest way to get started on [Google Cloud Platform (GCP)][gcp] is through
+the [Google Cloud Launcher][launcher] program.
+
+## Prerequisites
+
+There are only two prerequisites in order to install GitLab on GCP:
+
+1. You need to have a Google account.
+1. You need to sign up for the GCP program. If this is your first time, Google
+ gives you [$300 credit for free][freetrial] to consume over a 60-day period.
+
+Once you have performed those two steps, you can visit the
+[GCP launcher console][console] which has a list of all the things you can
+deploy on GCP.
+
+![GCP launcher console](img/gcp_launcher_console_home_page.png)
+
+The next step is to find and install GitLab.
+
+## Configuring and deploying the VM
+
+To deploy GitLab on GCP you need to follow five simple steps:
+
+1. Go to https://cloud.google.com/launcher and login with your Google credentials
+1. Search for GitLab from GitLab Inc. (not the same as Bitnami) and click on
+ the tile.
+
+ ![Search for GitLab](img/gcp_search_for_gitlab.png)
+
+1. In the next page, you can see an overview of the GitLab VM as well as some
+ estimated costs. Click the **Launch on Compute Engine** button to choose the
+ hardware and network settings.
+
+ ![Launch on Compute Engine](img/gcp_gitlab_overview.png)
+
+1. In the settings page you can choose things like the datacenter where your GitLab
+ server will be hosted, the number of CPUs and amount of RAM, the disk size
+ and type, etc. Read GitLab's [requirements documentation][req] for more
+ details on what to choose depending on your needs.
+
+ ![Deploy settings](img/new_gitlab_deployment_settings.png)
+
+1. As a last step, hit **Deploy** when ready. The process will finish in a few
+ seconds.
+
+ ![Deploy in progress](img/gcp_gitlab_being_deployed.png)
+
+
+## Visiting GitLab for the first time
+
+After a few seconds, GitLab will be successfully deployed and you should be
+able to see the IP address that Google assigned to the VM, as well as the
+credentials to the GitLab admin account.
+
+![Deploy settings](img/gitlab_deployed_page.png)
+
+1. Click on the IP under **Site address** to visit GitLab.
+1. Accept the self-signed certificate that Google automatically deployed in
+ order to securely reach GitLab's login page.
+1. Use the username and password that are present in the Google console page
+ to login into GitLab and click **Sign in**.
+
+ ![GitLab first sign in](img/gitlab_first_sign_in.png)
+
+Congratulations! GitLab is now installed and you can access it via your browser,
+but we're not done yet. There are some steps you need to take in order to have
+a fully functional GitLab installation.
+
+## Next steps
+
+These are the most important next steps to take after you installed GitLab for
+the first time.
+
+### Changing the admin password and email
+
+Google assigned a random password for the GitLab admin account and you should
+change it ASAP:
+
+1. Visit the GitLab admin page through the link in the Google console under
+ **Admin URL**.
+1. Find the Administrator user under the **Users** page and hit **Edit**.
+1. Change the email address to a real one and enter a new password.
+
+ ![Change GitLab admin password](img/change_admin_passwd_email.png)
+
+1. Hit **Save changes** for the changes to take effect.
+1. After changing the password, you will be signed out from GitLab. Use the
+ new credentials to login again.
+
+### Assigning a static IP
+
+By default, Google assigns an ephemeral IP to your instance. It is strongly
+recommended to assign a static IP if you are going to use GitLab in production
+and use a domain name as we'll see below.
+
+Read Google's documentation on how to [promote an ephemeral IP address][ip].
+
+### Using a domain name
+
+Assuming you have a domain name in your possession and you have correctly
+set up DNS to point to the static IP you configured in the previous step,
+here's how you configure GitLab to be aware of the change:
+
+1. SSH into the VM. You can easily use the **SSH** button in the Google console
+ and a new window will pop up.
+
+ ![SSH button](img/ssh_via_button.png)
+
+ In the future you might want to set up [connecting with an SSH key][ssh]
+ instead.
+
+1. Edit the config file of Omnibus GitLab using your favorite text editor:
+
+ ```
+ sudo vim /etc/gitlab/gitlab.rb
+ ```
+
+1. Set the `external_url` value to the domain name you wish GitLab to have
+ **without** `https`:
+
+ ```
+ external_url 'http://gitlab.example.com'
+ ```
+
+ We will set up HTTPS in the next step, no need to do this now.
+
+1. Reconfigure GitLab for the changes to take effect:
+
+ ```
+ sudo gitlab-ctl reconfigure
+ ```
+
+1. You can now visit GitLab using the domain name.
+
+### Configuring HTTPS with the domain name
+
+Although not needed, it's strongly recommended to secure GitLab with a TLS
+certificate. Follow the steps in the [Omnibus documentation][omni-ssl].
+
+### Configuring the email SMTP settings
+
+You need to configure the email SMTP settings correctly otherwise GitLab will
+not be able to send notification emails, like comments, and password changes.
+Check the [Omnibus documentation][omni-smtp] how to do so.
+
+## Further reading
+
+GitLab can be configured to authenticate with other OAuth providers, LDAP, SAML,
+Kerberos, etc. Here are some documents you might be interested in reading:
+
+- [Omnibus GitLab documentation](https://docs.gitlab.com/omnibus/)
+- [Integration documentation](https://docs.gitlab.com/ce/integration/)
+- [GitLab Pages configuration](https://docs.gitlab.com/ce/administration/pages/index.html)
+- [GitLab Container Registry configuration](https://docs.gitlab.com/ce/administration/container_registry.html)
+
+[console]: https://console.cloud.google.com/launcher "GCP launcher console"
+[freetrial]: https://console.cloud.google.com/freetrial "GCP free trial"
+[ip]: https://cloud.google.com/compute/docs/configure-instance-ip-addresses#promote_ephemeral_ip "Configuring an Instance's IP Addresses"
+[gcp]: https://cloud.google.com/ "Google Cloud Platform"
+[launcher]: https://cloud.google.com/launcher/ "Google Cloud Launcher home page"
+[req]: ../requirements.md "GitLab hardware and software requirements"
+[ssh]: https://cloud.google.com/compute/docs/instances/connecting-to-instance "Connecting to Linux Instances"
+[omni-smtp]: https://docs.gitlab.com/omnibus/settings/smtp.html#smtp-settings "Omnibus GitLab SMTP settings"
+[omni-ssl]: https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https "Omnibus GitLab enable HTTPS"
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 5ba338ba7d1..bb4141c6cd3 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -155,10 +155,9 @@ page](https://golang.org/dl).
## 4. Node
Since GitLab 8.17, GitLab requires the use of node >= v4.3.0 to compile
-javascript assets, and starting in GitLab 9.0, yarn >= v0.17.0 is required to
-manage javascript dependencies. In many distros the versions provided by the
-official package repositories are out of date, so we'll need to install through
-the following commands:
+javascript assets, and yarn >= v0.17.0 to manage javascript dependencies.
+In many distros the versions provided by the official package repositories
+are out of date, so we'll need to install through the following commands:
# install node v7.x
curl --location https://deb.nodesource.com/setup_7.x | bash -
diff --git a/doc/pages/README.md b/doc/pages/README.md
new file mode 100644
index 00000000000..7878bce3f10
--- /dev/null
+++ b/doc/pages/README.md
@@ -0,0 +1 @@
+This document was moved to [pages/index.md](../user/project/pages/index.md).
diff --git a/doc/pages/getting_started_part_one.md b/doc/pages/getting_started_part_one.md
index c5b1aa4b654..1d63ccb4d2f 100644
--- a/doc/pages/getting_started_part_one.md
+++ b/doc/pages/getting_started_part_one.md
@@ -1,266 +1 @@
-# GitLab Pages from A to Z: Part 1
-
-- **Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates**
-- _[Part 2: Quick Start Guide - Setting Up GitLab Pages](getting_started_part_two.md)_
-- _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_three.md)_
-
-----
-
-This is a comprehensive guide, made for those who want to
-publish a website with GitLab Pages but aren't familiar with
-the entire process involved.
-
-To **enable** GitLab Pages for GitLab CE (Community Edition)
-and GitLab EE (Enterprise Edition), please read the
-[admin documentation](https://docs.gitlab.com/ce/administration/pages/index.html),
-and/or watch this [video tutorial](https://youtu.be/dD8c7WNcc6s).
-
->**Note:**
-For this guide, we assume you already have GitLab Pages
-server up and running for your GitLab instance.
-
-## What you need to know before getting started
-
-Before we begin, let's understand a few concepts first.
-
-### Static sites
-
-GitLab Pages only supports static websites, meaning,
-your output files must be HTML, CSS, and JavaScript only.
-
-To create your static site, you can either hardcode in HTML,
-CSS, and JS, or use a [Static Site Generator (SSG)](https://www.staticgen.com/)
-to simplify your code and build the static site for you,
-which is highly recommendable and much faster than hardcoding.
-
----
-
-- Read through this technical overview on [Static versus Dynamic Websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/)
-- Understand [how modern Static Site Generators work](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/) and what you can add to your static site
-- You can use [any SSG with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/)
-- Fork an [example project](https://gitlab.com/pages) to build your website based upon
-
-### GitLab Pages domain
-
-If you set up a GitLab Pages project on GitLab.com,
-it will automatically be accessible under a
-[subdomain of `namespace.pages.io`](https://docs.gitlab.com/ce/user/project/pages/).
-The `namespace` is defined by your username on GitLab.com,
-or the group name you created this project under.
-
->**Note:**
-If you use your own GitLab instance to deploy your
-site with GitLab Pages, check with your sysadmin what's your
-Pages wildcard domain. This guide is valid for any GitLab instance,
-you just need to replace Pages wildcard domain on GitLab.com
-(`*.gitlab.io`) with your own.
-
-#### Practical examples
-
-**Project Websites:**
-
-- You created a project called `blog` under your username `john`,
-therefore your project URL is `https://gitlab.com/john/blog/`.
-Once you enable GitLab Pages for this project, and build your site,
-it will be available under `https://john.gitlab.io/blog/`.
-- You created a group for all your websites called `websites`,
-and a project within this group is called `blog`. Your project
-URL is `https://gitlab.com/websites/blog/`. Once you enable
-GitLab Pages for this project, the site will live under
-`https://websites.gitlab.io/blog/`.
-
-**User and Group Websites:**
-
-- Under your username, `john`, you created a project called
-`john.gitlab.io`. Your project URL will be `https://gitlab.com/john/john.gitlab.io`.
-Once you enable GitLab Pages for your project, your website
-will be published under `https://john.gitlab.io`.
-- Under your group `websites`, you created a project called
-`websites.gitlab.io`. your project's URL will be `https://gitlab.com/websites/websites.gitlab.io`. Once you enable GitLab Pages for your project,
-your website will be published under `https://websites.gitlab.io`.
-
-**General example:**
-
-- On GitLab.com, a project site will always be available under
-`https://namespace.gitlab.io/project-name`
-- On GitLab.com, a user or group website will be available under
-`https://namespace.gitlab.io/`
-- On your GitLab instance, replace `gitlab.io` above with your
-Pages server domain. Ask your sysadmin for this information.
-
-### DNS Records
-
-A Domain Name System (DNS) web service routes visitors to websites
-by translating domain names (such as `www.example.com`) into the
-numeric IP addresses (such as `192.0.2.1`) that computers use to
-connect to each other.
-
-A DNS record is created to point a (sub)domain to a certain location,
-which can be an IP address or another domain. In case you want to use
-GitLab Pages with your own (sub)domain, you need to access your domain's
-registrar control panel to add a DNS record pointing it back to your
-GitLab Pages site.
-
-Note that **how to** add DNS records depends on which server your domain
-is hosted on. Every control panel has its own place to do it. If you are
-not an admin of your domain, and don't have access to your registrar,
-you'll need to ask for the technical support of your hosting service
-to do it for you.
-
-To help you out, we've gathered some instructions on how to do that
-for the most popular hosting services:
-
-- [Amazon](http://docs.aws.amazon.com/gettingstarted/latest/swh/getting-started-configure-route53.html)
-- [Bluehost](https://my.bluehost.com/cgi/help/559)
-- [CloudFlare](https://support.cloudflare.com/hc/en-us/articles/200169096-How-do-I-add-A-records-)
-- [cPanel](https://documentation.cpanel.net/display/ALD/Edit+DNS+Zone)
-- [DreamHost](https://help.dreamhost.com/hc/en-us/articles/215414867-How-do-I-add-custom-DNS-records-)
-- [Go Daddy](https://www.godaddy.com/help/add-an-a-record-19238)
-- [Hostgator](http://support.hostgator.com/articles/changing-dns-records)
-- [Inmotion hosting](https://my.bluehost.com/cgi/help/559)
-- [Media Temple](https://mediatemple.net/community/products/dv/204403794/how-can-i-change-the-dns-records-for-my-domain)
-- [Microsoft](https://msdn.microsoft.com/en-us/library/bb727018.aspx)
-
-If your hosting service is not listed above, you can just try to
-search the web for "how to add dns record on <my hosting service>".
-
-#### DNS A record
-
-In case you want to point a root domain (`example.com`) to your
-GitLab Pages site, deployed to `namespace.gitlab.io`, you need to
-log into your domain's admin control panel and add a DNS `A` record
-pointing your domain to Pages' server IP address. For projects on
-GitLab.com, this IP is `104.208.235.32`. For projects leaving in
-other GitLab instances (CE or EE), please contact your sysadmin
-asking for this information (which IP address is Pages server
-running on your instance).
-
-**Practical Example:**
-
-![DNS A record pointing to GitLab.com Pages server](img/dns_a_record_example.png)
-
-#### DNS CNAME record
-
-In case you want to point a subdomain (`hello-world.example.com`)
-to your GitLab Pages site initially deployed to `namespace.gitlab.io`,
-you need to log into your domain's admin control panel and add a DNS
-`CNAME` record pointing your subdomain to your website URL
-(`namespace.gitlab.io`) address.
-
-Notice that, despite it's a user or project website, the `CNAME`
-should point to your Pages domain (`namespace.gitlab.io`),
-without any `/project-name`.
-
-**Practical Example:**
-
-![DNS CNAME record pointing to GitLab.com project](img/dns_cname_record_example.png)
-
-#### TL;DR
-
-| From | DNS Record | To |
-| ---- | ---------- | -- |
-| domain.com | A | 104.208.235.32 |
-| subdomain.domain.com | CNAME | namespace.gitlab.io |
-
-> **Notes**:
->
-> - **Do not** use a CNAME record if you want to point your
-`domain.com` to your GitLab Pages site. Use an `A` record instead.
-> - **Do not** add any special chars after the default Pages
-domain. E.g., **do not** point your `subdomain.domain.com` to
-`namespace.gitlab.io.` or `namespace.gitlab.io/`.
-
-### SSL/TLS Certificates
-
-Every GitLab Pages project on GitLab.com will be available under
-HTTPS for the default Pages domain (`*.gitlab.io`). Once you set
-up your Pages project with your custom (sub)domain, if you want
-it secured by HTTPS, you will have to issue a certificate for that
-(sub)domain and install it on your project.
-
->**Note:**
-Certificates are NOT required to add to your custom
-(sub)domain on your GitLab Pages project, though they are
-highly recommendable.
-
-The importance of having any website securely served under HTTPS
-is explained on the introductory section of the blog post
-[Secure GitLab Pages with StartSSL](https://about.gitlab.com/2016/06/24/secure-gitlab-pages-with-startssl/#https-a-quick-overview).
-
-The reason why certificates are so important is that they encrypt
-the connection between the **client** (you, me, your visitors)
-and the **server** (where you site lives), through a keychain of
-authentications and validations.
-
-### Issuing Certificates
-
-GitLab Pages accepts [PEM](https://support.quovadisglobal.com/kb/a37/what-is-pem-format.aspx) certificates issued by
-[Certificate Authorities (CA)](https://en.wikipedia.org/wiki/Certificate_authority)
-and self-signed certificates. Of course,
-[you'd rather issue a certificate than generate a self-signed](https://en.wikipedia.org/wiki/Self-signed_certificate),
-for security reasons and for having browsers trusting your
-site's certificate.
-
-There are several different kinds of certificates, each one
-with certain security level. A static personal website will
-not require the same security level as an online banking web app,
-for instance. There are a couple Certificate Authorities that
-offer free certificates, aiming to make the internet more secure
-to everyone. The most popular is [Let's Encrypt](https://letsencrypt.org/),
-which issues certificates trusted by most of browsers, it's open
-source, and free to use. Please read through this tutorial to
-understand [how to secure your GitLab Pages website with Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/).
-
-With the same popularity, there are [certificates issued by CloudFlare](https://www.cloudflare.com/ssl/),
-which also offers a [free CDN service](https://blog.cloudflare.com/cloudflares-free-cdn-and-you/).
-Their certs are valid up to 15 years. Read through the tutorial on
-[how to add a CloudFlare Certificate to your GitLab Pages website](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/).
-
-### Adding certificates to your project
-
-Regardless the CA you choose, the steps to add your certificate to
-your Pages project are the same.
-
-#### What do you need
-
-1. A PEM certificate
-1. An intermediate certificate
-1. A public key
-
-![Pages project - adding certificates](img/add_certificate_to_pages.png)
-
-These fields are found under your **Project**'s **Settings** > **Pages** > **New Domain**.
-
-#### What's what?
-
-- A PEM certificate is the certificate generated by the CA,
-which needs to be added to the field **Certificate (PEM)**.
-- An [intermediate certificate](https://en.wikipedia.org/wiki/Intermediate_certificate_authority) (aka "root certificate") is
-the part of the encryption keychain that identifies the CA.
-Usually it's combined with the PEM certificate, but there are
-some cases in which you need to add them manually.
-[CloudFlare certs](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/)
-are one of these cases.
-- A public key is an encrypted key which validates
-your PEM against your domain.
-
-#### Now what?
-
-Now that you hopefully understand why you need all
-of this, it's simple:
-
-- Your PEM certificate needs to be added to the first field
-- If your certificate is missing its intermediate, copy
-and paste the root certificate (usually available from your CA website)
-and paste it in the [same field as your PEM certificate](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/),
-just jumping a line between them.
-- Copy your public key and paste it in the last field
-
->**Note:**
-**Do not** open certificates or encryption keys in
-regular text editors. Always use code editors (such as
-Sublime Text, Atom, Dreamweaver, Brackets, etc).
-
-|||
-|:--|--:|
-||[**Part 2: Quick start guide - Setting up GitLab Pages →**](getting_started_part_two.md)|
+This document was moved to [another location](../user/project/pages/getting_started_part_one.md).
diff --git a/doc/pages/getting_started_part_three.md b/doc/pages/getting_started_part_three.md
index ef47abef3a0..1697b5cd6b4 100644
--- a/doc/pages/getting_started_part_three.md
+++ b/doc/pages/getting_started_part_three.md
@@ -1,383 +1 @@
-# GitLab Pages from A to Z: Part 3
-
-- _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](getting_started_part_one.md)_
-- _[Part 2: Quick Start Guide - Setting Up GitLab Pages](getting_started_part_two.md)_
-- **Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages**
-
----
-
-## Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages
-
-[GitLab CI](https://about.gitlab.com/gitlab-ci/) serves
-numerous purposes, to build, test, and deploy your app
-from GitLab through
-[Continuous Integration, Continuous Delivery, and Continuous Deployment](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/)
-methods. You will need it to build your website with GitLab Pages,
-and deploy it to the Pages server.
-
-What this file actually does is telling the
-[GitLab Runner](https://docs.gitlab.com/runner/) to run scripts
-as you would do from the command line. The Runner acts as your
-terminal. GitLab CI tells the Runner which commands to run.
-Both are built-in in GitLab, and you don't need to set up
-anything for them to work.
-
-Explaining [every detail of GitLab CI](https://docs.gitlab.com/ce/ci/yaml/README.html)
-and GitLab Runner is out of the scope of this guide, but we'll
-need to understand just a few things to be able to write our own
-`.gitlab-ci.yml` or tweak an existing one. It's an
-[Yaml](http://docs.ansible.com/ansible/YAMLSyntax.html) file,
-with its own syntax. You can always check your CI syntax with
-the [GitLab CI Lint Tool](https://gitlab.com/ci/lint).
-
-**Practical Example:**
-
-Let's consider you have a [Jekyll](https://jekyllrb.com/) site.
-To build it locally, you would open your terminal, and run `jekyll build`.
-Of course, before building it, you had to install Jekyll in your computer.
-For that, you had to open your terminal and run `gem install jekyll`.
-Right? GitLab CI + GitLab Runner do the same thing. But you need to
-write in the `.gitlab-ci.yml` the script you want to run so
-GitLab Runner will do it for you. It looks more complicated then it
-is. What you need to tell the Runner:
-
-```
-$ gem install jekyll
-$ jekyll build
-```
-
-### Script
-
-To transpose this script to Yaml, it would be like this:
-
-```yaml
-script:
- - gem install jekyll
- - jekyll build
-```
-
-### Job
-
-So far so good. Now, each `script`, in GitLab is organized by
-a `job`, which is a bunch of scripts and settings you want to
-apply to that specific task.
-
-```yaml
-job:
- script:
- - gem install jekyll
- - jekyll build
-```
-
-For GitLab Pages, this `job` has a specific name, called `pages`,
-which tells the Runner you want that task to deploy your website
-with GitLab Pages:
-
-```yaml
-pages:
- script:
- - gem install jekyll
- - jekyll build
-```
-
-### The `public` directory
-
-We also need to tell Jekyll where do you want the website to build,
-and GitLab Pages will only consider files in a directory called `public`.
-To do that with Jekyll, we need to add a flag specifying the
-[destination (`-d`)](https://jekyllrb.com/docs/usage/) of the
-built website: `jekyll build -d public`. Of course, we need
-to tell this to our Runner:
-
-```yaml
-pages:
- script:
- - gem install jekyll
- - jekyll build -d public
-```
-
-### Artifacts
-
-We also need to tell the Runner that this _job_ generates
-_artifacts_, which is the site built by Jekyll.
-Where are these artifacts stored? In the `public` directory:
-
-```yaml
-pages:
- script:
- - gem install jekyll
- - jekyll build -d public
- artifacts:
- paths:
- - public
-```
-
-The script above would be enough to build your Jekyll
-site with GitLab Pages. But, from Jekyll 3.4.0 on, its default
-template originated by `jekyll new project` requires
-[Bundler](http://bundler.io/) to install Jekyll dependencies
-and the default theme. To adjust our script to meet these new
-requirements, we only need to install and build Jekyll with Bundler:
-
-```yaml
-pages:
- script:
- - bundle install
- - bundle exec jekyll build -d public
- artifacts:
- paths:
- - public
-```
-
-That's it! A `.gitlab-ci.yml` with the content above would deploy
-your Jekyll 3.4.0 site with GitLab Pages. This is the minimum
-configuration for our example. On the steps below, we'll refine
-the script by adding extra options to our GitLab CI.
-
-### Image
-
-At this point, you probably ask yourself: "okay, but to install Jekyll
-I need Ruby. Where is Ruby on that script?". The answer is simple: the
-first thing GitLab Runner will look for in your `.gitlab-ci.yml` is a
-[Docker](https://www.docker.com/) image specifying what do you need in
-your container to run that script:
-
-```yaml
-image: ruby:2.3
-
-pages:
- script:
- - bundle install
- - bundle exec jekyll build -d public
- artifacts:
- paths:
- - public
-```
-
-In this case, you're telling the Runner to pull this image, which
-contains Ruby 2.3 as part of its file system. When you don't specify
-this image in your configuration, the Runner will use a default
-image, which is Ruby 2.1.
-
-If your SSG needs [NodeJS](https://nodejs.org/) to build, you'll
-need to specify which image you want to use, and this image should
-contain NodeJS as part of its file system. E.g., for a
-[Hexo](https://gitlab.com/pages/hexo) site, you can use `image: node:4.2.2`.
-
->**Note:**
-We're not trying to explain what a Docker image is,
-we just need to introduce the concept with a minimum viable
-explanation. To know more about Docker images, please visit
-their website or take a look at a
-[summarized explanation](http://paislee.io/how-to-automate-docker-deployments/) here.
-
-Let's go a little further.
-
-### Branching
-
-If you use GitLab as a version control platform, you will have your
-branching strategy to work on your project. Meaning, you will have
-other branches in your project, but you'll want only pushes to the
-default branch (usually `master`) to be deployed to your website.
-To do that, we need to add another line to our CI, telling the Runner
-to only perform that _job_ called `pages` on the `master` branch `only`:
-
-```yaml
-image: ruby:2.3
-
-pages:
- script:
- - bundle install
- - bundle exec jekyll build -d public
- artifacts:
- paths:
- - public
- only:
- - master
-```
-
-### Stages
-
-Another interesting concept to keep in mind are build stages.
-Your web app can pass through a lot of tests and other tasks
-until it's deployed to staging or production environments.
-There are three default stages on GitLab CI: build, test,
-and deploy. To specify which stage your _job_ is running,
-simply add another line to your CI:
-
-```yaml
-image: ruby:2.3
-
-pages:
- stage: deploy
- script:
- - bundle install
- - bundle exec jekyll build -d public
- artifacts:
- paths:
- - public
- only:
- - master
-```
-
-You might ask yourself: "why should I bother with stages
-at all?" Well, let's say you want to be able to test your
-script and check the built site before deploying your site
-to production. You want to run the test exactly as your
-script will do when you push to `master`. It's simple,
-let's add another task (_job_) to our CI, telling it to
-test every push to other branches, `except` the `master` branch:
-
-```yaml
-image: ruby:2.3
-
-pages:
- stage: deploy
- script:
- - bundle install
- - bundle exec jekyll build -d public
- artifacts:
- paths:
- - public
- only:
- - master
-
-test:
- stage: test
- script:
- - bundle install
- - bundle exec jekyll build -d test
- artifacts:
- paths:
- - test
- except:
- - master
-```
-
-The `test` job is running on the stage `test`, Jekyll
-will build the site in a directory called `test`, and
-this job will affect all the branches except `master`.
-
-The best benefit of applying _stages_ to different
-_jobs_ is that every job in the same stage builds in
-parallel. So, if your web app needs more than one test
-before being deployed, you can run all your test at the
-same time, it's not necessary to wait one test to finish
-to run the other. Of course, this is just a brief
-introduction of GitLab CI and GitLab Runner, which are
-tools much more powerful than that. This is what you
-need to be able to create and tweak your builds for
-your GitLab Pages site.
-
-### Before Script
-
-To avoid running the same script multiple times across
-your _jobs_, you can add the parameter `before_script`,
-in which you specify which commands you want to run for
-every single _job_. In our example, notice that we run
-`bundle install` for both jobs, `pages` and `test`.
-We don't need to repeat it:
-
-```yaml
-image: ruby:2.3
-
-before_script:
- - bundle install
-
-pages:
- stage: deploy
- script:
- - bundle exec jekyll build -d public
- artifacts:
- paths:
- - public
- only:
- - master
-
-test:
- stage: test
- script:
- - bundle exec jekyll build -d test
- artifacts:
- paths:
- - test
- except:
- - master
-```
-
-### Caching Dependencies
-
-If you want to cache the installation files for your
-projects dependencies, for building faster, you can
-use the parameter `cache`. For this example, we'll
-cache Jekyll dependencies in a `vendor` directory
-when we run `bundle install`:
-
-```yaml
-image: ruby:2.3
-
-cache:
- paths:
- - vendor/
-
-before_script:
- - bundle install --path vendor
-
-pages:
- stage: deploy
- script:
- - bundle exec jekyll build -d public
- artifacts:
- paths:
- - public
- only:
- - master
-
-test:
- stage: test
- script:
- - bundle exec jekyll build -d test
- artifacts:
- paths:
- - test
- except:
- - master
-```
-
-For this specific case, we need to exclude `/vendor`
-from Jekyll `_config.yml` file, otherwise Jekyll will
-understand it as a regular directory to build
-together with the site:
-
-```yml
-exclude:
- - vendor
-```
-
-There we go! Now our GitLab CI not only builds our website,
-but also **continuously test** pushes to feature-branches,
-**caches** dependencies installed with Bundler, and
-**continuously deploy** every push to the `master` branch.
-
-## Advanced GitLab CI for GitLab Pages
-
-What you can do with GitLab CI is pretty much up to your
-creativity. Once you get used to it, you start creating
-awesome scripts that automate most of tasks you'd do
-manually in the past. Read through the
-[documentation of GitLab CI](https://docs.gitlab.com/ce/ci/yaml/README.html)
-to understand how to go even further on your scripts.
-
-- On this blog post, understand the concept of
-[using GitLab CI `environments` to deploy your
-web app to staging and production](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/).
-- On this post, learn [how to run jobs sequentially,
-in parallel, or build a custom pipeline](https://about.gitlab.com/2016/07/29/the-basics-of-gitlab-ci/)
-- On this blog post, we go through the process of
-[pulling specific directories from different projects](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/)
-to deploy this website you're looking at, docs.gitlab.com.
-- On this blog post, we teach you [how to use GitLab Pages to produce a code coverage report](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/).
-
-|||
-|:--|--:|
-|[**← Part 2: Quick start guide - Setting up GitLab Pages**](getting_started_part_two.md)||
+This document was moved to [another location](../user/project/pages/getting_started_part_three.md).
diff --git a/doc/pages/getting_started_part_two.md b/doc/pages/getting_started_part_two.md
index 07dd24122c4..a58affec73d 100644
--- a/doc/pages/getting_started_part_two.md
+++ b/doc/pages/getting_started_part_two.md
@@ -1,152 +1 @@
-# GitLab Pages from A to Z: Part 2
-
-> Type: user guide
->
-> Level: beginner
-
-- _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](getting_started_part_one.md)_
-- **Part 2: Quick Start Guide - Setting Up GitLab Pages**
-- _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_three.md)_
-
-----
-
-## Setting up GitLab Pages
-
-For a complete step-by-step tutorial, please read the
-blog post [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/). The following sections will explain
-what do you need and why do you need them.
-
-## What you need to get started
-
-1. A project
-1. A configuration file (`.gitlab-ci.yml`) to deploy your site
-1. A specific `job` called `pages` in the configuration file
-that will make GitLab aware that you are deploying a GitLab Pages website
-
-Optional Features:
-
-1. A custom domain or subdomain
-1. A DNS pointing your (sub)domain to your Pages site
- 1. **Optional**: an SSL/TLS certificate so your custom
- domain is accessible under HTTPS.
-
-## Project
-
-Your GitLab Pages project is a regular project created the
-same way you do for the other ones. To get started with GitLab Pages, you have two ways:
-
-- Fork one of the templates from Page Examples, or
-- Create a new project from scratch
-
-Let's go over both options.
-
-### Fork a project to get started from
-
-To make things easy for you, we've created this
-[group](https://gitlab.com/pages) of default projects
-containing the most popular SSGs templates.
-
-Watch the [video tutorial](https://youtu.be/TWqh9MtT4Bg) we've
-created for the steps below.
-
-1. Choose your SSG template
-1. Fork a project from the [Pages group](https://gitlab.com/pages)
-1. Remove the fork relationship by navigating to your **Project**'s **Settings** > **Edit Project**
-
- ![remove fork relashionship](img/remove_fork_relashionship.png)
-
-1. Enable Shared Runners for your fork: navigate to your **Project**'s **Settings** > **CI/CD Pipelines**
-1. Trigger a build (push a change to any file)
-1. As soon as the build passes, your website will have been deployed with GitLab Pages. Your website URL will be available under your **Project**'s **Settings** > **Pages**
-
-To turn a **project website** forked from the Pages group into a **user/group** website, you'll need to:
-
-- Rename it to `namespace.gitlab.io`: navigate to **Project**'s **Settings** > **Edit Project** > **Rename repository**
-- Adjust your SSG's [base URL](#urls-and-baseurls) to from `"project-name"` to `""`. This setting will be at a different place for each SSG, as each of them have their own structure and file tree. Most likelly, it will be in the SSG's config file.
-
-> **Notes:**
->
->1. Why do I need to remove the fork relationship?
->
-> Unless you want to contribute to the original project,
-you won't need it connected to the upstream. A
-[fork](https://about.gitlab.com/2016/12/01/how-to-keep-your-fork-up-to-date-with-its-origin/#fork)
-is useful for submitting merge requests to the upstream.
->
-> 2. Why do I need to enable Shared Runners?
->
-> Shared Runners will run the script set by your GitLab CI
-configuration file. They're enabled by default to new projects,
-but not to forks.
-
-### Create a project from scratch
-
-1. From your **Project**'s **[Dashboard](https://gitlab.com/dashboard/projects)**,
-click **New project**, and name it considering the
-[practical examples](getting_started_part_one.md#practical-examples).
-1. Clone it to your local computer, add your website
-files to your project, add, commit and push to GitLab.
-1. From the your **Project**'s page, click **Set up CI**:
-
- ![setup GitLab CI](img/setup_ci.png)
-
-1. Choose one of the templates from the dropbox menu.
-Pick up the template corresponding to the SSG you're using (or plain HTML).
-
- ![gitlab-ci templates](img/choose_ci_template.png)
-
-Once you have both site files and `.gitlab-ci.yml` in your project's
-root, GitLab CI will build your site and deploy it with Pages.
-Once the first build passes, you see your site is live by
-navigating to your **Project**'s **Settings** > **Pages**,
-where you'll find its default URL.
-
-> **Notes:**
->
-> - GitLab Pages [supports any SSG](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/), but,
-if you don't find yours among the templates, you'll need
-to configure your own `.gitlab-ci.yml`. Do do that, please
-read through the article [Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_three.md). New SSGs are very welcome among
-the [example projects](https://gitlab.com/pages). If you set
-up a new one, please
-[contribute](https://gitlab.com/pages/pages.gitlab.io/blob/master/CONTRIBUTING.md)
-to our examples.
->
-> - The second step _"Clone it to your local computer"_, can be done
-differently, achieving the same results: instead of cloning the bare
-repository to you local computer and moving your site files into it,
-you can run `git init` in your local website directory, add the
-remote URL: `git remote add origin git@gitlab.com:namespace/project-name.git`,
-then add, commit, and push.
-
-### URLs and Baseurls
-
-Every Static Site Generator (SSG) default configuration expects
-to find your website under a (sub)domain (`example.com`), not
-in a subdirectory of that domain (`example.com/subdir`). Therefore,
-whenever you publish a project website (`namespace.gitlab.io/project-name`),
-you'll have to look for this configuration (base URL) on your SSG's
-documentation and set it up to reflect this pattern.
-
-For example, for a Jekyll site, the `baseurl` is defined in the Jekyll
-configuration file, `_config.yml`. If your website URL is
-`https://john.gitlab.io/blog/`, you need to add this line to `_config.yml`:
-
-```yaml
-baseurl: "/blog"
-```
-
-On the contrary, if you deploy your website after forking one of
-our [default examples](https://gitlab.com/pages), the baseurl will
-already be configured this way, as all examples there are project
-websites. If you decide to make yours a user or group website, you'll
-have to remove this configuration from your project. For the Jekyll
-example we've just mentioned, you'd have to change Jekyll's `_config.yml` to:
-
-```yaml
-baseurl: ""
-```
-
-|||
-|:--|--:|
-|[**← Part 1: Static sites, domains, DNS records, and SSL/TLS certificates**](getting_started_part_one.md)|[**Part 3: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages →**](getting_started_part_three.md)|
+This document was moved to [another location](../user/project/pages/getting_started_part_two.md).
diff --git a/doc/pages/img/dns_cname_record_example.png b/doc/pages/img/dns_cname_record_example.png
deleted file mode 100644
index d64a843a283..00000000000
--- a/doc/pages/img/dns_cname_record_example.png
+++ /dev/null
Binary files differ
diff --git a/doc/pages/img/remove_fork_relashionship.png b/doc/pages/img/remove_fork_relashionship.png
deleted file mode 100644
index f5b5e543f21..00000000000
--- a/doc/pages/img/remove_fork_relashionship.png
+++ /dev/null
Binary files differ
diff --git a/doc/pages/img/setup_ci.png b/doc/pages/img/setup_ci.png
deleted file mode 100644
index 7ce0431f4d4..00000000000
--- a/doc/pages/img/setup_ci.png
+++ /dev/null
Binary files differ
diff --git a/doc/pages/index.md b/doc/pages/index.md
deleted file mode 100644
index a6f928cc243..00000000000
--- a/doc/pages/index.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# All you need to know about GitLab Pages
-
-With GitLab Pages you can create static websites for your GitLab projects,
-groups, or user accounts. You can use any static website generator: Jekyll,
-Middleman, Hexo, Hugo, Pelican, you name it! Connect as many customs domains
-as you like and bring your own TLS certificate to secure them.
-
-Here's some info we have gathered to get you started.
-
-## General info
-
-- [Product webpage](https://pages.gitlab.io)
-- [We're bringing GitLab Pages to CE](https://about.gitlab.com/2016/12/24/were-bringing-gitlab-pages-to-community-edition/)
-- [Pages group - templates](https://gitlab.com/pages)
-
-## Getting started
-
-- GitLab Pages from A to Z
- - [Part 1: Static sites, domains, DNS records, and SSL/TLS certificates](getting_started_part_one.md)
- - [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md)
- - [Part 3: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_three.md)
-- [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) a comprehensive step-by-step guide
-- Secure GitLab Pages custom domain with SSL/TLS certificates
- - [Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/)
- - [CloudFlare](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/)
- - [StartSSL](https://about.gitlab.com/2016/06/24/secure-gitlab-pages-with-startssl/)
-- Static Site Generators - Blog posts series
- - [SSGs part 1: Static vs dynamic websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/)
- - [SSGs part 2: Modern static site generators](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/)
- - [SSGs part 3: Build any SSG site with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/)
-- [Posting to your GitLab Pages blog from iOS](https://about.gitlab.com/2016/08/19/posting-to-your-gitlab-pages-blog-from-ios/)
-
-## Video tutorials
-
-- [How to publish a website with GitLab Pages on GitLab.com: from a forked project](https://youtu.be/TWqh9MtT4Bg)
-- [How to Enable GitLab Pages for GitLab CE and EE](https://youtu.be/dD8c7WNcc6s)
-
-## Advanced use
-
-- Blog Posts:
- - [GitLab CI: Run jobs sequentially, in parallel, or build a custom pipeline](https://about.gitlab.com/2016/07/29/the-basics-of-gitlab-ci/)
- - [GitLab CI: Deployment & environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/)
- - [Building a new GitLab docs site with Nanoc, GitLab CI, and GitLab Pages](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/)
- - [Publish code coverage reports with GitLab Pages](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/)
-
-## Specific documentation
-
-- [User docs](../user/project/pages/index.md)
-- [Admin docs](../administration/pages/index.md)
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index a5b8cd6455c..96ec1b178b6 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -38,23 +38,6 @@ If you are running GitLab within a Docker container, you can run the backup from
docker exec -t <container name> gitlab-rake gitlab:backup:create
```
-You can specify that portions of the application data be skipped using the
-environment variable `SKIP`. You can skip:
-
-- `db` (database)
-- `uploads` (attachments)
-- `repositories` (Git repositories data)
-- `builds` (CI job output logs)
-- `artifacts` (CI job artifacts)
-- `lfs` (LFS objects)
-- `registry` (Container Registry images)
-
-Separate multiple data types to skip using a comma. For example:
-
-```
-sudo gitlab-rake gitlab:backup:create SKIP=db,uploads
-```
-
Example output:
```
@@ -111,13 +94,14 @@ To use the `copy` strategy instead of the default streaming strategy, specify
You can choose what should be backed up by adding the environment variable `SKIP`.
The available options are:
-* `db`
-* `uploads` (attachments)
-* `repositories`
-* `builds` (CI build output logs)
-* `artifacts` (CI build artifacts)
-* `lfs` (LFS objects)
-* `pages` (pages content)
+- `db` (database)
+- `uploads` (attachments)
+- `repositories` (Git repositories data)
+- `builds` (CI job output logs)
+- `artifacts` (CI job artifacts)
+- `lfs` (LFS objects)
+- `registry` (Container Registry images)
+- `pages` (Pages content)
Use a comma to specify several options at the same time:
@@ -416,7 +400,7 @@ sudo gitlab-rake gitlab:check SANITIZE=true
If there is a GitLab version mismatch between your backup tar file and the installed
version of GitLab, the restore command will abort with an error. Install the
-[correct GitLab version](https://www.gitlab.com/downloads/archives/) and try again.
+[correct GitLab version](https://about.gitlab.com/downloads/archives/) and try again.
## Configure cron to make daily backups
diff --git a/doc/ssh/README.md b/doc/ssh/README.md
index 9e391d647a8..678f5199b02 100644
--- a/doc/ssh/README.md
+++ b/doc/ssh/README.md
@@ -13,7 +13,7 @@ read [this nice tutorial by DigitalOcean](https://www.digitalocean.com/community
## Locating an existing SSH key pair
-Before generating a new SSH key check if your system already has one
+Before generating a new SSH key pair check if your system already has one
at the default location by opening a shell, or Command Prompt on Windows,
and running the following command:
@@ -23,43 +23,49 @@ and running the following command:
type %userprofile%\.ssh\id_rsa.pub
```
-**GNU/Linux / macOS / PowerShell:**
+**Git Bash on Windows / GNU/Linux / macOS / PowerShell:**
```bash
cat ~/.ssh/id_rsa.pub
```
If you see a string starting with `ssh-rsa` you already have an SSH key pair
-and you can skip the next step **Generating a new SSH key pair**
-and continue onto **Copying your public SSH key to the clipboard**.
+and you can skip the generate portion of the next section and skip to the copy
+to clipboard step.
If you don't see the string or would like to generate a SSH key pair with a
custom name continue onto the next step.
+>
+**Note:** Public SSH key may also be named as follows:
+- `id_dsa.pub`
+- `id_ecdsa.pub`
+- `id_ed25519.pub`
+
## Generating a new SSH key pair
-1. To generate a new SSH key, use the following command:
+1. To generate a new SSH key pair, use the following command:
- **GNU/Linux / macOS:**
+ **Git Bash on Windows / GNU/Linux / macOS:**
```bash
- ssh-keygen -t rsa -C "GitLab" -b 4096
+ ssh-keygen -t rsa -C "your.email@example.com" -b 4096
```
**Windows:**
- On Windows you will need to download
+ Alternatively on Windows you can download
[PuttyGen](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html)
- and follow this [documentation article][winputty] to generate a SSH key pair.
+ and follow [this documentation article][winputty] to generate a SSH key pair.
-1. Next, you will be prompted to input a file path to save your key pair to.
+1. Next, you will be prompted to input a file path to save your SSH key pair to.
If you don't already have an SSH key pair use the suggested path by pressing
- enter. Using the suggested path will allow your SSH client
- to automatically use the key pair with no additional configuration.
+ enter. Using the suggested path will normally allow your SSH client
+ to automatically use the SSH key pair with no additional configuration.
- If you already have a key pair with the suggested file path, you will need
- to input a new file path and declare what host this key pair will be used
- for in your `.ssh/config` file, see **Working with non-default SSH key pair paths**
+ If you already have a SSH key pair with the suggested file path, you will need
+ to input a new file path and declare what host this SSH key pair will be used
+ for in your `.ssh/config` file, see [**Working with non-default SSH key pair paths**](#working-with-non-default-ssh-key-pair-paths)
for more information.
1. Once you have input a file path you will be prompted to input a password to
@@ -68,12 +74,12 @@ custom name continue onto the next step.
pressing enter.
>**Note:**
- If you want to change the password of your key, you can use `ssh-keygen -p <keyname>`.
+ If you want to change the password of your SSH key pair, you can use
+ `ssh-keygen -p <keyname>`.
-1. The next step is to copy the public key as we will need it afterwards.
+1. The next step is to copy the public SSH key as we will need it afterwards.
- To copy your public key to the clipboard, use the appropriate code for your
- operating system below:
+ To copy your public SSH key to the clipboard, use the appropriate code below:
**macOS:**
@@ -93,7 +99,7 @@ custom name continue onto the next step.
type %userprofile%\.ssh\id_rsa.pub | clip
```
- **Windows PowerShell:**
+ **Git Bash on Windows / Windows PowerShell:**
```bash
cat ~/.ssh/id_rsa.pub | clip
@@ -101,22 +107,38 @@ custom name continue onto the next step.
1. The final step is to add your public SSH key to GitLab.
- Navigate to the 'SSH Keys' tab in you 'Profile Settings'.
+ Navigate to the 'SSH Keys' tab in your 'Profile Settings'.
Paste your key in the 'Key' section and give it a relevant 'Title'.
Use an identifiable title like 'Work Laptop - Windows 7' or
'Home MacBook Pro 15'.
If you manually copied your public SSH key make sure you copied the entire
key starting with `ssh-rsa` and ending with your email.
+
+1. Optionally you can test your setup by running `ssh -T git@example.com`
+ (replacing `example.com` with your GitLab domain) and verifying that you
+ receive a `Welcome to GitLab` message.
## Working with non-default SSH key pair paths
If you used a non-default file path for your GitLab SSH key pair,
-you must configure your SSH client to find your GitLab SSH private key
-for connections to your GitLab server (perhaps gitlab.com).
+you must configure your SSH client to find your GitLab private SSH key
+for connections to your GitLab server (perhaps `gitlab.com`).
+
+For your current terminal session you can do so using the following commands
+(replacing `other_id_rsa` with your private SSH key):
-For OpenSSH clients this is configured in the `~/.ssh/config` file.
-Below are two example host configurations using their own key:
+**Git Bash on Windows / GNU/Linux / macOS:**
+
+```bash
+eval $(ssh-agent -s)
+ssh-add ~/.ssh/other_id_rsa
+```
+
+To retain these settings you'll need to save them to a configuration file.
+For OpenSSH clients this is configured in the `~/.ssh/config` file for some
+operating systems.
+Below are two example host configurations using their own SSH key:
```
# GitLab.com server
@@ -140,8 +162,8 @@ That's why it needs to uniquely map to a single user.
## Deploy keys
-Deploy keys allow read-only access to multiple projects with a single SSH
-key.
+Deploy keys allow read-only or read-write (if enabled) access to one or
+multiple projects with a single SSH key pair.
This is really useful for cloning repositories to your Continuous
Integration (CI) server. By using deploy keys, you don't have to setup a
@@ -150,7 +172,8 @@ dummy user account.
If you are a project master or owner, you can add a deploy key in the
project settings under the section 'Deploy Keys'. Press the 'New Deploy
Key' button and upload a public SSH key. After this, the machine that uses
-the corresponding private key has read-only access to the project.
+the corresponding private SSH key has read-only or read-write (if enabled)
+access to the project.
You can't add the same deploy key twice with the 'New Deploy Key' option.
If you want to add the same key to another project, please enable it in the
@@ -166,6 +189,18 @@ project.
### Eclipse
-How to add your ssh key to Eclipse: https://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration
+How to add your SSH key to Eclipse: https://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration
[winputty]: https://the.earth.li/~sgtatham/putty/0.67/htmldoc/Chapter8.html#pubkey-puttygen
+
+## Troubleshooting
+
+If on Git clone you are prompted for a password like `git@gitlab.com's password:`
+something is wrong with your SSH setup.
+
+- Ensure that you generated your SSH key pair correctly and added the public SSH
+ key to your GitLab profile
+- Try manually registering your private SSH key using `ssh-agent` as documented
+ earlier in this document
+- Try to debug the connection by running `ssh -Tv git@example.com`
+ (replacing `example.com` with your GitLab domain)
diff --git a/doc/user/project/merge_requests/img/merge_when_build_succeeds_enable.png b/doc/user/project/merge_requests/img/merge_when_build_succeeds_enable.png
deleted file mode 100644
index f50a1be24f2..00000000000
--- a/doc/user/project/merge_requests/img/merge_when_build_succeeds_enable.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_when_build_succeeds_only_if_succeeds_settings.png b/doc/user/project/merge_requests/img/merge_when_build_succeeds_only_if_succeeds_settings.png
deleted file mode 100644
index ddc58ff2630..00000000000
--- a/doc/user/project/merge_requests/img/merge_when_build_succeeds_only_if_succeeds_settings.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_when_build_succeeds_status.png b/doc/user/project/merge_requests/img/merge_when_build_succeeds_status.png
deleted file mode 100644
index a98636ee359..00000000000
--- a/doc/user/project/merge_requests/img/merge_when_build_succeeds_status.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_enable.png b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_enable.png
new file mode 100644
index 00000000000..33f5a4a7a02
--- /dev/null
+++ b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_enable.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_when_build_succeeds_only_if_succeeds_msg.png b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_msg.png
index c43f76b058c..c43f76b058c 100644
--- a/doc/user/project/merge_requests/img/merge_when_build_succeeds_only_if_succeeds_msg.png
+++ b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_msg.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_settings.png b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_settings.png
new file mode 100644
index 00000000000..9629ed99838
--- /dev/null
+++ b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_settings.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_status.png b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_status.png
new file mode 100644
index 00000000000..d0691437c65
--- /dev/null
+++ b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_status.png
Binary files differ
diff --git a/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md b/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md
index c63a408505f..bdd7d0022e6 100644
--- a/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md
+++ b/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md
@@ -5,7 +5,7 @@ more CI jobs running, you can set it to be merged automatically when the
jobs pipeline succeeds. This way, you don't have to wait for the jobs to
finish and remember to merge the request manually.
-![Enable](img/merge_when_build_succeeds_enable.png)
+![Enable](img/merge_when_pipeline_succeeds_enable.png)
When you hit the "Merge When Pipeline Succeeds" button, the status of the merge
request will be updated to represent the impending merge. If you cannot wait
@@ -16,7 +16,7 @@ Both team developers and the author of the merge request have the option to
cancel the automatic merge if they find a reason why it shouldn't be merged
after all.
-![Status](img/merge_when_build_succeeds_status.png)
+![Status](img/merge_when_pipeline_succeeds_status.png)
When the pipeline succeeds, the merge request will automatically be merged.
When the pipeline fails, the author gets a chance to retry any failed jobs,
@@ -32,15 +32,16 @@ changes to be reviewed.
> **Note:**
You need to have jobs configured to enable this feature.
-You can prevent merge requests from being merged if their pipeline did not succeed.
+You can prevent merge requests from being merged if their pipeline did not succeed
+or if there are discussions to be resolved.
Navigate to your project's settings page, select the
**Only allow merge requests to be merged if the pipeline succeeds** check box and
hit **Save** for the changes to take effect.
-![Only allow merge if pipeline succeeds settings](img/merge_when_build_succeeds_only_if_succeeds_settings.png)
+![Only allow merge if pipeline succeeds settings](img/merge_when_pipeline_succeeds_only_if_succeeds_settings.png)
From now on, every time the pipeline fails you will not be able to merge the
merge request from the UI, until you make all relevant jobs pass.
-![Only allow merge if pipeline succeeds message](img/merge_when_build_succeeds_only_if_succeeds_msg.png)
+![Only allow merge if pipeline succeeds message](img/merge_when_pipeline_succeeds_only_if_succeeds_msg.png)
diff --git a/doc/user/project/pages/getting_started_part_four.md b/doc/user/project/pages/getting_started_part_four.md
new file mode 100644
index 00000000000..6edf99239ea
--- /dev/null
+++ b/doc/user/project/pages/getting_started_part_four.md
@@ -0,0 +1,382 @@
+# GitLab Pages from A to Z: Part 4
+
+- [Part 1: Static sites and GitLab Pages domains](getting_started_part_one.md)
+- [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md)
+- [Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](getting_started_part_three.md)
+- **Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages**
+
+## Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages
+
+[GitLab CI](https://about.gitlab.com/gitlab-ci/) serves
+numerous purposes, to build, test, and deploy your app
+from GitLab through
+[Continuous Integration, Continuous Delivery, and Continuous Deployment](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/)
+methods. You will need it to build your website with GitLab Pages,
+and deploy it to the Pages server.
+
+What this file actually does is telling the
+[GitLab Runner](https://docs.gitlab.com/runner/) to run scripts
+as you would do from the command line. The Runner acts as your
+terminal. GitLab CI tells the Runner which commands to run.
+Both are built-in in GitLab, and you don't need to set up
+anything for them to work.
+
+Explaining [every detail of GitLab CI](https://docs.gitlab.com/ce/ci/yaml/README.html)
+and GitLab Runner is out of the scope of this guide, but we'll
+need to understand just a few things to be able to write our own
+`.gitlab-ci.yml` or tweak an existing one. It's an
+[Yaml](http://docs.ansible.com/ansible/YAMLSyntax.html) file,
+with its own syntax. You can always check your CI syntax with
+the [GitLab CI Lint Tool](https://gitlab.com/ci/lint).
+
+**Practical Example:**
+
+Let's consider you have a [Jekyll](https://jekyllrb.com/) site.
+To build it locally, you would open your terminal, and run `jekyll build`.
+Of course, before building it, you had to install Jekyll in your computer.
+For that, you had to open your terminal and run `gem install jekyll`.
+Right? GitLab CI + GitLab Runner do the same thing. But you need to
+write in the `.gitlab-ci.yml` the script you want to run so
+GitLab Runner will do it for you. It looks more complicated then it
+is. What you need to tell the Runner:
+
+```
+$ gem install jekyll
+$ jekyll build
+```
+
+### Script
+
+To transpose this script to Yaml, it would be like this:
+
+```yaml
+script:
+ - gem install jekyll
+ - jekyll build
+```
+
+### Job
+
+So far so good. Now, each `script`, in GitLab is organized by
+a `job`, which is a bunch of scripts and settings you want to
+apply to that specific task.
+
+```yaml
+job:
+ script:
+ - gem install jekyll
+ - jekyll build
+```
+
+For GitLab Pages, this `job` has a specific name, called `pages`,
+which tells the Runner you want that task to deploy your website
+with GitLab Pages:
+
+```yaml
+pages:
+ script:
+ - gem install jekyll
+ - jekyll build
+```
+
+### The `public` directory
+
+We also need to tell Jekyll where do you want the website to build,
+and GitLab Pages will only consider files in a directory called `public`.
+To do that with Jekyll, we need to add a flag specifying the
+[destination (`-d`)](https://jekyllrb.com/docs/usage/) of the
+built website: `jekyll build -d public`. Of course, we need
+to tell this to our Runner:
+
+```yaml
+pages:
+ script:
+ - gem install jekyll
+ - jekyll build -d public
+```
+
+### Artifacts
+
+We also need to tell the Runner that this _job_ generates
+_artifacts_, which is the site built by Jekyll.
+Where are these artifacts stored? In the `public` directory:
+
+```yaml
+pages:
+ script:
+ - gem install jekyll
+ - jekyll build -d public
+ artifacts:
+ paths:
+ - public
+```
+
+The script above would be enough to build your Jekyll
+site with GitLab Pages. But, from Jekyll 3.4.0 on, its default
+template originated by `jekyll new project` requires
+[Bundler](http://bundler.io/) to install Jekyll dependencies
+and the default theme. To adjust our script to meet these new
+requirements, we only need to install and build Jekyll with Bundler:
+
+```yaml
+pages:
+ script:
+ - bundle install
+ - bundle exec jekyll build -d public
+ artifacts:
+ paths:
+ - public
+```
+
+That's it! A `.gitlab-ci.yml` with the content above would deploy
+your Jekyll 3.4.0 site with GitLab Pages. This is the minimum
+configuration for our example. On the steps below, we'll refine
+the script by adding extra options to our GitLab CI.
+
+### Image
+
+At this point, you probably ask yourself: "okay, but to install Jekyll
+I need Ruby. Where is Ruby on that script?". The answer is simple: the
+first thing GitLab Runner will look for in your `.gitlab-ci.yml` is a
+[Docker](https://www.docker.com/) image specifying what do you need in
+your container to run that script:
+
+```yaml
+image: ruby:2.3
+
+pages:
+ script:
+ - bundle install
+ - bundle exec jekyll build -d public
+ artifacts:
+ paths:
+ - public
+```
+
+In this case, you're telling the Runner to pull this image, which
+contains Ruby 2.3 as part of its file system. When you don't specify
+this image in your configuration, the Runner will use a default
+image, which is Ruby 2.1.
+
+If your SSG needs [NodeJS](https://nodejs.org/) to build, you'll
+need to specify which image you want to use, and this image should
+contain NodeJS as part of its file system. E.g., for a
+[Hexo](https://gitlab.com/pages/hexo) site, you can use `image: node:4.2.2`.
+
+>**Note:**
+We're not trying to explain what a Docker image is,
+we just need to introduce the concept with a minimum viable
+explanation. To know more about Docker images, please visit
+their website or take a look at a
+[summarized explanation](http://paislee.io/how-to-automate-docker-deployments/) here.
+
+Let's go a little further.
+
+### Branching
+
+If you use GitLab as a version control platform, you will have your
+branching strategy to work on your project. Meaning, you will have
+other branches in your project, but you'll want only pushes to the
+default branch (usually `master`) to be deployed to your website.
+To do that, we need to add another line to our CI, telling the Runner
+to only perform that _job_ called `pages` on the `master` branch `only`:
+
+```yaml
+image: ruby:2.3
+
+pages:
+ script:
+ - bundle install
+ - bundle exec jekyll build -d public
+ artifacts:
+ paths:
+ - public
+ only:
+ - master
+```
+
+### Stages
+
+Another interesting concept to keep in mind are build stages.
+Your web app can pass through a lot of tests and other tasks
+until it's deployed to staging or production environments.
+There are three default stages on GitLab CI: build, test,
+and deploy. To specify which stage your _job_ is running,
+simply add another line to your CI:
+
+```yaml
+image: ruby:2.3
+
+pages:
+ stage: deploy
+ script:
+ - bundle install
+ - bundle exec jekyll build -d public
+ artifacts:
+ paths:
+ - public
+ only:
+ - master
+```
+
+You might ask yourself: "why should I bother with stages
+at all?" Well, let's say you want to be able to test your
+script and check the built site before deploying your site
+to production. You want to run the test exactly as your
+script will do when you push to `master`. It's simple,
+let's add another task (_job_) to our CI, telling it to
+test every push to other branches, `except` the `master` branch:
+
+```yaml
+image: ruby:2.3
+
+pages:
+ stage: deploy
+ script:
+ - bundle install
+ - bundle exec jekyll build -d public
+ artifacts:
+ paths:
+ - public
+ only:
+ - master
+
+test:
+ stage: test
+ script:
+ - bundle install
+ - bundle exec jekyll build -d test
+ artifacts:
+ paths:
+ - test
+ except:
+ - master
+```
+
+The `test` job is running on the stage `test`, Jekyll
+will build the site in a directory called `test`, and
+this job will affect all the branches except `master`.
+
+The best benefit of applying _stages_ to different
+_jobs_ is that every job in the same stage builds in
+parallel. So, if your web app needs more than one test
+before being deployed, you can run all your test at the
+same time, it's not necessary to wait one test to finish
+to run the other. Of course, this is just a brief
+introduction of GitLab CI and GitLab Runner, which are
+tools much more powerful than that. This is what you
+need to be able to create and tweak your builds for
+your GitLab Pages site.
+
+### Before Script
+
+To avoid running the same script multiple times across
+your _jobs_, you can add the parameter `before_script`,
+in which you specify which commands you want to run for
+every single _job_. In our example, notice that we run
+`bundle install` for both jobs, `pages` and `test`.
+We don't need to repeat it:
+
+```yaml
+image: ruby:2.3
+
+before_script:
+ - bundle install
+
+pages:
+ stage: deploy
+ script:
+ - bundle exec jekyll build -d public
+ artifacts:
+ paths:
+ - public
+ only:
+ - master
+
+test:
+ stage: test
+ script:
+ - bundle exec jekyll build -d test
+ artifacts:
+ paths:
+ - test
+ except:
+ - master
+```
+
+### Caching Dependencies
+
+If you want to cache the installation files for your
+projects dependencies, for building faster, you can
+use the parameter `cache`. For this example, we'll
+cache Jekyll dependencies in a `vendor` directory
+when we run `bundle install`:
+
+```yaml
+image: ruby:2.3
+
+cache:
+ paths:
+ - vendor/
+
+before_script:
+ - bundle install --path vendor
+
+pages:
+ stage: deploy
+ script:
+ - bundle exec jekyll build -d public
+ artifacts:
+ paths:
+ - public
+ only:
+ - master
+
+test:
+ stage: test
+ script:
+ - bundle exec jekyll build -d test
+ artifacts:
+ paths:
+ - test
+ except:
+ - master
+```
+
+For this specific case, we need to exclude `/vendor`
+from Jekyll `_config.yml` file, otherwise Jekyll will
+understand it as a regular directory to build
+together with the site:
+
+```yml
+exclude:
+ - vendor
+```
+
+There we go! Now our GitLab CI not only builds our website,
+but also **continuously test** pushes to feature-branches,
+**caches** dependencies installed with Bundler, and
+**continuously deploy** every push to the `master` branch.
+
+## Advanced GitLab CI for GitLab Pages
+
+What you can do with GitLab CI is pretty much up to your
+creativity. Once you get used to it, you start creating
+awesome scripts that automate most of tasks you'd do
+manually in the past. Read through the
+[documentation of GitLab CI](https://docs.gitlab.com/ce/ci/yaml/README.html)
+to understand how to go even further on your scripts.
+
+- On this blog post, understand the concept of
+[using GitLab CI `environments` to deploy your
+web app to staging and production](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/).
+- On this post, learn [how to run jobs sequentially,
+in parallel, or build a custom pipeline](https://about.gitlab.com/2016/07/29/the-basics-of-gitlab-ci/)
+- On this blog post, we go through the process of
+[pulling specific directories from different projects](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/)
+to deploy this website you're looking at, docs.gitlab.com.
+- On this blog post, we teach you [how to use GitLab Pages to produce a code coverage report](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/).
+
+|||
+|:--|--:|
+|[**← Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates**](getting_started_part_three.md)||
diff --git a/doc/user/project/pages/getting_started_part_one.md b/doc/user/project/pages/getting_started_part_one.md
new file mode 100644
index 00000000000..582a4afbab4
--- /dev/null
+++ b/doc/user/project/pages/getting_started_part_one.md
@@ -0,0 +1,106 @@
+# GitLab Pages from A to Z: Part 1
+
+- **Part 1: Static sites and GitLab Pages domains**
+- [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md)
+- [Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](getting_started_part_three.md)
+- [Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md)
+
+## GitLab Pages form A to Z
+
+This is a comprehensive guide, made for those who want to
+publish a website with GitLab Pages but aren't familiar with
+the entire process involved.
+
+This [first part](#what-you-need-to-know-before-getting-started) of this series will present you to the concepts of
+static sites, and go over how the default Pages domains work.
+
+The [second part](getting_started_part_two.md) covers how to get started with GitLab Pages: deploy
+a website from a forked project or create a new one from scratch.
+
+The [third part](getting_started_part_three.md) will show you how to set up a custom domain or subdomain
+to your site already deployed.
+
+The [fourth part](getting_started_part_four.md) will show you how to create and tweak GitLab CI for
+GitLab Pages.
+
+To **enable** GitLab Pages for GitLab CE (Community Edition)
+and GitLab EE (Enterprise Edition), please read the
+[admin documentation](https://docs.gitlab.com/ce/administration/pages/index.html),
+and/or watch this [video tutorial](https://youtu.be/dD8c7WNcc6s).
+
+>**Note:**
+For this guide, we assume you already have GitLab Pages
+server up and running for your GitLab instance.
+
+## What you need to know before getting started
+
+Before we begin, let's understand a few concepts first.
+
+### Static sites
+
+GitLab Pages only supports static websites, meaning,
+your output files must be HTML, CSS, and JavaScript only.
+
+To create your static site, you can either hardcode in HTML,
+CSS, and JS, or use a [Static Site Generator (SSG)](https://www.staticgen.com/)
+to simplify your code and build the static site for you,
+which is highly recommendable and much faster than hardcoding.
+
+#### Further Reading
+
+- Read through this technical overview on [Static versus Dynamic Websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/)
+- Understand [how modern Static Site Generators work](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/) and what you can add to your static site
+- You can use [any SSG with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/)
+- Fork an [example project](https://gitlab.com/pages) to build your website based upon
+
+### GitLab Pages domain
+
+If you set up a GitLab Pages project on GitLab.com,
+it will automatically be accessible under a
+[subdomain of `namespace.pages.io`](https://docs.gitlab.com/ce/user/project/pages/).
+The `namespace` is defined by your username on GitLab.com,
+or the group name you created this project under.
+
+>**Note:**
+If you use your own GitLab instance to deploy your
+site with GitLab Pages, check with your sysadmin what's your
+Pages wildcard domain. This guide is valid for any GitLab instance,
+you just need to replace Pages wildcard domain on GitLab.com
+(`*.gitlab.io`) with your own.
+
+#### Practical examples
+
+**Project Websites:**
+
+- You created a project called `blog` under your username `john`,
+therefore your project URL is `https://gitlab.com/john/blog/`.
+Once you enable GitLab Pages for this project, and build your site,
+it will be available under `https://john.gitlab.io/blog/`.
+- You created a group for all your websites called `websites`,
+and a project within this group is called `blog`. Your project
+URL is `https://gitlab.com/websites/blog/`. Once you enable
+GitLab Pages for this project, the site will live under
+`https://websites.gitlab.io/blog/`.
+
+**User and Group Websites:**
+
+- Under your username, `john`, you created a project called
+`john.gitlab.io`. Your project URL will be `https://gitlab.com/john/john.gitlab.io`.
+Once you enable GitLab Pages for your project, your website
+will be published under `https://john.gitlab.io`.
+- Under your group `websites`, you created a project called
+`websites.gitlab.io`. your project's URL will be `https://gitlab.com/websites/websites.gitlab.io`. Once you enable GitLab Pages for your project,
+your website will be published under `https://websites.gitlab.io`.
+
+**General example:**
+
+- On GitLab.com, a project site will always be available under
+`https://namespace.gitlab.io/project-name`
+- On GitLab.com, a user or group website will be available under
+`https://namespace.gitlab.io/`
+- On your GitLab instance, replace `gitlab.io` above with your
+Pages server domain. Ask your sysadmin for this information.
+
+|||
+|:--|--:|
+||[**Part 2: Quick start guide - Setting up GitLab Pages →**](getting_started_part_two.md)|
diff --git a/doc/user/project/pages/getting_started_part_three.md b/doc/user/project/pages/getting_started_part_three.md
new file mode 100644
index 00000000000..dba5fb6c17a
--- /dev/null
+++ b/doc/user/project/pages/getting_started_part_three.md
@@ -0,0 +1,189 @@
+# GitLab Pages from A to Z: Part 3
+
+- [Part 1: Static sites and GitLab Pages domains](getting_started_part_one.md)
+- [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md)
+- **Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates**
+- [Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md)
+
+## Setting Up Custom Domains - DNS Records and SSL/TLS Certificates
+
+As described in the previous part of this series, setting up GitLab Pages with custom domains, and adding SSL/TLS certificates to them, are optional features of GitLab Pages.
+
+These steps assume you've already [set your site up](getting_started_part_two.md) and and it's served under the default Pages domain `namespace.gitlab.io`, or `namespace.gitlab.io/project-name`.
+
+### DNS Records
+
+A Domain Name System (DNS) web service routes visitors to websites
+by translating domain names (such as `www.example.com`) into the
+numeric IP addresses (such as `192.0.2.1`) that computers use to
+connect to each other.
+
+A DNS record is created to point a (sub)domain to a certain location,
+which can be an IP address or another domain. In case you want to use
+GitLab Pages with your own (sub)domain, you need to access your domain's
+registrar control panel to add a DNS record pointing it back to your
+GitLab Pages site.
+
+Note that **how to** add DNS records depends on which server your domain
+is hosted on. Every control panel has its own place to do it. If you are
+not an admin of your domain, and don't have access to your registrar,
+you'll need to ask for the technical support of your hosting service
+to do it for you.
+
+To help you out, we've gathered some instructions on how to do that
+for the most popular hosting services:
+
+- [Amazon](http://docs.aws.amazon.com/gettingstarted/latest/swh/getting-started-configure-route53.html)
+- [Bluehost](https://my.bluehost.com/cgi/help/559)
+- [CloudFlare](https://support.cloudflare.com/hc/en-us/articles/200169096-How-do-I-add-A-records-)
+- [cPanel](https://documentation.cpanel.net/display/ALD/Edit+DNS+Zone)
+- [DreamHost](https://help.dreamhost.com/hc/en-us/articles/215414867-How-do-I-add-custom-DNS-records-)
+- [Go Daddy](https://www.godaddy.com/help/add-an-a-record-19238)
+- [Hostgator](http://support.hostgator.com/articles/changing-dns-records)
+- [Inmotion hosting](https://my.bluehost.com/cgi/help/559)
+- [Media Temple](https://mediatemple.net/community/products/dv/204403794/how-can-i-change-the-dns-records-for-my-domain)
+- [Microsoft](https://msdn.microsoft.com/en-us/library/bb727018.aspx)
+
+If your hosting service is not listed above, you can just try to
+search the web for "how to add dns record on <my hosting service>".
+
+#### DNS A record
+
+In case you want to point a root domain (`example.com`) to your
+GitLab Pages site, deployed to `namespace.gitlab.io`, you need to
+log into your domain's admin control panel and add a DNS `A` record
+pointing your domain to Pages' server IP address. For projects on
+GitLab.com, this IP is `104.208.235.32`. For projects leaving in
+other GitLab instances (CE or EE), please contact your sysadmin
+asking for this information (which IP address is Pages server
+running on your instance).
+
+**Practical Example:**
+
+![DNS A record pointing to GitLab.com Pages server](img/dns_a_record_example.png)
+
+#### DNS CNAME record
+
+In case you want to point a subdomain (`hello-world.example.com`)
+to your GitLab Pages site initially deployed to `namespace.gitlab.io`,
+you need to log into your domain's admin control panel and add a DNS
+`CNAME` record pointing your subdomain to your website URL
+(`namespace.gitlab.io`) address.
+
+Notice that, despite it's a user or project website, the `CNAME`
+should point to your Pages domain (`namespace.gitlab.io`),
+without any `/project-name`.
+
+**Practical Example:**
+
+![DNS CNAME record pointing to GitLab.com project](img/dns_cname_record_example.png)
+
+#### TL;DR
+
+| From | DNS Record | To |
+| ---- | ---------- | -- |
+| domain.com | A | 104.208.235.32 |
+| subdomain.domain.com | CNAME | namespace.gitlab.io |
+
+> **Notes**:
+>
+> - **Do not** use a CNAME record if you want to point your
+`domain.com` to your GitLab Pages site. Use an `A` record instead.
+> - **Do not** add any special chars after the default Pages
+domain. E.g., **do not** point your `subdomain.domain.com` to
+`namespace.gitlab.io.` or `namespace.gitlab.io/`.
+
+### SSL/TLS Certificates
+
+Every GitLab Pages project on GitLab.com will be available under
+HTTPS for the default Pages domain (`*.gitlab.io`). Once you set
+up your Pages project with your custom (sub)domain, if you want
+it secured by HTTPS, you will have to issue a certificate for that
+(sub)domain and install it on your project.
+
+>**Note:**
+Certificates are NOT required to add to your custom
+(sub)domain on your GitLab Pages project, though they are
+highly recommendable.
+
+The importance of having any website securely served under HTTPS
+is explained on the introductory section of the blog post
+[Secure GitLab Pages with StartSSL](https://about.gitlab.com/2016/06/24/secure-gitlab-pages-with-startssl/#https-a-quick-overview).
+
+The reason why certificates are so important is that they encrypt
+the connection between the **client** (you, me, your visitors)
+and the **server** (where you site lives), through a keychain of
+authentications and validations.
+
+### Issuing Certificates
+
+GitLab Pages accepts [PEM](https://support.quovadisglobal.com/kb/a37/what-is-pem-format.aspx) certificates issued by
+[Certificate Authorities (CA)](https://en.wikipedia.org/wiki/Certificate_authority)
+and self-signed certificates. Of course,
+[you'd rather issue a certificate than generate a self-signed](https://en.wikipedia.org/wiki/Self-signed_certificate),
+for security reasons and for having browsers trusting your
+site's certificate.
+
+There are several different kinds of certificates, each one
+with certain security level. A static personal website will
+not require the same security level as an online banking web app,
+for instance. There are a couple Certificate Authorities that
+offer free certificates, aiming to make the internet more secure
+to everyone. The most popular is [Let's Encrypt](https://letsencrypt.org/),
+which issues certificates trusted by most of browsers, it's open
+source, and free to use. Please read through this tutorial to
+understand [how to secure your GitLab Pages website with Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/).
+
+With the same popularity, there are [certificates issued by CloudFlare](https://www.cloudflare.com/ssl/),
+which also offers a [free CDN service](https://blog.cloudflare.com/cloudflares-free-cdn-and-you/).
+Their certs are valid up to 15 years. Read through the tutorial on
+[how to add a CloudFlare Certificate to your GitLab Pages website](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/).
+
+### Adding certificates to your project
+
+Regardless the CA you choose, the steps to add your certificate to
+your Pages project are the same.
+
+#### What do you need
+
+1. A PEM certificate
+1. An intermediate certificate
+1. A public key
+
+![Pages project - adding certificates](img/add_certificate_to_pages.png)
+
+These fields are found under your **Project**'s **Settings** > **Pages** > **New Domain**.
+
+#### What's what?
+
+- A PEM certificate is the certificate generated by the CA,
+which needs to be added to the field **Certificate (PEM)**.
+- An [intermediate certificate](https://en.wikipedia.org/wiki/Intermediate_certificate_authority) (aka "root certificate") is
+the part of the encryption keychain that identifies the CA.
+Usually it's combined with the PEM certificate, but there are
+some cases in which you need to add them manually.
+[CloudFlare certs](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/)
+are one of these cases.
+- A public key is an encrypted key which validates
+your PEM against your domain.
+
+#### Now what?
+
+Now that you hopefully understand why you need all
+of this, it's simple:
+
+- Your PEM certificate needs to be added to the first field
+- If your certificate is missing its intermediate, copy
+and paste the root certificate (usually available from your CA website)
+and paste it in the [same field as your PEM certificate](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/),
+just jumping a line between them.
+- Copy your public key and paste it in the last field
+
+>**Note:**
+**Do not** open certificates or encryption keys in
+regular text editors. Always use code editors (such as
+Sublime Text, Atom, Dreamweaver, Brackets, etc).
+
+|||
+|:--|--:|
+|[**← Part 2: Quick start guide - Setting up GitLab Pages**](getting_started_part_two.md)|[**Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages →**](getting_started_part_four.md)|
diff --git a/doc/user/project/pages/getting_started_part_two.md b/doc/user/project/pages/getting_started_part_two.md
new file mode 100644
index 00000000000..d0e2c467fee
--- /dev/null
+++ b/doc/user/project/pages/getting_started_part_two.md
@@ -0,0 +1,154 @@
+# GitLab Pages from A to Z: Part 2
+
+- [Part 1: Static sites and GitLab Pages domains](getting_started_part_one.md)
+- **Part 2: Quick start guide - Setting up GitLab Pages**
+- [Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](getting_started_part_three.md)
+- [Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md)
+
+## Setting up GitLab Pages
+
+For a complete step-by-step tutorial, please read the
+blog post [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/). The following sections will explain
+what do you need and why do you need them.
+
+## What you need to get started
+
+1. A project
+1. A configuration file (`.gitlab-ci.yml`) to deploy your site
+1. A specific `job` called `pages` in the configuration file
+that will make GitLab aware that you are deploying a GitLab Pages website
+
+Optional Features:
+
+1. A custom domain or subdomain
+1. A DNS pointing your (sub)domain to your Pages site
+ 1. **Optional**: an SSL/TLS certificate so your custom
+ domain is accessible under HTTPS.
+
+The optional settings, custom domain, DNS records, and SSL/TLS certificates, are described in [Part 3](getting_started_part_three.md)).
+
+## Project
+
+Your GitLab Pages project is a regular project created the
+same way you do for the other ones. To get started with GitLab Pages, you have two ways:
+
+- Fork one of the templates from Page Examples, or
+- Create a new project from scratch
+
+Let's go over both options.
+
+### Fork a project to get started from
+
+To make things easy for you, we've created this
+[group](https://gitlab.com/pages) of default projects
+containing the most popular SSGs templates.
+
+Watch the [video tutorial](https://youtu.be/TWqh9MtT4Bg) we've
+created for the steps below.
+
+1. Choose your SSG template
+1. Fork a project from the [Pages group](https://gitlab.com/pages)
+1. Remove the fork relationship by navigating to your **Project**'s **Settings** > **Edit Project**
+
+ ![remove fork relashionship](img/remove_fork_relashionship.png)
+
+1. Enable Shared Runners for your fork: navigate to your **Project**'s **Settings** > **CI/CD Pipelines**
+1. Trigger a build (push a change to any file)
+1. As soon as the build passes, your website will have been deployed with GitLab Pages. Your website URL will be available under your **Project**'s **Settings** > **Pages**
+
+To turn a **project website** forked from the Pages group into a **user/group** website, you'll need to:
+
+- Rename it to `namespace.gitlab.io`: navigate to **Project**'s **Settings** > **Edit Project** > **Rename repository**
+- Adjust your SSG's [base URL](#urls-and-baseurls) to from `"project-name"` to `""`. This setting will be at a different place for each SSG, as each of them have their own structure and file tree. Most likelly, it will be in the SSG's config file.
+
+> **Notes:**
+>
+>1. Why do I need to remove the fork relationship?
+>
+> Unless you want to contribute to the original project,
+you won't need it connected to the upstream. A
+[fork](https://about.gitlab.com/2016/12/01/how-to-keep-your-fork-up-to-date-with-its-origin/#fork)
+is useful for submitting merge requests to the upstream.
+>
+> 2. Why do I need to enable Shared Runners?
+>
+> Shared Runners will run the script set by your GitLab CI
+configuration file. They're enabled by default to new projects,
+but not to forks.
+
+### Create a project from scratch
+
+1. From your **Project**'s **[Dashboard](https://gitlab.com/dashboard/projects)**,
+click **New project**, and name it considering the
+[practical examples](getting_started_part_one.md#practical-examples).
+1. Clone it to your local computer, add your website
+files to your project, add, commit and push to GitLab.
+1. From the your **Project**'s page, click **Set up CI**:
+
+ ![setup GitLab CI](img/setup_ci.png)
+
+1. Choose one of the templates from the dropbox menu.
+Pick up the template corresponding to the SSG you're using (or plain HTML).
+
+ ![gitlab-ci templates](img/choose_ci_template.png)
+
+Once you have both site files and `.gitlab-ci.yml` in your project's
+root, GitLab CI will build your site and deploy it with Pages.
+Once the first build passes, you see your site is live by
+navigating to your **Project**'s **Settings** > **Pages**,
+where you'll find its default URL.
+
+> **Notes:**
+>
+> - GitLab Pages [supports any SSG](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/), but,
+if you don't find yours among the templates, you'll need
+to configure your own `.gitlab-ci.yml`. Do do that, please
+read through the article [Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md). New SSGs are very welcome among
+the [example projects](https://gitlab.com/pages). If you set
+up a new one, please
+[contribute](https://gitlab.com/pages/pages.gitlab.io/blob/master/CONTRIBUTING.md)
+to our examples.
+>
+> - The second step _"Clone it to your local computer"_, can be done
+differently, achieving the same results: instead of cloning the bare
+repository to you local computer and moving your site files into it,
+you can run `git init` in your local website directory, add the
+remote URL: `git remote add origin git@gitlab.com:namespace/project-name.git`,
+then add, commit, and push.
+
+### URLs and Baseurls
+
+Every Static Site Generator (SSG) default configuration expects
+to find your website under a (sub)domain (`example.com`), not
+in a subdirectory of that domain (`example.com/subdir`). Therefore,
+whenever you publish a project website (`namespace.gitlab.io/project-name`),
+you'll have to look for this configuration (base URL) on your SSG's
+documentation and set it up to reflect this pattern.
+
+For example, for a Jekyll site, the `baseurl` is defined in the Jekyll
+configuration file, `_config.yml`. If your website URL is
+`https://john.gitlab.io/blog/`, you need to add this line to `_config.yml`:
+
+```yaml
+baseurl: "/blog"
+```
+
+On the contrary, if you deploy your website after forking one of
+our [default examples](https://gitlab.com/pages), the baseurl will
+already be configured this way, as all examples there are project
+websites. If you decide to make yours a user or group website, you'll
+have to remove this configuration from your project. For the Jekyll
+example we've just mentioned, you'd have to change Jekyll's `_config.yml` to:
+
+```yaml
+baseurl: ""
+```
+
+### Custom Domains
+
+GitLab Pages supports custom domains and subdomains, served under HTTPS or HTTPS.
+Please check the [next part](getting_started_part_three.md) of this series for an overview.
+
+|||
+|:--|--:|
+|[**← Part 1: Static sites, domains, DNS records, and SSL/TLS certificates**](getting_started_part_one.md)|[**Setting Up Custom Domains - DNS Records and SSL/TLS Certificates →**](getting_started_part_three.md)|
diff --git a/doc/pages/img/add_certificate_to_pages.png b/doc/user/project/pages/img/add_certificate_to_pages.png
index d92a981dc60..d92a981dc60 100644
--- a/doc/pages/img/add_certificate_to_pages.png
+++ b/doc/user/project/pages/img/add_certificate_to_pages.png
Binary files differ
diff --git a/doc/pages/img/choose_ci_template.png b/doc/user/project/pages/img/choose_ci_template.png
index 0697542abc8..0697542abc8 100644
--- a/doc/pages/img/choose_ci_template.png
+++ b/doc/user/project/pages/img/choose_ci_template.png
Binary files differ
diff --git a/doc/pages/img/dns_a_record_example.png b/doc/user/project/pages/img/dns_a_record_example.png
index b923730388a..b923730388a 100644
--- a/doc/pages/img/dns_a_record_example.png
+++ b/doc/user/project/pages/img/dns_a_record_example.png
Binary files differ
diff --git a/doc/user/project/pages/img/dns_cname_record_example.png b/doc/user/project/pages/img/dns_cname_record_example.png
new file mode 100644
index 00000000000..43d1a838544
--- /dev/null
+++ b/doc/user/project/pages/img/dns_cname_record_example.png
Binary files differ
diff --git a/doc/user/project/pages/img/pages_create_project.png b/doc/user/project/pages/img/pages_create_project.png
index a936d8e5dbd..be47f9d2a44 100644
--- a/doc/user/project/pages/img/pages_create_project.png
+++ b/doc/user/project/pages/img/pages_create_project.png
Binary files differ
diff --git a/doc/user/project/pages/img/pages_create_user_page.png b/doc/user/project/pages/img/pages_create_user_page.png
index 3f615d3757d..2f1a19ae424 100644
--- a/doc/user/project/pages/img/pages_create_user_page.png
+++ b/doc/user/project/pages/img/pages_create_user_page.png
Binary files differ
diff --git a/doc/user/project/pages/img/pages_dns_details.png b/doc/user/project/pages/img/pages_dns_details.png
index 8d34f3b7f38..274e98fde4d 100644
--- a/doc/user/project/pages/img/pages_dns_details.png
+++ b/doc/user/project/pages/img/pages_dns_details.png
Binary files differ
diff --git a/doc/user/project/pages/img/pages_multiple_domains.png b/doc/user/project/pages/img/pages_multiple_domains.png
index 2bc7cee07a6..6bc92db6b41 100644
--- a/doc/user/project/pages/img/pages_multiple_domains.png
+++ b/doc/user/project/pages/img/pages_multiple_domains.png
Binary files differ
diff --git a/doc/user/project/pages/img/pages_new_domain_button.png b/doc/user/project/pages/img/pages_new_domain_button.png
index c3640133bb2..cd59defa006 100644
--- a/doc/user/project/pages/img/pages_new_domain_button.png
+++ b/doc/user/project/pages/img/pages_new_domain_button.png
Binary files differ
diff --git a/doc/user/project/pages/img/pages_remove.png b/doc/user/project/pages/img/pages_remove.png
index adbfb654877..b064310380e 100644
--- a/doc/user/project/pages/img/pages_remove.png
+++ b/doc/user/project/pages/img/pages_remove.png
Binary files differ
diff --git a/doc/user/project/pages/img/pages_upload_cert.png b/doc/user/project/pages/img/pages_upload_cert.png
index 06d85ab1971..dc431ea3fef 100644
--- a/doc/user/project/pages/img/pages_upload_cert.png
+++ b/doc/user/project/pages/img/pages_upload_cert.png
Binary files differ
diff --git a/doc/user/project/pages/img/remove_fork_relashionship.png b/doc/user/project/pages/img/remove_fork_relashionship.png
new file mode 100644
index 00000000000..67c45491f08
--- /dev/null
+++ b/doc/user/project/pages/img/remove_fork_relashionship.png
Binary files differ
diff --git a/doc/user/project/pages/img/setup_ci.png b/doc/user/project/pages/img/setup_ci.png
new file mode 100644
index 00000000000..214c1cc668f
--- /dev/null
+++ b/doc/user/project/pages/img/setup_ci.png
Binary files differ
diff --git a/doc/user/project/pages/index.md b/doc/user/project/pages/index.md
index 276fbd26835..1366756d593 100644
--- a/doc/user/project/pages/index.md
+++ b/doc/user/project/pages/index.md
@@ -1,449 +1,48 @@
-# GitLab Pages
-
-> **Notes:**
-> - This feature was [introduced][ee-80] in GitLab EE 8.3.
-> - Custom CNAMEs with TLS support were [introduced][ee-173] in GitLab EE 8.5.
-> - GitLab Pages [were ported][ce-14605] to Community Edition in GitLab 8.17.
-> - This document is about the user guide. To learn how to enable GitLab Pages
-> across your GitLab instance, visit the [administrator documentation](../../../administration/pages/index.md).
-
-With GitLab Pages you can host for free your static websites on GitLab.
-Combined with the power of [GitLab CI] and the help of [GitLab Runner] you can
-deploy static pages for your individual projects, your user or your group.
-
-Read [GitLab Pages on GitLab.com](#gitlab-pages-on-gitlab-com) for specific
-information, if you are using GitLab.com to host your website.
-
-Read through [All you Need to Know About GitLab Pages][pages-index-guide] for a list of all learning materials we have prepared for GitLab Pages (webpages, articles, guides, blog posts, video tutorials).
-
-## Getting started with GitLab Pages
-
-> **Note:**
-> In the rest of this document we will assume that the general domain name that
-> is used for GitLab Pages is `example.io`.
-
-In general there are two types of pages one might create:
-
-- Pages per user (`username.example.io`) or per group (`groupname.example.io`)
-- Pages per project (`username.example.io/projectname` or `groupname.example.io/projectname`)
-
-In GitLab, usernames and groupnames are unique and we often refer to them
-as namespaces. There can be only one namespace in a GitLab instance. Below you
-can see the connection between the type of GitLab Pages, what the project name
-that is created on GitLab looks like and the website URL it will be ultimately
-be served on.
-
-| Type of GitLab Pages | The name of the project created in GitLab | Website URL |
-| -------------------- | ------------ | ----------- |
-| User pages | `username.example.io` | `http(s)://username.example.io` |
-| Group pages | `groupname.example.io` | `http(s)://groupname.example.io` |
-| Project pages owned by a user | `projectname` | `http(s)://username.example.io/projectname` |
-| Project pages owned by a group | `projectname` | `http(s)://groupname.example.io/projectname`|
-
-> **Warning:**
-> There are some known [limitations](#limitations) regarding namespaces served
-> under the general domain name and HTTPS. Make sure to read that section.
-
-### GitLab Pages requirements
-
-In brief, this is what you need to upload your website in GitLab Pages:
-
-1. Find out the general domain name that is used for GitLab Pages
- (ask your administrator). This is very important, so you should first make
- sure you get that right.
-1. Create a project
-1. Push a [`.gitlab-ci.yml` file][yaml] in the root directory
- of your repository with a specific job named [`pages`][pages]
-1. Set up a GitLab Runner to build your website
-
-> **Note:**
-If [shared runners](../../../ci/runners/README.md) are enabled by your GitLab
-administrator, you should be able to use them instead of bringing your own.
-
-### User or group Pages
-
-For user and group pages, the name of the project should be specific to the
-username or groupname and the general domain name that is used for GitLab Pages.
-Head over your GitLab instance that supports GitLab Pages and create a
-repository named `username.example.io`, where `username` is your username on
-GitLab. If the first part of the project name doesn't match exactly your
-username, it won’t work, so make sure to get it right.
-
-To create a group page, the steps are the same like when creating a website for
-users. Just make sure that you are creating the project within the group's
-namespace.
-
-![Create a user-based pages project](img/pages_create_user_page.png)
-
----
-
-After you push some static content to your repository and GitLab Runner uploads
-the artifacts to GitLab CI, you will be able to access your website under
-`http(s)://username.example.io`. Keep reading to find out how.
-
->**Note:**
-If your username/groupname contains a dot, for example `foo.bar`, you will not
-be able to use the wildcard domain HTTPS, read more at [limitations](#limitations).
-
-### Project Pages
-
-GitLab Pages for projects can be created by both user and group accounts.
-The steps to create a project page for a user or a group are identical:
-
-1. Create a new project
-1. Push a [`.gitlab-ci.yml` file][yaml] in the root directory
- of your repository with a specific job named [`pages`][pages].
-1. Set up a GitLab Runner to build your website
-
-A user's project will be served under `http(s)://username.example.io/projectname`
-whereas a group's project under `http(s)://groupname.example.io/projectname`.
-
-## Quick Start
-
-Read through [GitLab Pages Quick Start Guide][pages-quick] or watch the video tutorial on
-[how to publish a website with GitLab Pages on GitLab.com from a forked project][video-pages-fork].
-
-See also [All you Need to Know About GitLab Pages][pages-index-guide] for a list with all the resources we have for GitLab Pages.
-
-### Explore the contents of `.gitlab-ci.yml`
-
-The key thing about GitLab Pages is the `.gitlab-ci.yml` file, something that
-gives you absolute control over the build process. You can actually watch your
-website being built live by following the CI job traces.
-
-> **Note:**
-> Before reading this section, make sure you familiarize yourself with GitLab CI
-> and the specific syntax of[`.gitlab-ci.yml`][yaml] by
-> following our [quick start guide].
-
-To make use of GitLab Pages, the contents of `.gitlab-ci.yml` must follow the
-rules below:
-
-1. A special job named [`pages`][pages] must be defined
-1. Any static content which will be served by GitLab Pages must be placed under
- a `public/` directory
-1. `artifacts` with a path to the `public/` directory must be defined
-
-In its simplest form, `.gitlab-ci.yml` looks like:
-
-```yaml
-pages:
- script:
- - my_commands
- artifacts:
- paths:
- - public
-```
-
-When the Runner reaches to build the `pages` job, it executes whatever is
-defined in the `script` parameter and if the job completes with a non-zero
-exit status, it then uploads the `public/` directory to GitLab Pages.
-
-The `public/` directory should contain all the static content of your website.
-Depending on how you plan to publish your website, the steps defined in the
-[`script` parameter](../../../ci/yaml/README.md#script) may differ.
-
-Be aware that Pages are by default branch/tag agnostic and their deployment
-relies solely on what you specify in `.gitlab-ci.yml`. If you don't limit the
-`pages` job with the [`only` parameter](../../../ci/yaml/README.md#only-and-except),
-whenever a new commit is pushed to whatever branch or tag, the Pages will be
-overwritten. In the example below, we limit the Pages to be deployed whenever
-a commit is pushed only on the `master` branch:
-
-```yaml
-pages:
- script:
- - my_commands
- artifacts:
- paths:
- - public
- only:
- - master
-```
-
-We then tell the Runner to treat the `public/` directory as `artifacts` and
-upload it to GitLab. And since all these parameters were all under a `pages`
-job, the contents of the `public` directory will be served by GitLab Pages.
-
-#### How `.gitlab-ci.yml` looks like when the static content is in your repository
-
-Supposedly your repository contained the following files:
-
-```
-├── index.html
-├── css
-│   └── main.css
-└── js
- └── main.js
-```
-
-Then the `.gitlab-ci.yml` example below simply moves all files from the root
-directory of the project to the `public/` directory. The `.public` workaround
-is so `cp` doesn't also copy `public/` to itself in an infinite loop:
-
-```yaml
-pages:
- script:
- - mkdir .public
- - cp -r * .public
- - mv .public public
- artifacts:
- paths:
- - public
- only:
- - master
-```
-
-#### How `.gitlab-ci.yml` looks like when using a static generator
-
-In general, GitLab Pages support any kind of [static site generator][staticgen],
-since `.gitlab-ci.yml` can be configured to run any possible command.
-
-In the root directory of your Git repository, place the source files of your
-favorite static generator. Then provide a `.gitlab-ci.yml` file which is
-specific to your static generator.
-
-The example below, uses [Jekyll] to build the static site:
-
-```yaml
-image: ruby:2.1 # the script will run in Ruby 2.1 using the Docker image ruby:2.1
-
-pages: # the build job must be named pages
- script:
- - gem install jekyll # we install jekyll
- - jekyll build -d public/ # we tell jekyll to build the site for us
- artifacts:
- paths:
- - public # this is where the site will live and the Runner uploads it in GitLab
- only:
- - master # this script is only affecting the master branch
-```
-
-Here, we used the Docker executor and in the first line we specified the base
-image against which our jobs will run.
-
-You have to make sure that the generated static files are ultimately placed
-under the `public` directory, that's why in the `script` section we run the
-`jekyll` command that jobs the website and puts all content in the `public/`
-directory. Depending on the static generator of your choice, this command will
-differ. Search in the documentation of the static generator you will use if
-there is an option to explicitly set the output directory. If there is not
-such an option, you can always add one more line under `script` to rename the
-resulting directory in `public/`.
-
-We then tell the Runner to treat the `public/` directory as `artifacts` and
-upload it to GitLab.
-
----
-
-See the [jekyll example project][pages-jekyll] to better understand how this
-works.
-
-For a list of Pages projects, see the [example projects](#example-projects) to
-get you started.
-
-#### How to set up GitLab Pages in a repository where there's also actual code
-
-Remember that GitLab Pages are by default branch/tag agnostic and their
-deployment relies solely on what you specify in `.gitlab-ci.yml`. You can limit
-the `pages` job with the [`only` parameter](../../../ci/yaml/README.md#only-and-except),
-whenever a new commit is pushed to a branch that will be used specifically for
-your pages.
-
-That way, you can have your project's code in the `master` branch and use an
-orphan branch (let's name it `pages`) that will host your static generator site.
-
-You can create a new empty branch like this:
-
-```bash
-git checkout --orphan pages
-```
-
-The first commit made on this new branch will have no parents and it will be
-the root of a new history totally disconnected from all the other branches and
-commits. Push the source files of your static generator in the `pages` branch.
-
-Below is a copy of `.gitlab-ci.yml` where the most significant line is the last
-one, specifying to execute everything in the `pages` branch:
-
-```
-image: ruby:2.1
-
-pages:
- script:
- - gem install jekyll
- - jekyll build -d public/
- artifacts:
- paths:
- - public
- only:
- - pages
-```
-
-See an example that has different files in the [`master` branch][jekyll-master]
-and the source files for Jekyll are in a [`pages` branch][jekyll-pages] which
-also includes `.gitlab-ci.yml`.
-
-[jekyll-master]: https://gitlab.com/pages/jekyll-branched/tree/master
-[jekyll-pages]: https://gitlab.com/pages/jekyll-branched/tree/pages
-
-## Next steps
-
-So you have successfully deployed your website, congratulations! Let's check
-what more you can do with GitLab Pages.
-
-### Example projects
-
-Below is a list of example projects for GitLab Pages with a plain HTML website
-or various static site generators. Contributions are very welcome.
-
-- [Plain HTML](https://gitlab.com/pages/plain-html)
-- [Jekyll](https://gitlab.com/pages/jekyll)
-- [Hugo](https://gitlab.com/pages/hugo)
-- [Middleman](https://gitlab.com/pages/middleman)
-- [Hexo](https://gitlab.com/pages/hexo)
-- [Brunch](https://gitlab.com/pages/brunch)
-- [Metalsmith](https://gitlab.com/pages/metalsmith)
-- [Harp](https://gitlab.com/pages/harp)
-
-Visit the GitLab Pages group for a full list of example projects:
-<https://gitlab.com/groups/pages>.
-
-### Add a custom domain to your Pages website
-
-If this setting is enabled by your GitLab administrator, you should be able to
-see the **New Domain** button when visiting your project's settings through the
-gear icon in the top right and then navigating to **Pages**.
-
-![New domain button](img/pages_new_domain_button.png)
-
----
-
-You can add multiple domains pointing to your website hosted under GitLab.
-Once the domain is added, you can see it listed under the **Domains** section.
-
-![Pages multiple domains](img/pages_multiple_domains.png)
-
----
-
-As a last step, you need to configure your DNS and add a CNAME pointing to your
-user/group page. Click on the **Details** button of a domain for further
-instructions.
-
-![Pages DNS details](img/pages_dns_details.png)
-
----
-
->**Note:**
-Currently there is support only for custom domains on per-project basis. That
-means that if you add a custom domain (`example.com`) for your user website
-(`username.example.io`), a project that is served under `username.example.io/foo`,
-will not be accessible under `example.com/foo`.
-
-### Secure your custom domain website with TLS
-
-When you add a new custom domain, you also have the chance to add a TLS
-certificate. If this setting is enabled by your GitLab administrator, you
-should be able to see the option to upload the public certificate and the
-private key when adding a new domain.
-
-![Pages upload cert](img/pages_upload_cert.png)
-
-### Custom error codes pages
-
-You can provide your own 403 and 404 error pages by creating the `403.html` and
-`404.html` files respectively in the root directory of the `public/` directory
-that will be included in the artifacts. Usually this is the root directory of
-your project, but that may differ depending on your static generator
-configuration.
-
-If the case of `404.html`, there are different scenarios. For example:
-
-- If you use project Pages (served under `/projectname/`) and try to access
- `/projectname/non/exsiting_file`, GitLab Pages will try to serve first
- `/projectname/404.html`, and then `/404.html`.
-- If you use user/group Pages (served under `/`) and try to access
- `/non/existing_file` GitLab Pages will try to serve `/404.html`.
-- If you use a custom domain and try to access `/non/existing_file`, GitLab
- Pages will try to serve only `/404.html`.
-
-### Remove the contents of your pages
-
-If you ever feel the need to purge your Pages content, you can do so by going
-to your project's settings through the gear icon in the top right, and then
-navigating to **Pages**. Hit the **Remove pages** button and your Pages website
-will be deleted. Simple as that.
-
-![Remove pages](img/pages_remove.png)
-
-## GitLab Pages on GitLab.com
-
-If you are using GitLab.com to host your website, then:
-
-- The general domain name for GitLab Pages on GitLab.com is `gitlab.io`.
-- Custom domains and TLS support are enabled.
-- Shared runners are enabled by default, provided for free and can be used to
- build your website. If you want you can still bring your own Runner.
-
-The rest of the guide still applies.
-
-## Limitations
-
-When using Pages under the general domain of a GitLab instance (`*.example.io`),
-you _cannot_ use HTTPS with sub-subdomains. That means that if your
-username/groupname contains a dot, for example `foo.bar`, the domain
-`https://foo.bar.example.io` will _not_ work. This is a limitation of the
-[HTTP Over TLS protocol][rfc]. HTTP pages will continue to work provided you
-don't redirect HTTP to HTTPS.
-
-[rfc]: https://tools.ietf.org/html/rfc2818#section-3.1 "HTTP Over TLS RFC"
-
-## Redirects in GitLab Pages
-
-Since you cannot use any custom server configuration files, like `.htaccess` or
-any `.conf` file for that matter, if you want to redirect a web page to another
-location, you can use the [HTTP meta refresh tag][metarefresh].
-
-Some static site generators provide plugins for that functionality so that you
-don't have to create and edit HTML files manually. For example, Jekyll has the
-[redirect-from plugin](https://github.com/jekyll/jekyll-redirect-from).
-
-## Frequently Asked Questions
-
-### Can I download my generated pages?
-
-Sure. All you need to do is download the artifacts archive from the job page.
-
-### Can I use GitLab Pages if my project is private?
-
-Yes. GitLab Pages don't care whether you set your project's visibility level
-to private, internal or public.
-
-### Do I need to create a user/group website before creating a project website?
-
-No, you don't. You can create your project first and it will be accessed under
-`http(s)://namespace.example.io/projectname`.
-
-## Known issues
-
-For a list of known issues, visit GitLab's [public issue tracker].
-
----
-
-[jekyll]: http://jekyllrb.com/
-[ee-80]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/80
-[ee-173]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/173
-[pages-daemon]: https://gitlab.com/gitlab-org/gitlab-pages
-[gitlab ci]: https://about.gitlab.com/gitlab-ci
-[gitlab runner]: https://docs.gitlab.com/runner
-[pages]: ../../../ci/yaml/README.md#pages
-[yaml]: ../../../ci/yaml/README.md
-[staticgen]: https://www.staticgen.com/
-[pages-jekyll]: https://gitlab.com/pages/jekyll
-[metarefresh]: https://en.wikipedia.org/wiki/Meta_refresh
-[public issue tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name=Pages
-[ce-14605]: https://gitlab.com/gitlab-org/gitlab-ce/issues/14605
-[quick start guide]: ../../../ci/quick_start/README.md
-[pages-index-guide]: ../../../pages/index.md
-[pages-quick]: ../../../pages/getting_started_part_one.md
-[video-pages-fork]: https://youtu.be/TWqh9MtT4Bg
+# GitLab Pages documentation
+
+With GitLab Pages you can create static websites for your GitLab projects,
+groups, or user accounts. You can use any static website generator: Jekyll,
+Middleman, Hexo, Hugo, Pelican, you name it! Connect as many customs domains
+as you like and bring your own TLS certificate to secure them.
+
+Here's some info we've gathered to get you started.
+
+## General info
+
+- [Product webpage](https://pages.gitlab.io)
+- ["We're bringing GitLab Pages to CE" blog post](https://about.gitlab.com/2016/12/24/were-bringing-gitlab-pages-to-community-edition/)
+- [Pages group - templates](https://gitlab.com/pages)
+- [General user documentation](introduction.md)
+- [Admin documentation - Set GitLab Pages on your own GitLab instance](../../../administration/pages/index.md)
+
+## Getting started
+
+- **GitLab Pages from A to Z**
+ - [Part 1: Static sites and GitLab Pages domains](getting_started_part_one.md)
+ - [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md)
+ - [Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](getting_started_part_three.md)
+ - [Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md)
+- **Static Site Generators - Blog posts series**
+ - [SSGs part 1: Static vs dynamic websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/)
+ - [SSGs part 2: Modern static site generators](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/)
+ - [SSGs part 3: Build any SSG site with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/)
+- **Secure GitLab Pages custom domain with SSL/TLS certificates**
+ - [Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/)
+ - [CloudFlare](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/)
+ - [StartSSL](https://about.gitlab.com/2016/06/24/secure-gitlab-pages-with-startssl/)
+- **General**
+ - [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) a comprehensive step-by-step guide
+ - [Posting to your GitLab Pages blog from iOS](https://about.gitlab.com/2016/08/19/posting-to-your-gitlab-pages-blog-from-ios/)
+
+## Video tutorials
+
+- [How to publish a website with GitLab Pages on GitLab.com: from a forked project](https://youtu.be/TWqh9MtT4Bg)
+- [How to Enable GitLab Pages for GitLab CE and EE (for Admins only)](https://youtu.be/dD8c7WNcc6s)
+
+## Advanced use
+
+- **Blog Posts**
+ - [GitLab CI: Run jobs sequentially, in parallel, or build a custom pipeline](https://about.gitlab.com/2016/07/29/the-basics-of-gitlab-ci/)
+ - [GitLab CI: Deployment & environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/)
+ - [Building a new GitLab docs site with Nanoc, GitLab CI, and GitLab Pages](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/)
+ - [Publish code coverage reports with GitLab Pages](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/)
diff --git a/doc/user/project/pages/introduction.md b/doc/user/project/pages/introduction.md
new file mode 100644
index 00000000000..deaceabb7c5
--- /dev/null
+++ b/doc/user/project/pages/introduction.md
@@ -0,0 +1,447 @@
+# GitLab Pages
+
+> **Notes:**
+> - This feature was [introduced][ee-80] in GitLab EE 8.3.
+> - Custom CNAMEs with TLS support were [introduced][ee-173] in GitLab EE 8.5.
+> - GitLab Pages [were ported][ce-14605] to Community Edition in GitLab 8.17.
+> - This document is about the user guide. To learn how to enable GitLab Pages
+> across your GitLab instance, visit the [administrator documentation](../../../administration/pages/index.md).
+
+With GitLab Pages you can host for free your static websites on GitLab.
+Combined with the power of [GitLab CI] and the help of [GitLab Runner] you can
+deploy static pages for your individual projects, your user or your group.
+
+Read [GitLab Pages on GitLab.com](#gitlab-pages-on-gitlab-com) for specific
+information, if you are using GitLab.com to host your website.
+
+Read through [All you Need to Know About GitLab Pages][pages-index-guide] for a list of all learning materials we have prepared for GitLab Pages (webpages, articles, guides, blog posts, video tutorials).
+
+## Getting started with GitLab Pages
+
+> **Note:**
+> In the rest of this document we will assume that the general domain name that
+> is used for GitLab Pages is `example.io`.
+
+In general there are two types of pages one might create:
+
+- Pages per user (`username.example.io`) or per group (`groupname.example.io`)
+- Pages per project (`username.example.io/projectname` or `groupname.example.io/projectname`)
+
+In GitLab, usernames and groupnames are unique and we often refer to them
+as namespaces. There can be only one namespace in a GitLab instance. Below you
+can see the connection between the type of GitLab Pages, what the project name
+that is created on GitLab looks like and the website URL it will be ultimately
+be served on.
+
+| Type of GitLab Pages | The name of the project created in GitLab | Website URL |
+| -------------------- | ------------ | ----------- |
+| User pages | `username.example.io` | `http(s)://username.example.io` |
+| Group pages | `groupname.example.io` | `http(s)://groupname.example.io` |
+| Project pages owned by a user | `projectname` | `http(s)://username.example.io/projectname` |
+| Project pages owned by a group | `projectname` | `http(s)://groupname.example.io/projectname`|
+
+> **Warning:**
+> There are some known [limitations](#limitations) regarding namespaces served
+> under the general domain name and HTTPS. Make sure to read that section.
+
+### GitLab Pages requirements
+
+In brief, this is what you need to upload your website in GitLab Pages:
+
+1. Find out the general domain name that is used for GitLab Pages
+ (ask your administrator). This is very important, so you should first make
+ sure you get that right.
+1. Create a project
+1. Push a [`.gitlab-ci.yml` file][yaml] in the root directory
+ of your repository with a specific job named [`pages`][pages]
+1. Set up a GitLab Runner to build your website
+
+> **Note:**
+If [shared runners](../../../ci/runners/README.md) are enabled by your GitLab
+administrator, you should be able to use them instead of bringing your own.
+
+### User or group Pages
+
+For user and group pages, the name of the project should be specific to the
+username or groupname and the general domain name that is used for GitLab Pages.
+Head over your GitLab instance that supports GitLab Pages and create a
+repository named `username.example.io`, where `username` is your username on
+GitLab. If the first part of the project name doesn't match exactly your
+username, it won’t work, so make sure to get it right.
+
+To create a group page, the steps are the same like when creating a website for
+users. Just make sure that you are creating the project within the group's
+namespace.
+
+![Create a user-based pages project](img/pages_create_user_page.png)
+
+---
+
+After you push some static content to your repository and GitLab Runner uploads
+the artifacts to GitLab CI, you will be able to access your website under
+`http(s)://username.example.io`. Keep reading to find out how.
+
+>**Note:**
+If your username/groupname contains a dot, for example `foo.bar`, you will not
+be able to use the wildcard domain HTTPS, read more at [limitations](#limitations).
+
+### Project Pages
+
+GitLab Pages for projects can be created by both user and group accounts.
+The steps to create a project page for a user or a group are identical:
+
+1. Create a new project
+1. Push a [`.gitlab-ci.yml` file][yaml] in the root directory
+ of your repository with a specific job named [`pages`][pages].
+1. Set up a GitLab Runner to build your website
+
+A user's project will be served under `http(s)://username.example.io/projectname`
+whereas a group's project under `http(s)://groupname.example.io/projectname`.
+
+## Quick Start
+
+Read through [GitLab Pages Quick Start Guide][pages-quick] or watch the video tutorial on
+[how to publish a website with GitLab Pages on GitLab.com from a forked project][video-pages-fork].
+
+See also [All you Need to Know About GitLab Pages][pages-index-guide] for a list with all the resources we have for GitLab Pages.
+
+### Explore the contents of `.gitlab-ci.yml`
+
+The key thing about GitLab Pages is the `.gitlab-ci.yml` file, something that
+gives you absolute control over the build process. You can actually watch your
+website being built live by following the CI job traces.
+
+> **Note:**
+> Before reading this section, make sure you familiarize yourself with GitLab CI
+> and the specific syntax of[`.gitlab-ci.yml`][yaml] by
+> following our [quick start guide].
+
+To make use of GitLab Pages, the contents of `.gitlab-ci.yml` must follow the
+rules below:
+
+1. A special job named [`pages`][pages] must be defined
+1. Any static content which will be served by GitLab Pages must be placed under
+ a `public/` directory
+1. `artifacts` with a path to the `public/` directory must be defined
+
+In its simplest form, `.gitlab-ci.yml` looks like:
+
+```yaml
+pages:
+ script:
+ - my_commands
+ artifacts:
+ paths:
+ - public
+```
+
+When the Runner reaches to build the `pages` job, it executes whatever is
+defined in the `script` parameter and if the job completes with a non-zero
+exit status, it then uploads the `public/` directory to GitLab Pages.
+
+The `public/` directory should contain all the static content of your website.
+Depending on how you plan to publish your website, the steps defined in the
+[`script` parameter](../../../ci/yaml/README.md#script) may differ.
+
+Be aware that Pages are by default branch/tag agnostic and their deployment
+relies solely on what you specify in `.gitlab-ci.yml`. If you don't limit the
+`pages` job with the [`only` parameter](../../../ci/yaml/README.md#only-and-except),
+whenever a new commit is pushed to whatever branch or tag, the Pages will be
+overwritten. In the example below, we limit the Pages to be deployed whenever
+a commit is pushed only on the `master` branch:
+
+```yaml
+pages:
+ script:
+ - my_commands
+ artifacts:
+ paths:
+ - public
+ only:
+ - master
+```
+
+We then tell the Runner to treat the `public/` directory as `artifacts` and
+upload it to GitLab. And since all these parameters were all under a `pages`
+job, the contents of the `public` directory will be served by GitLab Pages.
+
+#### How `.gitlab-ci.yml` looks like when the static content is in your repository
+
+Supposedly your repository contained the following files:
+
+```
+├── index.html
+├── css
+│   └── main.css
+└── js
+ └── main.js
+```
+
+Then the `.gitlab-ci.yml` example below simply moves all files from the root
+directory of the project to the `public/` directory. The `.public` workaround
+is so `cp` doesn't also copy `public/` to itself in an infinite loop:
+
+```yaml
+pages:
+ script:
+ - mkdir .public
+ - cp -r * .public
+ - mv .public public
+ artifacts:
+ paths:
+ - public
+ only:
+ - master
+```
+
+#### How `.gitlab-ci.yml` looks like when using a static generator
+
+In general, GitLab Pages support any kind of [static site generator][staticgen],
+since `.gitlab-ci.yml` can be configured to run any possible command.
+
+In the root directory of your Git repository, place the source files of your
+favorite static generator. Then provide a `.gitlab-ci.yml` file which is
+specific to your static generator.
+
+The example below, uses [Jekyll] to build the static site:
+
+```yaml
+image: ruby:2.1 # the script will run in Ruby 2.1 using the Docker image ruby:2.1
+
+pages: # the build job must be named pages
+ script:
+ - gem install jekyll # we install jekyll
+ - jekyll build -d public/ # we tell jekyll to build the site for us
+ artifacts:
+ paths:
+ - public # this is where the site will live and the Runner uploads it in GitLab
+ only:
+ - master # this script is only affecting the master branch
+```
+
+Here, we used the Docker executor and in the first line we specified the base
+image against which our jobs will run.
+
+You have to make sure that the generated static files are ultimately placed
+under the `public` directory, that's why in the `script` section we run the
+`jekyll` command that jobs the website and puts all content in the `public/`
+directory. Depending on the static generator of your choice, this command will
+differ. Search in the documentation of the static generator you will use if
+there is an option to explicitly set the output directory. If there is not
+such an option, you can always add one more line under `script` to rename the
+resulting directory in `public/`.
+
+We then tell the Runner to treat the `public/` directory as `artifacts` and
+upload it to GitLab.
+
+---
+
+See the [jekyll example project][pages-jekyll] to better understand how this
+works.
+
+For a list of Pages projects, see the [example projects](#example-projects) to
+get you started.
+
+#### How to set up GitLab Pages in a repository where there's also actual code
+
+Remember that GitLab Pages are by default branch/tag agnostic and their
+deployment relies solely on what you specify in `.gitlab-ci.yml`. You can limit
+the `pages` job with the [`only` parameter](../../../ci/yaml/README.md#only-and-except),
+whenever a new commit is pushed to a branch that will be used specifically for
+your pages.
+
+That way, you can have your project's code in the `master` branch and use an
+orphan branch (let's name it `pages`) that will host your static generator site.
+
+You can create a new empty branch like this:
+
+```bash
+git checkout --orphan pages
+```
+
+The first commit made on this new branch will have no parents and it will be
+the root of a new history totally disconnected from all the other branches and
+commits. Push the source files of your static generator in the `pages` branch.
+
+Below is a copy of `.gitlab-ci.yml` where the most significant line is the last
+one, specifying to execute everything in the `pages` branch:
+
+```
+image: ruby:2.1
+
+pages:
+ script:
+ - gem install jekyll
+ - jekyll build -d public/
+ artifacts:
+ paths:
+ - public
+ only:
+ - pages
+```
+
+See an example that has different files in the [`master` branch][jekyll-master]
+and the source files for Jekyll are in a [`pages` branch][jekyll-pages] which
+also includes `.gitlab-ci.yml`.
+
+[jekyll-master]: https://gitlab.com/pages/jekyll-branched/tree/master
+[jekyll-pages]: https://gitlab.com/pages/jekyll-branched/tree/pages
+
+## Next steps
+
+So you have successfully deployed your website, congratulations! Let's check
+what more you can do with GitLab Pages.
+
+### Example projects
+
+Below is a list of example projects for GitLab Pages with a plain HTML website
+or various static site generators. Contributions are very welcome.
+
+- [Plain HTML](https://gitlab.com/pages/plain-html)
+- [Jekyll](https://gitlab.com/pages/jekyll)
+- [Hugo](https://gitlab.com/pages/hugo)
+- [Middleman](https://gitlab.com/pages/middleman)
+- [Hexo](https://gitlab.com/pages/hexo)
+- [Brunch](https://gitlab.com/pages/brunch)
+- [Metalsmith](https://gitlab.com/pages/metalsmith)
+- [Harp](https://gitlab.com/pages/harp)
+
+Visit the GitLab Pages group for a full list of example projects:
+<https://gitlab.com/groups/pages>.
+
+### Add a custom domain to your Pages website
+
+If this setting is enabled by your GitLab administrator, you should be able to
+see the **New Domain** button when visiting your project's settings through the
+gear icon in the top right and then navigating to **Pages**.
+
+![New domain button](img/pages_new_domain_button.png)
+
+---
+
+You can add multiple domains pointing to your website hosted under GitLab.
+Once the domain is added, you can see it listed under the **Domains** section.
+
+![Pages multiple domains](img/pages_multiple_domains.png)
+
+---
+
+As a last step, you need to configure your DNS and add a CNAME pointing to your
+user/group page. Click on the **Details** button of a domain for further
+instructions.
+
+![Pages DNS details](img/pages_dns_details.png)
+
+---
+
+>**Note:**
+Currently there is support only for custom domains on per-project basis. That
+means that if you add a custom domain (`example.com`) for your user website
+(`username.example.io`), a project that is served under `username.example.io/foo`,
+will not be accessible under `example.com/foo`.
+
+### Secure your custom domain website with TLS
+
+When you add a new custom domain, you also have the chance to add a TLS
+certificate. If this setting is enabled by your GitLab administrator, you
+should be able to see the option to upload the public certificate and the
+private key when adding a new domain.
+
+![Pages upload cert](img/pages_upload_cert.png)
+
+### Custom error codes pages
+
+You can provide your own 403 and 404 error pages by creating the `403.html` and
+`404.html` files respectively in the root directory of the `public/` directory
+that will be included in the artifacts. Usually this is the root directory of
+your project, but that may differ depending on your static generator
+configuration.
+
+If the case of `404.html`, there are different scenarios. For example:
+
+- If you use project Pages (served under `/projectname/`) and try to access
+ `/projectname/non/exsiting_file`, GitLab Pages will try to serve first
+ `/projectname/404.html`, and then `/404.html`.
+- If you use user/group Pages (served under `/`) and try to access
+ `/non/existing_file` GitLab Pages will try to serve `/404.html`.
+- If you use a custom domain and try to access `/non/existing_file`, GitLab
+ Pages will try to serve only `/404.html`.
+
+### Remove the contents of your pages
+
+If you ever feel the need to purge your Pages content, you can do so by going
+to your project's settings through the gear icon in the top right, and then
+navigating to **Pages**. Hit the **Remove pages** button and your Pages website
+will be deleted. Simple as that.
+
+![Remove pages](img/pages_remove.png)
+
+## GitLab Pages on GitLab.com
+
+If you are using GitLab.com to host your website, then:
+
+- The general domain name for GitLab Pages on GitLab.com is `gitlab.io`.
+- Custom domains and TLS support are enabled.
+- Shared runners are enabled by default, provided for free and can be used to
+ build your website. If you want you can still bring your own Runner.
+
+The rest of the guide still applies.
+
+## Limitations
+
+When using Pages under the general domain of a GitLab instance (`*.example.io`),
+you _cannot_ use HTTPS with sub-subdomains. That means that if your
+username/groupname contains a dot, for example `foo.bar`, the domain
+`https://foo.bar.example.io` will _not_ work. This is a limitation of the
+[HTTP Over TLS protocol][rfc]. HTTP pages will continue to work provided you
+don't redirect HTTP to HTTPS.
+
+[rfc]: https://tools.ietf.org/html/rfc2818#section-3.1 "HTTP Over TLS RFC"
+
+## Redirects in GitLab Pages
+
+Since you cannot use any custom server configuration files, like `.htaccess` or
+any `.conf` file for that matter, if you want to redirect a web page to another
+location, you can use the [HTTP meta refresh tag][metarefresh].
+
+Some static site generators provide plugins for that functionality so that you
+don't have to create and edit HTML files manually. For example, Jekyll has the
+[redirect-from plugin](https://github.com/jekyll/jekyll-redirect-from).
+
+## Frequently Asked Questions
+
+### Can I download my generated pages?
+
+Sure. All you need to do is download the artifacts archive from the job page.
+
+### Can I use GitLab Pages if my project is private?
+
+Yes. GitLab Pages don't care whether you set your project's visibility level
+to private, internal or public.
+
+### Do I need to create a user/group website before creating a project website?
+
+No, you don't. You can create your project first and it will be accessed under
+`http(s)://namespace.example.io/projectname`.
+
+## Known issues
+
+For a list of known issues, visit GitLab's [public issue tracker].
+
+[jekyll]: http://jekyllrb.com/
+[ee-80]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/80
+[ee-173]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/173
+[pages-daemon]: https://gitlab.com/gitlab-org/gitlab-pages
+[gitlab ci]: https://about.gitlab.com/gitlab-ci
+[gitlab runner]: https://docs.gitlab.com/runner/
+[pages]: ../../../ci/yaml/README.md#pages
+[yaml]: ../../../ci/yaml/README.md
+[staticgen]: https://www.staticgen.com/
+[pages-jekyll]: https://gitlab.com/pages/jekyll
+[metarefresh]: https://en.wikipedia.org/wiki/Meta_refresh
+[public issue tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=pages
+[ce-14605]: https://gitlab.com/gitlab-org/gitlab-ce/issues/14605
+[quick start guide]: ../../../ci/quick_start/README.md
+[pages-index-guide]: index.md
+[pages-quick]: getting_started_part_one.md
+[video-pages-fork]: https://youtu.be/TWqh9MtT4Bg
diff --git a/doc/user/project/slash_commands.md b/doc/user/project/slash_commands.md
index ad5d51d34f2..45176fde9db 100644
--- a/doc/user/project/slash_commands.md
+++ b/doc/user/project/slash_commands.md
@@ -35,3 +35,4 @@ do.
| <code>/spend &lt;1h 30m &#124; -1h 5m&gt;</code> | Add or subtract spent time |
| `/remove_time_spent` | Remove time spent |
| `/target_branch <Branch Name>` | Set target branch for current merge request |
+| `/award :emoji:` | Toggle award for :emoji: |
diff --git a/doc/workflow/gitlab_flow.md b/doc/workflow/gitlab_flow.md
index 4889e3ec50c..d12c0c6d0c4 100644
--- a/doc/workflow/gitlab_flow.md
+++ b/doc/workflow/gitlab_flow.md
@@ -203,7 +203,7 @@ But the advantages of having stable identifiers outweigh this drawback.
And to understand a change in context one can always look at the merge commit that groups all the commits together when the code is merged into the master branch.
After you merge multiple commits from a feature branch into the master branch this is harder to undo.
-If you would have squashed all the commits into one you could have just reverted this commit but as we indicated you should not rebase commits after they are pushed.
+If you had squashed all the commits into one you could have just reverted this commit but as we indicated you should not rebase commits after they are pushed.
Fortunately [reverting a merge made some time ago](https://git-scm.com/blog/2010/03/02/undoing-merges.html) can be done with git.
This however, requires having specific merge commits for the commits your want to revert.
If you revert a merge and you change your mind, revert the revert instead of merging again since git will not allow you to merge the code again otherwise.
diff --git a/doc/workflow/shortcuts.md b/doc/workflow/shortcuts.md
index 2a5e622dc7d..65e67aa1512 100644
--- a/doc/workflow/shortcuts.md
+++ b/doc/workflow/shortcuts.md
@@ -42,12 +42,10 @@ You can see GitLab's keyboard shortcuts by using 'shift + ?'
| Keyboard Shortcut | Description |
| ----------------- | ----------- |
| <kbd>g</kbd> + <kbd>p</kbd> | Go to the project's home page |
-| <kbd>g</kbd> + <kbd>e</kbd> | Go to the project's activity feed |
| <kbd>g</kbd> + <kbd>f</kbd> | Go to files |
| <kbd>g</kbd> + <kbd>c</kbd> | Go to commits |
| <kbd>g</kbd> + <kbd>b</kbd> | Go to jobs |
| <kbd>g</kbd> + <kbd>n</kbd> | Go to network graph |
-| <kbd>g</kbd> + <kbd>g</kbd> | Go to graphs |
| <kbd>g</kbd> + <kbd>i</kbd> | Go to issues |
| <kbd>g</kbd> + <kbd>m</kbd> | Go to merge requests |
| <kbd>g</kbd> + <kbd>s</kbd> | Go to snippets |
diff --git a/features/project/active_tab.feature b/features/project/active_tab.feature
index 5c14c5db665..1dd2bdd9b36 100644
--- a/features/project/active_tab.feature
+++ b/features/project/active_tab.feature
@@ -80,9 +80,9 @@ Feature: Project Active Tab
And no other sub tabs should be active
And the active main tab should be Repository
- Scenario: On Project Repository/Network
- Given I visit my project's network page
- Then the active sub tab should be Network
+ Scenario: On Project Repository/Graph
+ Given I visit my project's graph page
+ Then the active sub tab should be Graph
And no other sub tabs should be active
And the active main tab should be Repository
diff --git a/features/project/graph.feature b/features/project/graph.feature
index 63793d6f989..b25c73ad870 100644
--- a/features/project/graph.feature
+++ b/features/project/graph.feature
@@ -9,9 +9,10 @@ Feature: Project Graph
Then page should have graphs
@javascript
- Scenario: I should see project commits graphs
+ Scenario: I should see project languages & commits graphs on commits graph url
When I visit project "Shop" commits graph page
Then page should have commits graphs
+ Then page should have languages graphs
@javascript
Scenario: I should see project ci graphs
@@ -20,6 +21,13 @@ Feature: Project Graph
Then page should have CI graphs
@javascript
- Scenario: I should see project languages graphs
+ Scenario: I should see project languages & commits graphs on language graph url
When I visit project "Shop" languages graph page
Then page should have languages graphs
+ Then page should have commits graphs
+
+ @javascript
+ Scenario: I should see project languages & commits graphs on charts url
+ When I visit project "Shop" chart page
+ Then page should have languages graphs
+ Then page should have commits graphs
diff --git a/features/project/issues/award_emoji.feature b/features/project/issues/award_emoji.feature
index f0fd414a9f9..1d7adfdd2c2 100644
--- a/features/project/issues/award_emoji.feature
+++ b/features/project/issues/award_emoji.feature
@@ -42,4 +42,4 @@ Feature: Award Emoji
@javascript
Scenario: I add award emoji using regular comment
Given I leave comment with a single emoji
- Then I have award added
+ Then I have new comment with emoji added
diff --git a/features/project/shortcuts.feature b/features/project/shortcuts.feature
index f71f69ef060..95de63ba21a 100644
--- a/features/project/shortcuts.feature
+++ b/features/project/shortcuts.feature
@@ -19,17 +19,12 @@ Feature: Project Shortcuts
Then the active sub tab should be Commits
@javascript
- Scenario: Navigate to network tab
+ Scenario: Navigate to graph tab
Given I press "g" and "n"
- Then the active sub tab should be Network
+ Then the active sub tab should be Graph
And the active main tab should be Repository
@javascript
- Scenario: Navigate to graphs tab
- Given I press "g" and "g"
- Then the active main tab should be Graphs
-
- @javascript
Scenario: Navigate to issues tab
Given I press "g" and "i"
Then the active main tab should be Issues
@@ -53,8 +48,3 @@ Feature: Project Shortcuts
Scenario: Navigate to project home
Given I press "g" and "p"
Then the active main tab should be Home
-
- @javascript
- Scenario: Navigate to project feed
- Given I press "g" and "e"
- Then the active main tab should be Activity
diff --git a/features/steps/project/graph.rb b/features/steps/project/graph.rb
index 48ac7a98f0d..176d04d721c 100644
--- a/features/steps/project/graph.rb
+++ b/features/steps/project/graph.rb
@@ -18,6 +18,10 @@ class Spinach::Features::ProjectGraph < Spinach::FeatureSteps
visit languages_namespace_project_graph_path(project.namespace, project, "master")
end
+ step 'I visit project "Shop" chart page' do
+ visit charts_namespace_project_graph_path(project.namespace, project, "master")
+ end
+
step 'page should have languages graphs' do
expect(page).to have_content /Ruby 66.* %/
expect(page).to have_content /JavaScript 22.* %/
diff --git a/features/steps/project/issues/award_emoji.rb b/features/steps/project/issues/award_emoji.rb
index cbe5738e7e4..dd7a58b454a 100644
--- a/features/steps/project/issues/award_emoji.rb
+++ b/features/steps/project/issues/award_emoji.rb
@@ -44,6 +44,10 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps
end
end
+ step 'I have new comment with emoji added' do
+ expect(page).to have_selector ".emoji[title=':smile:']"
+ end
+
step 'I have award added' do
page.within '.awards' do
expect(page).to have_selector '.js-emoji-btn'
diff --git a/features/steps/project/network_graph.rb b/features/steps/project/network_graph.rb
index ff9251615c9..370e46265c7 100644
--- a/features/steps/project/network_graph.rb
+++ b/features/steps/project/network_graph.rb
@@ -66,7 +66,7 @@ class Spinach::Features::ProjectNetworkGraph < Spinach::FeatureSteps
end
step 'page should have "v1.0.0" in title' do
- expect(page).to have_css 'title', text: 'Network · v1.0.0', visible: false
+ expect(page).to have_css 'title', text: 'Graph · v1.0.0', visible: false
end
step 'page should only have content from "v1.0.0"' do
diff --git a/features/steps/project/project_shortcuts.rb b/features/steps/project/project_shortcuts.rb
index 8143b01ca40..02c08b784bc 100644
--- a/features/steps/project/project_shortcuts.rb
+++ b/features/steps/project/project_shortcuts.rb
@@ -34,9 +34,4 @@ class Spinach::Features::ProjectShortcuts < Spinach::FeatureSteps
find('body').native.send_key('g')
find('body').native.send_key('w')
end
-
- step 'I press "g" and "e"' do
- find('body').native.send_key('g')
- find('body').native.send_key('e')
- end
end
diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb
index 718cf924729..d5b3bb34d7a 100644
--- a/features/steps/shared/paths.rb
+++ b/features/steps/shared/paths.rb
@@ -232,7 +232,7 @@ module SharedPaths
visit stats_namespace_project_repository_path(@project.namespace, @project)
end
- step "I visit my project's network page" do
+ step "I visit my project's graph page" do
# Stub Graph max_size to speed up test (10 commits vs. 650)
Network::Graph.stub(max_count: 10)
diff --git a/features/steps/shared/project_tab.rb b/features/steps/shared/project_tab.rb
index d6024212601..83446afe424 100644
--- a/features/steps/shared/project_tab.rb
+++ b/features/steps/shared/project_tab.rb
@@ -12,10 +12,6 @@ module SharedProjectTab
ensure_active_main_tab('Repository')
end
- step 'the active main tab should be Graphs' do
- ensure_active_main_tab('Graphs')
- end
-
step 'the active main tab should be Issues' do
ensure_active_main_tab('Issues')
end
@@ -40,12 +36,8 @@ module SharedProjectTab
expect(page).to have_selector('.layout-nav .nav-links > li.active', count: 0)
end
- step 'the active main tab should be Activity' do
- ensure_active_main_tab('Activity')
- end
-
- step 'the active sub tab should be Network' do
- ensure_active_sub_tab('Network')
+ step 'the active sub tab should be Graph' do
+ ensure_active_sub_tab('Graph')
end
step 'the active sub tab should be Files' do
diff --git a/lib/api/api.rb b/lib/api/api.rb
index b27ac3f1d15..02f7bc2fbbf 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -26,6 +26,8 @@ module API
mount ::API::V3::Repositories
mount ::API::V3::Runners
mount ::API::V3::Services
+ mount ::API::V3::Settings
+ mount ::API::V3::Snippets
mount ::API::V3::Subscriptions
mount ::API::V3::SystemHooks
mount ::API::V3::Tags
diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb
index c11f8529183..409cb5b924f 100644
--- a/lib/api/api_guard.rb
+++ b/lib/api/api_guard.rb
@@ -160,13 +160,10 @@ module API
# Exceptions
#
- class MissingTokenError < StandardError; end
-
- class TokenNotFoundError < StandardError; end
-
- class ExpiredError < StandardError; end
-
- class RevokedError < StandardError; end
+ MissingTokenError = Class.new(StandardError)
+ TokenNotFoundError = Class.new(StandardError)
+ ExpiredError = Class.new(StandardError)
+ RevokedError = Class.new(StandardError)
class InsufficientScopeError < StandardError
attr_reader :scopes
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index dba0831664c..9d9f82fdb83 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -72,14 +72,15 @@ module API
status = GenericCommitStatus.running_or_pending.find_or_initialize_by(
project: @project,
pipeline: pipeline,
- user: current_user,
name: name,
ref: ref,
- target_url: params[:target_url],
- description: params[:description],
- coverage: params[:coverage]
+ user: current_user
)
+ optional_attributes =
+ attributes_for_keys(%w[target_url description coverage])
+
+ status.update(optional_attributes) if optional_attributes.any?
render_validation_error!(status) if status.invalid?
begin
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index a99d9cadc8a..d2d21f5d03a 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -69,9 +69,8 @@ module API
class Project < Grape::Entity
expose :id, :description, :default_branch, :tag_list
- expose :public?, as: :public
expose :archived?, as: :archived
- expose :visibility_level, :ssh_url_to_repo, :http_url_to_repo, :web_url
+ expose :visibility, :ssh_url_to_repo, :http_url_to_repo, :web_url
expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
expose :name, :name_with_namespace
expose :path, :path_with_namespace
@@ -98,7 +97,7 @@ module API
expose :shared_with_groups do |project, options|
SharedGroup.represent(project.project_group_links.all, options)
end
- expose :only_allow_merge_if_build_succeeds
+ expose :only_allow_merge_if_pipeline_succeeds
expose :request_access_enabled
expose :only_allow_merge_if_all_discussions_are_resolved
@@ -132,7 +131,7 @@ module API
end
class Group < Grape::Entity
- expose :id, :name, :path, :description, :visibility_level
+ expose :id, :name, :path, :description, :visibility
expose :lfs_enabled?, as: :lfs_enabled
expose :avatar_url
expose :web_url
@@ -288,7 +287,7 @@ module API
expose :label_names, as: :labels
expose :work_in_progress?, as: :work_in_progress
expose :milestone, using: Entities::Milestone
- expose :merge_when_build_succeeds
+ expose :merge_when_pipeline_succeeds
expose :merge_status
expose :diff_head_sha, as: :sha
expose :merge_commit_sha
@@ -394,7 +393,8 @@ module API
expose :target_type
expose :target do |todo, options|
- Entities.const_get(todo.target_type).represent(todo.target, options)
+ target = todo.target_type == 'Commit' ? 'RepoCommit' : todo.target_type
+ Entities.const_get(target).represent(todo.target, options)
end
expose :target_url do |todo, options|
@@ -551,12 +551,14 @@ module API
expose :updated_at
expose :home_page_url
expose :default_branch_protection
- expose :restricted_visibility_levels
+ expose(:restricted_visibility_levels) do |setting, _options|
+ setting.restricted_visibility_levels.map { |level| Gitlab::VisibilityLevel.string_level(level) }
+ end
expose :max_attachment_size
expose :session_expire_delay
- expose :default_project_visibility
- expose :default_snippet_visibility
- expose :default_group_visibility
+ expose(:default_project_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_project_visibility) }
+ expose(:default_snippet_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_snippet_visibility) }
+ expose(:default_group_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_group_visibility) }
expose :default_artifacts_expire_in
expose :domain_whitelist
expose :domain_blacklist_enabled
diff --git a/lib/api/environments.rb b/lib/api/environments.rb
index dbdf29a9640..ebe8c3a5b2c 100644
--- a/lib/api/environments.rb
+++ b/lib/api/environments.rb
@@ -81,6 +81,23 @@ module API
environment.destroy
end
+
+ desc 'Stops an existing environment' do
+ success Entities::Environment
+ end
+ params do
+ requires :environment_id, type: Integer, desc: 'The environment ID'
+ end
+ post ':id/environments/:environment_id/stop' do
+ authorize! :create_deployment, user_project
+
+ environment = user_project.environments.find(params[:environment_id])
+
+ environment.stop_with_action!(current_user)
+
+ status 200
+ present environment, with: Entities::Environment
+ end
end
end
end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 9cffd6180ae..b862ff70b31 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -7,7 +7,7 @@ module API
helpers do
params :optional_params do
optional :description, type: String, desc: 'The description of the group'
- optional :visibility_level, type: Integer, desc: 'The visibility level of the group'
+ optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the group'
optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group'
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
end
@@ -92,7 +92,7 @@ module API
optional :name, type: String, desc: 'The name of the group'
optional :path, type: String, desc: 'The path of the group'
use :optional_params
- at_least_one_of :name, :path, :description, :visibility_level,
+ at_least_one_of :name, :path, :description, :visibility,
:lfs_enabled, :request_access_enabled
end
put ':id' do
@@ -126,7 +126,7 @@ module API
end
params do
optional :archived, type: Boolean, default: false, desc: 'Limit by archived status'
- optional :visibility, type: String, values: %w[public internal private],
+ optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values,
desc: 'Limit by visibility'
optional :search, type: String, desc: 'Return list of authorized projects matching the search criteria'
optional :order_by, type: String, values: %w[id name path created_at updated_at last_activity_at],
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 72d2b320077..4600abc7dc7 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -164,6 +164,10 @@ module API
items.where(iid: iid)
end
+ def filter_by_search(items, text)
+ items.search(text)
+ end
+
# error helpers
def forbidden!(reason = nil)
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index d235977fbd8..7eed93aba00 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -132,6 +132,18 @@ module API
{ success: true, recovery_codes: codes }
end
+
+ post "/notify_post_receive" do
+ status 200
+
+ return unless Gitlab::GitalyClient.enabled?
+
+ begin
+ Gitlab::GitalyClient::Notifications.new.post_receive(params[:repo_path])
+ rescue GRPC::Unavailable => e
+ render_api_error(e, 500)
+ end
+ end
end
end
end
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 6d30c5d81b1..1d6d0b05750 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -25,6 +25,7 @@ module API
optional :sort, type: String, values: %w[asc desc], default: 'desc',
desc: 'Return issues sorted in `asc` or `desc` order.'
optional :milestone, type: String, desc: 'Return issues for a specific milestone'
+ optional :iids, type: Array[Integer], desc: 'The IID array of issues'
use :pagination
end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index bdd764abfeb..4638a66811d 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -168,8 +168,8 @@ module API
optional :merge_commit_message, type: String, desc: 'Custom merge commit message'
optional :should_remove_source_branch, type: Boolean,
desc: 'When true, the source branch will be deleted if possible'
- optional :merge_when_build_succeeds, type: Boolean,
- desc: 'When true, this merge request will be merged when the pipeline succeeds'
+ optional :merge_when_pipeline_succeeds, type: Boolean,
+ desc: 'When true, this merge request will be merged when the pipeline succeeds'
optional :sha, type: String, desc: 'When present, must have the HEAD SHA of the source branch'
end
put ':id/merge_requests/:merge_request_id/merge' do
@@ -192,7 +192,7 @@ module API
should_remove_source_branch: params[:should_remove_source_branch]
}
- if params[:merge_when_build_succeeds] && merge_request.head_pipeline && merge_request.head_pipeline.active?
+ if params[:merge_when_pipeline_succeeds] && merge_request.head_pipeline && merge_request.head_pipeline.active?
::MergeRequests::MergeWhenPipelineSucceedsService
.new(merge_request.target_project, current_user, merge_params)
.execute(merge_request)
@@ -208,10 +208,10 @@ module API
desc 'Cancel merge if "Merge When Pipeline Succeeds" is enabled' do
success Entities::MergeRequest
end
- post ':id/merge_requests/:merge_request_id/cancel_merge_when_build_succeeds' do
+ post ':id/merge_requests/:merge_request_id/cancel_merge_when_pipeline_succeeds' do
merge_request = find_project_merge_request(params[:merge_request_id])
- unauthorized! unless merge_request.can_cancel_merge_when_build_succeeds?(current_user)
+ unauthorized! unless merge_request.can_cancel_merge_when_pipeline_succeeds?(current_user)
::MergeRequest::MergeWhenPipelineSucceedsService
.new(merge_request.target_project, current_user)
diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb
index 0b4ed76b35c..44bdaea7fa4 100644
--- a/lib/api/milestones.rb
+++ b/lib/api/milestones.rb
@@ -31,6 +31,7 @@ module API
optional :state, type: String, values: %w[active closed all], default: 'all',
desc: 'Return "active", "closed", or "all" milestones'
optional :iid, type: Array[Integer], desc: 'The IID of the milestone'
+ optional :search, type: String, desc: 'The search criteria for the title or description of the milestone'
use :pagination
end
get ":id/milestones" do
@@ -39,6 +40,7 @@ module API
milestones = user_project.milestones
milestones = filter_milestones_state(milestones, params[:state])
milestones = filter_by_iid(milestones, params[:iid]) if params[:iid].present?
+ milestones = filter_by_search(milestones, params[:search]) if params[:search]
present paginate(milestones), with: Entities::Milestone
end
diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb
index 2a1cce73f3f..f57e7ea4032 100644
--- a/lib/api/project_snippets.rb
+++ b/lib/api/project_snippets.rb
@@ -50,11 +50,9 @@ module API
requires :title, type: String, desc: 'The title of the snippet'
requires :file_name, type: String, desc: 'The file name of the snippet'
requires :code, type: String, desc: 'The content of the snippet'
- requires :visibility_level, type: Integer,
- values: [Gitlab::VisibilityLevel::PRIVATE,
- Gitlab::VisibilityLevel::INTERNAL,
- Gitlab::VisibilityLevel::PUBLIC],
- desc: 'The visibility level of the snippet'
+ requires :visibility, type: String,
+ values: Gitlab::VisibilityLevel.string_values,
+ desc: 'The visibility of the snippet'
end
post ":id/snippets" do
authorize! :create_project_snippet, user_project
@@ -80,11 +78,9 @@ module API
optional :title, type: String, desc: 'The title of the snippet'
optional :file_name, type: String, desc: 'The file name of the snippet'
optional :code, type: String, desc: 'The content of the snippet'
- optional :visibility_level, type: Integer,
- values: [Gitlab::VisibilityLevel::PRIVATE,
- Gitlab::VisibilityLevel::INTERNAL,
- Gitlab::VisibilityLevel::PUBLIC],
- desc: 'The visibility level of the snippet'
+ optional :visibility, type: String,
+ values: Gitlab::VisibilityLevel.string_values,
+ desc: 'The visibility of the snippet'
at_least_one_of :title, :file_name, :code, :visibility_level
end
put ":id/snippets/:snippet_id" do
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index b8a8cee0cea..f302496c12b 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -16,14 +16,10 @@ module API
optional :shared_runners_enabled, type: Boolean, desc: 'Flag indication if shared runners are enabled for that project'
optional :container_registry_enabled, type: Boolean, desc: 'Flag indication if the container registry is enabled for that project'
optional :lfs_enabled, type: Boolean, desc: 'Flag indication if Git LFS is enabled for that project'
- optional :visibility_level, type: Integer, values: [
- Gitlab::VisibilityLevel::PRIVATE,
- Gitlab::VisibilityLevel::INTERNAL,
- Gitlab::VisibilityLevel::PUBLIC
- ], desc: 'Create a public project. The same as visibility_level = 20.'
+ optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the project.'
optional :public_builds, type: Boolean, desc: 'Perform public builds'
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
- optional :only_allow_merge_if_build_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed'
+ optional :only_allow_merge_if_pipeline_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed'
optional :only_allow_merge_if_all_discussions_are_resolved, type: Boolean, desc: 'Only allow to merge if all discussions are resolved'
end
end
@@ -48,7 +44,7 @@ module API
params :filter_params do
optional :archived, type: Boolean, default: false, desc: 'Limit by archived status'
- optional :visibility, type: String, values: %w[public internal private],
+ optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values,
desc: 'Limit by visibility'
optional :search, type: String, desc: 'Return list of authorized projects matching the search criteria'
optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user'
@@ -208,8 +204,8 @@ module API
at_least_one_of :name, :description, :issues_enabled, :merge_requests_enabled,
:wiki_enabled, :builds_enabled, :snippets_enabled,
:shared_runners_enabled, :container_registry_enabled,
- :lfs_enabled, :visibility_level, :public_builds,
- :request_access_enabled, :only_allow_merge_if_build_succeeds,
+ :lfs_enabled, :visibility, :public_builds,
+ :request_access_enabled, :only_allow_merge_if_pipeline_succeeds,
:only_allow_merge_if_all_discussions_are_resolved, :path,
:default_branch
end
@@ -217,7 +213,7 @@ module API
authorize_admin_project
attrs = declared_params(include_missing: false)
authorize! :rename_project, user_project if attrs[:name].present?
- authorize! :change_visibility_level, user_project if attrs[:visibility_level].present?
+ authorize! :change_visibility_level, user_project if attrs[:visibility].present?
result = ::Projects::UpdateService.new(user_project, current_user, attrs).execute
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index 936c7e0930b..d4d3229f0d1 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -21,9 +21,9 @@ module API
end
params do
optional :default_branch_protection, type: Integer, values: [0, 1, 2], desc: 'Determine if developers can push to master'
- optional :default_project_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default project visibility'
- optional :default_snippet_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default snippet visibility'
- optional :default_group_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default group visibility'
+ optional :default_project_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default project visibility'
+ optional :default_snippet_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default snippet visibility'
+ optional :default_group_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default group visibility'
optional :restricted_visibility_levels, type: Array[String], desc: 'Selected levels cannot be used by non-admin users for projects or snippets. If the public level is restricted, user profiles are only visible to logged in users.'
optional :import_sources, type: Array[String], values: %w[github bitbucket gitlab google_code fogbugz git gitlab_project],
desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com'
@@ -128,7 +128,9 @@ module API
:housekeeping_enabled, :terminal_max_session_time
end
put "application/settings" do
- if current_settings.update_attributes(declared_params(include_missing: false))
+ attrs = declared_params(include_missing: false)
+
+ if current_settings.update_attributes(attrs)
present current_settings, with: Entities::ApplicationSetting
else
render_validation_error!(current_settings)
diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb
index 0f86fdb3075..b93fdc62808 100644
--- a/lib/api/snippets.rb
+++ b/lib/api/snippets.rb
@@ -58,10 +58,10 @@ module API
requires :title, type: String, desc: 'The title of a snippet'
requires :file_name, type: String, desc: 'The name of a snippet file'
requires :content, type: String, desc: 'The content of a snippet'
- optional :visibility_level, type: Integer,
- values: Gitlab::VisibilityLevel.values,
- default: Gitlab::VisibilityLevel::INTERNAL,
- desc: 'The visibility level of the snippet'
+ optional :visibility, type: String,
+ values: Gitlab::VisibilityLevel.string_values,
+ default: 'internal',
+ desc: 'The visibility of the snippet'
end
post do
attrs = declared_params(include_missing: false).merge(request: request, api: true)
@@ -85,10 +85,10 @@ module API
optional :title, type: String, desc: 'The title of a snippet'
optional :file_name, type: String, desc: 'The name of a snippet file'
optional :content, type: String, desc: 'The content of a snippet'
- optional :visibility_level, type: Integer,
- values: Gitlab::VisibilityLevel.values,
- desc: 'The visibility level of the snippet'
- at_least_one_of :title, :file_name, :content, :visibility_level
+ optional :visibility, type: String,
+ values: Gitlab::VisibilityLevel.string_values,
+ desc: 'The visibility of the snippet'
+ at_least_one_of :title, :file_name, :content, :visibility
end
put ':id' do
snippet = snippets_for_current_user.find_by(id: params.delete(:id))
diff --git a/lib/api/v3/entities.rb b/lib/api/v3/entities.rb
index 11d0e6dbf71..270d99a2348 100644
--- a/lib/api/v3/entities.rb
+++ b/lib/api/v3/entities.rb
@@ -45,6 +45,147 @@ module API
expose :created_at, :updated_at
expose :awardable_id, :awardable_type
end
+
+ class Project < Grape::Entity
+ expose :id, :description, :default_branch, :tag_list
+ expose :public?, as: :public
+ expose :archived?, as: :archived
+ expose :visibility_level, :ssh_url_to_repo, :http_url_to_repo, :web_url
+ expose :owner, using: ::API::Entities::UserBasic, unless: ->(project, options) { project.group }
+ expose :name, :name_with_namespace
+ expose :path, :path_with_namespace
+ expose :container_registry_enabled
+
+ # Expose old field names with the new permissions methods to keep API compatible
+ expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:current_user]) }
+ expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) }
+ expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) }
+ expose(:builds_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) }
+ expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) }
+
+ expose :created_at, :last_activity_at
+ expose :shared_runners_enabled
+ expose :lfs_enabled?, as: :lfs_enabled
+ expose :creator_id
+ expose :namespace, using: 'API::Entities::Namespace'
+ expose :forked_from_project, using: ::API::Entities::BasicProjectDetails, if: lambda{ |project, options| project.forked? }
+ expose :avatar_url
+ expose :star_count, :forks_count
+ expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) && project.default_issues_tracker? }
+ expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
+ expose :public_builds
+ expose :shared_with_groups do |project, options|
+ ::API::Entities::SharedGroup.represent(project.project_group_links.all, options)
+ end
+ expose :only_allow_merge_if_pipeline_succeeds, as: :only_allow_merge_if_build_succeeds
+ expose :request_access_enabled
+ expose :only_allow_merge_if_all_discussions_are_resolved
+
+ expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics
+ end
+
+ class ProjectWithAccess < Project
+ expose :permissions do
+ expose :project_access, using: ::API::Entities::ProjectAccess do |project, options|
+ project.project_members.find_by(user_id: options[:current_user].id)
+ end
+
+ expose :group_access, using: ::API::Entities::GroupAccess do |project, options|
+ if project.group
+ project.group.group_members.find_by(user_id: options[:current_user].id)
+ end
+ end
+ end
+ end
+
+ class MergeRequest < Grape::Entity
+ expose :id, :iid
+ expose(:project_id) { |entity| entity.project.id }
+ expose :title, :description
+ expose :state, :created_at, :updated_at
+ expose :target_branch, :source_branch
+ expose :upvotes, :downvotes
+ expose :author, :assignee, using: ::API::Entities::UserBasic
+ expose :source_project_id, :target_project_id
+ expose :label_names, as: :labels
+ expose :work_in_progress?, as: :work_in_progress
+ expose :milestone, using: ::API::Entities::Milestone
+ expose :merge_when_pipeline_succeeds, as: :merge_when_build_succeeds
+ expose :merge_status
+ expose :diff_head_sha, as: :sha
+ expose :merge_commit_sha
+ expose :subscribed do |merge_request, options|
+ merge_request.subscribed?(options[:current_user], options[:project])
+ end
+ expose :user_notes_count
+ expose :should_remove_source_branch?, as: :should_remove_source_branch
+ expose :force_remove_source_branch?, as: :force_remove_source_branch
+
+ expose :web_url do |merge_request, options|
+ Gitlab::UrlBuilder.build(merge_request)
+ end
+ end
+
+ class Group < Grape::Entity
+ expose :id, :name, :path, :description, :visibility_level
+ expose :lfs_enabled?, as: :lfs_enabled
+ expose :avatar_url
+ expose :web_url
+ expose :request_access_enabled
+ expose :full_name, :full_path
+ expose :parent_id
+
+ expose :statistics, if: :statistics do
+ with_options format_with: -> (value) { value.to_i } do
+ expose :storage_size
+ expose :repository_size
+ expose :lfs_objects_size
+ expose :build_artifacts_size
+ end
+ end
+ end
+
+ class GroupDetail < Group
+ expose :projects, using: Entities::Project
+ expose :shared_projects, using: Entities::Project
+ end
+
+ class ApplicationSetting < Grape::Entity
+ expose :id
+ expose :default_projects_limit
+ expose :signup_enabled
+ expose :signin_enabled
+ expose :gravatar_enabled
+ expose :sign_in_text
+ expose :after_sign_up_text
+ expose :created_at
+ expose :updated_at
+ expose :home_page_url
+ expose :default_branch_protection
+ expose :restricted_visibility_levels
+ expose :max_attachment_size
+ expose :session_expire_delay
+ expose :default_project_visibility
+ expose :default_snippet_visibility
+ expose :default_group_visibility
+ expose :domain_whitelist
+ expose :domain_blacklist_enabled
+ expose :domain_blacklist
+ expose :user_oauth_applications
+ expose :after_sign_out_path
+ expose :container_registry_token_expire_delay
+ expose :repository_storage
+ expose :repository_storages
+ expose :koding_enabled
+ expose :koding_url
+ expose :plantuml_enabled
+ expose :plantuml_url
+ expose :terminal_max_session_time
+ end
+
+ class Environment < ::API::Entities::EnvironmentBasic
+ expose :project, using: Entities::Project
+ end
end
end
end
diff --git a/lib/api/v3/environments.rb b/lib/api/v3/environments.rb
index 3effccfa708..3056b70e6ef 100644
--- a/lib/api/v3/environments.rb
+++ b/lib/api/v3/environments.rb
@@ -1,6 +1,7 @@
module API
module V3
class Environments < Grape::API
+ include ::API::Helpers::CustomValidators
include PaginationParams
before { authenticate! }
@@ -9,9 +10,66 @@ module API
requires :id, type: String, desc: 'The project ID'
end
resource :projects do
+ desc 'Get all environments of the project' do
+ detail 'This feature was introduced in GitLab 8.11.'
+ success Entities::Environment
+ end
+ params do
+ use :pagination
+ end
+ get ':id/environments' do
+ authorize! :read_environment, user_project
+
+ present paginate(user_project.environments), with: Entities::Environment
+ end
+
+ desc 'Creates a new environment' do
+ detail 'This feature was introduced in GitLab 8.11.'
+ success Entities::Environment
+ end
+ params do
+ requires :name, type: String, desc: 'The name of the environment to be created'
+ optional :external_url, type: String, desc: 'URL on which this deployment is viewable'
+ optional :slug, absence: { message: "is automatically generated and cannot be changed" }
+ end
+ post ':id/environments' do
+ authorize! :create_environment, user_project
+
+ environment = user_project.environments.create(declared_params)
+
+ if environment.persisted?
+ present environment, with: Entities::Environment
+ else
+ render_validation_error!(environment)
+ end
+ end
+
+ desc 'Updates an existing environment' do
+ detail 'This feature was introduced in GitLab 8.11.'
+ success Entities::Environment
+ end
+ params do
+ requires :environment_id, type: Integer, desc: 'The environment ID'
+ optional :name, type: String, desc: 'The new environment name'
+ optional :external_url, type: String, desc: 'The new URL on which this deployment is viewable'
+ optional :slug, absence: { message: "is automatically generated and cannot be changed" }
+ end
+ put ':id/environments/:environment_id' do
+ authorize! :update_environment, user_project
+
+ environment = user_project.environments.find(params[:environment_id])
+
+ update_params = declared_params(include_missing: false).extract!(:name, :external_url)
+ if environment.update(update_params)
+ present environment, with: Entities::Environment
+ else
+ render_validation_error!(environment)
+ end
+ end
+
desc 'Deletes an existing environment' do
detail 'This feature was introduced in GitLab 8.11.'
- success ::API::Entities::Environment
+ success Entities::Environment
end
params do
requires :environment_id, type: Integer, desc: 'The environment ID'
@@ -21,7 +79,7 @@ module API
environment = user_project.environments.find(params[:environment_id])
- present environment.destroy, with: ::API::Entities::Environment
+ present environment.destroy, with: Entities::Environment
end
end
end
diff --git a/lib/api/v3/groups.rb b/lib/api/v3/groups.rb
index c826bc4fe0b..0aad87a3f58 100644
--- a/lib/api/v3/groups.rb
+++ b/lib/api/v3/groups.rb
@@ -6,13 +6,20 @@ module API
before { authenticate! }
helpers do
+ params :optional_params do
+ optional :description, type: String, desc: 'The description of the group'
+ optional :visibility_level, type: Integer, desc: 'The visibility level of the group'
+ optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group'
+ optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
+ end
+
params :statistics_params do
optional :statistics, type: Boolean, default: false, desc: 'Include project statistics'
end
def present_groups(groups, options = {})
options = options.reverse_merge(
- with: ::API::Entities::Group,
+ with: Entities::Group,
current_user: current_user,
)
@@ -22,8 +29,36 @@ module API
end
resource :groups do
+ desc 'Get a groups list' do
+ success Entities::Group
+ end
+ params do
+ use :statistics_params
+ optional :skip_groups, type: Array[Integer], desc: 'Array of group ids to exclude from list'
+ optional :all_available, type: Boolean, desc: 'Show all group that you have access to'
+ optional :search, type: String, desc: 'Search for a specific group'
+ optional :order_by, type: String, values: %w[name path], default: 'name', desc: 'Order by name or path'
+ optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
+ use :pagination
+ end
+ get do
+ groups = if current_user.admin
+ Group.all
+ elsif params[:all_available]
+ GroupsFinder.new.execute(current_user)
+ else
+ current_user.groups
+ end
+
+ groups = groups.search(params[:search]) if params[:search].present?
+ groups = groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present?
+ groups = groups.reorder(params[:order_by] => params[:sort])
+
+ present_groups groups, statistics: params[:statistics] && current_user.is_admin?
+ end
+
desc 'Get list of owned groups for authenticated user' do
- success ::API::Entities::Group
+ success Entities::Group
end
params do
use :pagination
@@ -32,6 +67,114 @@ module API
get '/owned' do
present_groups current_user.owned_groups, statistics: params[:statistics]
end
+
+ desc 'Create a group. Available only for users who can create groups.' do
+ success Entities::Group
+ end
+ params do
+ requires :name, type: String, desc: 'The name of the group'
+ requires :path, type: String, desc: 'The path of the group'
+ optional :parent_id, type: Integer, desc: 'The parent group id for creating nested group'
+ use :optional_params
+ end
+ post do
+ authorize! :create_group
+
+ group = ::Groups::CreateService.new(current_user, declared_params(include_missing: false)).execute
+
+ if group.persisted?
+ present group, with: Entities::Group, current_user: current_user
+ else
+ render_api_error!("Failed to save group #{group.errors.messages}", 400)
+ end
+ end
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID of a group'
+ end
+ resource :groups do
+ desc 'Update a group. Available only for users who can administrate groups.' do
+ success Entities::Group
+ end
+ params do
+ optional :name, type: String, desc: 'The name of the group'
+ optional :path, type: String, desc: 'The path of the group'
+ use :optional_params
+ at_least_one_of :name, :path, :description, :visibility_level,
+ :lfs_enabled, :request_access_enabled
+ end
+ put ':id' do
+ group = find_group!(params[:id])
+ authorize! :admin_group, group
+
+ if ::Groups::UpdateService.new(group, current_user, declared_params(include_missing: false)).execute
+ present group, with: Entities::GroupDetail, current_user: current_user
+ else
+ render_validation_error!(group)
+ end
+ end
+
+ desc 'Get a single group, with containing projects.' do
+ success Entities::GroupDetail
+ end
+ get ":id" do
+ group = find_group!(params[:id])
+ present group, with: Entities::GroupDetail, current_user: current_user
+ end
+
+ desc 'Remove a group.'
+ delete ":id" do
+ group = find_group!(params[:id])
+ authorize! :admin_group, group
+ present ::Groups::DestroyService.new(group, current_user).execute, with: Entities::GroupDetail, current_user: current_user
+ end
+
+ desc 'Get a list of projects in this group.' do
+ success Entities::Project
+ end
+ params do
+ optional :archived, type: Boolean, default: false, desc: 'Limit by archived status'
+ optional :visibility, type: String, values: %w[public internal private],
+ desc: 'Limit by visibility'
+ optional :search, type: String, desc: 'Return list of authorized projects matching the search criteria'
+ optional :order_by, type: String, values: %w[id name path created_at updated_at last_activity_at],
+ default: 'created_at', desc: 'Return projects ordered by field'
+ optional :sort, type: String, values: %w[asc desc], default: 'desc',
+ desc: 'Return projects sorted in ascending and descending order'
+ optional :simple, type: Boolean, default: false,
+ desc: 'Return only the ID, URL, name, and path of each project'
+ optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user'
+ optional :starred, type: Boolean, default: false, desc: 'Limit by starred status'
+
+ use :pagination
+ end
+ get ":id/projects" do
+ group = find_group!(params[:id])
+ projects = GroupProjectsFinder.new(group).execute(current_user)
+ projects = filter_projects(projects)
+ entity = params[:simple] ? ::API::Entities::BasicProjectDetails : Entities::Project
+ present paginate(projects), with: entity, current_user: current_user
+ end
+
+ desc 'Transfer a project to the group namespace. Available only for admin.' do
+ success Entities::GroupDetail
+ end
+ params do
+ requires :project_id, type: String, desc: 'The ID or path of the project'
+ end
+ post ":id/projects/:project_id" do
+ authenticated_as_admin!
+ group = find_group!(params[:id])
+ project = find_project!(params[:project_id])
+ result = ::Projects::TransferService.new(project, current_user).execute(group)
+
+ if result
+ present group, with: Entities::GroupDetail, current_user: current_user
+ else
+ render_api_error!("Failed to transfer project #{project.errors.messages}", 400)
+ end
+ end
end
end
end
diff --git a/lib/api/v3/merge_requests.rb b/lib/api/v3/merge_requests.rb
index c6574a9104b..654e818e1b5 100644
--- a/lib/api/v3/merge_requests.rb
+++ b/lib/api/v3/merge_requests.rb
@@ -39,7 +39,7 @@ module API
desc 'List merge requests' do
detail 'iid filter is deprecated have been removed on V4'
- success ::API::Entities::MergeRequest
+ success ::API::V3::Entities::MergeRequest
end
params do
optional :state, type: String, values: %w[opened closed merged all], default: 'all',
@@ -66,11 +66,11 @@ module API
end
merge_requests = merge_requests.reorder(params[:order_by] => params[:sort])
- present paginate(merge_requests), with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project
+ present paginate(merge_requests), with: ::API::V3::Entities::MergeRequest, current_user: current_user, project: user_project
end
desc 'Create a merge request' do
- success ::API::Entities::MergeRequest
+ success ::API::V3::Entities::MergeRequest
end
params do
requires :title, type: String, desc: 'The title of the merge request'
@@ -89,7 +89,7 @@ module API
merge_request = ::MergeRequests::CreateService.new(user_project, current_user, mr_params).execute
if merge_request.valid?
- present merge_request, with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project
+ present merge_request, with: ::API::V3::Entities::MergeRequest, current_user: current_user, project: user_project
else
handle_merge_request_errors! merge_request.errors
end
@@ -116,12 +116,12 @@ module API
if status == :deprecated
detail DEPRECATION_MESSAGE
end
- success ::API::Entities::MergeRequest
+ success ::API::V3::Entities::MergeRequest
end
get path do
merge_request = find_merge_request_with_access(params[:merge_request_id])
- present merge_request, with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project
+ present merge_request, with: ::API::V3::Entities::MergeRequest, current_user: current_user, project: user_project
end
desc 'Get the commits of a merge request' do
@@ -143,7 +143,7 @@ module API
end
desc 'Update a merge request' do
- success ::API::Entities::MergeRequest
+ success ::API::V3::Entities::MergeRequest
end
params do
optional :title, type: String, allow_blank: false, desc: 'The title of the merge request'
@@ -164,21 +164,21 @@ module API
merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, mr_params).execute(merge_request)
if merge_request.valid?
- present merge_request, with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project
+ present merge_request, with: ::API::V3::Entities::MergeRequest, current_user: current_user, project: user_project
else
handle_merge_request_errors! merge_request.errors
end
end
desc 'Merge a merge request' do
- success ::API::Entities::MergeRequest
+ success ::API::V3::Entities::MergeRequest
end
params do
optional :merge_commit_message, type: String, desc: 'Custom merge commit message'
optional :should_remove_source_branch, type: Boolean,
desc: 'When true, the source branch will be deleted if possible'
optional :merge_when_build_succeeds, type: Boolean,
- desc: 'When true, this merge request will be merged when the pipeline succeeds'
+ desc: 'When true, this merge request will be merged when the build succeeds'
optional :sha, type: String, desc: 'When present, must have the HEAD SHA of the source branch'
end
put "#{path}/merge" do
@@ -211,16 +211,16 @@ module API
.execute(merge_request)
end
- present merge_request, with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project
+ present merge_request, with: ::API::V3::Entities::MergeRequest, current_user: current_user, project: user_project
end
- desc 'Cancel merge if "Merge When Pipeline Succeeds" is enabled' do
- success ::API::Entities::MergeRequest
+ desc 'Cancel merge if "Merge When Build succeeds" is enabled' do
+ success ::API::V3::Entities::MergeRequest
end
post "#{path}/cancel_merge_when_build_succeeds" do
merge_request = find_project_merge_request(params[:merge_request_id])
- unauthorized! unless merge_request.can_cancel_merge_when_build_succeeds?(current_user)
+ unauthorized! unless merge_request.can_cancel_merge_when_pipeline_succeeds?(current_user)
::MergeRequest::MergeWhenPipelineSucceedsService
.new(merge_request.target_project, current_user)
diff --git a/lib/api/v3/projects.rb b/lib/api/v3/projects.rb
index 881d52e4aa4..47bfc12035a 100644
--- a/lib/api/v3/projects.rb
+++ b/lib/api/v3/projects.rb
@@ -5,6 +5,10 @@ module API
before { authenticate_non_get! }
+ after_validation do
+ set_only_allow_merge_if_pipeline_succeeds!
+ end
+
helpers do
params :optional_params do
optional :description, type: String, desc: 'The description of the project'
@@ -25,6 +29,7 @@ module API
optional :public_builds, type: Boolean, desc: 'Perform public builds'
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
optional :only_allow_merge_if_build_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed'
+ optional :only_allow_merge_if_pipeline_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed'
optional :only_allow_merge_if_all_discussions_are_resolved, type: Boolean, desc: 'Only allow to merge if all discussions are resolved'
end
@@ -37,6 +42,12 @@ module API
end
attrs
end
+
+ def set_only_allow_merge_if_pipeline_succeeds!
+ if params.has_key?(:only_allow_merge_if_build_succeeds)
+ params[:only_allow_merge_if_pipeline_succeeds] = params.delete(:only_allow_merge_if_build_succeeds)
+ end
+ end
end
resource :projects do
@@ -75,7 +86,7 @@ module API
def present_projects(projects, options = {})
options = options.reverse_merge(
- with: ::API::Entities::Project,
+ with: ::API::V3::Entities::Project,
current_user: current_user,
simple: params[:simple],
)
@@ -95,7 +106,7 @@ module API
use :collection_params
end
get '/visible' do
- entity = current_user ? ::API::Entities::ProjectWithAccess : ::API::Entities::BasicProjectDetails
+ entity = current_user ? ::API::V3::Entities::ProjectWithAccess : ::API::Entities::BasicProjectDetails
present_projects ProjectsFinder.new.execute(current_user), with: entity
end
@@ -109,7 +120,7 @@ module API
authenticate!
present_projects current_user.authorized_projects,
- with: ::API::Entities::ProjectWithAccess
+ with: ::API::V3::Entities::ProjectWithAccess
end
desc 'Get an owned projects list for authenticated user' do
@@ -123,7 +134,7 @@ module API
authenticate!
present_projects current_user.owned_projects,
- with: ::API::Entities::ProjectWithAccess,
+ with: ::API::V3::Entities::ProjectWithAccess,
statistics: params[:statistics]
end
@@ -149,11 +160,11 @@ module API
get '/all' do
authenticated_as_admin!
- present_projects Project.all, with: ::API::Entities::ProjectWithAccess, statistics: params[:statistics]
+ present_projects Project.all, with: ::API::V3::Entities::ProjectWithAccess, statistics: params[:statistics]
end
desc 'Search for projects the current user has access to' do
- success ::API::Entities::Project
+ success ::API::V3::Entities::Project
end
params do
requires :query, type: String, desc: 'The project name to be searched'
@@ -165,11 +176,11 @@ module API
projects = search_service.objects('projects', params[:page])
projects = projects.reorder(params[:order_by] => params[:sort])
- present paginate(projects), with: ::API::Entities::Project
+ present paginate(projects), with: ::API::V3::Entities::Project
end
desc 'Create new project' do
- success ::API::Entities::Project
+ success ::API::V3::Entities::Project
end
params do
optional :name, type: String, desc: 'The name of the project'
@@ -183,7 +194,7 @@ module API
project = ::Projects::CreateService.new(current_user, attrs).execute
if project.saved?
- present project, with: ::API::Entities::Project,
+ present project, with: ::API::V3::Entities::Project,
user_can_admin_project: can?(current_user, :admin_project, project)
else
if project.errors[:limit_reached].present?
@@ -194,7 +205,7 @@ module API
end
desc 'Create new project for a specified user. Only available to admin users.' do
- success ::API::Entities::Project
+ success ::API::V3::Entities::Project
end
params do
requires :name, type: String, desc: 'The name of the project'
@@ -212,7 +223,7 @@ module API
project = ::Projects::CreateService.new(user, attrs).execute
if project.saved?
- present project, with: ::API::Entities::Project,
+ present project, with: ::API::V3::Entities::Project,
user_can_admin_project: can?(current_user, :admin_project, project)
else
render_validation_error!(project)
@@ -225,10 +236,10 @@ module API
end
resource :projects, requirements: { id: /[^\/]+/ } do
desc 'Get a single project' do
- success ::API::Entities::ProjectWithAccess
+ success ::API::V3::Entities::ProjectWithAccess
end
get ":id" do
- entity = current_user ? ::API::Entities::ProjectWithAccess : ::API::Entities::BasicProjectDetails
+ entity = current_user ? ::API::V3::Entities::ProjectWithAccess : ::API::Entities::BasicProjectDetails
present user_project, with: entity, current_user: current_user,
user_can_admin_project: can?(current_user, :admin_project, user_project)
end
@@ -244,7 +255,7 @@ module API
end
desc 'Fork new project for the current user or provided namespace.' do
- success ::API::Entities::Project
+ success ::API::V3::Entities::Project
end
params do
optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be forked into'
@@ -270,13 +281,13 @@ module API
if forked_project.errors.any?
conflict!(forked_project.errors.messages)
else
- present forked_project, with: ::API::Entities::Project,
+ present forked_project, with: ::API::V3::Entities::Project,
user_can_admin_project: can?(current_user, :admin_project, forked_project)
end
end
desc 'Update an existing project' do
- success ::API::Entities::Project
+ success ::API::V3::Entities::Project
end
params do
optional :name, type: String, desc: 'The name of the project'
@@ -300,7 +311,7 @@ module API
result = ::Projects::UpdateService.new(user_project, current_user, attrs).execute
if result[:status] == :success
- present user_project, with: ::API::Entities::Project,
+ present user_project, with: ::API::V3::Entities::Project,
user_can_admin_project: can?(current_user, :admin_project, user_project)
else
render_validation_error!(user_project)
@@ -308,29 +319,29 @@ module API
end
desc 'Archive a project' do
- success ::API::Entities::Project
+ success ::API::V3::Entities::Project
end
post ':id/archive' do
authorize!(:archive_project, user_project)
user_project.archive!
- present user_project, with: ::API::Entities::Project
+ present user_project, with: ::API::V3::Entities::Project
end
desc 'Unarchive a project' do
- success ::API::Entities::Project
+ success ::API::V3::Entities::Project
end
post ':id/unarchive' do
authorize!(:archive_project, user_project)
user_project.unarchive!
- present user_project, with: ::API::Entities::Project
+ present user_project, with: ::API::V3::Entities::Project
end
desc 'Star a project' do
- success ::API::Entities::Project
+ success ::API::V3::Entities::Project
end
post ':id/star' do
if current_user.starred?(user_project)
@@ -339,19 +350,19 @@ module API
current_user.toggle_star(user_project)
user_project.reload
- present user_project, with: ::API::Entities::Project
+ present user_project, with: ::API::V3::Entities::Project
end
end
desc 'Unstar a project' do
- success ::API::Entities::Project
+ success ::API::V3::Entities::Project
end
delete ':id/star' do
if current_user.starred?(user_project)
current_user.toggle_star(user_project)
user_project.reload
- present user_project, with: ::API::Entities::Project
+ present user_project, with: ::API::V3::Entities::Project
else
not_modified!
end
diff --git a/lib/api/v3/settings.rb b/lib/api/v3/settings.rb
new file mode 100644
index 00000000000..748d6b97d4f
--- /dev/null
+++ b/lib/api/v3/settings.rb
@@ -0,0 +1,137 @@
+module API
+ module V3
+ class Settings < Grape::API
+ before { authenticated_as_admin! }
+
+ helpers do
+ def current_settings
+ @current_setting ||=
+ (ApplicationSetting.current || ApplicationSetting.create_from_defaults)
+ end
+ end
+
+ desc 'Get the current application settings' do
+ success Entities::ApplicationSetting
+ end
+ get "application/settings" do
+ present current_settings, with: Entities::ApplicationSetting
+ end
+
+ desc 'Modify application settings' do
+ success Entities::ApplicationSetting
+ end
+ params do
+ optional :default_branch_protection, type: Integer, values: [0, 1, 2], desc: 'Determine if developers can push to master'
+ optional :default_project_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default project visibility'
+ optional :default_snippet_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default snippet visibility'
+ optional :default_group_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default group visibility'
+ optional :restricted_visibility_levels, type: Array[String], desc: 'Selected levels cannot be used by non-admin users for projects or snippets. If the public level is restricted, user profiles are only visible to logged in users.'
+ optional :import_sources, type: Array[String], values: %w[github bitbucket gitlab google_code fogbugz git gitlab_project],
+ desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com'
+ optional :disabled_oauth_sign_in_sources, type: Array[String], desc: 'Disable certain OAuth sign-in sources'
+ optional :enabled_git_access_protocol, type: String, values: %w[ssh http nil], desc: 'Allow only the selected protocols to be used for Git access.'
+ optional :gravatar_enabled, type: Boolean, desc: 'Flag indicating if the Gravatar service is enabled'
+ optional :default_projects_limit, type: Integer, desc: 'The maximum number of personal projects'
+ optional :max_attachment_size, type: Integer, desc: 'Maximum attachment size in MB'
+ optional :session_expire_delay, type: Integer, desc: 'Session duration in minutes. GitLab restart is required to apply changes.'
+ optional :user_oauth_applications, type: Boolean, desc: 'Allow users to register any application to use GitLab as an OAuth provider'
+ optional :user_default_external, type: Boolean, desc: 'Newly registered users will by default be external'
+ optional :signup_enabled, type: Boolean, desc: 'Flag indicating if sign up is enabled'
+ optional :send_user_confirmation_email, type: Boolean, desc: 'Send confirmation email on sign-up'
+ optional :domain_whitelist, type: String, desc: 'ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com'
+ optional :domain_blacklist_enabled, type: Boolean, desc: 'Enable domain blacklist for sign ups'
+ given domain_blacklist_enabled: ->(val) { val } do
+ requires :domain_blacklist, type: String, desc: 'Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com'
+ end
+ optional :after_sign_up_text, type: String, desc: 'Text shown after sign up'
+ optional :signin_enabled, type: Boolean, desc: 'Flag indicating if sign in is enabled'
+ optional :require_two_factor_authentication, type: Boolean, desc: 'Require all users to setup Two-factor authentication'
+ given require_two_factor_authentication: ->(val) { val } do
+ requires :two_factor_grace_period, type: Integer, desc: 'Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication'
+ end
+ optional :home_page_url, type: String, desc: 'We will redirect non-logged in users to this page'
+ optional :after_sign_out_path, type: String, desc: 'We will redirect users to this page after they sign out'
+ optional :sign_in_text, type: String, desc: 'The sign in text of the GitLab application'
+ optional :help_page_text, type: String, desc: 'Custom text displayed on the help page'
+ optional :shared_runners_enabled, type: Boolean, desc: 'Enable shared runners for new projects'
+ given shared_runners_enabled: ->(val) { val } do
+ requires :shared_runners_text, type: String, desc: 'Shared runners text '
+ end
+ optional :max_artifacts_size, type: Integer, desc: "Set the maximum file size each build's artifacts can have"
+ optional :max_pages_size, type: Integer, desc: 'Maximum size of pages in MB'
+ optional :container_registry_token_expire_delay, type: Integer, desc: 'Authorization token duration (minutes)'
+ optional :metrics_enabled, type: Boolean, desc: 'Enable the InfluxDB metrics'
+ given metrics_enabled: ->(val) { val } do
+ requires :metrics_host, type: String, desc: 'The InfluxDB host'
+ requires :metrics_port, type: Integer, desc: 'The UDP port to use for connecting to InfluxDB'
+ requires :metrics_pool_size, type: Integer, desc: 'The amount of InfluxDB connections to open'
+ requires :metrics_timeout, type: Integer, desc: 'The amount of seconds after which an InfluxDB connection will time out'
+ requires :metrics_method_call_threshold, type: Integer, desc: 'A method call is only tracked when it takes longer to complete than the given amount of milliseconds.'
+ requires :metrics_sample_interval, type: Integer, desc: 'The sampling interval in seconds'
+ requires :metrics_packet_size, type: Integer, desc: 'The amount of points to store in a single UDP packet'
+ end
+ optional :sidekiq_throttling_enabled, type: Boolean, desc: 'Enable Sidekiq Job Throttling'
+ given sidekiq_throttling_enabled: ->(val) { val } do
+ requires :sidekiq_throttling_queus, type: Array[String], desc: 'Choose which queues you wish to throttle'
+ requires :sidekiq_throttling_factor, type: Float, desc: 'The factor by which the queues should be throttled. A value between 0.0 and 1.0, exclusive.'
+ end
+ optional :recaptcha_enabled, type: Boolean, desc: 'Helps prevent bots from creating accounts'
+ given recaptcha_enabled: ->(val) { val } do
+ requires :recaptcha_site_key, type: String, desc: 'Generate site key at http://www.google.com/recaptcha'
+ requires :recaptcha_private_key, type: String, desc: 'Generate private key at http://www.google.com/recaptcha'
+ end
+ optional :akismet_enabled, type: Boolean, desc: 'Helps prevent bots from creating issues'
+ given akismet_enabled: ->(val) { val } do
+ requires :akismet_api_key, type: String, desc: 'Generate API key at http://www.akismet.com'
+ end
+ optional :admin_notification_email, type: String, desc: 'Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area.'
+ optional :sentry_enabled, type: Boolean, desc: 'Sentry is an error reporting and logging tool which is currently not shipped with GitLab, get it here: https://getsentry.com'
+ given sentry_enabled: ->(val) { val } do
+ requires :sentry_dsn, type: String, desc: 'Sentry Data Source Name'
+ end
+ optional :repository_storage, type: String, desc: 'Storage paths for new projects'
+ optional :repository_checks_enabled, type: Boolean, desc: "GitLab will periodically run 'git fsck' in all project and wiki repositories to look for silent disk corruption issues."
+ optional :koding_enabled, type: Boolean, desc: 'Enable Koding'
+ given koding_enabled: ->(val) { val } do
+ requires :koding_url, type: String, desc: 'The Koding team URL'
+ end
+ optional :plantuml_enabled, type: Boolean, desc: 'Enable PlantUML'
+ given plantuml_enabled: ->(val) { val } do
+ requires :plantuml_url, type: String, desc: 'The PlantUML server URL'
+ end
+ optional :version_check_enabled, type: Boolean, desc: 'Let GitLab inform you when an update is available.'
+ optional :email_author_in_body, type: Boolean, desc: 'Some email servers do not support overriding the email sender name. Enable this option to include the name of the author of the issue, merge request or comment in the email body instead.'
+ optional :html_emails_enabled, type: Boolean, desc: 'By default GitLab sends emails in HTML and plain text formats so mail clients can choose what format to use. Disable this option if you only want to send emails in plain text format.'
+ optional :housekeeping_enabled, type: Boolean, desc: 'Enable automatic repository housekeeping (git repack, git gc)'
+ given housekeeping_enabled: ->(val) { val } do
+ requires :housekeeping_bitmaps_enabled, type: Boolean, desc: "Creating pack file bitmaps makes housekeeping take a little longer but bitmaps should accelerate 'git clone' performance."
+ requires :housekeeping_incremental_repack_period, type: Integer, desc: "Number of Git pushes after which an incremental 'git repack' is run."
+ requires :housekeeping_full_repack_period, type: Integer, desc: "Number of Git pushes after which a full 'git repack' is run."
+ requires :housekeeping_gc_period, type: Integer, desc: "Number of Git pushes after which 'git gc' is run."
+ end
+ optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.'
+ at_least_one_of :default_branch_protection, :default_project_visibility, :default_snippet_visibility,
+ :default_group_visibility, :restricted_visibility_levels, :import_sources,
+ :enabled_git_access_protocol, :gravatar_enabled, :default_projects_limit,
+ :max_attachment_size, :session_expire_delay, :disabled_oauth_sign_in_sources,
+ :user_oauth_applications, :user_default_external, :signup_enabled,
+ :send_user_confirmation_email, :domain_whitelist, :domain_blacklist_enabled,
+ :after_sign_up_text, :signin_enabled, :require_two_factor_authentication,
+ :home_page_url, :after_sign_out_path, :sign_in_text, :help_page_text,
+ :shared_runners_enabled, :max_artifacts_size, :max_pages_size, :container_registry_token_expire_delay,
+ :metrics_enabled, :sidekiq_throttling_enabled, :recaptcha_enabled,
+ :akismet_enabled, :admin_notification_email, :sentry_enabled,
+ :repository_storage, :repository_checks_enabled, :koding_enabled, :plantuml_enabled,
+ :version_check_enabled, :email_author_in_body, :html_emails_enabled,
+ :housekeeping_enabled, :terminal_max_session_time
+ end
+ put "application/settings" do
+ if current_settings.update_attributes(declared_params(include_missing: false))
+ present current_settings, with: Entities::ApplicationSetting
+ else
+ render_validation_error!(current_settings)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/v3/snippets.rb b/lib/api/v3/snippets.rb
new file mode 100644
index 00000000000..07dac7e9904
--- /dev/null
+++ b/lib/api/v3/snippets.rb
@@ -0,0 +1,138 @@
+module API
+ module V3
+ class Snippets < Grape::API
+ include PaginationParams
+
+ before { authenticate! }
+
+ resource :snippets do
+ helpers do
+ def snippets_for_current_user
+ SnippetsFinder.new.execute(current_user, filter: :by_user, user: current_user)
+ end
+
+ def public_snippets
+ SnippetsFinder.new.execute(current_user, filter: :public)
+ end
+ end
+
+ desc 'Get a snippets list for authenticated user' do
+ detail 'This feature was introduced in GitLab 8.15.'
+ success ::API::Entities::PersonalSnippet
+ end
+ params do
+ use :pagination
+ end
+ get do
+ present paginate(snippets_for_current_user), with: ::API::Entities::PersonalSnippet
+ end
+
+ desc 'List all public snippets current_user has access to' do
+ detail 'This feature was introduced in GitLab 8.15.'
+ success ::API::Entities::PersonalSnippet
+ end
+ params do
+ use :pagination
+ end
+ get 'public' do
+ present paginate(public_snippets), with: ::API::Entities::PersonalSnippet
+ end
+
+ desc 'Get a single snippet' do
+ detail 'This feature was introduced in GitLab 8.15.'
+ success ::API::Entities::PersonalSnippet
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of a snippet'
+ end
+ get ':id' do
+ snippet = snippets_for_current_user.find(params[:id])
+ present snippet, with: ::API::Entities::PersonalSnippet
+ end
+
+ desc 'Create new snippet' do
+ detail 'This feature was introduced in GitLab 8.15.'
+ success ::API::Entities::PersonalSnippet
+ end
+ params do
+ requires :title, type: String, desc: 'The title of a snippet'
+ requires :file_name, type: String, desc: 'The name of a snippet file'
+ requires :content, type: String, desc: 'The content of a snippet'
+ optional :visibility_level, type: Integer,
+ values: Gitlab::VisibilityLevel.values,
+ default: Gitlab::VisibilityLevel::INTERNAL,
+ desc: 'The visibility level of the snippet'
+ end
+ post do
+ attrs = declared_params(include_missing: false).merge(request: request, api: true)
+ snippet = CreateSnippetService.new(nil, current_user, attrs).execute
+
+ if snippet.persisted?
+ present snippet, with: ::API::Entities::PersonalSnippet
+ else
+ render_validation_error!(snippet)
+ end
+ end
+
+ desc 'Update an existing snippet' do
+ detail 'This feature was introduced in GitLab 8.15.'
+ success ::API::Entities::PersonalSnippet
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of a snippet'
+ optional :title, type: String, desc: 'The title of a snippet'
+ optional :file_name, type: String, desc: 'The name of a snippet file'
+ optional :content, type: String, desc: 'The content of a snippet'
+ optional :visibility_level, type: Integer,
+ values: Gitlab::VisibilityLevel.values,
+ desc: 'The visibility level of the snippet'
+ at_least_one_of :title, :file_name, :content, :visibility_level
+ end
+ put ':id' do
+ snippet = snippets_for_current_user.find_by(id: params.delete(:id))
+ return not_found!('Snippet') unless snippet
+ authorize! :update_personal_snippet, snippet
+
+ attrs = declared_params(include_missing: false)
+
+ UpdateSnippetService.new(nil, current_user, snippet, attrs).execute
+ if snippet.persisted?
+ present snippet, with: ::API::Entities::PersonalSnippet
+ else
+ render_validation_error!(snippet)
+ end
+ end
+
+ desc 'Remove snippet' do
+ detail 'This feature was introduced in GitLab 8.15.'
+ success ::API::Entities::PersonalSnippet
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of a snippet'
+ end
+ delete ':id' do
+ snippet = snippets_for_current_user.find_by(id: params.delete(:id))
+ return not_found!('Snippet') unless snippet
+ authorize! :destroy_personal_snippet, snippet
+ snippet.destroy
+ no_content!
+ end
+
+ desc 'Get a raw snippet' do
+ detail 'This feature was introduced in GitLab 8.15.'
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of a snippet'
+ end
+ get ":id/raw" do
+ snippet = snippets_for_current_user.find_by(id: params.delete(:id))
+ return not_found!('Snippet') unless snippet
+
+ env['api.format'] = :txt
+ content_type 'text/plain'
+ present snippet.content
+ end
+ end
+ end
+ end
+end
diff --git a/lib/banzai/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb
index c973897f420..849e1142841 100644
--- a/lib/banzai/filter/user_reference_filter.rb
+++ b/lib/banzai/filter/user_reference_filter.rb
@@ -133,7 +133,7 @@ module Banzai
data = data_attribute(group: namespace.id)
content = link_content || Group.reference_prefix + group
- link_tag(url, data, content, namespace.name)
+ link_tag(url, data, content, namespace.full_name)
end
def link_to_user(user, namespace, link_content: nil)
diff --git a/lib/bitbucket/error/unauthorized.rb b/lib/bitbucket/error/unauthorized.rb
index 5e2eb57bb0e..efe10542f19 100644
--- a/lib/bitbucket/error/unauthorized.rb
+++ b/lib/bitbucket/error/unauthorized.rb
@@ -1,6 +1,5 @@
module Bitbucket
module Error
- class Unauthorized < StandardError
- end
+ Unauthorized = Class.new(StandardError)
end
end
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index 649ee4d018b..e390919ae1d 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -1,6 +1,6 @@
module Ci
class GitlabCiYamlProcessor
- class ValidationError < StandardError; end
+ ValidationError = Class.new(StandardError)
include Gitlab::Ci::Config::Entry::LegacyValidationHelpers
diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb
index 9ece84cc469..dd864eea3fa 100644
--- a/lib/extracts_path.rb
+++ b/lib/extracts_path.rb
@@ -2,7 +2,7 @@
# file path string when combined in a request parameter
module ExtractsPath
# Raised when given an invalid file path
- class InvalidPathError < StandardError; end
+ InvalidPathError = Class.new(StandardError)
# Given a string containing both a Git tree-ish, such as a branch or tag, and
# a filesystem path joined by forward slashes, attempts to separate the two.
diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb
index 3b210eeda9d..8c28009b9c6 100644
--- a/lib/gitlab/access.rb
+++ b/lib/gitlab/access.rb
@@ -5,7 +5,7 @@
#
module Gitlab
module Access
- class AccessDeniedError < StandardError; end
+ AccessDeniedError = Class.new(StandardError)
NO_ACCESS = 0
GUEST = 10
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index 89db6c3da46..0a5abc92190 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -1,6 +1,6 @@
module Gitlab
module Auth
- class MissingPersonalTokenError < StandardError; end
+ MissingPersonalTokenError = Class.new(StandardError)
SCOPES = [:api, :read_user].freeze
DEFAULT_SCOPES = [:api].freeze
diff --git a/lib/gitlab/ci/build/artifacts/metadata.rb b/lib/gitlab/ci/build/artifacts/metadata.rb
index cd2e83b4c27..a375ccbece0 100644
--- a/lib/gitlab/ci/build/artifacts/metadata.rb
+++ b/lib/gitlab/ci/build/artifacts/metadata.rb
@@ -6,7 +6,7 @@ module Gitlab
module Build
module Artifacts
class Metadata
- class ParserError < StandardError; end
+ ParserError = Class.new(StandardError)
VERSION_PATTERN = /^[\w\s]+(\d+\.\d+\.\d+)/
INVALID_PATH_PATTERN = %r{(^\.?\.?/)|(/\.?\.?/)}
diff --git a/lib/gitlab/ci/config/entry/factory.rb b/lib/gitlab/ci/config/entry/factory.rb
index 9f5e393d191..6be8288748f 100644
--- a/lib/gitlab/ci/config/entry/factory.rb
+++ b/lib/gitlab/ci/config/entry/factory.rb
@@ -6,7 +6,7 @@ module Gitlab
# Factory class responsible for fabricating entry objects.
#
class Factory
- class InvalidFactory < StandardError; end
+ InvalidFactory = Class.new(StandardError)
def initialize(entry)
@entry = entry
diff --git a/lib/gitlab/ci/config/entry/node.rb b/lib/gitlab/ci/config/entry/node.rb
index 5eef2868cd6..55a5447ab51 100644
--- a/lib/gitlab/ci/config/entry/node.rb
+++ b/lib/gitlab/ci/config/entry/node.rb
@@ -6,7 +6,7 @@ module Gitlab
# Base abstract class for each configuration entry node.
#
class Node
- class InvalidError < StandardError; end
+ InvalidError = Class.new(StandardError)
attr_reader :config, :metadata
attr_accessor :key, :parent, :description
diff --git a/lib/gitlab/ci/config/loader.rb b/lib/gitlab/ci/config/loader.rb
index dbf6eb0edbe..e7d9f6a7761 100644
--- a/lib/gitlab/ci/config/loader.rb
+++ b/lib/gitlab/ci/config/loader.rb
@@ -2,7 +2,7 @@ module Gitlab
module Ci
class Config
class Loader
- class FormatError < StandardError; end
+ FormatError = Class.new(StandardError)
def initialize(config)
@config = YAML.safe_load(config, [Symbol], [], true)
diff --git a/lib/gitlab/conflict/file.rb b/lib/gitlab/conflict/file.rb
index d80bc748209..75a213ef752 100644
--- a/lib/gitlab/conflict/file.rb
+++ b/lib/gitlab/conflict/file.rb
@@ -4,8 +4,7 @@ module Gitlab
include Gitlab::Routing.url_helpers
include IconsHelper
- class MissingResolution < ResolutionError
- end
+ MissingResolution = Class.new(ResolutionError)
CONTEXT_LINES = 3
diff --git a/lib/gitlab/conflict/file_collection.rb b/lib/gitlab/conflict/file_collection.rb
index fa5bd4649d4..990b719ecfd 100644
--- a/lib/gitlab/conflict/file_collection.rb
+++ b/lib/gitlab/conflict/file_collection.rb
@@ -1,8 +1,7 @@
module Gitlab
module Conflict
class FileCollection
- class ConflictSideMissing < StandardError
- end
+ ConflictSideMissing = Class.new(StandardError)
attr_reader :merge_request, :our_commit, :their_commit
diff --git a/lib/gitlab/conflict/parser.rb b/lib/gitlab/conflict/parser.rb
index ddd657903fb..d3524c338ee 100644
--- a/lib/gitlab/conflict/parser.rb
+++ b/lib/gitlab/conflict/parser.rb
@@ -1,25 +1,15 @@
module Gitlab
module Conflict
class Parser
- class UnresolvableError < StandardError
- end
-
- class UnmergeableFile < UnresolvableError
- end
-
- class UnsupportedEncoding < UnresolvableError
- end
+ UnresolvableError = Class.new(StandardError)
+ UnmergeableFile = Class.new(UnresolvableError)
+ UnsupportedEncoding = Class.new(UnresolvableError)
# Recoverable errors - the conflict can be resolved in an editor, but not with
# sections.
- class ParserError < StandardError
- end
-
- class UnexpectedDelimiter < ParserError
- end
-
- class MissingEndDelimiter < ParserError
- end
+ ParserError = Class.new(StandardError)
+ UnexpectedDelimiter = Class.new(ParserError)
+ MissingEndDelimiter = Class.new(ParserError)
def parse(text, our_path:, their_path:, parent_file: nil)
raise UnmergeableFile if text.blank? # Typically a binary file
diff --git a/lib/gitlab/conflict/resolution_error.rb b/lib/gitlab/conflict/resolution_error.rb
index a0f2006bc24..0b61256b35a 100644
--- a/lib/gitlab/conflict/resolution_error.rb
+++ b/lib/gitlab/conflict/resolution_error.rb
@@ -1,6 +1,5 @@
module Gitlab
module Conflict
- class ResolutionError < StandardError
- end
+ ResolutionError = Class.new(StandardError)
end
end
diff --git a/lib/gitlab/email/receiver.rb b/lib/gitlab/email/receiver.rb
index b64db5d01ae..ec0529b5a4b 100644
--- a/lib/gitlab/email/receiver.rb
+++ b/lib/gitlab/email/receiver.rb
@@ -4,19 +4,19 @@ require_dependency 'gitlab/email/handler'
# Inspired in great part by Discourse's Email::Receiver
module Gitlab
module Email
- class ProcessingError < StandardError; end
- class EmailUnparsableError < ProcessingError; end
- class SentNotificationNotFoundError < ProcessingError; end
- class ProjectNotFound < ProcessingError; end
- class EmptyEmailError < ProcessingError; end
- class AutoGeneratedEmailError < ProcessingError; end
- class UserNotFoundError < ProcessingError; end
- class UserBlockedError < ProcessingError; end
- class UserNotAuthorizedError < ProcessingError; end
- class NoteableNotFoundError < ProcessingError; end
- class InvalidNoteError < ProcessingError; end
- class InvalidIssueError < ProcessingError; end
- class UnknownIncomingEmail < ProcessingError; end
+ ProcessingError = Class.new(StandardError)
+ EmailUnparsableError = Class.new(ProcessingError)
+ SentNotificationNotFoundError = Class.new(ProcessingError)
+ ProjectNotFound = Class.new(ProcessingError)
+ EmptyEmailError = Class.new(ProcessingError)
+ AutoGeneratedEmailError = Class.new(ProcessingError)
+ UserNotFoundError = Class.new(ProcessingError)
+ UserBlockedError = Class.new(ProcessingError)
+ UserNotAuthorizedError = Class.new(ProcessingError)
+ NoteableNotFoundError = Class.new(ProcessingError)
+ InvalidNoteError = Class.new(ProcessingError)
+ InvalidIssueError = Class.new(ProcessingError)
+ UnknownIncomingEmail = Class.new(ProcessingError)
class Receiver
def initialize(raw)
diff --git a/lib/gitlab/git/diff.rb b/lib/gitlab/git/diff.rb
index d6b3b5705a9..2a017c93f57 100644
--- a/lib/gitlab/git/diff.rb
+++ b/lib/gitlab/git/diff.rb
@@ -2,7 +2,7 @@
module Gitlab
module Git
class Diff
- class TimeoutError < StandardError; end
+ TimeoutError = Class.new(StandardError)
include Gitlab::Git::EncodingHelper
# Diff properties
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 8ec90885231..6540730ca7a 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -10,9 +10,9 @@ module Gitlab
SEARCH_CONTEXT_LINES = 3
- class NoRepository < StandardError; end
- class InvalidBlobName < StandardError; end
- class InvalidRef < StandardError; end
+ NoRepository = Class.new(StandardError)
+ InvalidBlobName = Class.new(StandardError)
+ InvalidRef = Class.new(StandardError)
# Full path to repo
attr_reader :path
@@ -199,13 +199,17 @@ module Gitlab
nil
end
+ def archive_prefix(ref, sha)
+ project_name = self.name.chomp('.git')
+ "#{project_name}-#{ref.parameterize}-#{sha}"
+ end
+
def archive_metadata(ref, storage_path, format = "tar.gz")
ref ||= root_ref
commit = Gitlab::Git::Commit.find(self, ref)
return {} if commit.nil?
- project_name = self.name.chomp('.git')
- prefix = "#{project_name}-#{ref}-#{commit.id}"
+ prefix = archive_prefix(ref, commit.id)
{
'RepoPath' => path,
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
new file mode 100644
index 00000000000..b981a629fb0
--- /dev/null
+++ b/lib/gitlab/gitaly_client.rb
@@ -0,0 +1,29 @@
+require 'gitaly'
+
+module Gitlab
+ module GitalyClient
+ def self.gitaly_address
+ if Gitlab.config.gitaly.socket_path
+ "unix://#{Gitlab.config.gitaly.socket_path}"
+ end
+ end
+
+ def self.channel
+ return @channel if defined?(@channel)
+
+ @channel =
+ if enabled?
+ # NOTE: Gitaly currently runs on a Unix socket, so permissions are
+ # handled using the file system and no additional authentication is
+ # required (therefore the :this_channel_is_insecure flag)
+ GRPC::Core::Channel.new(gitaly_address, {}, :this_channel_is_insecure)
+ else
+ nil
+ end
+ end
+
+ def self.enabled?
+ gitaly_address.present?
+ end
+ end
+end
diff --git a/lib/gitlab/gitaly_client/notifications.rb b/lib/gitlab/gitaly_client/notifications.rb
new file mode 100644
index 00000000000..b827a56207f
--- /dev/null
+++ b/lib/gitlab/gitaly_client/notifications.rb
@@ -0,0 +1,17 @@
+module Gitlab
+ module GitalyClient
+ class Notifications
+ attr_accessor :stub
+
+ def initialize
+ @stub = Gitaly::Notifications::Stub.new(nil, nil, channel_override: GitalyClient.channel)
+ end
+
+ def post_receive(repo_path)
+ repository = Gitaly::Repository.new(path: repo_path)
+ request = Gitaly::PostReceiveRequest.new(repository: repository)
+ stub.post_receive(request)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index 101b1b80c1e..9c384069661 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -1,7 +1,7 @@
module Gitlab
module GonHelper
def add_gon_variables
- gon.api_version = API::API.version
+ gon.api_version = 'v3' # v4 Is not officially released yet, therefore can't be considered as "frozen"
gon.default_avatar_url = URI.join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s
gon.max_file_size = current_application_settings.max_attachment_size
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
diff --git a/lib/gitlab/import_export/error.rb b/lib/gitlab/import_export/error.rb
index e341c4d9cf8..788eedf2686 100644
--- a/lib/gitlab/import_export/error.rb
+++ b/lib/gitlab/import_export/error.rb
@@ -1,5 +1,5 @@
module Gitlab
module ImportExport
- class Error < StandardError; end
+ Error = Class.new(StandardError)
end
end
diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb
index 95d2f559588..fcf51b7fc5b 100644
--- a/lib/gitlab/o_auth/user.rb
+++ b/lib/gitlab/o_auth/user.rb
@@ -5,7 +5,7 @@
#
module Gitlab
module OAuth
- class SignupDisabledError < StandardError; end
+ SignupDisabledError = Class.new(StandardError)
class User
attr_accessor :auth_hash, :gl_user
diff --git a/lib/gitlab/route_map.rb b/lib/gitlab/route_map.rb
index 72d00abfcc2..36791fae60f 100644
--- a/lib/gitlab/route_map.rb
+++ b/lib/gitlab/route_map.rb
@@ -1,6 +1,6 @@
module Gitlab
class RouteMap
- class FormatError < StandardError; end
+ FormatError = Class.new(StandardError)
def initialize(data)
begin
diff --git a/lib/gitlab/serializer/pagination.rb b/lib/gitlab/serializer/pagination.rb
index bf2c0acc729..9c92b83dddc 100644
--- a/lib/gitlab/serializer/pagination.rb
+++ b/lib/gitlab/serializer/pagination.rb
@@ -1,7 +1,7 @@
module Gitlab
module Serializer
class Pagination
- class InvalidResourceError < StandardError; end
+ InvalidResourceError = Class.new(StandardError)
include ::API::Helpers::Pagination
def initialize(request, response)
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index 7374d2bc8b8..da8d8ddb8ed 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -2,7 +2,7 @@ require 'securerandom'
module Gitlab
class Shell
- class Error < StandardError; end
+ Error = Class.new(StandardError)
KeyAdder = Struct.new(:io) do
def add_key(id, key)
diff --git a/lib/gitlab/template/finders/repo_template_finder.rb b/lib/gitlab/template/finders/repo_template_finder.rb
index 22c39436cb2..cb7957e2af9 100644
--- a/lib/gitlab/template/finders/repo_template_finder.rb
+++ b/lib/gitlab/template/finders/repo_template_finder.rb
@@ -4,7 +4,7 @@ module Gitlab
module Finders
class RepoTemplateFinder < BaseTemplateFinder
# Raised when file is not found
- class FileNotFoundError < StandardError; end
+ FileNotFoundError = Class.new(StandardError)
def initialize(project, base_dir, extension, categories = {})
@categories = categories
diff --git a/lib/gitlab/update_path_error.rb b/lib/gitlab/update_path_error.rb
index ce14cc887d0..8947ecfb92e 100644
--- a/lib/gitlab/update_path_error.rb
+++ b/lib/gitlab/update_path_error.rb
@@ -1,3 +1,3 @@
module Gitlab
- class UpdatePathError < StandardError; end
+ UpdatePathError = Class.new(StandardError)
end
diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb
index b28708c34e1..2248763c106 100644
--- a/lib/gitlab/visibility_level.rb
+++ b/lib/gitlab/visibility_level.rb
@@ -35,6 +35,10 @@ module Gitlab
class << self
delegate :values, to: :options
+ def string_values
+ string_options.keys
+ end
+
def options
{
'Private' => PRIVATE,
@@ -43,6 +47,14 @@ module Gitlab
}
end
+ def string_options
+ {
+ 'private' => PRIVATE,
+ 'internal' => INTERNAL,
+ 'public' => PUBLIC
+ }
+ end
+
def highest_allowed_level
restricted_levels = current_application_settings.restricted_visibility_levels
@@ -82,18 +94,39 @@ module Gitlab
level_name
end
+
+ def level_value(level)
+ return string_options[level] if level.is_a? String
+ level
+ end
+
+ def string_level(level)
+ string_options.key(level)
+ end
end
def private?
- visibility_level_field == PRIVATE
+ visibility_level_value == PRIVATE
end
def internal?
- visibility_level_field == INTERNAL
+ visibility_level_value == INTERNAL
end
def public?
- visibility_level_field == PUBLIC
+ visibility_level_value == PUBLIC
+ end
+
+ def visibility_level_value
+ self[visibility_level_field]
+ end
+
+ def visibility
+ Gitlab::VisibilityLevel.string_level(visibility_level_value)
+ end
+
+ def visibility=(level)
+ self[visibility_level_field] = Gitlab::VisibilityLevel.level_value(level)
end
end
end
diff --git a/lib/mattermost/client.rb b/lib/mattermost/client.rb
index e55c0d6ac49..ad6df246091 100644
--- a/lib/mattermost/client.rb
+++ b/lib/mattermost/client.rb
@@ -1,5 +1,5 @@
module Mattermost
- class ClientError < Mattermost::Error; end
+ ClientError = Class.new(Mattermost::Error)
class Client
attr_reader :user
diff --git a/lib/mattermost/error.rb b/lib/mattermost/error.rb
index 014df175be0..dee6deb7974 100644
--- a/lib/mattermost/error.rb
+++ b/lib/mattermost/error.rb
@@ -1,3 +1,3 @@
module Mattermost
- class Error < StandardError; end
+ Error = Class.new(StandardError)
end
diff --git a/lib/mattermost/session.rb b/lib/mattermost/session.rb
index 377cb7b1021..5388966605d 100644
--- a/lib/mattermost/session.rb
+++ b/lib/mattermost/session.rb
@@ -5,7 +5,7 @@ module Mattermost
end
end
- class ConnectionError < Mattermost::Error; end
+ ConnectionError = Class.new(Mattermost::Error)
# This class' prime objective is to obtain a session token on a Mattermost
# instance with SSO configured where this GitLab instance is the provider.
diff --git a/package.json b/package.json
index 48cff0d7661..6b2991f9608 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,6 @@
"dropzone": "^4.2.0",
"es6-promise": "^4.0.5",
"jquery": "^2.2.1",
- "jquery-ui": "git+https://github.com/jquery/jquery-ui#1.11.4",
"jquery-ujs": "^1.2.1",
"js-cookie": "^2.1.3",
"mousetrap": "^1.4.6",
diff --git a/public/ci/build-canceled.svg b/public/ci/build-canceled.svg
deleted file mode 100644
index 922e28bf696..00000000000
--- a/public/ci/build-canceled.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="97" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="97" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h37v20H0z"/><path fill="#9f9f9f" d="M37 0h60v20H37z"/><path fill="url(#b)" d="M0 0h97v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text><text x="18.5" y="14">build</text><text x="66" y="15" fill="#010101" fill-opacity=".3">canceled</text><text x="66" y="14">canceled</text></g></svg> \ No newline at end of file
diff --git a/public/ci/build-failed.svg b/public/ci/build-failed.svg
deleted file mode 100644
index 1aefd3f1761..00000000000
--- a/public/ci/build-failed.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="78" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="78" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h37v20H0z"/><path fill="#e05d44" d="M37 0h41v20H37z"/><path fill="url(#b)" d="M0 0h78v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text><text x="18.5" y="14">build</text><text x="56.5" y="15" fill="#010101" fill-opacity=".3">failed</text><text x="56.5" y="14">failed</text></g></svg> \ No newline at end of file
diff --git a/public/ci/build-pending.svg b/public/ci/build-pending.svg
deleted file mode 100644
index 536931af84d..00000000000
--- a/public/ci/build-pending.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="92" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="92" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h37v20H0z"/><path fill="#dfb317" d="M37 0h55v20H37z"/><path fill="url(#b)" d="M0 0h92v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text><text x="18.5" y="14">build</text><text x="63.5" y="15" fill="#010101" fill-opacity=".3">pending</text><text x="63.5" y="14">pending</text></g></svg> \ No newline at end of file
diff --git a/public/ci/build-running.svg b/public/ci/build-running.svg
deleted file mode 100644
index 0d71eef3c34..00000000000
--- a/public/ci/build-running.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="90" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="90" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h37v20H0z"/><path fill="#dfb317" d="M37 0h53v20H37z"/><path fill="url(#b)" d="M0 0h90v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text><text x="18.5" y="14">build</text><text x="62.5" y="15" fill="#010101" fill-opacity=".3">running</text><text x="62.5" y="14">running</text></g></svg> \ No newline at end of file
diff --git a/public/ci/build-skipped.svg b/public/ci/build-skipped.svg
deleted file mode 100644
index f15507188e0..00000000000
--- a/public/ci/build-skipped.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="97" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="97" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h37v20H0z"/><path fill="#9f9f9f" d="M37 0h60v20H37z"/><path fill="url(#b)" d="M0 0h97v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text><text x="18.5" y="14">build</text><text x="66" y="15" fill="#010101" fill-opacity=".3">skipped</text><text x="66" y="14">skipped</text></g></svg> \ No newline at end of file
diff --git a/public/ci/build-success.svg b/public/ci/build-success.svg
deleted file mode 100644
index 43b67e45f42..00000000000
--- a/public/ci/build-success.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="91" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="91" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h37v20H0z"/><path fill="#4c1" d="M37 0h54v20H37z"/><path fill="url(#b)" d="M0 0h91v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text><text x="18.5" y="14">build</text><text x="63" y="15" fill="#010101" fill-opacity=".3">success</text><text x="63" y="14">success</text></g></svg> \ No newline at end of file
diff --git a/public/ci/build-unknown.svg b/public/ci/build-unknown.svg
deleted file mode 100644
index c72a2f5a7f5..00000000000
--- a/public/ci/build-unknown.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="98" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="98" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h37v20H0z"/><path fill="#9f9f9f" d="M37 0h61v20H37z"/><path fill="url(#b)" d="M0 0h98v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text><text x="18.5" y="14">build</text><text x="66.5" y="15" fill="#010101" fill-opacity=".3">unknown</text><text x="66.5" y="14">unknown</text></g></svg> \ No newline at end of file
diff --git a/rubocop/cop/custom_error_class.rb b/rubocop/cop/custom_error_class.rb
new file mode 100644
index 00000000000..38d93acfe88
--- /dev/null
+++ b/rubocop/cop/custom_error_class.rb
@@ -0,0 +1,64 @@
+module RuboCop
+ module Cop
+ # This cop makes sure that custom error classes, when empty, are declared
+ # with Class.new.
+ #
+ # @example
+ # # bad
+ # class FooError < StandardError
+ # end
+ #
+ # # okish
+ # class FooError < StandardError; end
+ #
+ # # good
+ # FooError = Class.new(StandardError)
+ class CustomErrorClass < RuboCop::Cop::Cop
+ MSG = 'Use `Class.new(SuperClass)` to define an empty custom error class.'.freeze
+
+ def on_class(node)
+ _klass, parent, body = node.children
+
+ return if body
+
+ parent_klass = class_name_from_node(parent)
+
+ return unless parent_klass && parent_klass.to_s.end_with?('Error')
+
+ add_offense(node, :expression)
+ end
+
+ def autocorrect(node)
+ klass, parent, _body = node.children
+ replacement = "#{class_name_from_node(klass)} = Class.new(#{class_name_from_node(parent)})"
+
+ lambda do |corrector|
+ corrector.replace(node.source_range, replacement)
+ end
+ end
+
+ private
+
+ # The nested constant `Foo::Bar::Baz` looks like:
+ #
+ # s(:const,
+ # s(:const,
+ # s(:const, nil, :Foo), :Bar), :Baz)
+ #
+ # So recurse through that to get the name as written in the source.
+ #
+ def class_name_from_node(node, suffix = nil)
+ return unless node&.type == :const
+
+ name = node.children[1].to_s
+ name = "#{name}::#{suffix}" if suffix
+
+ if node.children[0]
+ class_name_from_node(node.children[0], name)
+ else
+ name
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb
index d4266d0deae..ea8e0f64b0d 100644
--- a/rubocop/rubocop.rb
+++ b/rubocop/rubocop.rb
@@ -1,3 +1,4 @@
+require_relative 'cop/custom_error_class'
require_relative 'cop/gem_fetcher'
require_relative 'cop/migration/add_column'
require_relative 'cop/migration/add_column_with_default'
diff --git a/spec/controllers/ci/projects_controller_spec.rb b/spec/controllers/ci/projects_controller_spec.rb
deleted file mode 100644
index 86f01f437a2..00000000000
--- a/spec/controllers/ci/projects_controller_spec.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-require 'spec_helper'
-
-describe Ci::ProjectsController do
- let(:visibility) { :public }
- let!(:project) { create(:empty_project, visibility, ci_id: 1) }
- let(:ci_id) { project.ci_id }
-
- describe '#index' do
- context 'user signed in' do
- before do
- sign_in(create(:user))
- get(:index)
- end
-
- it 'redirects to /' do
- expect(response).to redirect_to(root_path)
- end
- end
-
- context 'user not signed in' do
- before { get(:index) }
-
- it 'redirects to sign in page' do
- expect(response).to redirect_to(new_user_session_path)
- end
- end
- end
-
- ##
- # Specs for *deprecated* CI badge
- #
- describe '#badge' do
- shared_examples 'badge provider' do
- it 'shows badge' do
- expect(response.status).to eq 200
- expect(response.headers)
- .to include('Content-Type' => 'image/svg+xml')
- end
- end
-
- context 'user not signed in' do
- before { get(:badge, id: ci_id) }
-
- context 'project has no ci_id reference' do
- let(:ci_id) { 123 }
-
- it 'returns 404' do
- expect(response.status).to eq 404
- end
- end
-
- context 'project is public' do
- let(:visibility) { :public }
- it_behaves_like 'badge provider'
- end
-
- context 'project is private' do
- let(:visibility) { :private }
- it_behaves_like 'badge provider'
- end
- end
-
- context 'user signed in' do
- let(:user) { create(:user) }
- before { sign_in(user) }
- before { get(:badge, id: ci_id) }
-
- context 'private is internal' do
- let(:visibility) { :internal }
- it_behaves_like 'badge provider'
- end
- end
- end
-end
diff --git a/spec/controllers/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb
index c4a7aa7d63e..049bae1899d 100644
--- a/spec/controllers/projects/graphs_controller_spec.rb
+++ b/spec/controllers/projects/graphs_controller_spec.rb
@@ -9,7 +9,23 @@ describe Projects::GraphsController do
project.team << [user, :master]
end
- describe 'GET #languages' do
+ describe 'GET languages' do
+ it "redirects_to action charts" do
+ get(:commits, namespace_id: project.namespace.path, project_id: project.path, id: 'master')
+
+ expect(response).to redirect_to action: :charts
+ end
+ end
+
+ describe 'GET commits' do
+ it "redirects_to action charts" do
+ get(:commits, namespace_id: project.namespace.path, project_id: project.path, id: 'master')
+
+ expect(response).to redirect_to action: :charts
+ end
+ end
+
+ describe 'GET charts' do
let(:linguist_repository) do
double(languages: {
'Ruby' => 1000,
@@ -34,7 +50,7 @@ describe Projects::GraphsController do
end
it 'sets the correct colour according to language' do
- get(:languages, namespace_id: project.namespace, project_id: project, id: 'master')
+ get(:charts, namespace_id: project.namespace, project_id: project, id: 'master')
expected_values.each do |val|
expect(assigns(:languages)).to include(a_hash_including(val))
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 1ced666bb36..250d64f7055 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -43,7 +43,8 @@ describe Projects::MergeRequestsController do
submit_new_merge_request(format: :json)
expect(response).to be_ok
- expect(json_response).not_to be_empty
+ expect(json_response).to have_key 'pipelines'
+ expect(json_response['pipelines']).not_to be_empty
end
end
end
@@ -319,41 +320,41 @@ describe Projects::MergeRequestsController do
merge_with_sha
end
- context 'when merge_when_build_succeeds is passed' do
- def merge_when_build_succeeds
- post :merge, base_params.merge(sha: merge_request.diff_head_sha, merge_when_build_succeeds: '1')
+ context 'when the pipeline succeeds is passed' do
+ def merge_when_pipeline_succeeds
+ post :merge, base_params.merge(sha: merge_request.diff_head_sha, merge_when_pipeline_succeeds: '1')
end
before do
create(:ci_empty_pipeline, project: project, sha: merge_request.diff_head_sha, ref: merge_request.source_branch)
end
- it 'returns :merge_when_build_succeeds' do
- merge_when_build_succeeds
+ it 'returns :merge_when_pipeline_succeeds' do
+ merge_when_pipeline_succeeds
- expect(assigns(:status)).to eq(:merge_when_build_succeeds)
+ expect(assigns(:status)).to eq(:merge_when_pipeline_succeeds)
end
- it 'sets the MR to merge when the build succeeds' do
- service = double(:merge_when_build_succeeds_service)
+ it 'sets the MR to merge when the pipeline succeeds' do
+ service = double(:merge_when_pipeline_succeeds_service)
expect(MergeRequests::MergeWhenPipelineSucceedsService)
.to receive(:new).with(project, anything, anything)
.and_return(service)
expect(service).to receive(:execute).with(merge_request)
- merge_when_build_succeeds
+ merge_when_pipeline_succeeds
end
- context 'when project.only_allow_merge_if_build_succeeds? is true' do
+ context 'when project.only_allow_merge_if_pipeline_succeeds? is true' do
before do
- project.update_column(:only_allow_merge_if_build_succeeds, true)
+ project.update_column(:only_allow_merge_if_pipeline_succeeds, true)
end
- it 'returns :merge_when_build_succeeds' do
- merge_when_build_succeeds
+ it 'returns :merge_when_pipeline_succeeds' do
+ merge_when_pipeline_succeeds
- expect(assigns(:status)).to eq(:merge_when_build_succeeds)
+ expect(assigns(:status)).to eq(:merge_when_pipeline_succeeds)
end
end
end
@@ -1134,14 +1135,14 @@ describe Projects::MergeRequestsController do
end
context 'when waiting for build' do
- let(:merge_request) { create(:merge_request, source_project: project, merge_when_build_succeeds: true, merge_user: user) }
+ let(:merge_request) { create(:merge_request, source_project: project, merge_when_pipeline_succeeds: true, merge_user: user) }
it 'returns an OK response' do
expect(response).to have_http_status(:ok)
end
- it 'sets status to :merge_when_build_succeeds' do
- expect(assigns(:status)).to eq(:merge_when_build_succeeds)
+ it 'sets status to :merge_when_pipeline_succeeds' do
+ expect(assigns(:status)).to eq(:merge_when_pipeline_succeeds)
expect(response).to render_template('merge')
end
end
diff --git a/spec/controllers/root_controller_spec.rb b/spec/controllers/root_controller_spec.rb
index b14d275f7fa..b32eb39b1fb 100644
--- a/spec/controllers/root_controller_spec.rb
+++ b/spec/controllers/root_controller_spec.rb
@@ -2,6 +2,26 @@ require 'spec_helper'
describe RootController do
describe 'GET index' do
+ context 'when user is not logged in' do
+ it 'redirects to the sign-in page' do
+ get :index
+
+ expect(response).to redirect_to(new_user_session_path)
+ end
+
+ context 'when a custom home page URL is defined' do
+ before do
+ stub_application_setting(home_page_url: 'https://gitlab.com')
+ end
+
+ it 'redirects the user to the custom home page URL' do
+ get :index
+
+ expect(response).to redirect_to('https://gitlab.com')
+ end
+ end
+ end
+
context 'with a user' do
let(:user) { create(:user) }
@@ -12,55 +32,60 @@ describe RootController do
context 'who has customized their dashboard setting for starred projects' do
before do
- user.update_attribute(:dashboard, 'stars')
+ user.dashboard = 'stars'
end
it 'redirects to their specified dashboard' do
get :index
+
expect(response).to redirect_to starred_dashboard_projects_path
end
end
context 'who has customized their dashboard setting for project activities' do
before do
- user.update_attribute(:dashboard, 'project_activity')
+ user.dashboard = 'project_activity'
end
it 'redirects to the activity list' do
get :index
+
expect(response).to redirect_to activity_dashboard_path
end
end
context 'who has customized their dashboard setting for starred project activities' do
before do
- user.update_attribute(:dashboard, 'starred_project_activity')
+ user.dashboard = 'starred_project_activity'
end
it 'redirects to the activity list' do
get :index
+
expect(response).to redirect_to activity_dashboard_path(filter: 'starred')
end
end
context 'who has customized their dashboard setting for groups' do
before do
- user.update_attribute(:dashboard, 'groups')
+ user.dashboard = 'groups'
end
it 'redirects to their group list' do
get :index
+
expect(response).to redirect_to dashboard_groups_path
end
end
context 'who has customized their dashboard setting for todos' do
before do
- user.update_attribute(:dashboard, 'todos')
+ user.dashboard = 'todos'
end
it 'redirects to their todo list' do
get :index
+
expect(response).to redirect_to dashboard_todos_path
end
end
@@ -68,6 +93,7 @@ describe RootController do
context 'who uses the default dashboard setting' do
it 'renders the default dashboard' do
get :index
+
expect(response).to render_template 'dashboard/projects/index'
end
end
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index 22f84150bb3..ae0bbbd6aeb 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -59,8 +59,8 @@ FactoryGirl.define do
target_branch "master"
end
- trait :merge_when_build_succeeds do
- merge_when_build_succeeds true
+ trait :merge_when_pipeline_succeeds do
+ merge_when_pipeline_succeeds true
merge_user author
end
diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb
index a5265f1b189..c1ac3bb84ad 100644
--- a/spec/factories/todos.rb
+++ b/spec/factories/todos.rb
@@ -18,11 +18,6 @@ FactoryGirl.define do
action { Todo::DIRECTLY_ADDRESSED }
end
- trait :on_commit do
- commit_id RepoHelpers.sample_commit.id
- target_type "Commit"
- end
-
trait :build_failed do
action { Todo::BUILD_FAILED }
target factory: :merge_request
@@ -48,4 +43,13 @@ FactoryGirl.define do
state :done
end
end
+
+ factory :on_commit_todo, class: Todo do
+ project factory: :empty_project
+ author
+ user
+ action { Todo::ASSIGNED }
+ commit_id RepoHelpers.sample_commit.id
+ target_type "Commit"
+ end
end
diff --git a/spec/features/dashboard/activity_spec.rb b/spec/features/dashboard/activity_spec.rb
new file mode 100644
index 00000000000..c977f266296
--- /dev/null
+++ b/spec/features/dashboard/activity_spec.rb
@@ -0,0 +1,11 @@
+require 'spec_helper'
+
+RSpec.describe 'Dashboard Activity', feature: true do
+ before do
+ login_as(create :user)
+ visit activity_dashboard_path
+ end
+
+ it_behaves_like "it has an RSS button with current_user's private token"
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+end
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
new file mode 100644
index 00000000000..ca04107d33a
--- /dev/null
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+
+describe 'Dashboard Groups page', js: true, feature: true do
+ include WaitForAjax
+
+ let!(:user) { create :user }
+ let!(:group) { create(:group) }
+ let!(:nested_group) { create(:group, :nested) }
+ let!(:another_group) { create(:group) }
+
+ before do
+ group.add_owner(user)
+ nested_group.add_owner(user)
+
+ login_as(user)
+
+ visit dashboard_groups_path
+ end
+
+ it 'shows groups user is member of' do
+ expect(page).to have_content(group.full_name)
+ expect(page).to have_content(nested_group.full_name)
+ expect(page).not_to have_content(another_group.full_name)
+ end
+
+ it 'filters groups' do
+ fill_in 'filter_groups', with: group.name
+ wait_for_ajax
+
+ expect(page).to have_content(group.full_name)
+ expect(page).not_to have_content(nested_group.full_name)
+ expect(page).not_to have_content(another_group.full_name)
+ end
+
+ it 'resets search when user cleans the input' do
+ fill_in 'filter_groups', with: group.name
+ wait_for_ajax
+
+ fill_in 'filter_groups', with: ""
+ wait_for_ajax
+
+ expect(page).to have_content(group.full_name)
+ expect(page).to have_content(nested_group.full_name)
+ expect(page).not_to have_content(another_group.full_name)
+ expect(page.all('.js-groups-list-holder .content-list li').length).to eq 2
+ end
+end
diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb
index 2db1cf71209..f4420814c3a 100644
--- a/spec/features/dashboard/issues_spec.rb
+++ b/spec/features/dashboard/issues_spec.rb
@@ -45,4 +45,7 @@ RSpec.describe 'Dashboard Issues', feature: true do
expect(page).to have_content(assigned_issue.title)
expect(page).to have_content(other_issue.title)
end
+
+ it_behaves_like "it has an RSS button with current_user's private token"
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
end
diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb
new file mode 100644
index 00000000000..63eb5c697c2
--- /dev/null
+++ b/spec/features/dashboard/projects_spec.rb
@@ -0,0 +1,10 @@
+require 'spec_helper'
+
+RSpec.describe 'Dashboard Projects', feature: true do
+ before do
+ login_as(create :user)
+ visit dashboard_projects_path
+ end
+
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+end
diff --git a/spec/features/explore/groups_list_spec.rb b/spec/features/explore/groups_list_spec.rb
new file mode 100644
index 00000000000..773ae4b38bc
--- /dev/null
+++ b/spec/features/explore/groups_list_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe 'Explore Groups page', js: true, feature: true do
+ include WaitForAjax
+
+ let!(:user) { create :user }
+ let!(:group) { create(:group) }
+ let!(:public_group) { create(:group, :public) }
+ let!(:private_group) { create(:group, :private) }
+
+ before do
+ group.add_owner(user)
+
+ login_as(user)
+
+ visit explore_groups_path
+ end
+
+ it 'shows groups user is member of' do
+ expect(page).to have_content(group.full_name)
+ expect(page).to have_content(public_group.full_name)
+ expect(page).not_to have_content(private_group.full_name)
+ end
+
+ it 'filters groups' do
+ fill_in 'filter_groups', with: group.name
+ wait_for_ajax
+
+ expect(page).to have_content(group.full_name)
+ expect(page).not_to have_content(public_group.full_name)
+ expect(page).not_to have_content(private_group.full_name)
+ end
+
+ it 'resets search when user cleans the input' do
+ fill_in 'filter_groups', with: group.name
+ wait_for_ajax
+
+ fill_in 'filter_groups', with: ""
+ wait_for_ajax
+
+ expect(page).to have_content(group.full_name)
+ expect(page).to have_content(public_group.full_name)
+ expect(page).not_to have_content(private_group.full_name)
+ expect(page.all('.js-groups-list-holder .content-list li').length).to eq 2
+ end
+end
diff --git a/spec/features/groups/activity_spec.rb b/spec/features/groups/activity_spec.rb
new file mode 100644
index 00000000000..3b481cba424
--- /dev/null
+++ b/spec/features/groups/activity_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+
+feature 'Group activity page', feature: true do
+ let(:group) { create(:group) }
+ let(:path) { activity_group_path(group) }
+
+ context 'when signed in' do
+ before do
+ user = create(:group_member, :developer, user: create(:user), group: group ).user
+ login_as(user)
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button with current_user's private token"
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ end
+
+ context 'when signed out' do
+ before do
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button without a private token"
+ it_behaves_like "an autodiscoverable RSS feed without a private token"
+ end
+end
diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb
index 476eca17a9d..1b3747c390b 100644
--- a/spec/features/groups/issues_spec.rb
+++ b/spec/features/groups/issues_spec.rb
@@ -5,4 +5,22 @@ feature 'Group issues page', feature: true do
let(:issuable) { create(:issue, project: project, title: "this is my created issuable")}
include_examples 'project features apply to issuables', Issue
+
+ context 'rss feed' do
+ let(:access_level) { ProjectFeature::ENABLED }
+
+ context 'when signed in' do
+ let(:user) { user_in_group }
+
+ it_behaves_like "it has an RSS button with current_user's private token"
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ end
+
+ context 'when signed out' do
+ let(:user) { nil }
+
+ it_behaves_like "it has an RSS button without a private token"
+ it_behaves_like "an autodiscoverable RSS feed without a private token"
+ end
+ end
end
diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb
new file mode 100644
index 00000000000..fb39693e8ca
--- /dev/null
+++ b/spec/features/groups/show_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+feature 'Group show page', feature: true do
+ let(:group) { create(:group) }
+ let(:path) { group_path(group) }
+
+ context 'when signed in' do
+ before do
+ user = create(:group_member, :developer, user: create(:user), group: group ).user
+ login_as(user)
+ visit path
+ end
+
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ end
+
+ context 'when signed out' do
+ before do
+ visit path
+ end
+
+ it_behaves_like "an autodiscoverable RSS feed without a private token"
+ end
+end
diff --git a/spec/features/issues/award_emoji_spec.rb b/spec/features/issues/award_emoji_spec.rb
index 73e43316dc7..3ab3d2d4229 100644
--- a/spec/features/issues/award_emoji_spec.rb
+++ b/spec/features/issues/award_emoji_spec.rb
@@ -67,6 +67,18 @@ describe 'Awards Emoji', feature: true do
expect(page).not_to have_selector(emoji_counter)
end
end
+
+ context 'execute /award slash command' do
+ it 'toggles the emoji award on noteable', js: true do
+ execute_slash_command('/award :100:')
+
+ expect(find(noteable_award_counter)).to have_text("1")
+
+ execute_slash_command('/award :100:')
+
+ expect(page).not_to have_selector(noteable_award_counter)
+ end
+ end
end
end
@@ -80,6 +92,15 @@ describe 'Awards Emoji', feature: true do
end
end
+ def execute_slash_command(cmd)
+ within('.js-main-target-form') do
+ fill_in 'note[note]', with: cmd
+ click_button 'Comment'
+ end
+
+ wait_for_ajax
+ end
+
def thumbsup_emoji
page.all(emoji_counter).first
end
@@ -92,6 +113,10 @@ describe 'Awards Emoji', feature: true do
'span.js-counter'
end
+ def noteable_award_counter
+ ".awards .active"
+ end
+
def toggle_smiley_emoji(status)
within('.note') do
find('.note-emoji-button').click
diff --git a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
index 2ea9c317bd1..ed7193b9777 100644
--- a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
@@ -75,7 +75,7 @@ feature 'Merge When Pipeline Succeeds', :feature, :js do
context 'when it was enabled and then canceled' do
let(:merge_request) do
create(:merge_request_with_diffs,
- :merge_when_build_succeeds,
+ :merge_when_pipeline_succeeds,
source_project: project,
title: 'Bug NS-04',
author: user,
@@ -97,7 +97,7 @@ feature 'Merge When Pipeline Succeeds', :feature, :js do
author: user,
merge_user: user,
title: 'MepMep',
- merge_when_build_succeeds: true)
+ merge_when_pipeline_succeeds: true)
end
let!(:build) do
diff --git a/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb b/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
index d2f5c4afc93..447764566e0 100644
--- a/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
+++ b/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Only allow merge requests to be merged if the build succeeds', feature: true do
+feature 'Only allow merge requests to be merged if the pipeline succeeds', feature: true do
let(:merge_request) { create(:merge_request_with_diffs) }
let(:project) { merge_request.target_project }
@@ -27,9 +27,9 @@ feature 'Only allow merge requests to be merged if the build succeeds', feature:
status: status)
end
- context 'when merge requests can only be merged if the build succeeds' do
+ context 'when merge requests can only be merged if the pipeline succeeds' do
before do
- project.update_attribute(:only_allow_merge_if_build_succeeds, true)
+ project.update_attribute(:only_allow_merge_if_pipeline_succeeds, true)
end
context 'when CI is running' do
@@ -88,7 +88,7 @@ feature 'Only allow merge requests to be merged if the build succeeds', feature:
context 'when merge requests can be merged when the build failed' do
before do
- project.update_attribute(:only_allow_merge_if_build_succeeds, false)
+ project.update_attribute(:only_allow_merge_if_pipeline_succeeds, false)
end
context 'when CI is running' do
diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb
index a2e40546588..c3297de709a 100644
--- a/spec/features/milestone_spec.rb
+++ b/spec/features/milestone_spec.rb
@@ -24,7 +24,7 @@ feature 'Milestone', feature: true do
find('input[name="commit"]').click
expect(find('.alert-success')).to have_content('Assign some issues to this milestone.')
- expect(page).to have_content('Nov 16, 2016 - Dec 16, 2016')
+ expect(page).to have_content('Nov 16, 2016–Dec 16, 2016')
end
end
diff --git a/spec/features/projects/activity/rss_spec.rb b/spec/features/projects/activity/rss_spec.rb
new file mode 100644
index 00000000000..b47c6d431eb
--- /dev/null
+++ b/spec/features/projects/activity/rss_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+
+feature 'Project Activity RSS' do
+ let(:project) { create(:empty_project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+ let(:path) { activity_namespace_project_path(project.namespace, project) }
+
+ before do
+ create(:issue, project: project)
+ end
+
+ context 'when signed in' do
+ before do
+ user = create(:user)
+ project.team << [user, :developer]
+ login_as(user)
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button with current_user's private token"
+ end
+
+ context 'when signed out' do
+ before do
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button without a private token"
+ end
+end
diff --git a/spec/features/projects/commit/rss_spec.rb b/spec/features/projects/commit/rss_spec.rb
new file mode 100644
index 00000000000..6e0e1916f87
--- /dev/null
+++ b/spec/features/projects/commit/rss_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+feature 'Project Commits RSS' do
+ let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+ let(:path) { namespace_project_commits_path(project.namespace, project, :master) }
+
+ context 'when signed in' do
+ before do
+ user = create(:user)
+ project.team << [user, :developer]
+ login_as(user)
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button with current_user's private token"
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ end
+
+ context 'when signed out' do
+ before do
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button without a private token"
+ it_behaves_like "an autodiscoverable RSS feed without a private token"
+ end
+end
diff --git a/spec/features/projects/guest_navigation_menu_spec.rb b/spec/features/projects/guest_navigation_menu_spec.rb
index 8120a51c515..726469daba4 100644
--- a/spec/features/projects/guest_navigation_menu_spec.rb
+++ b/spec/features/projects/guest_navigation_menu_spec.rb
@@ -15,13 +15,11 @@ describe "Guest navigation menu" do
within(".nav-links") do
expect(page).to have_content 'Project'
- expect(page).to have_content 'Activity'
expect(page).to have_content 'Issues'
expect(page).to have_content 'Wiki'
expect(page).not_to have_content 'Repository'
expect(page).not_to have_content 'Pipelines'
- expect(page).not_to have_content 'Graphs'
expect(page).not_to have_content 'Merge Requests'
end
end
diff --git a/spec/features/projects/issues/rss_spec.rb b/spec/features/projects/issues/rss_spec.rb
new file mode 100644
index 00000000000..71429f00095
--- /dev/null
+++ b/spec/features/projects/issues/rss_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+feature 'Project Issues RSS' do
+ let(:project) { create(:empty_project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+ let(:path) { namespace_project_issues_path(project.namespace, project) }
+
+ before do
+ create(:issue, project: project)
+ end
+
+ context 'when signed in' do
+ before do
+ user = create(:user)
+ project.team << [user, :developer]
+ login_as(user)
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button with current_user's private token"
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ end
+
+ context 'when signed out' do
+ before do
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button without a private token"
+ it_behaves_like "an autodiscoverable RSS feed without a private token"
+ end
+end
diff --git a/spec/features/projects/main/rss_spec.rb b/spec/features/projects/main/rss_spec.rb
new file mode 100644
index 00000000000..b1a3af612a1
--- /dev/null
+++ b/spec/features/projects/main/rss_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+feature 'Project RSS' do
+ let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+ let(:path) { namespace_project_path(project.namespace, project) }
+
+ context 'when signed in' do
+ before do
+ user = create(:user)
+ project.team << [user, :developer]
+ login_as(user)
+ visit path
+ end
+
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ end
+
+ context 'when signed out' do
+ before do
+ visit path
+ end
+
+ it_behaves_like "an autodiscoverable RSS feed without a private token"
+ end
+end
diff --git a/spec/features/projects/tree/rss_spec.rb b/spec/features/projects/tree/rss_spec.rb
new file mode 100644
index 00000000000..9ac51997d65
--- /dev/null
+++ b/spec/features/projects/tree/rss_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+feature 'Project Tree RSS' do
+ let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+ let(:path) { namespace_project_tree_path(project.namespace, project, :master) }
+
+ context 'when signed in' do
+ before do
+ user = create(:user)
+ project.team << [user, :developer]
+ login_as(user)
+ visit path
+ end
+
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ end
+
+ context 'when signed out' do
+ before do
+ visit path
+ end
+
+ it_behaves_like "an autodiscoverable RSS feed without a private token"
+ end
+end
diff --git a/spec/features/users/rss_spec.rb b/spec/features/users/rss_spec.rb
new file mode 100644
index 00000000000..14564abb16d
--- /dev/null
+++ b/spec/features/users/rss_spec.rb
@@ -0,0 +1,22 @@
+require 'spec_helper'
+
+feature 'User RSS' do
+ let(:path) { user_path(create(:user)) }
+
+ context 'when signed in' do
+ before do
+ login_as(create(:user))
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button with current_user's private token"
+ end
+
+ context 'when signed out' do
+ before do
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button without a private token"
+ end
+end
diff --git a/spec/helpers/milestones_helper_spec.rb b/spec/helpers/milestones_helper_spec.rb
index 14a95479339..68b20a1e4fc 100644
--- a/spec/helpers/milestones_helper_spec.rb
+++ b/spec/helpers/milestones_helper_spec.rb
@@ -17,7 +17,7 @@ describe MilestonesHelper do
it { expect(result_for(due_date: yesterday)).to eq("expired on #{yesterday_formatted}") }
it { expect(result_for(start_date: tomorrow)).to eq("starts on #{tomorrow_formatted}") }
it { expect(result_for(start_date: yesterday)).to eq("started on #{yesterday_formatted}") }
- it { expect(result_for(start_date: yesterday, due_date: tomorrow)).to eq("#{yesterday_formatted} - #{tomorrow_formatted}") }
+ it { expect(result_for(start_date: yesterday, due_date: tomorrow)).to eq("#{yesterday_formatted}–#{tomorrow_formatted}") }
end
describe '#milestone_counts' do
diff --git a/spec/helpers/rss_helper_spec.rb b/spec/helpers/rss_helper_spec.rb
new file mode 100644
index 00000000000..f3f174f3d14
--- /dev/null
+++ b/spec/helpers/rss_helper_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe RssHelper do
+ describe '#rss_url_options' do
+ context 'when signed in' do
+ it "includes the current_user's private_token" do
+ current_user = create(:user)
+ allow(helper).to receive(:current_user).and_return(current_user)
+ expect(helper.rss_url_options).to include private_token: current_user.private_token
+ end
+ end
+
+ context 'when signed out' do
+ it "does not have a private_token" do
+ allow(helper).to receive(:current_user).and_return(nil)
+ expect(helper.rss_url_options[:private_token]).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/javascripts/boards/board_card_spec.js b/spec/javascripts/boards/board_card_spec.js
index 192916fbc6a..be31f644e20 100644
--- a/spec/javascripts/boards/board_card_spec.js
+++ b/spec/javascripts/boards/board_card_spec.js
@@ -8,7 +8,7 @@
require('~/boards/models/list');
require('~/boards/models/label');
require('~/boards/stores/boards_store');
-const boardCard = require('~/boards/components/board_card');
+const boardCard = require('~/boards/components/board_card').default;
require('./mock_data');
describe('Issue card', () => {
diff --git a/spec/javascripts/boards/board_new_issue_spec.js b/spec/javascripts/boards/board_new_issue_spec.js
new file mode 100644
index 00000000000..22c9f12951b
--- /dev/null
+++ b/spec/javascripts/boards/board_new_issue_spec.js
@@ -0,0 +1,191 @@
+/* global boardsMockInterceptor */
+/* global BoardService */
+/* global List */
+/* global listObj */
+
+import Vue from 'vue';
+import boardNewIssue from '~/boards/components/board_new_issue';
+
+require('~/boards/models/list');
+require('./mock_data');
+require('es6-promise').polyfill();
+
+describe('Issue boards new issue form', () => {
+ let vm;
+ let list;
+ const promiseReturn = {
+ json() {
+ return {
+ iid: 100,
+ };
+ },
+ };
+ const submitIssue = () => {
+ vm.$el.querySelector('.btn-success').click();
+ };
+
+ beforeEach((done) => {
+ const BoardNewIssueComp = Vue.extend(boardNewIssue);
+
+ Vue.http.interceptors.push(boardsMockInterceptor);
+ gl.boardService = new BoardService('/test/issue-boards/board', '', '1');
+ gl.issueBoards.BoardsStore.create();
+ gl.IssueBoardsApp = new Vue();
+
+ setTimeout(() => {
+ list = new List(listObj);
+
+ spyOn(gl.boardService, 'newIssue').and.callFake(() => new Promise((resolve, reject) => {
+ if (vm.title === 'error') {
+ reject();
+ } else {
+ resolve(promiseReturn);
+ }
+ }));
+
+ vm = new BoardNewIssueComp({
+ propsData: {
+ list,
+ },
+ }).$mount();
+
+ done();
+ }, 0);
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(Vue.http.interceptors, boardsMockInterceptor);
+ });
+
+ it('disables submit button if title is empty', () => {
+ expect(vm.$el.querySelector('.btn-success').disabled).toBe(true);
+ });
+
+ it('enables submit button if title is not empty', (done) => {
+ vm.title = 'Testing Title';
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.form-control').value).toBe('Testing Title');
+ expect(vm.$el.querySelector('.btn-success').disabled).not.toBe(true);
+
+ done();
+ }, 0);
+ });
+
+ it('clears title after clicking cancel', (done) => {
+ vm.$el.querySelector('.btn-default').click();
+
+ setTimeout(() => {
+ expect(vm.title).toBe('');
+ done();
+ }, 0);
+ });
+
+ it('does not create new issue if title is empty', (done) => {
+ submitIssue();
+
+ setTimeout(() => {
+ expect(gl.boardService.newIssue).not.toHaveBeenCalled();
+ done();
+ }, 0);
+ });
+
+ describe('submit success', () => {
+ it('creates new issue', (done) => {
+ vm.title = 'submit title';
+
+ setTimeout(() => {
+ submitIssue();
+
+ expect(gl.boardService.newIssue).toHaveBeenCalled();
+ done();
+ }, 0);
+ });
+
+ it('enables button after submit', (done) => {
+ vm.title = 'submit issue';
+
+ setTimeout(() => {
+ submitIssue();
+
+ expect(vm.$el.querySelector('.btn-success').disbled).not.toBe(true);
+ done();
+ }, 0);
+ });
+
+ it('clears title after submit', (done) => {
+ vm.title = 'submit issue';
+
+ setTimeout(() => {
+ submitIssue();
+
+ expect(vm.title).toBe('');
+ done();
+ }, 0);
+ });
+
+ it('adds new issue to list after submit', (done) => {
+ vm.title = 'submit issue';
+
+ setTimeout(() => {
+ submitIssue();
+
+ expect(list.issues.length).toBe(2);
+ expect(list.issues[1].title).toBe('submit issue');
+ expect(list.issues[1].subscribed).toBe(true);
+ done();
+ }, 0);
+ });
+
+ it('sets detail issue after submit', (done) => {
+ vm.title = 'submit issue';
+
+ setTimeout(() => {
+ submitIssue();
+
+ expect(gl.issueBoards.BoardsStore.detail.issue.title).toBe('submit issue');
+ done();
+ });
+ });
+
+ it('sets detail list after submit', (done) => {
+ vm.title = 'submit issue';
+
+ setTimeout(() => {
+ submitIssue();
+
+ expect(gl.issueBoards.BoardsStore.detail.list.id).toBe(list.id);
+ done();
+ }, 0);
+ });
+ });
+
+ describe('submit error', () => {
+ it('removes issue', (done) => {
+ vm.title = 'error';
+
+ setTimeout(() => {
+ submitIssue();
+
+ setTimeout(() => {
+ expect(list.issues.length).toBe(1);
+ done();
+ }, 500);
+ }, 0);
+ });
+
+ it('shows error', (done) => {
+ vm.title = 'error';
+ submitIssue();
+
+ setTimeout(() => {
+ submitIssue();
+
+ setTimeout(() => {
+ expect(vm.error).toBe(true);
+ done();
+ }, 500);
+ }, 0);
+ });
+ });
+});
diff --git a/spec/javascripts/fixtures/projects.json b/spec/javascripts/fixtures/projects.json
index 4ce7f5c601a..1339ee00870 100644
--- a/spec/javascripts/fixtures/projects.json
+++ b/spec/javascripts/fixtures/projects.json
@@ -43,7 +43,7 @@
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"open_issues_count": 0,
"permissions": {
"project_access": null,
@@ -88,7 +88,7 @@
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"open_issues_count": 5,
"permissions": {
"project_access": {
@@ -139,7 +139,7 @@
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
- "only_allow_merge_if_build_succeeds": true,
+ "only_allow_merge_if_pipeline_succeeds": true,
"open_issues_count": 4,
"permissions": {
"project_access": null,
@@ -187,7 +187,7 @@
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
- "only_allow_merge_if_build_succeeds": true,
+ "only_allow_merge_if_pipeline_succeeds": true,
"open_issues_count": 4,
"permissions": {
"project_access": null,
@@ -235,7 +235,7 @@
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"open_issues_count": 5,
"permissions": {
"project_access": null,
@@ -283,7 +283,7 @@
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"open_issues_count": 5,
"permissions": {
"project_access": {
@@ -334,7 +334,7 @@
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"open_issues_count": 3,
"permissions": {
"project_access": null,
@@ -382,7 +382,7 @@
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"open_issues_count": 5,
"permissions": {
"project_access": {
@@ -433,7 +433,7 @@
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"open_issues_count": 5,
"permissions": {
"project_access": null,
diff --git a/spec/javascripts/new_branch_spec.js b/spec/javascripts/new_branch_spec.js
index f132537b943..90a429beeca 100644
--- a/spec/javascripts/new_branch_spec.js
+++ b/spec/javascripts/new_branch_spec.js
@@ -1,7 +1,6 @@
/* eslint-disable space-before-function-paren, one-var, no-var, one-var-declaration-per-line, no-return-assign, quotes, max-len */
/* global NewBranchForm */
-require('jquery-ui/ui/autocomplete');
require('~/new_branch_form');
(function() {
diff --git a/spec/javascripts/user_callout_spec.js.es6 b/spec/javascripts/user_callout_spec.js.es6
index 6ee63f56a26..205e72af600 100644
--- a/spec/javascripts/user_callout_spec.js.es6
+++ b/spec/javascripts/user_callout_spec.js.es6
@@ -3,35 +3,55 @@ const UserCallout = require('~/user_callout');
const USER_CALLOUT_COOKIE = 'user_callout_dismissed';
const Cookie = window.Cookies;
-describe('UserCallout', () => {
+describe('UserCallout', function () {
const fixtureName = 'static/user_callout.html.raw';
preloadFixtures(fixtureName);
- beforeEach(function () {
+ beforeEach(() => {
loadFixtures(fixtureName);
+ Cookie.remove(USER_CALLOUT_COOKIE);
+
this.userCallout = new UserCallout();
this.closeButton = $('.close-user-callout');
this.userCalloutBtn = $('.user-callout-btn');
this.userCalloutContainer = $('.user-callout');
- Cookie.set(USER_CALLOUT_COOKIE, 'false');
});
- afterEach(function () {
- Cookie.set(USER_CALLOUT_COOKIE, 'false');
+ it('does not show when cookie is set not defined', () => {
+ expect(Cookie.get(USER_CALLOUT_COOKIE)).toBeUndefined();
+ expect(this.userCalloutContainer.is(':visible')).toBe(true);
});
- it('shows when cookie is set to false', function () {
+ it('shows when cookie is set to false', () => {
+ Cookie.set(USER_CALLOUT_COOKIE, 'false');
+
expect(Cookie.get(USER_CALLOUT_COOKIE)).toBeDefined();
expect(this.userCalloutContainer.is(':visible')).toBe(true);
});
- it('hides when user clicks on the dismiss-icon', function () {
+ it('hides when user clicks on the dismiss-icon', () => {
this.closeButton.click();
expect(Cookie.get(USER_CALLOUT_COOKIE)).toBe('true');
});
- it('hides when user clicks on the "check it out" button', function () {
+ it('hides when user clicks on the "check it out" button', () => {
this.userCalloutBtn.click();
expect(Cookie.get(USER_CALLOUT_COOKIE)).toBe('true');
});
});
+
+describe('UserCallout when cookie is present', function () {
+ const fixtureName = 'static/user_callout.html.raw';
+ preloadFixtures(fixtureName);
+
+ beforeEach(() => {
+ loadFixtures(fixtureName);
+ Cookie.set(USER_CALLOUT_COOKIE, 'true');
+ this.userCallout = new UserCallout();
+ this.userCalloutContainer = $('.user-callout');
+ });
+
+ it('removes the DOM element', () => {
+ expect(this.userCalloutContainer.length).toBe(0);
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/table_pagination_spec.js.es6 b/spec/javascripts/vue_shared/components/table_pagination_spec.js.es6
index dd495cb43bc..9cb067921a7 100644
--- a/spec/javascripts/vue_shared/components/table_pagination_spec.js.es6
+++ b/spec/javascripts/vue_shared/components/table_pagination_spec.js.es6
@@ -6,12 +6,10 @@ describe('Pagination component', () => {
const changeChanges = {
one: '',
- two: '',
};
- const change = (one, two) => {
+ const change = (one) => {
changeChanges.one = one;
- changeChanges.two = two;
};
it('should render and start at page 1', () => {
@@ -34,7 +32,6 @@ describe('Pagination component', () => {
component.changePage({ target: { innerText: '1' } });
expect(changeChanges.one).toEqual(1);
- expect(changeChanges.two).toEqual(null);
});
it('should go to the previous page', () => {
@@ -55,7 +52,6 @@ describe('Pagination component', () => {
component.changePage({ target: { innerText: 'Prev' } });
expect(changeChanges.one).toEqual(1);
- expect(changeChanges.two).toEqual(null);
});
it('should go to the next page', () => {
@@ -76,7 +72,6 @@ describe('Pagination component', () => {
component.changePage({ target: { innerText: 'Next' } });
expect(changeChanges.one).toEqual(5);
- expect(changeChanges.two).toEqual(null);
});
it('should go to the last page', () => {
@@ -97,7 +92,6 @@ describe('Pagination component', () => {
component.changePage({ target: { innerText: 'Last >>' } });
expect(changeChanges.one).toEqual(10);
- expect(changeChanges.two).toEqual(null);
});
it('should go to the first page', () => {
@@ -118,7 +112,6 @@ describe('Pagination component', () => {
component.changePage({ target: { innerText: '<< First' } });
expect(changeChanges.one).toEqual(1);
- expect(changeChanges.two).toEqual(null);
});
it('should do nothing', () => {
@@ -139,7 +132,6 @@ describe('Pagination component', () => {
component.changePage({ target: { innerText: '...' } });
expect(changeChanges.one).toEqual(1);
- expect(changeChanges.two).toEqual(null);
});
});
diff --git a/spec/lib/banzai/filter/user_reference_filter_spec.rb b/spec/lib/banzai/filter/user_reference_filter_spec.rb
index d5d128c1907..9873774909e 100644
--- a/spec/lib/banzai/filter/user_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/user_reference_filter_spec.rb
@@ -123,6 +123,12 @@ describe Banzai::Filter::UserReferenceFilter, lib: true do
expect(doc.css('a').first.attr('href')).to eq urls.group_url(group)
end
+
+ it 'has the full group name as a title' do
+ doc = reference_filter("Hey #{reference}")
+
+ expect(doc.css('a').first.attr('title')).to eq group.full_name
+ end
end
it 'links with adjacent text' do
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 1919542ca25..3f11f0a4516 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -47,7 +47,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe :branch_names do
+ describe '#branch_names' do
subject { repository.branch_names }
it 'has SeedRepo::Repo::BRANCHES.size elements' do
@@ -57,7 +57,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
it { is_expected.not_to include("branch-from-space") }
end
- describe :tag_names do
+ describe '#tag_names' do
subject { repository.tag_names }
it { is_expected.to be_kind_of Array }
@@ -78,49 +78,63 @@ describe Gitlab::Git::Repository, seed_helper: true do
it { expect(metadata['ArchivePath']).to end_with extenstion }
end
- describe :archive do
+ describe '#archive_prefix' do
+ let(:project_name) { 'project-name'}
+
+ before do
+ expect(repository).to receive(:name).once.and_return(project_name)
+ end
+
+ it 'returns parameterised string for a ref containing slashes' do
+ prefix = repository.archive_prefix('test/branch', 'SHA')
+
+ expect(prefix).to eq("#{project_name}-test-branch-SHA")
+ end
+ end
+
+ describe '#archive' do
let(:metadata) { repository.archive_metadata('master', '/tmp') }
it_should_behave_like 'archive check', '.tar.gz'
end
- describe :archive_zip do
+ describe '#archive_zip' do
let(:metadata) { repository.archive_metadata('master', '/tmp', 'zip') }
it_should_behave_like 'archive check', '.zip'
end
- describe :archive_bz2 do
+ describe '#archive_bz2' do
let(:metadata) { repository.archive_metadata('master', '/tmp', 'tbz2') }
it_should_behave_like 'archive check', '.tar.bz2'
end
- describe :archive_fallback do
+ describe '#archive_fallback' do
let(:metadata) { repository.archive_metadata('master', '/tmp', 'madeup') }
it_should_behave_like 'archive check', '.tar.gz'
end
- describe :size do
+ describe '#size' do
subject { repository.size }
it { is_expected.to be < 2 }
end
- describe :has_commits? do
+ describe '#has_commits?' do
it { expect(repository.has_commits?).to be_truthy }
end
- describe :empty? do
+ describe '#empty?' do
it { expect(repository.empty?).to be_falsey }
end
- describe :bare? do
+ describe '#bare?' do
it { expect(repository.bare?).to be_truthy }
end
- describe :heads do
+ describe '#heads' do
let(:heads) { repository.heads }
subject { heads }
@@ -147,7 +161,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe :ref_names do
+ describe '#ref_names' do
let(:ref_names) { repository.ref_names }
subject { ref_names }
@@ -164,7 +178,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe :search_files do
+ describe '#search_files' do
let(:results) { repository.search_files('rails', 'master') }
subject { results }
@@ -200,7 +214,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- context :submodules do
+ context '#submodules' do
let(:repository) { Gitlab::Git::Repository.new(TEST_REPO_PATH) }
context 'where repo has submodules' do
@@ -264,7 +278,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe :commit_count do
+ describe '#commit_count' do
it { expect(repository.commit_count("master")).to eq(25) }
it { expect(repository.commit_count("feature")).to eq(9) }
end
diff --git a/spec/lib/gitlab/gitaly_client/notifications_spec.rb b/spec/lib/gitlab/gitaly_client/notifications_spec.rb
new file mode 100644
index 00000000000..a6252c99aa1
--- /dev/null
+++ b/spec/lib/gitlab/gitaly_client/notifications_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe Gitlab::GitalyClient::Notifications do
+ let(:client) { Gitlab::GitalyClient::Notifications.new }
+
+ before do
+ allow(Gitlab.config.gitaly).to receive(:socket_path).and_return('path/to/gitaly.socket')
+ end
+
+ describe '#post_receive' do
+ let(:repo_path) { '/path/to/my_repo.git' }
+
+ it 'sends a post_receive message' do
+ expect_any_instance_of(Gitaly::Notifications::Stub).
+ to receive(:post_receive).with(post_receive_request_with_repo_path(repo_path))
+
+ client.post_receive(repo_path)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 06617f3b007..eef283c2460 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -153,6 +153,7 @@ project:
- gitlab_issue_tracker_service
- external_wiki_service
- kubernetes_service
+- mock_ci_service
- forked_project_link
- forked_from_project
- forked_project_links
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index 2e9f60432b4..c3d5c451a3c 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -2539,7 +2539,7 @@
"merge_params": {
"force_remove_source_branch": null
},
- "merge_when_build_succeeds": true,
+ "merge_when_pipeline_succeeds": true,
"merge_user_id": null,
"merge_commit_sha": null,
"deleted_at": null,
@@ -2976,7 +2976,7 @@
"merge_params": {
"force_remove_source_branch": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_user_id": null,
"merge_commit_sha": null,
"deleted_at": null,
@@ -3260,7 +3260,7 @@
"merge_params": {
"force_remove_source_branch": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_user_id": null,
"merge_commit_sha": null,
"deleted_at": null,
@@ -3544,7 +3544,7 @@
"merge_params": {
"force_remove_source_branch": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_user_id": null,
"merge_commit_sha": null,
"deleted_at": null,
@@ -4234,7 +4234,7 @@
"merge_params": {
"force_remove_source_branch": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_user_id": null,
"merge_commit_sha": null,
"deleted_at": null,
@@ -4782,7 +4782,7 @@
"merge_params": {
"force_remove_source_branch": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_user_id": null,
"merge_commit_sha": null,
"deleted_at": null,
@@ -5281,7 +5281,7 @@
"merge_params": {
"force_remove_source_branch": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_user_id": null,
"merge_commit_sha": null,
"deleted_at": null,
@@ -5541,7 +5541,7 @@
"merge_params": {
"force_remove_source_branch": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_user_id": null,
"merge_commit_sha": null,
"deleted_at": null,
@@ -6231,7 +6231,7 @@
"merge_params": {
"force_remove_source_branch": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_user_id": null,
"merge_commit_sha": null,
"deleted_at": null,
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index c5ac702d831..6534902b52d 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -142,7 +142,7 @@ MergeRequest:
- updated_by_id
- merge_error
- merge_params
-- merge_when_build_succeeds
+- merge_when_pipeline_succeeds
- merge_user_id
- merge_commit_sha
- deleted_at
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index b963ca4e542..5743c555cbe 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -181,20 +181,6 @@ describe Ci::Build, :models do
end
end
- describe '#create_from' do
- before do
- build.status = 'success'
- build.save
- end
- let(:create_from_build) { Ci::Build.create_from build }
-
- it 'exists a pending task' do
- expect(Ci::Build.pending.count(:all)).to eq 0
- create_from_build
- expect(Ci::Build.pending.count(:all)).to be > 0
- end
- end
-
describe '#depends_on_builds' do
let!(:build) { create(:ci_build, pipeline: pipeline, name: 'build', stage_idx: 0, stage: 'build') }
let!(:rspec_test) { create(:ci_build, pipeline: pipeline, name: 'rspec', stage_idx: 1, stage: 'test') }
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 9331dc41a5e..e000d0d38b3 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -37,12 +37,12 @@ describe MergeRequest, models: true do
end
it "is invalid without merge user" do
- subject.merge_when_build_succeeds = true
+ subject.merge_when_pipeline_succeeds = true
expect(subject).not_to be_valid
end
it "is valid with merge user" do
- subject.merge_when_build_succeeds = true
+ subject.merge_when_pipeline_succeeds = true
subject.merge_user = build(:user)
expect(subject).to be_valid
@@ -55,7 +55,7 @@ describe MergeRequest, models: true do
it { is_expected.to respond_to(:can_be_merged?) }
it { is_expected.to respond_to(:cannot_be_merged?) }
it { is_expected.to respond_to(:merge_params) }
- it { is_expected.to respond_to(:merge_when_build_succeeds) }
+ it { is_expected.to respond_to(:merge_when_pipeline_succeeds) }
end
describe '.in_projects' do
@@ -508,17 +508,17 @@ describe MergeRequest, models: true do
end
end
- describe "#reset_merge_when_build_succeeds" do
+ describe "#reset_merge_when_pipeline_succeeds" do
let(:merge_if_green) do
- create :merge_request, merge_when_build_succeeds: true, merge_user: create(:user),
+ create :merge_request, merge_when_pipeline_succeeds: true, merge_user: create(:user),
merge_params: { "should_remove_source_branch" => "1", "commit_message" => "msg" }
end
it "sets the item to false" do
- merge_if_green.reset_merge_when_build_succeeds
+ merge_if_green.reset_merge_when_pipeline_succeeds
merge_if_green.reload
- expect(merge_if_green.merge_when_build_succeeds).to be_falsey
+ expect(merge_if_green.merge_when_pipeline_succeeds).to be_falsey
expect(merge_if_green.merge_params["should_remove_source_branch"]).to be_nil
expect(merge_if_green.merge_params["commit_message"]).to be_nil
end
@@ -812,7 +812,7 @@ describe MergeRequest, models: true do
end
describe '#check_if_can_be_merged' do
- let(:project) { create(:empty_project, only_allow_merge_if_build_succeeds: true) }
+ let(:project) { create(:empty_project, only_allow_merge_if_pipeline_succeeds: true) }
subject { create(:merge_request, source_project: project, merge_status: :unchecked) }
@@ -927,7 +927,7 @@ describe MergeRequest, models: true do
end
describe '#mergeable_ci_state?' do
- let(:project) { create(:empty_project, only_allow_merge_if_build_succeeds: true) }
+ let(:project) { create(:empty_project, only_allow_merge_if_pipeline_succeeds: true) }
let(:pipeline) { create(:ci_empty_pipeline) }
subject { build(:merge_request, target_project: project) }
@@ -970,7 +970,7 @@ describe MergeRequest, models: true do
end
context 'when merges are not restricted to green builds' do
- subject { build(:merge_request, target_project: build(:empty_project, only_allow_merge_if_build_succeeds: false)) }
+ subject { build(:merge_request, target_project: build(:empty_project, only_allow_merge_if_pipeline_succeeds: false)) }
context 'and a failed pipeline is associated' do
before do
@@ -1575,7 +1575,7 @@ describe MergeRequest, models: true do
status: status)
end
- let(:project) { create(:project, :public, :repository, only_allow_merge_if_build_succeeds: true) }
+ let(:project) { create(:project, :public, :repository, only_allow_merge_if_pipeline_succeeds: true) }
let(:developer) { create(:user) }
let(:user) { create(:user) }
let(:merge_request) { create(:merge_request, source_project: project) }
diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb
index 81a8856b8f1..d8b3cc041a5 100644
--- a/spec/requests/api/commit_statuses_spec.rb
+++ b/spec/requests/api/commit_statuses_spec.rb
@@ -151,26 +151,62 @@ describe API::CommitStatuses, api: true do
end
context 'with all optional parameters' do
- before do
- optional_params = { state: 'success',
- context: 'coverage',
- ref: 'develop',
- description: 'test',
- coverage: 80.0,
- target_url: 'http://gitlab.com/status' }
-
- post api(post_url, developer), optional_params
+ context 'when creating a commit status' do
+ it 'creates commit status' do
+ post api(post_url, developer), {
+ state: 'success',
+ context: 'coverage',
+ ref: 'develop',
+ description: 'test',
+ coverage: 80.0,
+ target_url: 'http://gitlab.com/status'
+ }
+
+ expect(response).to have_http_status(201)
+ expect(json_response['sha']).to eq(commit.id)
+ expect(json_response['status']).to eq('success')
+ expect(json_response['name']).to eq('coverage')
+ expect(json_response['ref']).to eq('develop')
+ expect(json_response['coverage']).to eq(80.0)
+ expect(json_response['description']).to eq('test')
+ expect(json_response['target_url']).to eq('http://gitlab.com/status')
+ end
end
- it 'creates commit status' do
- expect(response).to have_http_status(201)
- expect(json_response['sha']).to eq(commit.id)
- expect(json_response['status']).to eq('success')
- expect(json_response['name']).to eq('coverage')
- expect(json_response['ref']).to eq('develop')
- expect(json_response['coverage']).to eq(80.0)
- expect(json_response['description']).to eq('test')
- expect(json_response['target_url']).to eq('http://gitlab.com/status')
+ context 'when updatig a commit status' do
+ before do
+ post api(post_url, developer), {
+ state: 'running',
+ context: 'coverage',
+ ref: 'develop',
+ description: 'coverage test',
+ coverage: 0.0,
+ target_url: 'http://gitlab.com/status'
+ }
+
+ post api(post_url, developer), {
+ state: 'success',
+ name: 'coverage',
+ ref: 'develop',
+ description: 'new description',
+ coverage: 90.0
+ }
+ end
+
+ it 'updates a commit status' do
+ expect(response).to have_http_status(201)
+ expect(json_response['sha']).to eq(commit.id)
+ expect(json_response['status']).to eq('success')
+ expect(json_response['name']).to eq('coverage')
+ expect(json_response['ref']).to eq('develop')
+ expect(json_response['coverage']).to eq(90.0)
+ expect(json_response['description']).to eq('new description')
+ expect(json_response['target_url']).to eq('http://gitlab.com/status')
+ end
+
+ it 'does not create a new commit status' do
+ expect(CommitStatus.count).to eq 1
+ end
end
end
diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb
index d66eb63fd0a..f2fd1dfc8db 100644
--- a/spec/requests/api/environments_spec.rb
+++ b/spec/requests/api/environments_spec.rb
@@ -24,6 +24,7 @@ describe API::Environments, api: true do
expect(json_response.first['name']).to eq(environment.name)
expect(json_response.first['external_url']).to eq(environment.external_url)
expect(json_response.first['project']['id']).to eq(project.id)
+ expect(json_response.first['project']['visibility']).to be_present
end
end
@@ -141,4 +142,39 @@ describe API::Environments, api: true do
end
end
end
+
+ describe 'POST /projects/:id/environments/:environment_id/stop' do
+ context 'as a master' do
+ context 'with a stoppable environment' do
+ before do
+ environment.update(state: :available)
+
+ post api("/projects/#{project.id}/environments/#{environment.id}/stop", user)
+ end
+
+ it 'returns a 200' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'actually stops the environment' do
+ expect(environment.reload).to be_stopped
+ end
+ end
+
+ it 'returns a 404 for non existing id' do
+ post api("/projects/#{project.id}/environments/12345/stop", user)
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Not found')
+ end
+ end
+
+ context 'a non member' do
+ it 'rejects the request' do
+ post api("/projects/#{project.id}/environments/#{environment.id}/stop", non_member)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
end
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index b0ba3ea912d..2b8fd7e31a1 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -176,7 +176,7 @@ describe API::Groups, api: true do
expect(json_response['name']).to eq(group1.name)
expect(json_response['path']).to eq(group1.path)
expect(json_response['description']).to eq(group1.description)
- expect(json_response['visibility_level']).to eq(group1.visibility_level)
+ expect(json_response['visibility']).to eq(Gitlab::VisibilityLevel.string_level(group1.visibility_level))
expect(json_response['avatar_url']).to eq(group1.avatar_url)
expect(json_response['web_url']).to eq(group1.web_url)
expect(json_response['request_access_enabled']).to eq(group1.request_access_enabled)
@@ -295,7 +295,7 @@ describe API::Groups, api: true do
expect(json_response.length).to eq(2)
project_names = json_response.map { |proj| proj['name'] }
expect(project_names).to match_array([project1.name, project3.name])
- expect(json_response.first['visibility_level']).to be_present
+ expect(json_response.first['visibility']).to be_present
end
it "returns the group's projects with simple representation" do
@@ -306,7 +306,7 @@ describe API::Groups, api: true do
expect(json_response.length).to eq(2)
project_names = json_response.map { |proj| proj['name'] }
expect(project_names).to match_array([project1.name, project3.name])
- expect(json_response.first['visibility_level']).not_to be_present
+ expect(json_response.first['visibility']).not_to be_present
end
it 'filters the groups projects' do
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index ffeacb15f17..f18b8e98707 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -409,6 +409,34 @@ describe API::Internal, api: true do
end
end
+ describe 'POST /notify_post_receive' do
+ let(:valid_params) do
+ { repo_path: project.repository.path, secret_token: secret_token }
+ end
+
+ before do
+ allow(Gitlab.config.gitaly).to receive(:socket_path).and_return('path/to/gitaly.socket')
+ end
+
+ it "calls the Gitaly client if it's enabled" do
+ expect_any_instance_of(Gitlab::GitalyClient::Notifications).
+ to receive(:post_receive).with(project.repository.path)
+
+ post api("/internal/notify_post_receive"), valid_params
+
+ expect(response).to have_http_status(200)
+ end
+
+ it "returns 500 if the gitaly call fails" do
+ expect_any_instance_of(Gitlab::GitalyClient::Notifications).
+ to receive(:post_receive).with(project.repository.path).and_raise(GRPC::Unavailable)
+
+ post api("/internal/notify_post_receive"), valid_params
+
+ expect(response).to have_http_status(500)
+ end
+ end
+
def project_with_repo_path(path)
double().tap do |fake_project|
allow(fake_project).to receive_message_chain('repository.path_to_repo' => path)
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index ddc2e51821e..710e4320fd1 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -212,6 +212,25 @@ describe API::Issues, api: true do
expect(json_response.first['id']).to eq(confidential_issue.id)
end
+ it 'returns an array of issues found by iids' do
+ get api('/issues', user), iids: [closed_issue.iid]
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(closed_issue.id)
+ end
+
+ it 'returns an empty array if iid does not exist' do
+ get api("/issues", user), iids: [99999]
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
+ end
+
it 'sorts by created_at descending by default' do
get api('/issues', user)
@@ -377,6 +396,25 @@ describe API::Issues, api: true do
expect(json_response.first['labels']).to eq([label_c.title, label_b.title, group_label.title])
end
+ it 'returns an array of issues found by iids' do
+ get api(base_url, user), iids: [group_issue.iid]
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(group_issue.id)
+ end
+
+ it 'returns an empty array if iid does not exist' do
+ get api(base_url, user), iids: [99999]
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
+ end
+
it 'returns an empty array if no group issue matches labels' do
get api("#{base_url}?labels=foo,bar", user)
@@ -586,6 +624,25 @@ describe API::Issues, api: true do
expect(json_response.first['labels']).to eq([label_c.title, label_b.title, label.title])
end
+ it 'returns an array of issues found by iids' do
+ get api("#{base_url}/issues", user), iids: [issue.iid]
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(issue.id)
+ end
+
+ it 'returns an empty array if iid does not exist' do
+ get api("#{base_url}/issues", user), iids: [99999]
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
+ end
+
it 'returns an empty array if not all labels matches' do
get api("#{base_url}/issues?labels=#{label.title},foo", user)
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 5522154899c..b3f0876c822 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -170,7 +170,7 @@ describe API::MergeRequests, api: true do
expect(json_response['source_project_id']).to eq(merge_request.source_project.id)
expect(json_response['target_project_id']).to eq(merge_request.target_project.id)
expect(json_response['work_in_progress']).to be_falsy
- expect(json_response['merge_when_build_succeeds']).to be_falsy
+ expect(json_response['merge_when_pipeline_succeeds']).to be_falsy
expect(json_response['merge_status']).to eq('can_be_merged')
expect(json_response['should_close_merge_request']).to be_falsy
expect(json_response['force_close_merge_request']).to be_falsy
@@ -483,11 +483,11 @@ describe API::MergeRequests, api: true do
allow_any_instance_of(MergeRequest).to receive(:head_pipeline).and_return(pipeline)
allow(pipeline).to receive(:active?).and_return(true)
- put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), merge_when_build_succeeds: true
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), merge_when_pipeline_succeeds: true
expect(response).to have_http_status(200)
expect(json_response['title']).to eq('Test')
- expect(json_response['merge_when_build_succeeds']).to eq(true)
+ expect(json_response['merge_when_pipeline_succeeds']).to eq(true)
end
end
diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb
index 418bf5a507c..570651165ea 100644
--- a/spec/requests/api/milestones_spec.rb
+++ b/spec/requests/api/milestones_spec.rb
@@ -4,8 +4,8 @@ describe API::Milestones, api: true do
include ApiHelpers
let(:user) { create(:user) }
let!(:project) { create(:empty_project, namespace: user.namespace ) }
- let!(:closed_milestone) { create(:closed_milestone, project: project) }
- let!(:milestone) { create(:milestone, project: project) }
+ let!(:closed_milestone) { create(:closed_milestone, project: project, title: 'version1', description: 'closed milestone') }
+ let!(:milestone) { create(:milestone, project: project, title: 'version2', description: 'open milestone') }
before { project.team << [user, :developer] }
@@ -60,17 +60,28 @@ describe API::Milestones, api: true do
get api("/projects/#{project.id}/milestones", user), iid: [milestone.iid, closed_milestone.iid]
expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
expect(json_response.size).to eq(2)
expect(json_response.first['title']).to eq milestone.title
expect(json_response.first['id']).to eq milestone.id
end
- it 'returns a project milestone by iid array' do
- get api("/projects/#{project.id}/milestones", user), iid: [milestone.iid, closed_milestone.iid]
+ it 'returns a project milestone by searching for title' do
+ get api("/projects/#{project.id}/milestones", user), search: 'version2'
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
- expect(json_response.size).to eq(2)
+ expect(json_response.size).to eq(1)
+ expect(json_response.first['title']).to eq milestone.title
+ expect(json_response.first['id']).to eq milestone.id
+ end
+
+ it 'returns a project milestones by searching for description' do
+ get api("/projects/#{project.id}/milestones", user), search: 'open'
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response.size).to eq(1)
expect(json_response.first['title']).to eq milestone.title
expect(json_response.first['id']).to eq milestone.id
end
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index 9d3c821b692..347f8f6fa3b 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -225,11 +225,11 @@ describe API::Notes, api: true do
context 'when the user is posting an award emoji on an issue created by someone else' do
let(:issue2) { create(:issue, project: project) }
- it 'returns an award emoji' do
+ it 'creates a new issue note' do
post api("/projects/#{project.id}/issues/#{issue2.id}/notes", user), body: ':+1:'
expect(response).to have_http_status(201)
- expect(json_response['awardable_id']).to eq issue2.id
+ expect(json_response['body']).to eq(':+1:')
end
end
diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb
index 2c4602faf2c..9e88c19b0bc 100644
--- a/spec/requests/api/project_snippets_spec.rb
+++ b/spec/requests/api/project_snippets_spec.rb
@@ -44,7 +44,7 @@ describe API::ProjectSnippets, api: true do
title: 'Test Title',
file_name: 'test.rb',
code: 'puts "hello world"',
- visibility_level: Snippet::PUBLIC
+ visibility: 'public'
}
end
@@ -56,7 +56,7 @@ describe API::ProjectSnippets, api: true do
expect(snippet.content).to eq(params[:code])
expect(snippet.title).to eq(params[:title])
expect(snippet.file_name).to eq(params[:file_name])
- expect(snippet.visibility_level).to eq(params[:visibility_level])
+ expect(snippet.visibility_level).to eq(Snippet::PUBLIC)
end
it 'returns 400 for missing parameters' do
@@ -80,14 +80,14 @@ describe API::ProjectSnippets, api: true do
context 'when the snippet is private' do
it 'creates the snippet' do
- expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }.
+ expect { create_snippet(project, visibility: 'private') }.
to change { Snippet.count }.by(1)
end
end
context 'when the snippet is public' do
- it 'rejects the shippet' do
- expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }.
+ it 'rejects the snippet' do
+ expect { create_snippet(project, visibility: 'public') }.
not_to change { Snippet.count }
expect(response).to have_http_status(400)
@@ -95,7 +95,7 @@ describe API::ProjectSnippets, api: true do
end
it 'creates a spam log' do
- expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }.
+ expect { create_snippet(project, visibility: 'public') }.
to change { SpamLog.count }.by(1)
end
end
@@ -165,7 +165,7 @@ describe API::ProjectSnippets, api: true do
let(:visibility_level) { Snippet::PRIVATE }
it 'rejects the snippet' do
- expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
+ expect { update_snippet(title: 'Foo', visibility: 'public') }.
not_to change { snippet.reload.title }
expect(response).to have_http_status(400)
@@ -173,7 +173,7 @@ describe API::ProjectSnippets, api: true do
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
+ expect { update_snippet(title: 'Foo', visibility: 'public') }.
to change { SpamLog.count }.by(1)
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 3a00d974633..2e8b557e9e2 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -320,7 +320,7 @@ describe API::Projects, api: true do
issues_enabled: false,
merge_requests_enabled: false,
wiki_enabled: false,
- only_allow_merge_if_build_succeeds: false,
+ only_allow_merge_if_pipeline_succeeds: false,
request_access_enabled: true,
only_allow_merge_if_all_discussions_are_resolved: false
})
@@ -340,36 +340,39 @@ describe API::Projects, api: true do
end
it 'sets a project as public' do
- project = attributes_for(:project, :public)
+ project = attributes_for(:project, visibility: 'public')
+
post api('/projects', user), project
- expect(json_response['public']).to be_truthy
- expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
+
+ expect(json_response['visibility']).to eq('public')
end
it 'sets a project as internal' do
- project = attributes_for(:project, :internal)
+ project = attributes_for(:project, visibility: 'internal')
+
post api('/projects', user), project
- expect(json_response['public']).to be_falsey
- expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
+
+ expect(json_response['visibility']).to eq('internal')
end
it 'sets a project as private' do
- project = attributes_for(:project, :private)
+ project = attributes_for(:project, visibility: 'private')
+
post api('/projects', user), project
- expect(json_response['public']).to be_falsey
- expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
+
+ expect(json_response['visibility']).to eq('private')
end
it 'sets a project as allowing merge even if build fails' do
- project = attributes_for(:project, { only_allow_merge_if_build_succeeds: false })
+ project = attributes_for(:project, { only_allow_merge_if_pipeline_succeeds: false })
post api('/projects', user), project
- expect(json_response['only_allow_merge_if_build_succeeds']).to be_falsey
+ expect(json_response['only_allow_merge_if_pipeline_succeeds']).to be_falsey
end
- it 'sets a project as allowing merge only if build succeeds' do
- project = attributes_for(:project, { only_allow_merge_if_build_succeeds: true })
+ it 'sets a project as allowing merge only if merge_when_pipeline_succeeds' do
+ project = attributes_for(:project, { only_allow_merge_if_pipeline_succeeds: true })
post api('/projects', user), project
- expect(json_response['only_allow_merge_if_build_succeeds']).to be_truthy
+ expect(json_response['only_allow_merge_if_pipeline_succeeds']).to be_truthy
end
it 'sets a project as allowing merge even if discussions are unresolved' do
@@ -397,7 +400,7 @@ describe API::Projects, api: true do
end
context 'when a visibility level is restricted' do
- let(:project_param) { attributes_for(:project, :public) }
+ let(:project_param) { attributes_for(:project, visibility: 'public') }
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
@@ -415,10 +418,7 @@ describe API::Projects, api: true do
it 'allows an admin to override restricted visibility settings' do
post api('/projects', admin), project_param
- expect(json_response['public']).to be_truthy
- expect(json_response['visibility_level']).to(
- eq(Gitlab::VisibilityLevel::PUBLIC)
- )
+ expect(json_response['visibility']).to eq('public')
end
end
end
@@ -459,40 +459,41 @@ describe API::Projects, api: true do
end
it 'sets a project as public' do
- project = attributes_for(:project, :public)
+ project = attributes_for(:project, visibility: 'public')
+
post api("/projects/user/#{user.id}", admin), project
expect(response).to have_http_status(201)
- expect(json_response['public']).to be_truthy
- expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
+ expect(json_response['visibility']).to eq('public')
end
it 'sets a project as internal' do
- project = attributes_for(:project, :internal)
+ project = attributes_for(:project, visibility: 'internal')
+
post api("/projects/user/#{user.id}", admin), project
expect(response).to have_http_status(201)
- expect(json_response['public']).to be_falsey
- expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
+ expect(json_response['visibility']).to eq('internal')
end
it 'sets a project as private' do
- project = attributes_for(:project, :private)
+ project = attributes_for(:project, visibility: 'private')
+
post api("/projects/user/#{user.id}", admin), project
- expect(json_response['public']).to be_falsey
- expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
+
+ expect(json_response['visibility']).to eq('private')
end
it 'sets a project as allowing merge even if build fails' do
- project = attributes_for(:project, { only_allow_merge_if_build_succeeds: false })
+ project = attributes_for(:project, { only_allow_merge_if_pipeline_succeeds: false })
post api("/projects/user/#{user.id}", admin), project
- expect(json_response['only_allow_merge_if_build_succeeds']).to be_falsey
+ expect(json_response['only_allow_merge_if_pipeline_succeeds']).to be_falsey
end
- it 'sets a project as allowing merge only if build succeeds' do
- project = attributes_for(:project, { only_allow_merge_if_build_succeeds: true })
+ it 'sets a project as allowing merge only if merge_when_pipeline_succeeds' do
+ project = attributes_for(:project, { only_allow_merge_if_pipeline_succeeds: true })
post api("/projects/user/#{user.id}", admin), project
- expect(json_response['only_allow_merge_if_build_succeeds']).to be_truthy
+ expect(json_response['only_allow_merge_if_pipeline_succeeds']).to be_truthy
end
it 'sets a project as allowing merge even if discussions are unresolved' do
@@ -556,9 +557,8 @@ describe API::Projects, api: true do
expect(json_response['description']).to eq(project.description)
expect(json_response['default_branch']).to eq(project.default_branch)
expect(json_response['tag_list']).to be_an Array
- expect(json_response['public']).to be_falsey
expect(json_response['archived']).to be_falsey
- expect(json_response['visibility_level']).to be_present
+ expect(json_response['visibility']).to be_present
expect(json_response['ssh_url_to_repo']).to be_present
expect(json_response['http_url_to_repo']).to be_present
expect(json_response['web_url']).to be_present
@@ -586,7 +586,7 @@ describe API::Projects, api: true do
expect(json_response['shared_with_groups'][0]['group_id']).to eq(group.id)
expect(json_response['shared_with_groups'][0]['group_name']).to eq(group.name)
expect(json_response['shared_with_groups'][0]['group_access_level']).to eq(link.group_access)
- expect(json_response['only_allow_merge_if_build_succeeds']).to eq(project.only_allow_merge_if_build_succeeds)
+ expect(json_response['only_allow_merge_if_pipeline_succeeds']).to eq(project.only_allow_merge_if_pipeline_succeeds)
expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to eq(project.only_allow_merge_if_all_discussions_are_resolved)
end
@@ -812,8 +812,7 @@ describe API::Projects, api: true do
describe 'POST /projects/:id/snippets' do
it 'creates a new project snippet' do
post api("/projects/#{project.id}/snippets", user),
- title: 'api test', file_name: 'sample.rb', code: 'test',
- visibility_level: Gitlab::VisibilityLevel::PRIVATE
+ title: 'api test', file_name: 'sample.rb', code: 'test', visibility: 'private'
expect(response).to have_http_status(201)
expect(json_response['title']).to eq('api test')
end
@@ -1065,7 +1064,7 @@ describe API::Projects, api: true do
end
it 'updates visibility_level' do
- project_param = { visibility_level: Gitlab::VisibilityLevel::PUBLIC }
+ project_param = { visibility: 'public' }
put api("/projects/#{project3.id}", user), project_param
expect(response).to have_http_status(200)
project_param.each_pair do |k, v|
@@ -1075,13 +1074,13 @@ describe API::Projects, api: true do
it 'updates visibility_level from public to private' do
project3.update_attributes({ visibility_level: Gitlab::VisibilityLevel::PUBLIC })
- project_param = { visibility_level: Gitlab::VisibilityLevel::PRIVATE }
+ project_param = { visibility: 'private' }
put api("/projects/#{project3.id}", user), project_param
expect(response).to have_http_status(200)
project_param.each_pair do |k, v|
expect(json_response[k.to_s]).to eq(v)
end
- expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ expect(json_response['visibility']).to eq('private')
end
it 'does not update name to existing name' do
@@ -1148,7 +1147,7 @@ describe API::Projects, api: true do
end
it 'does not update visibility_level' do
- project_param = { visibility_level: Gitlab::VisibilityLevel::PUBLIC }
+ project_param = { visibility: 'public' }
put api("/projects/#{project3.id}", user4), project_param
expect(response).to have_http_status(403)
end
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 411905edb49..11b4b718e2c 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -18,6 +18,9 @@ describe API::Settings, 'Settings', api: true do
expect(json_response['koding_url']).to be_nil
expect(json_response['plantuml_enabled']).to be_falsey
expect(json_response['plantuml_url']).to be_nil
+ expect(json_response['default_project_visibility']).to be_a String
+ expect(json_response['default_snippet_visibility']).to be_a String
+ expect(json_response['default_group_visibility']).to be_a String
end
end
@@ -37,6 +40,8 @@ describe API::Settings, 'Settings', api: true do
koding_url: 'http://koding.example.com',
plantuml_enabled: true,
plantuml_url: 'http://plantuml.example.com',
+ default_snippet_visibility: 'internal',
+ restricted_visibility_levels: ['public'],
default_artifacts_expire_in: '2 days'
expect(response).to have_http_status(200)
expect(json_response['default_projects_limit']).to eq(3)
@@ -47,6 +52,8 @@ describe API::Settings, 'Settings', api: true do
expect(json_response['koding_url']).to eq('http://koding.example.com')
expect(json_response['plantuml_enabled']).to be_truthy
expect(json_response['plantuml_url']).to eq('http://plantuml.example.com')
+ expect(json_response['default_snippet_visibility']).to eq('internal')
+ expect(json_response['restricted_visibility_levels']).to eq(['public'])
expect(json_response['default_artifacts_expire_in']).to eq('2 days')
end
end
diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb
index 5219f6eed42..5d75b47b3cd 100644
--- a/spec/requests/api/snippets_spec.rb
+++ b/spec/requests/api/snippets_spec.rb
@@ -87,7 +87,7 @@ describe API::Snippets, api: true do
title: 'Test Title',
file_name: 'test.rb',
content: 'puts "hello world"',
- visibility_level: Snippet::PUBLIC
+ visibility: 'public'
}
end
@@ -120,14 +120,14 @@ describe API::Snippets, api: true do
context 'when the snippet is private' do
it 'creates the snippet' do
- expect { create_snippet(visibility_level: Snippet::PRIVATE) }.
+ expect { create_snippet(visibility: 'private') }.
to change { Snippet.count }.by(1)
end
end
context 'when the snippet is public' do
it 'rejects the shippet' do
- expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
+ expect { create_snippet(visibility: 'public') }.
not_to change { Snippet.count }
expect(response).to have_http_status(400)
@@ -135,7 +135,7 @@ describe API::Snippets, api: true do
end
it 'creates a spam log' do
- expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
+ expect { create_snippet(visibility: 'public') }.
to change { SpamLog.count }.by(1)
end
end
@@ -218,12 +218,12 @@ describe API::Snippets, api: true do
let(:visibility_level) { Snippet::PRIVATE }
it 'rejects the snippet' do
- expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
+ expect { update_snippet(title: 'Foo', visibility: 'public') }.
not_to change { snippet.reload.title }
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
+ expect { update_snippet(title: 'Foo', visibility: 'public') }.
to change { SpamLog.count }.by(1)
end
end
diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb
index f35e963a14b..1e401935662 100644
--- a/spec/requests/api/todos_spec.rb
+++ b/spec/requests/api/todos_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe API::Todos, api: true do
include ApiHelpers
- let(:project_1) { create(:empty_project) }
+ let(:project_1) { create(:empty_project, :test_repo) }
let(:project_2) { create(:empty_project) }
let(:author_1) { create(:user) }
let(:author_2) { create(:user) }
@@ -11,7 +11,7 @@ describe API::Todos, api: true do
let(:merge_request) { create(:merge_request, source_project: project_1) }
let!(:pending_1) { create(:todo, :mentioned, project: project_1, author: author_1, user: john_doe) }
let!(:pending_2) { create(:todo, project: project_2, author: author_2, user: john_doe) }
- let!(:pending_3) { create(:todo, project: project_1, author: author_2, user: john_doe) }
+ let!(:pending_3) { create(:on_commit_todo, project: project_1, author: author_2, user: john_doe) }
let!(:done) { create(:todo, :done, project: project_1, author: author_1, user: john_doe) }
before do
diff --git a/spec/requests/api/v3/environments_spec.rb b/spec/requests/api/v3/environments_spec.rb
index 1ac666ab240..216192c9d34 100644
--- a/spec/requests/api/v3/environments_spec.rb
+++ b/spec/requests/api/v3/environments_spec.rb
@@ -12,6 +12,132 @@ describe API::V3::Environments, api: true do
project.team << [user, :master]
end
+ shared_examples 'a paginated resources' do
+ before do
+ # Fires the request
+ request
+ end
+
+ it 'has pagination headers' do
+ expect(response.headers).to include('X-Total')
+ expect(response.headers).to include('X-Total-Pages')
+ expect(response.headers).to include('X-Per-Page')
+ expect(response.headers).to include('X-Page')
+ expect(response.headers).to include('X-Next-Page')
+ expect(response.headers).to include('X-Prev-Page')
+ expect(response.headers).to include('Link')
+ end
+ end
+
+ describe 'GET /projects/:id/environments' do
+ context 'as member of the project' do
+ it_behaves_like 'a paginated resources' do
+ let(:request) { get v3_api("/projects/#{project.id}/environments", user) }
+ end
+
+ it 'returns project environments' do
+ get v3_api("/projects/#{project.id}/environments", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(1)
+ expect(json_response.first['name']).to eq(environment.name)
+ expect(json_response.first['external_url']).to eq(environment.external_url)
+ expect(json_response.first['project']['id']).to eq(project.id)
+ expect(json_response.first['project']['visibility_level']).to be_present
+ end
+ end
+
+ context 'as non member' do
+ it 'returns a 404 status code' do
+ get v3_api("/projects/#{project.id}/environments", non_member)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ describe 'POST /projects/:id/environments' do
+ context 'as a member' do
+ it 'creates a environment with valid params' do
+ post v3_api("/projects/#{project.id}/environments", user), name: "mepmep"
+
+ expect(response).to have_http_status(201)
+ expect(json_response['name']).to eq('mepmep')
+ expect(json_response['slug']).to eq('mepmep')
+ expect(json_response['external']).to be nil
+ end
+
+ it 'requires name to be passed' do
+ post v3_api("/projects/#{project.id}/environments", user), external_url: 'test.gitlab.com'
+
+ expect(response).to have_http_status(400)
+ end
+
+ it 'returns a 400 if environment already exists' do
+ post v3_api("/projects/#{project.id}/environments", user), name: environment.name
+
+ expect(response).to have_http_status(400)
+ end
+
+ it 'returns a 400 if slug is specified' do
+ post v3_api("/projects/#{project.id}/environments", user), name: "foo", slug: "foo"
+
+ expect(response).to have_http_status(400)
+ expect(json_response["error"]).to eq("slug is automatically generated and cannot be changed")
+ end
+ end
+
+ context 'a non member' do
+ it 'rejects the request' do
+ post v3_api("/projects/#{project.id}/environments", non_member), name: 'gitlab.com'
+
+ expect(response).to have_http_status(404)
+ end
+
+ it 'returns a 400 when the required params are missing' do
+ post v3_api("/projects/12345/environments", non_member), external_url: 'http://env.git.com'
+ end
+ end
+ end
+
+ describe 'PUT /projects/:id/environments/:environment_id' do
+ it 'returns a 200 if name and external_url are changed' do
+ url = 'https://mepmep.whatever.ninja'
+ put v3_api("/projects/#{project.id}/environments/#{environment.id}", user),
+ name: 'Mepmep', external_url: url
+
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq('Mepmep')
+ expect(json_response['external_url']).to eq(url)
+ end
+
+ it "won't allow slug to be changed" do
+ slug = environment.slug
+ api_url = v3_api("/projects/#{project.id}/environments/#{environment.id}", user)
+ put api_url, slug: slug + "-foo"
+
+ expect(response).to have_http_status(400)
+ expect(json_response["error"]).to eq("slug is automatically generated and cannot be changed")
+ end
+
+ it "won't update the external_url if only the name is passed" do
+ url = environment.external_url
+ put v3_api("/projects/#{project.id}/environments/#{environment.id}", user),
+ name: 'Mepmep'
+
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq('Mepmep')
+ expect(json_response['external_url']).to eq(url)
+ end
+
+ it 'returns a 404 if the environment does not exist' do
+ put v3_api("/projects/#{project.id}/environments/12345", user)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
describe 'DELETE /projects/:id/environments/:environment_id' do
context 'as a master' do
it 'returns a 200 for an existing environment' do
diff --git a/spec/requests/api/v3/groups_spec.rb b/spec/requests/api/v3/groups_spec.rb
index 8b29ad03737..a71b7d4b008 100644
--- a/spec/requests/api/v3/groups_spec.rb
+++ b/spec/requests/api/v3/groups_spec.rb
@@ -4,14 +4,144 @@ describe API::V3::Groups, api: true do
include ApiHelpers
include UploadHelpers
+ let(:user1) { create(:user, can_create_group: false) }
let(:user2) { create(:user) }
+ let(:user3) { create(:user) }
+ let(:admin) { create(:admin) }
+ let!(:group1) { create(:group, avatar: File.open(uploaded_image_temp_path)) }
let!(:group2) { create(:group, :private) }
+ let!(:project1) { create(:empty_project, namespace: group1) }
let!(:project2) { create(:empty_project, namespace: group2) }
+ let!(:project3) { create(:empty_project, namespace: group1, path: 'test', visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
before do
+ group1.add_owner(user1)
group2.add_owner(user2)
end
+ describe "GET /groups" do
+ context "when unauthenticated" do
+ it "returns authentication error" do
+ get v3_api("/groups")
+
+ expect(response).to have_http_status(401)
+ end
+ end
+
+ context "when authenticated as user" do
+ it "normal user: returns an array of groups of user1" do
+ get v3_api("/groups", user1)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response)
+ .to satisfy_one { |group| group['name'] == group1.name }
+ end
+
+ it "does not include statistics" do
+ get v3_api("/groups", user1), statistics: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first).not_to include 'statistics'
+ end
+ end
+
+ context "when authenticated as admin" do
+ it "admin: returns an array of all groups" do
+ get v3_api("/groups", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(2)
+ end
+
+ it "does not include statistics by default" do
+ get v3_api("/groups", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first).not_to include('statistics')
+ end
+
+ it "includes statistics if requested" do
+ attributes = {
+ storage_size: 702,
+ repository_size: 123,
+ lfs_objects_size: 234,
+ build_artifacts_size: 345,
+ }.stringify_keys
+
+ project1.statistics.update!(attributes)
+
+ get v3_api("/groups", admin), statistics: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response)
+ .to satisfy_one { |group| group['statistics'] == attributes }
+ end
+ end
+
+ context "when using skip_groups in request" do
+ it "returns all groups excluding skipped groups" do
+ get v3_api("/groups", admin), skip_groups: [group2.id]
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ end
+ end
+
+ context "when using all_available in request" do
+ let(:response_groups) { json_response.map { |group| group['name'] } }
+
+ it "returns all groups you have access to" do
+ public_group = create :group, :public
+
+ get v3_api("/groups", user1), all_available: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_groups).to contain_exactly(public_group.name, group1.name)
+ end
+ end
+
+ context "when using sorting" do
+ let(:group3) { create(:group, name: "a#{group1.name}", path: "z#{group1.path}") }
+ let(:response_groups) { json_response.map { |group| group['name'] } }
+
+ before do
+ group3.add_owner(user1)
+ end
+
+ it "sorts by name ascending by default" do
+ get v3_api("/groups", user1)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_groups).to eq([group3.name, group1.name])
+ end
+
+ it "sorts in descending order when passed" do
+ get v3_api("/groups", user1), sort: "desc"
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_groups).to eq([group1.name, group3.name])
+ end
+
+ it "sorts by the order_by param" do
+ get v3_api("/groups", user1), order_by: "path"
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_groups).to eq([group1.name, group3.name])
+ end
+ end
+ end
+
describe 'GET /groups/owned' do
context 'when unauthenticated' do
it 'returns authentication error' do
@@ -32,4 +162,404 @@ describe API::V3::Groups, api: true do
end
end
end
+
+ describe "GET /groups/:id" do
+ context "when authenticated as user" do
+ it "returns one of user1's groups" do
+ project = create(:empty_project, namespace: group2, path: 'Foo')
+ create(:project_group_link, project: project, group: group1)
+
+ get v3_api("/groups/#{group1.id}", user1)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['id']).to eq(group1.id)
+ expect(json_response['name']).to eq(group1.name)
+ expect(json_response['path']).to eq(group1.path)
+ expect(json_response['description']).to eq(group1.description)
+ expect(json_response['visibility_level']).to eq(group1.visibility_level)
+ expect(json_response['avatar_url']).to eq(group1.avatar_url)
+ expect(json_response['web_url']).to eq(group1.web_url)
+ expect(json_response['request_access_enabled']).to eq(group1.request_access_enabled)
+ expect(json_response['full_name']).to eq(group1.full_name)
+ expect(json_response['full_path']).to eq(group1.full_path)
+ expect(json_response['parent_id']).to eq(group1.parent_id)
+ expect(json_response['projects']).to be_an Array
+ expect(json_response['projects'].length).to eq(2)
+ expect(json_response['shared_projects']).to be_an Array
+ expect(json_response['shared_projects'].length).to eq(1)
+ expect(json_response['shared_projects'][0]['id']).to eq(project.id)
+ end
+
+ it "does not return a non existing group" do
+ get v3_api("/groups/1328", user1)
+
+ expect(response).to have_http_status(404)
+ end
+
+ it "does not return a group not attached to user1" do
+ get v3_api("/groups/#{group2.id}", user1)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context "when authenticated as admin" do
+ it "returns any existing group" do
+ get v3_api("/groups/#{group2.id}", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(group2.name)
+ end
+
+ it "does not return a non existing group" do
+ get v3_api("/groups/1328", admin)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'when using group path in URL' do
+ it 'returns any existing group' do
+ get v3_api("/groups/#{group1.path}", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(group1.name)
+ end
+
+ it 'does not return a non existing group' do
+ get v3_api('/groups/unknown', admin)
+
+ expect(response).to have_http_status(404)
+ end
+
+ it 'does not return a group not attached to user1' do
+ get v3_api("/groups/#{group2.path}", user1)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ describe 'PUT /groups/:id' do
+ let(:new_group_name) { 'New Group'}
+
+ context 'when authenticated as the group owner' do
+ it 'updates the group' do
+ put v3_api("/groups/#{group1.id}", user1), name: new_group_name, request_access_enabled: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(new_group_name)
+ expect(json_response['request_access_enabled']).to eq(true)
+ end
+
+ it 'returns 404 for a non existing group' do
+ put v3_api('/groups/1328', user1), name: new_group_name
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'when authenticated as the admin' do
+ it 'updates the group' do
+ put v3_api("/groups/#{group1.id}", admin), name: new_group_name
+
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(new_group_name)
+ end
+ end
+
+ context 'when authenticated as an user that can see the group' do
+ it 'does not updates the group' do
+ put v3_api("/groups/#{group1.id}", user2), name: new_group_name
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'when authenticated as an user that cannot see the group' do
+ it 'returns 404 when trying to update the group' do
+ put v3_api("/groups/#{group2.id}", user1), name: new_group_name
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ describe "GET /groups/:id/projects" do
+ context "when authenticated as user" do
+ it "returns the group's projects" do
+ get v3_api("/groups/#{group1.id}/projects", user1)
+
+ expect(response).to have_http_status(200)
+ expect(json_response.length).to eq(2)
+ project_names = json_response.map { |proj| proj['name'] }
+ expect(project_names).to match_array([project1.name, project3.name])
+ expect(json_response.first['visibility_level']).to be_present
+ end
+
+ it "returns the group's projects with simple representation" do
+ get v3_api("/groups/#{group1.id}/projects", user1), simple: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response.length).to eq(2)
+ project_names = json_response.map { |proj| proj['name'] }
+ expect(project_names).to match_array([project1.name, project3.name])
+ expect(json_response.first['visibility_level']).not_to be_present
+ end
+
+ it 'filters the groups projects' do
+ public_project = create(:empty_project, :public, path: 'test1', group: group1)
+
+ get v3_api("/groups/#{group1.id}/projects", user1), visibility: 'public'
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an(Array)
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['name']).to eq(public_project.name)
+ end
+
+ it "does not return a non existing group" do
+ get v3_api("/groups/1328/projects", user1)
+
+ expect(response).to have_http_status(404)
+ end
+
+ it "does not return a group not attached to user1" do
+ get v3_api("/groups/#{group2.id}/projects", user1)
+
+ expect(response).to have_http_status(404)
+ end
+
+ it "only returns projects to which user has access" do
+ project3.team << [user3, :developer]
+
+ get v3_api("/groups/#{group1.id}/projects", user3)
+
+ expect(response).to have_http_status(200)
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['name']).to eq(project3.name)
+ end
+
+ it 'only returns the projects owned by user' do
+ project2.group.add_owner(user3)
+
+ get v3_api("/groups/#{project2.group.id}/projects", user3), owned: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['name']).to eq(project2.name)
+ end
+
+ it 'only returns the projects starred by user' do
+ user1.starred_projects = [project1]
+
+ get v3_api("/groups/#{group1.id}/projects", user1), starred: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['name']).to eq(project1.name)
+ end
+ end
+
+ context "when authenticated as admin" do
+ it "returns any existing group" do
+ get v3_api("/groups/#{group2.id}/projects", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['name']).to eq(project2.name)
+ end
+
+ it "does not return a non existing group" do
+ get v3_api("/groups/1328/projects", admin)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'when using group path in URL' do
+ it 'returns any existing group' do
+ get v3_api("/groups/#{group1.path}/projects", admin)
+
+ expect(response).to have_http_status(200)
+ project_names = json_response.map { |proj| proj['name'] }
+ expect(project_names).to match_array([project1.name, project3.name])
+ end
+
+ it 'does not return a non existing group' do
+ get v3_api('/groups/unknown/projects', admin)
+
+ expect(response).to have_http_status(404)
+ end
+
+ it 'does not return a group not attached to user1' do
+ get v3_api("/groups/#{group2.path}/projects", user1)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ describe "POST /groups" do
+ context "when authenticated as user without group permissions" do
+ it "does not create group" do
+ post v3_api("/groups", user1), attributes_for(:group)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context "when authenticated as user with group permissions" do
+ it "creates group" do
+ group = attributes_for(:group, { request_access_enabled: false })
+
+ post v3_api("/groups", user3), group
+
+ expect(response).to have_http_status(201)
+
+ expect(json_response["name"]).to eq(group[:name])
+ expect(json_response["path"]).to eq(group[:path])
+ expect(json_response["request_access_enabled"]).to eq(group[:request_access_enabled])
+ end
+
+ it "creates a nested group" do
+ parent = create(:group)
+ parent.add_owner(user3)
+ group = attributes_for(:group, { parent_id: parent.id })
+
+ post v3_api("/groups", user3), group
+
+ expect(response).to have_http_status(201)
+
+ expect(json_response["full_path"]).to eq("#{parent.path}/#{group[:path]}")
+ expect(json_response["parent_id"]).to eq(parent.id)
+ end
+
+ it "does not create group, duplicate" do
+ post v3_api("/groups", user3), { name: 'Duplicate Test', path: group2.path }
+
+ expect(response).to have_http_status(400)
+ expect(response.message).to eq("Bad Request")
+ end
+
+ it "returns 400 bad request error if name not given" do
+ post v3_api("/groups", user3), { path: group2.path }
+
+ expect(response).to have_http_status(400)
+ end
+
+ it "returns 400 bad request error if path not given" do
+ post v3_api("/groups", user3), { name: 'test' }
+
+ expect(response).to have_http_status(400)
+ end
+ end
+ end
+
+ describe "DELETE /groups/:id" do
+ context "when authenticated as user" do
+ it "removes group" do
+ delete v3_api("/groups/#{group1.id}", user1)
+
+ expect(response).to have_http_status(200)
+ end
+
+ it "does not remove a group if not an owner" do
+ user4 = create(:user)
+ group1.add_master(user4)
+
+ delete v3_api("/groups/#{group1.id}", user3)
+
+ expect(response).to have_http_status(403)
+ end
+
+ it "does not remove a non existing group" do
+ delete v3_api("/groups/1328", user1)
+
+ expect(response).to have_http_status(404)
+ end
+
+ it "does not remove a group not attached to user1" do
+ delete v3_api("/groups/#{group2.id}", user1)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context "when authenticated as admin" do
+ it "removes any existing group" do
+ delete v3_api("/groups/#{group2.id}", admin)
+
+ expect(response).to have_http_status(200)
+ end
+
+ it "does not remove a non existing group" do
+ delete v3_api("/groups/1328", admin)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ describe "POST /groups/:id/projects/:project_id" do
+ let(:project) { create(:empty_project) }
+ let(:project_path) { "#{project.namespace.path}%2F#{project.path}" }
+
+ before(:each) do
+ allow_any_instance_of(Projects::TransferService).
+ to receive(:execute).and_return(true)
+ end
+
+ context "when authenticated as user" do
+ it "does not transfer project to group" do
+ post v3_api("/groups/#{group1.id}/projects/#{project.id}", user2)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context "when authenticated as admin" do
+ it "transfers project to group" do
+ post v3_api("/groups/#{group1.id}/projects/#{project.id}", admin)
+
+ expect(response).to have_http_status(201)
+ end
+
+ context 'when using project path in URL' do
+ context 'with a valid project path' do
+ it "transfers project to group" do
+ post v3_api("/groups/#{group1.id}/projects/#{project_path}", admin)
+
+ expect(response).to have_http_status(201)
+ end
+ end
+
+ context 'with a non-existent project path' do
+ it "does not transfer project to group" do
+ post v3_api("/groups/#{group1.id}/projects/nogroup%2Fnoproject", admin)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ context 'when using a group path in URL' do
+ context 'with a valid group path' do
+ it "transfers project to group" do
+ post v3_api("/groups/#{group1.path}/projects/#{project_path}", admin)
+
+ expect(response).to have_http_status(201)
+ end
+ end
+
+ context 'with a non-existent group path' do
+ it "does not transfer project to group" do
+ post v3_api("/groups/noexist/projects/#{project_path}", admin)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/requests/api/v3/notes_spec.rb b/spec/requests/api/v3/notes_spec.rb
index b8f0260c6a2..ddef2d5eb04 100644
--- a/spec/requests/api/v3/notes_spec.rb
+++ b/spec/requests/api/v3/notes_spec.rb
@@ -228,11 +228,11 @@ describe API::V3::Notes, api: true do
context 'when the user is posting an award emoji on an issue created by someone else' do
let(:issue2) { create(:issue, project: project) }
- it 'returns an award emoji' do
+ it 'creates a new issue note' do
post v3_api("/projects/#{project.id}/issues/#{issue2.id}/notes", user), body: ':+1:'
expect(response).to have_http_status(201)
- expect(json_response['awardable_id']).to eq issue2.id
+ expect(json_response['body']).to eq(':+1:')
end
end
diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb
index 34940b2f1c7..d8bb562587d 100644
--- a/spec/requests/api/v3/projects_spec.rb
+++ b/spec/requests/api/v3/projects_spec.rb
@@ -427,7 +427,7 @@ describe API::V3::Projects, api: true do
expect(json_response['only_allow_merge_if_build_succeeds']).to be_falsey
end
- it 'sets a project as allowing merge only if build succeeds' do
+ it 'sets a project as allowing merge only if merge_when_pipeline_succeeds' do
project = attributes_for(:project, { only_allow_merge_if_build_succeeds: true })
post v3_api('/projects', user), project
expect(json_response['only_allow_merge_if_build_succeeds']).to be_truthy
@@ -572,7 +572,7 @@ describe API::V3::Projects, api: true do
expect(json_response['only_allow_merge_if_build_succeeds']).to be_falsey
end
- it 'sets a project as allowing merge only if build succeeds' do
+ it 'sets a project as allowing merge only if merge_when_pipeline_succeeds' do
project = attributes_for(:project, { only_allow_merge_if_build_succeeds: true })
post v3_api("/projects/user/#{user.id}", admin), project
expect(json_response['only_allow_merge_if_build_succeeds']).to be_truthy
@@ -669,7 +669,7 @@ describe API::V3::Projects, api: true do
expect(json_response['shared_with_groups'][0]['group_id']).to eq(group.id)
expect(json_response['shared_with_groups'][0]['group_name']).to eq(group.name)
expect(json_response['shared_with_groups'][0]['group_access_level']).to eq(link.group_access)
- expect(json_response['only_allow_merge_if_build_succeeds']).to eq(project.only_allow_merge_if_build_succeeds)
+ expect(json_response['only_allow_merge_if_build_succeeds']).to eq(project.only_allow_merge_if_pipeline_succeeds)
expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to eq(project.only_allow_merge_if_all_discussions_are_resolved)
end
diff --git a/spec/requests/api/v3/settings_spec.rb b/spec/requests/api/v3/settings_spec.rb
new file mode 100644
index 00000000000..a9fa5adac17
--- /dev/null
+++ b/spec/requests/api/v3/settings_spec.rb
@@ -0,0 +1,65 @@
+require 'spec_helper'
+
+describe API::V3::Settings, 'Settings', api: true do
+ include ApiHelpers
+
+ let(:user) { create(:user) }
+ let(:admin) { create(:admin) }
+
+ describe "GET /application/settings" do
+ it "returns application settings" do
+ get v3_api("/application/settings", admin)
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Hash
+ expect(json_response['default_projects_limit']).to eq(42)
+ expect(json_response['signin_enabled']).to be_truthy
+ expect(json_response['repository_storage']).to eq('default')
+ expect(json_response['koding_enabled']).to be_falsey
+ expect(json_response['koding_url']).to be_nil
+ expect(json_response['plantuml_enabled']).to be_falsey
+ expect(json_response['plantuml_url']).to be_nil
+ end
+ end
+
+ describe "PUT /application/settings" do
+ context "custom repository storage type set in the config" do
+ before do
+ storages = { 'custom' => 'tmp/tests/custom_repositories' }
+ allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
+ end
+
+ it "updates application settings" do
+ put v3_api("/application/settings", admin),
+ default_projects_limit: 3, signin_enabled: false, repository_storage: 'custom', koding_enabled: true, koding_url: 'http://koding.example.com',
+ plantuml_enabled: true, plantuml_url: 'http://plantuml.example.com'
+ expect(response).to have_http_status(200)
+ expect(json_response['default_projects_limit']).to eq(3)
+ expect(json_response['signin_enabled']).to be_falsey
+ expect(json_response['repository_storage']).to eq('custom')
+ expect(json_response['repository_storages']).to eq(['custom'])
+ expect(json_response['koding_enabled']).to be_truthy
+ expect(json_response['koding_url']).to eq('http://koding.example.com')
+ expect(json_response['plantuml_enabled']).to be_truthy
+ expect(json_response['plantuml_url']).to eq('http://plantuml.example.com')
+ end
+ end
+
+ context "missing koding_url value when koding_enabled is true" do
+ it "returns a blank parameter error message" do
+ put v3_api("/application/settings", admin), koding_enabled: true
+
+ expect(response).to have_http_status(400)
+ expect(json_response['error']).to eq('koding_url is missing')
+ end
+ end
+
+ context "missing plantuml_url value when plantuml_enabled is true" do
+ it "returns a blank parameter error message" do
+ put v3_api("/application/settings", admin), plantuml_enabled: true
+
+ expect(response).to have_http_status(400)
+ expect(json_response['error']).to eq('plantuml_url is missing')
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/v3/snippets_spec.rb b/spec/requests/api/v3/snippets_spec.rb
new file mode 100644
index 00000000000..05653bd0d51
--- /dev/null
+++ b/spec/requests/api/v3/snippets_spec.rb
@@ -0,0 +1,187 @@
+require 'rails_helper'
+
+describe API::V3::Snippets, api: true do
+ include ApiHelpers
+ let!(:user) { create(:user) }
+
+ describe 'GET /snippets/' do
+ it 'returns snippets available' do
+ public_snippet = create(:personal_snippet, :public, author: user)
+ private_snippet = create(:personal_snippet, :private, author: user)
+ internal_snippet = create(:personal_snippet, :internal, author: user)
+
+ get v3_api("/snippets/", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response.map { |snippet| snippet['id']} ).to contain_exactly(
+ public_snippet.id,
+ internal_snippet.id,
+ private_snippet.id)
+ expect(json_response.last).to have_key('web_url')
+ expect(json_response.last).to have_key('raw_url')
+ end
+
+ it 'hides private snippets from regular user' do
+ create(:personal_snippet, :private)
+
+ get v3_api("/snippets/", user)
+ expect(response).to have_http_status(200)
+ expect(json_response.size).to eq(0)
+ end
+ end
+
+ describe 'GET /snippets/public' do
+ let!(:other_user) { create(:user) }
+ let!(:public_snippet) { create(:personal_snippet, :public, author: user) }
+ let!(:private_snippet) { create(:personal_snippet, :private, author: user) }
+ let!(:internal_snippet) { create(:personal_snippet, :internal, author: user) }
+ let!(:public_snippet_other) { create(:personal_snippet, :public, author: other_user) }
+ let!(:private_snippet_other) { create(:personal_snippet, :private, author: other_user) }
+ let!(:internal_snippet_other) { create(:personal_snippet, :internal, author: other_user) }
+
+ it 'returns all snippets with public visibility from all users' do
+ get v3_api("/snippets/public", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response.map { |snippet| snippet['id']} ).to contain_exactly(
+ public_snippet.id,
+ public_snippet_other.id)
+ expect(json_response.map{ |snippet| snippet['web_url']} ).to include(
+ "http://localhost/snippets/#{public_snippet.id}",
+ "http://localhost/snippets/#{public_snippet_other.id}")
+ expect(json_response.map{ |snippet| snippet['raw_url']} ).to include(
+ "http://localhost/snippets/#{public_snippet.id}/raw",
+ "http://localhost/snippets/#{public_snippet_other.id}/raw")
+ end
+ end
+
+ describe 'GET /snippets/:id/raw' do
+ let(:snippet) { create(:personal_snippet, author: user) }
+
+ it 'returns raw text' do
+ get v3_api("/snippets/#{snippet.id}/raw", user)
+
+ expect(response).to have_http_status(200)
+ expect(response.content_type).to eq 'text/plain'
+ expect(response.body).to eq(snippet.content)
+ end
+
+ it 'returns 404 for invalid snippet id' do
+ delete v3_api("/snippets/1234", user)
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Snippet Not Found')
+ end
+ end
+
+ describe 'POST /snippets/' do
+ let(:params) do
+ {
+ title: 'Test Title',
+ file_name: 'test.rb',
+ content: 'puts "hello world"',
+ visibility_level: Snippet::PUBLIC
+ }
+ end
+
+ it 'creates a new snippet' do
+ expect do
+ post v3_api("/snippets/", user), params
+ end.to change { PersonalSnippet.count }.by(1)
+
+ expect(response).to have_http_status(201)
+ expect(json_response['title']).to eq(params[:title])
+ expect(json_response['file_name']).to eq(params[:file_name])
+ end
+
+ it 'returns 400 for missing parameters' do
+ params.delete(:title)
+
+ post v3_api("/snippets/", user), params
+
+ expect(response).to have_http_status(400)
+ end
+
+ context 'when the snippet is spam' do
+ def create_snippet(snippet_params = {})
+ post v3_api('/snippets', user), params.merge(snippet_params)
+ end
+
+ before do
+ allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true)
+ end
+
+ context 'when the snippet is private' do
+ it 'creates the snippet' do
+ expect { create_snippet(visibility_level: Snippet::PRIVATE) }.
+ to change { Snippet.count }.by(1)
+ end
+ end
+
+ context 'when the snippet is public' do
+ it 'rejects the shippet' do
+ expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
+ not_to change { Snippet.count }
+ expect(response).to have_http_status(400)
+ end
+
+ it 'creates a spam log' do
+ expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
+ to change { SpamLog.count }.by(1)
+ end
+ end
+ end
+ end
+
+ describe 'PUT /snippets/:id' do
+ let(:other_user) { create(:user) }
+ let(:public_snippet) { create(:personal_snippet, :public, author: user) }
+ it 'updates snippet' do
+ new_content = 'New content'
+
+ put v3_api("/snippets/#{public_snippet.id}", user), content: new_content
+
+ expect(response).to have_http_status(200)
+ public_snippet.reload
+ expect(public_snippet.content).to eq(new_content)
+ end
+
+ it 'returns 404 for invalid snippet id' do
+ put v3_api("/snippets/1234", user), title: 'foo'
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Snippet Not Found')
+ end
+
+ it "returns 404 for another user's snippet" do
+ put v3_api("/snippets/#{public_snippet.id}", other_user), title: 'fubar'
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Snippet Not Found')
+ end
+
+ it 'returns 400 for missing parameters' do
+ put v3_api("/snippets/1234", user)
+
+ expect(response).to have_http_status(400)
+ end
+ end
+
+ describe 'DELETE /snippets/:id' do
+ let!(:public_snippet) { create(:personal_snippet, :public, author: user) }
+ it 'deletes snippet' do
+ expect do
+ delete v3_api("/snippets/#{public_snippet.id}", user)
+
+ expect(response).to have_http_status(204)
+ end.to change { PersonalSnippet.count }.by(-1)
+ end
+
+ it 'returns 404 for invalid snippet id' do
+ delete v3_api("/snippets/1234", user)
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Snippet Not Found')
+ end
+ end
+end
diff --git a/spec/rubocop/cop/custom_error_class_spec.rb b/spec/rubocop/cop/custom_error_class_spec.rb
new file mode 100644
index 00000000000..381d7871a40
--- /dev/null
+++ b/spec/rubocop/cop/custom_error_class_spec.rb
@@ -0,0 +1,111 @@
+require 'spec_helper'
+
+require 'rubocop'
+require 'rubocop/rspec/support'
+
+require_relative '../../../rubocop/cop/custom_error_class'
+
+describe RuboCop::Cop::CustomErrorClass do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ context 'when a class has a body' do
+ it 'does nothing' do
+ inspect_source(cop, 'class CustomError < StandardError; def foo; end; end')
+
+ expect(cop.offenses).to be_empty
+ end
+ end
+
+ context 'when a class has no explicit superclass' do
+ it 'does nothing' do
+ inspect_source(cop, 'class CustomError; end')
+
+ expect(cop.offenses).to be_empty
+ end
+ end
+
+ context 'when a class has a superclass that does not end in Error' do
+ it 'does nothing' do
+ inspect_source(cop, 'class CustomError < BasicObject; end')
+
+ expect(cop.offenses).to be_empty
+ end
+ end
+
+ context 'when a class is empty and inherits from a class ending in Error' do
+ context 'when the class is on a single line' do
+ let(:source) do
+ <<-SOURCE
+ module Foo
+ class CustomError < Bar::Baz::BaseError; end
+ end
+ SOURCE
+ end
+
+ let(:expected) do
+ <<-EXPECTED
+ module Foo
+ CustomError = Class.new(Bar::Baz::BaseError)
+ end
+ EXPECTED
+ end
+
+ it 'registers an offense' do
+ expected_highlights = source.split("\n")[1].strip
+
+ inspect_source(cop, source)
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(1)
+ expect(cop.offenses.map(&:line)).to eq([2])
+ expect(cop.highlights).to contain_exactly(expected_highlights)
+ end
+ end
+
+ it 'autocorrects to the right version' do
+ autocorrected = autocorrect_source(cop, source, 'foo/custom_error.rb')
+
+ expect(autocorrected).to eq(expected)
+ end
+ end
+
+ context 'when the class is on multiple lines' do
+ let(:source) do
+ <<-SOURCE
+ module Foo
+ class CustomError < Bar::Baz::BaseError
+ end
+ end
+ SOURCE
+ end
+
+ let(:expected) do
+ <<-EXPECTED
+ module Foo
+ CustomError = Class.new(Bar::Baz::BaseError)
+ end
+ EXPECTED
+ end
+
+ it 'registers an offense' do
+ expected_highlights = source.split("\n")[1..2].join("\n").strip
+
+ inspect_source(cop, source)
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(1)
+ expect(cop.offenses.map(&:line)).to eq([2])
+ expect(cop.highlights).to contain_exactly(expected_highlights)
+ end
+ end
+
+ it 'autocorrects to the right version' do
+ autocorrected = autocorrect_source(cop, source, 'foo/custom_error.rb')
+
+ expect(autocorrected).to eq(expected)
+ end
+ end
+ end
+end
diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb
deleted file mode 100644
index b3e0a7b9b58..00000000000
--- a/spec/services/ci/image_for_build_service_spec.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-require 'spec_helper'
-
-module Ci
- describe ImageForBuildService, services: true do
- let(:service) { ImageForBuildService.new }
- let(:project) { FactoryGirl.create(:empty_project) }
- let(:commit_sha) { '01234567890123456789' }
- let(:pipeline) { project.ensure_pipeline('master', commit_sha) }
- let(:build) { FactoryGirl.create(:ci_build, pipeline: pipeline) }
-
- describe '#execute' do
- before { build }
-
- context 'branch name' do
- before { allow(project).to receive(:commit).and_return(OpenStruct.new(sha: commit_sha)) }
- before { build.run! }
- let(:image) { service.execute(project, ref: 'master') }
-
- it { expect(image).to be_kind_of(OpenStruct) }
- it { expect(image.path.to_s).to include('public/ci/build-running.svg') }
- it { expect(image.name).to eq('build-running.svg') }
- end
-
- context 'unknown branch name' do
- let(:image) { service.execute(project, ref: 'feature') }
-
- it { expect(image).to be_kind_of(OpenStruct) }
- it { expect(image.path.to_s).to include('public/ci/build-unknown.svg') }
- it { expect(image.name).to eq('build-unknown.svg') }
- end
-
- context 'commit sha' do
- before { build.run! }
- let(:image) { service.execute(project, sha: build.sha) }
-
- it { expect(image).to be_kind_of(OpenStruct) }
- it { expect(image.path.to_s).to include('public/ci/build-running.svg') }
- it { expect(image.name).to eq('build-running.svg') }
- end
-
- context 'unknown commit sha' do
- let(:image) { service.execute(project, sha: '0000000') }
-
- it { expect(image).to be_kind_of(OpenStruct) }
- it { expect(image.path.to_s).to include('public/ci/build-unknown.svg') }
- it { expect(image.name).to eq('build-unknown.svg') }
- 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 b818dfdd50c..de68fb64726 100644
--- a/spec/services/ci/process_pipeline_service_spec.rb
+++ b/spec/services/ci/process_pipeline_service_spec.rb
@@ -341,7 +341,7 @@ describe Ci::ProcessPipelineService, :services do
expect(builds.pending.count).to eq(1)
expect(all_builds.count).to eq(4)
- # When pending build succeeds in stage test, we enqueue deploy stage.
+ # When pending merge_when_pipeline_succeeds in stage test, we enqueue deploy stage.
#
succeed_pending
process_pipeline
diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb
index d9f774a1095..cd7dd53025c 100644
--- a/spec/services/ci/register_build_service_spec.rb
+++ b/spec/services/ci/register_build_service_spec.rb
@@ -170,6 +170,51 @@ module Ci
end
end
+ context 'when first build is stalled' do
+ before do
+ pending_build.lock_version = 10
+ end
+
+ subject { described_class.new(specific_runner).execute }
+
+ context 'with multiple builds are in queue' do
+ let!(:other_build) { create :ci_build, pipeline: pipeline }
+
+ before do
+ allow_any_instance_of(Ci::RegisterBuildService).to receive(:builds_for_specific_runner)
+ .and_return([pending_build, other_build])
+ end
+
+ it "receives second build from the queue" do
+ expect(subject).to be_valid
+ expect(subject.build).to eq(other_build)
+ end
+ end
+
+ context 'when single build is in queue' do
+ before do
+ allow_any_instance_of(Ci::RegisterBuildService).to receive(:builds_for_specific_runner)
+ .and_return([pending_build])
+ end
+
+ it "does not receive any valid result" do
+ expect(subject).not_to be_valid
+ end
+ end
+
+ context 'when there is no build in queue' do
+ before do
+ allow_any_instance_of(Ci::RegisterBuildService).to receive(:builds_for_specific_runner)
+ .and_return([])
+ end
+
+ it "does not receive builds but result is valid" do
+ expect(subject).to be_valid
+ expect(subject.build).to be_nil
+ end
+ end
+ end
+
def execute(runner)
described_class.new(runner).execute.build
end
diff --git a/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
index 0ff6e8fda16..c2f205c389d 100644
--- a/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
+++ b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
@@ -5,7 +5,7 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
let(:project) { create(:project) }
let(:mr_merge_if_green_enabled) do
- create(:merge_request, merge_when_build_succeeds: true, merge_user: user,
+ create(:merge_request, merge_when_pipeline_succeeds: true, merge_user: user,
source_branch: "master", target_branch: 'feature',
source_project: project, target_project: project, state: "opened")
end
@@ -36,7 +36,7 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
it 'sets the params, merge_user, and flag' do
expect(merge_request).to be_valid
- expect(merge_request.merge_when_build_succeeds).to be_truthy
+ expect(merge_request.merge_when_pipeline_succeeds).to be_truthy
expect(merge_request.merge_params).to eq commit_message: 'Awesome message'
expect(merge_request.merge_user).to be user
end
@@ -62,7 +62,7 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
end
it 'updates the merge params' do
- expect(SystemNoteService).not_to receive(:merge_when_build_succeeds)
+ expect(SystemNoteService).not_to receive(:merge_when_pipeline_succeeds)
service.execute(mr_merge_if_green_enabled)
expect(mr_merge_if_green_enabled.merge_params).to have_key(:new_key)
@@ -82,7 +82,7 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
sha: merge_request_head, status: 'success')
end
- it "merges all merge requests with merge when build succeeds enabled" do
+ it "merges all merge requests with merge when the pipeline succeeds enabled" do
expect(MergeWorker).to receive(:perform_async)
service.trigger(triggering_pipeline)
end
@@ -114,7 +114,7 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
context 'when the merge request is not mergeable' do
let(:mr_conflict) do
- create(:merge_request, merge_when_build_succeeds: true, merge_user: user,
+ create(:merge_request, merge_when_pipeline_succeeds: true, merge_user: user,
source_branch: 'master', target_branch: 'feature-conflict',
source_project: project, target_project: project)
end
@@ -143,8 +143,8 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
service.cancel(mr_merge_if_green_enabled)
end
- it "resets all the merge_when_build_succeeds params" do
- expect(mr_merge_if_green_enabled.merge_when_build_succeeds).to be_falsey
+ it "resets all the pipeline succeeds params" do
+ expect(mr_merge_if_green_enabled.merge_when_pipeline_succeeds).to be_falsey
expect(mr_merge_if_green_enabled.merge_params).to eq({})
expect(mr_merge_if_green_enabled.merge_user).to be nil
end
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 983dac6efdb..ff367f54d2a 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -18,7 +18,7 @@ describe MergeRequests::RefreshService, services: true do
source_branch: 'master',
target_branch: 'feature',
target_project: @project,
- merge_when_build_succeeds: true,
+ merge_when_pipeline_succeeds: true,
merge_user: @user)
@fork_merge_request = create(:merge_request,
@@ -62,7 +62,7 @@ describe MergeRequests::RefreshService, services: true do
it { expect(@merge_request.notes).not_to be_empty }
it { expect(@merge_request).to be_open }
- it { expect(@merge_request.merge_when_build_succeeds).to be_falsey }
+ it { expect(@merge_request.merge_when_pipeline_succeeds).to be_falsey }
it { expect(@merge_request.diff_head_sha).to eq(@newrev) }
it { expect(@fork_merge_request).to be_open }
it { expect(@fork_merge_request.notes).to be_empty }
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index 9c92a5080c6..152c6d20daa 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -102,47 +102,19 @@ describe Notes::CreateService, services: true do
expect(subject.note).to eq(params[:note])
end
end
- end
-
- describe "award emoji" do
- before do
- project.team << [user, :master]
- end
-
- it "creates an award emoji" do
- opts = {
- note: ':smile: ',
- noteable_type: 'Issue',
- noteable_id: issue.id
- }
- note = described_class.new(project, user, opts).execute
-
- expect(note).to be_valid
- expect(note.name).to eq('smile')
- end
- it "creates regular note if emoji name is invalid" do
- opts = {
- note: ':smile: moretext:',
- noteable_type: 'Issue',
- noteable_id: issue.id
- }
- note = described_class.new(project, user, opts).execute
-
- expect(note).to be_valid
- expect(note.note).to eq(opts[:note])
- end
-
- it "normalizes the emoji name" do
- opts = {
- note: ':+1:',
- noteable_type: 'Issue',
- noteable_id: issue.id
- }
-
- expect_any_instance_of(TodoService).to receive(:new_award_emoji).with(issue, user)
+ describe 'note with emoji only' do
+ it 'creates regular note' do
+ opts = {
+ note: ':smile: ',
+ noteable_type: 'Issue',
+ noteable_id: issue.id
+ }
+ note = described_class.new(project, user, opts).execute
- described_class.new(project, user, opts).execute
+ expect(note).to be_valid
+ expect(note.note).to eq(':smile:')
+ end
end
end
end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 839250b7d84..ebbaea4e59a 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -1050,22 +1050,22 @@ describe NotificationService, services: true do
should_not_email(@u_lazy_participant)
end
- it "notifies the merger when merge_when_build_succeeds is true" do
- merge_request.merge_when_build_succeeds = true
+ it "notifies the merger when the pipeline succeeds is true" do
+ merge_request.merge_when_pipeline_succeeds = true
notification.merge_mr(merge_request, @u_watcher)
should_email(@u_watcher)
end
- it "does not notify the merger when merge_when_build_succeeds is false" do
- merge_request.merge_when_build_succeeds = false
+ it "does not notify the merger when the pipeline succeeds is false" do
+ merge_request.merge_when_pipeline_succeeds = false
notification.merge_mr(merge_request, @u_watcher)
should_not_email(@u_watcher)
end
- it "notifies the merger when merge_when_build_succeeds is false but they've opted into notifications about their activity" do
- merge_request.merge_when_build_succeeds = false
+ it "notifies the merger when the pipeline succeeds is false but they've opted into notifications about their activity" do
+ merge_request.merge_when_pipeline_succeeds = false
@u_watcher.notified_of_own_activity = true
notification.merge_mr(merge_request, @u_watcher)
diff --git a/spec/services/slash_commands/interpret_service_spec.rb b/spec/services/slash_commands/interpret_service_spec.rb
index 0b0925983eb..52e8678cb9d 100644
--- a/spec/services/slash_commands/interpret_service_spec.rb
+++ b/spec/services/slash_commands/interpret_service_spec.rb
@@ -267,6 +267,14 @@ describe SlashCommands::InterpretService, services: true do
end
end
+ shared_examples 'award command' do
+ it 'toggle award 100 emoji if content containts /award :100:' do
+ _, updates = service.execute(content, issuable)
+
+ expect(updates).to eq(emoji_award: "100")
+ end
+ end
+
it_behaves_like 'reopen command' do
let(:content) { '/reopen' }
let(:issuable) { issue }
@@ -654,6 +662,37 @@ describe SlashCommands::InterpretService, services: true do
end
end
+ context '/award command' do
+ it_behaves_like 'award command' do
+ let(:content) { '/award :100:' }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'award command' do
+ let(:content) { '/award :100:' }
+ let(:issuable) { merge_request }
+ end
+
+ context 'ignores command with no argument' do
+ it_behaves_like 'empty command' do
+ let(:content) { '/award' }
+ let(:issuable) { issue }
+ end
+ end
+
+ context 'ignores non-existing / invalid emojis' do
+ it_behaves_like 'empty command' do
+ let(:content) { '/award noop' }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'empty command' do
+ let(:content) { '/award :lorem_ipsum:' }
+ let(:issuable) { issue }
+ end
+ end
+ end
+
context '/target_branch command' do
let(:non_empty_project) { create(:project) }
let(:another_merge_request) { create(:merge_request, author: developer, source_project: non_empty_project) }
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index eca5a418f2a..1f2ec9eacf0 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -215,13 +215,13 @@ describe SystemNoteService, services: true do
end
end
- describe '.merge_when_build_succeeds' do
+ describe '.merge_when_pipeline_succeeds' do
let(:pipeline) { build(:ci_pipeline_without_jobs )}
let(:noteable) do
create(:merge_request, source_project: project, target_project: project)
end
- subject { described_class.merge_when_build_succeeds(noteable, project, author, noteable.diff_head_commit) }
+ subject { described_class.merge_when_pipeline_succeeds(noteable, project, author, noteable.diff_head_commit) }
it_behaves_like 'a system note'
@@ -230,12 +230,12 @@ describe SystemNoteService, services: true do
end
end
- describe '.cancel_merge_when_build_succeeds' do
+ describe '.cancel_merge_when_pipeline_succeeds' do
let(:noteable) do
create(:merge_request, source_project: project, target_project: project)
end
- subject { described_class.cancel_merge_when_build_succeeds(noteable, project, author) }
+ subject { described_class.cancel_merge_when_pipeline_succeeds(noteable, project, author) }
it_behaves_like 'a system note'
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index 9f24cc0f3f2..fb9a8462f84 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -680,7 +680,7 @@ describe TodoService, services: true do
end
it 'creates a pending todo for merge_user' do
- mr_unassigned.update(merge_when_build_succeeds: true, merge_user: admin)
+ mr_unassigned.update(merge_when_pipeline_succeeds: true, merge_user: admin)
service.merge_request_build_failed(mr_unassigned)
should_create_todo(user: admin, author: admin, target: mr_unassigned, action: Todo::BUILD_FAILED)
@@ -700,7 +700,7 @@ describe TodoService, services: true do
describe '#merge_request_became_unmergeable' do
it 'creates a pending todo for a merge_user' do
- mr_unassigned.update(merge_when_build_succeeds: true, merge_user: admin)
+ mr_unassigned.update(merge_when_pipeline_succeeds: true, merge_user: admin)
service.merge_request_became_unmergeable(mr_unassigned)
should_create_todo(user: admin, author: admin, target: mr_unassigned, action: Todo::UNMERGEABLE)
diff --git a/spec/support/features/rss_shared_examples.rb b/spec/support/features/rss_shared_examples.rb
new file mode 100644
index 00000000000..9a3b0a731ad
--- /dev/null
+++ b/spec/support/features/rss_shared_examples.rb
@@ -0,0 +1,23 @@
+shared_examples "an autodiscoverable RSS feed with current_user's private token" do
+ it "has an RSS autodiscovery link tag with current_user's private token" do
+ expect(page).to have_css("link[type*='atom+xml'][href*='private_token=#{Thread.current[:current_user].private_token}']", visible: false)
+ end
+end
+
+shared_examples "it has an RSS button with current_user's private token" do
+ it "shows the RSS button with current_user's private token" do
+ expect(page).to have_css("a:has(.fa-rss)[href*='private_token=#{Thread.current[:current_user].private_token}']")
+ end
+end
+
+shared_examples "an autodiscoverable RSS feed without a private token" do
+ it "has an RSS autodiscovery link tag without a private token" do
+ expect(page).to have_css("link[type*='atom+xml']:not([href*='private_token'])", visible: false)
+ end
+end
+
+shared_examples "it has an RSS button without a private token" do
+ it "shows the RSS button without a private token" do
+ expect(page).to have_css("a:has(.fa-rss):not([href*='private_token'])")
+ end
+end
diff --git a/spec/support/matchers/gitaly_matchers.rb b/spec/support/matchers/gitaly_matchers.rb
new file mode 100644
index 00000000000..d7a53820684
--- /dev/null
+++ b/spec/support/matchers/gitaly_matchers.rb
@@ -0,0 +1,3 @@
+RSpec::Matchers.define :post_receive_request_with_repo_path do |path|
+ match { |actual| actual.repository.path == path }
+end
diff --git a/spec/support/project_features_apply_to_issuables_shared_examples.rb b/spec/support/project_features_apply_to_issuables_shared_examples.rb
index 4621d17549b..f8b7d0527ba 100644
--- a/spec/support/project_features_apply_to_issuables_shared_examples.rb
+++ b/spec/support/project_features_apply_to_issuables_shared_examples.rb
@@ -18,7 +18,7 @@ shared_examples 'project features apply to issuables' do |klass|
before do
_ = issuable
- login_as(user)
+ login_as(user) if user
visit path
end
diff --git a/spec/views/ci/status/_badge.html.haml_spec.rb b/spec/views/ci/status/_badge.html.haml_spec.rb
new file mode 100644
index 00000000000..c62450fb8e2
--- /dev/null
+++ b/spec/views/ci/status/_badge.html.haml_spec.rb
@@ -0,0 +1,89 @@
+require 'spec_helper'
+
+describe 'ci/status/_badge', :view do
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project, :private) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+
+ context 'when rendering status for build' do
+ let(:build) do
+ create(:ci_build, :success, pipeline: pipeline)
+ end
+
+ context 'when user has ability to see details' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'has link to build details page' do
+ details_path = namespace_project_build_path(
+ project.namespace, project, build)
+
+ render_status(build)
+
+ expect(rendered).to have_link 'passed', href: details_path
+ end
+ end
+
+ context 'when user do not have ability to see build details' do
+ before do
+ render_status(build)
+ end
+
+ it 'contains build status text' do
+ expect(rendered).to have_content 'passed'
+ end
+
+ it 'does not contain links' do
+ expect(rendered).not_to have_link 'passed'
+ end
+ end
+ end
+
+ context 'when rendering status for external job' do
+ context 'when user has ability to see commit status details' do
+ before do
+ project.add_developer(user)
+ end
+
+ context 'status has external target url' do
+ before do
+ external_job = create(:generic_commit_status,
+ status: :running,
+ pipeline: pipeline,
+ target_url: 'http://gitlab.com')
+
+ render_status(external_job)
+ end
+
+ it 'contains valid commit status text' do
+ expect(rendered).to have_content 'running'
+ end
+
+ it 'has link to external status page' do
+ expect(rendered).to have_link 'running', href: 'http://gitlab.com'
+ end
+ end
+
+ context 'status do not have external target url' do
+ before do
+ external_job = create(:generic_commit_status, status: :canceled)
+
+ render_status(external_job)
+ end
+
+ it 'contains valid commit status text' do
+ expect(rendered).to have_content 'canceled'
+ end
+
+ it 'has link to external status page' do
+ expect(rendered).not_to have_link 'canceled'
+ end
+ end
+ end
+ end
+
+ def render_status(resource)
+ render 'ci/status/badge', status: resource.detailed_status(user)
+ end
+end
diff --git a/vendor/assets/javascripts/jquery.highlight.js b/vendor/assets/javascripts/jquery.highlight.js
deleted file mode 100644
index 7a67cf99844..00000000000
--- a/vendor/assets/javascripts/jquery.highlight.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
-
-highlight v3
-
-Highlights arbitrary terms.
-
-<http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html>
-
-MIT license.
-
-Johann Burkard
-<http://johannburkard.de>
-<mailto:jb@eaio.com>
-
-*/
-
-jQuery.fn.highlight = function(pat) {
- function innerHighlight(node, pat) {
- var skip = 0;
- if (node.nodeType == 3) {
- var pos = node.data.toUpperCase().indexOf(pat);
- if (pos >= 0) {
- var spannode = document.createElement('span');
- spannode.className = 'highlight_word';
- var middlebit = node.splitText(pos);
- var endbit = middlebit.splitText(pat.length);
- var middleclone = middlebit.cloneNode(true);
- spannode.appendChild(middleclone);
- middlebit.parentNode.replaceChild(spannode, middlebit);
- skip = 1;
- }
- }
- else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) {
- for (var i = 0; i < node.childNodes.length; ++i) {
- i += innerHighlight(node.childNodes[i], pat);
- }
- }
- return skip;
- }
- return this.each(function() {
- innerHighlight(this, pat.toUpperCase());
- });
-};
-
-jQuery.fn.removeHighlight = function() {
- return this.find("span.highlight").each(function() {
- this.parentNode.firstChild.nodeName;
- with (this.parentNode) {
- replaceChild(this.firstChild, this);
- normalize();
- }
- }).end();
-};
diff --git a/yarn.lock b/yarn.lock
index e4e5c40089b..fd8d67a6b0d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2639,10 +2639,6 @@ jodid25519@^1.0.0:
dependencies:
jsbn "~0.1.0"
-"jquery-ui@git+https://github.com/jquery/jquery-ui#1.11.4":
- version "1.11.4"
- resolved "git+https://github.com/jquery/jquery-ui#d6713024e16de90ea71dc0544ba34e1df01b4d8a"
-
jquery-ujs@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/jquery-ujs/-/jquery-ujs-1.2.1.tgz#6ee75b1ef4e9ac95e7124f8d71f7d351f5548e92"