summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml35
-rw-r--r--CHANGELOG20
-rw-r--r--Gemfile10
-rw-r--r--Gemfile.lock57
-rw-r--r--README.md2
-rw-r--r--app/assets/javascripts/api.js.coffee31
-rw-r--r--app/assets/javascripts/dispatcher.js.coffee2
-rw-r--r--app/assets/javascripts/merge_request_tabs.js.coffee22
-rw-r--r--app/assets/javascripts/new_commit_form.js.coffee6
-rw-r--r--app/assets/javascripts/notes.js.coffee4
-rw-r--r--app/assets/javascripts/project_select.js.coffee39
-rw-r--r--app/assets/stylesheets/framework/callout.scss5
-rw-r--r--app/assets/stylesheets/framework/common.scss30
-rw-r--r--app/assets/stylesheets/framework/files.scss1
-rw-r--r--app/assets/stylesheets/framework/lists.scss35
-rw-r--r--app/assets/stylesheets/framework/mobile.scss3
-rw-r--r--app/assets/stylesheets/framework/panels.scss6
-rw-r--r--app/assets/stylesheets/framework/typography.scss1
-rw-r--r--app/assets/stylesheets/pages/builds.scss5
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss10
-rw-r--r--app/assets/stylesheets/pages/notes.scss10
-rw-r--r--app/assets/stylesheets/pages/profile.scss6
-rw-r--r--app/assets/stylesheets/pages/ui_dev_kit.scss5
-rw-r--r--app/controllers/projects/application_controller.rb4
-rw-r--r--app/controllers/projects/blob_controller.rb12
-rw-r--r--app/controllers/projects/commit_controller.rb8
-rw-r--r--app/controllers/projects/graphs_controller.rb22
-rw-r--r--app/controllers/projects/hooks_controller.rb5
-rw-r--r--app/controllers/projects/merge_requests_controller.rb76
-rw-r--r--app/controllers/projects/project_members_controller.rb2
-rw-r--r--app/controllers/projects/raw_controller.rb41
-rw-r--r--app/helpers/application_helper.rb2
-rw-r--r--app/helpers/blob_helper.rb50
-rw-r--r--app/helpers/branches_helper.rb2
-rw-r--r--app/helpers/ci_status_helper.rb16
-rw-r--r--app/helpers/page_layout_helper.rb3
-rw-r--r--app/helpers/projects_helper.rb12
-rw-r--r--app/helpers/selects_helper.rb13
-rw-r--r--app/helpers/tree_helper.rb18
-rw-r--r--app/helpers/visibility_level_helper.rb26
-rw-r--r--app/models/ability.rb12
-rw-r--r--app/models/application_setting.rb4
-rw-r--r--app/models/broadcast_message.rb8
-rw-r--r--app/models/ci/commit.rb10
-rw-r--r--app/models/ci/web_hook.rb3
-rw-r--r--app/models/commit_status.rb58
-rw-r--r--app/models/hooks/web_hook.rb39
-rw-r--r--app/models/label.rb4
-rw-r--r--app/models/lfs_object.rb24
-rw-r--r--app/models/lfs_objects_project.rb11
-rw-r--r--app/models/merge_request.rb73
-rw-r--r--app/models/namespace.rb14
-rw-r--r--app/models/note.rb3
-rw-r--r--app/models/project.rb3
-rw-r--r--app/models/project_services/bamboo_service.rb7
-rw-r--r--app/models/project_services/drone_ci_service.rb29
-rw-r--r--app/models/project_services/external_wiki_service.rb6
-rw-r--r--app/models/project_services/teamcity_service.rb10
-rw-r--r--app/models/repository.rb15
-rw-r--r--app/models/sent_notification.rb2
-rw-r--r--app/models/user.rb7
-rw-r--r--app/services/files/base_service.rb2
-rw-r--r--app/services/merge_requests/merge_service.rb16
-rw-r--r--app/services/merge_requests/merge_when_build_succeeds_service.rb55
-rw-r--r--app/services/merge_requests/refresh_service.rb6
-rw-r--r--app/services/system_note_service.rb14
-rw-r--r--app/validators/color_validator.rb20
-rw-r--r--app/validators/email_validator.rb (renamed from lib/email_validator.rb)11
-rw-r--r--app/validators/line_code_validator.rb12
-rw-r--r--app/validators/namespace_name_validator.rb10
-rw-r--r--app/validators/namespace_validator.rb50
-rw-r--r--app/validators/url_validator.rb36
-rw-r--r--app/views/admin/application_settings/_form.html.haml4
-rw-r--r--app/views/dashboard/_projects_head.html.haml2
-rw-r--r--app/views/dashboard/issues.html.haml20
-rw-r--r--app/views/dashboard/merge_requests.html.haml12
-rw-r--r--app/views/dashboard/milestones/index.html.haml10
-rw-r--r--app/views/groups/issues.html.haml27
-rw-r--r--app/views/groups/merge_requests.html.haml19
-rw-r--r--app/views/groups/milestones/index.html.haml24
-rw-r--r--app/views/help/ui.html.haml48
-rw-r--r--app/views/profiles/accounts/show.html.haml10
-rw-r--r--app/views/profiles/keys/new.html.haml2
-rw-r--r--app/views/projects/_last_commit.html.haml2
-rw-r--r--app/views/projects/blob/_actions.html.haml16
-rw-r--r--app/views/projects/blob/_blob.html.haml6
-rw-r--r--app/views/projects/blob/_download.html.haml2
-rw-r--r--app/views/projects/blob/show.html.haml2
-rw-r--r--app/views/projects/branches/_branch.html.haml22
-rw-r--r--app/views/projects/builds/index.html.haml5
-rw-r--r--app/views/projects/builds/show.html.haml16
-rw-r--r--app/views/projects/commit/_builds.html.haml67
-rw-r--r--app/views/projects/commit/_ci_menu.html.haml2
-rw-r--r--app/views/projects/commit/_commit_box.html.haml5
-rw-r--r--app/views/projects/commit/builds.html.haml68
-rw-r--r--app/views/projects/commit_statuses/_commit_status.html.haml11
-rw-r--r--app/views/projects/diffs/_file.html.haml7
-rw-r--r--app/views/projects/graphs/_head.html.haml2
-rw-r--r--app/views/projects/graphs/languages.html.haml32
-rw-r--r--app/views/projects/issues/_discussion.html.haml7
-rw-r--r--app/views/projects/issues/_issue.html.haml25
-rw-r--r--app/views/projects/merge_requests/_discussion.html.haml6
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml42
-rw-r--r--app/views/projects/merge_requests/_new_submit.html.haml12
-rw-r--r--app/views/projects/merge_requests/_show.html.haml16
-rw-r--r--app/views/projects/merge_requests/cancel_merge_when_build_succeeds.js.haml2
-rw-r--r--app/views/projects/merge_requests/merge.js.haml6
-rw-r--r--app/views/projects/merge_requests/show/_builds.html.haml1
-rw-r--r--app/views/projects/merge_requests/widget/_heading.html.haml39
-rw-r--r--app/views/projects/merge_requests/widget/_merged.html.haml4
-rw-r--r--app/views/projects/merge_requests/widget/_open.html.haml2
-rw-r--r--app/views/projects/merge_requests/widget/open/_accept.html.haml70
-rw-r--r--app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml26
-rw-r--r--app/views/projects/milestones/index.html.haml15
-rw-r--r--app/views/projects/network/_head.html.haml9
-rw-r--r--app/views/projects/project_members/_group_members.html.haml11
-rw-r--r--app/views/projects/protected_branches/index.html.haml2
-rw-r--r--app/views/projects/tags/_tag.html.haml12
-rw-r--r--app/views/projects/tags/show.html.haml8
-rw-r--r--app/views/projects/tree/_tree_header.html.haml6
-rw-r--r--app/views/shared/_new_commit_form.html.haml15
-rw-r--r--app/views/shared/_new_project_item_select.html.haml20
-rw-r--r--app/views/shared/issuable/_participants.html.haml (renamed from app/views/projects/merge_requests/show/_participants.html.haml)3
-rw-r--r--app/views/shared/snippets/_header.html.haml2
-rw-r--r--app/views/users/show.html.haml2
-rw-r--r--app/workers/merge_worker.rb13
-rw-r--r--app/workers/stuck_ci_builds_worker.rb3
-rwxr-xr-xbin/parallel-rsync-repos54
-rw-r--r--config/gitlab.yml.example2
-rw-r--r--config/initializers/omniauth.rb2
-rw-r--r--config/initializers/sidekiq.rb6
-rw-r--r--config/routes.rb8
-rw-r--r--config/schedule.yml10
-rw-r--r--db/migrate/20151028152939_add_merge_when_build_succeeds_to_merge_request.rb7
-rw-r--r--db/schema.rb13
-rw-r--r--doc/README.md12
-rw-r--r--doc/api/groups.md70
-rw-r--r--doc/api/merge_requests.md54
-rw-r--r--doc/api/projects.md1
-rw-r--r--doc/ci/README.md12
-rw-r--r--doc/ci/docker/using_docker_images.md285
-rw-r--r--doc/ci/languages/README.md7
-rw-r--r--doc/ci/languages/php.md284
-rw-r--r--doc/ci/services/README.md9
-rw-r--r--doc/ci/services/docker-services.md5
-rw-r--r--doc/ci/services/mysql.md118
-rw-r--r--doc/ci/services/postgres.md114
-rw-r--r--doc/ci/services/redis.md69
-rw-r--r--doc/ci/ssh_keys/README.md109
-rw-r--r--doc/customization/issue_closing.md27
-rw-r--r--doc/integration/bitbucket.md2
-rw-r--r--doc/integration/crowd.md2
-rw-r--r--doc/integration/github.md2
-rw-r--r--doc/integration/gitlab.md2
-rw-r--r--doc/integration/google.md2
-rw-r--r--doc/integration/ldap.md8
-rw-r--r--doc/integration/saml.md2
-rw-r--r--doc/integration/twitter.md2
-rw-r--r--doc/operations/moving_repositories.md180
-rw-r--r--doc/raketasks/list_repos.md30
-rw-r--r--doc/release/README.md6
-rw-r--r--doc/update/8.1-to-8.2.md3
-rw-r--r--doc/update/patch_versions.md2
-rw-r--r--doc/workflow/README.md1
-rw-r--r--doc/workflow/merge_when_build_succeeds.md15
-rw-r--r--doc/workflow/merge_when_build_succeeds/enable.pngbin0 -> 151112 bytes
-rw-r--r--doc/workflow/merge_when_build_succeeds/status.pngbin0 -> 180318 bytes
-rw-r--r--features/admin/groups.feature16
-rw-r--r--features/admin/projects.feature16
-rw-r--r--features/explore/projects.feature21
-rw-r--r--features/project/commits/branches.feature1
-rw-r--r--features/project/commits/comments.feature1
-rw-r--r--features/project/commits/commits.feature1
-rw-r--r--features/project/commits/diff_comments.feature1
-rw-r--r--features/project/commits/tags.feature1
-rw-r--r--features/project/commits/user_lookup.feature1
-rw-r--r--features/project/create.feature2
-rw-r--r--features/project/graph.feature5
-rw-r--r--features/project/issues/award_emoji.feature1
-rw-r--r--features/project/issues/filter_labels.feature1
-rw-r--r--features/project/issues/issues.feature2
-rw-r--r--features/project/issues/labels.feature1
-rw-r--r--features/project/issues/milestones.feature1
-rw-r--r--features/project/merge_requests.feature1
-rw-r--r--features/project/merge_requests/accept.feature7
-rw-r--r--features/project/source/browse_files.feature12
-rw-r--r--features/steps/admin/groups.rb29
-rw-r--r--features/steps/admin/labels.rb2
-rw-r--r--features/steps/admin/projects.rb37
-rw-r--r--features/steps/dashboard/dashboard.rb2
-rw-r--r--features/steps/explore/projects.rb1
-rw-r--r--features/steps/project/commits/commits.rb2
-rw-r--r--features/steps/project/create.rb1
-rw-r--r--features/steps/project/graph.rb9
-rw-r--r--features/steps/project/hooks.rb4
-rw-r--r--features/steps/project/issues/labels.rb2
-rw-r--r--features/steps/project/merge_requests.rb2
-rw-r--r--features/steps/project/merge_requests/acceptance.rb4
-rw-r--r--features/steps/project/source/browse_files.rb31
-rw-r--r--features/steps/shared/group.rb4
-rw-r--r--features/steps/shared/user.rb8
-rw-r--r--lib/api/entities.rb1
-rw-r--r--lib/api/groups.rb12
-rw-r--r--lib/api/merge_requests.rb64
-rw-r--r--lib/api/projects.rb8
-rw-r--r--lib/gitlab/blacklist.rb34
-rw-r--r--lib/gitlab/ldap/access.rb4
-rw-r--r--lib/gitlab/lfs/response.rb2
-rw-r--r--lib/gitlab/markdown/commit_reference_filter.rb11
-rw-r--r--lib/gitlab/markdown/merge_request_reference_filter.rb8
-rw-r--r--lib/omni_auth/request_forgery_protection.rb63
-rw-r--r--lib/tasks/gitlab/git.rake55
-rw-r--r--lib/tasks/gitlab/import.rake2
-rw-r--r--lib/tasks/gitlab/list_repos.rake17
-rw-r--r--lib/tasks/gitlab/task_helpers.rake8
-rw-r--r--lib/tasks/spec.rake29
-rw-r--r--lib/tasks/spinach.rake26
-rw-r--r--shared/lfs-objects/.gitkeep0
-rw-r--r--spec/controllers/commit_controller_spec.rb20
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb24
-rw-r--r--spec/controllers/projects/raw_controller_spec.rb34
-rw-r--r--spec/factories/ci/commits.rb23
-rw-r--r--spec/factories/commit_statuses.rb2
-rw-r--r--spec/factories/lfs_objects.rb12
-rw-r--r--spec/factories/lfs_objects_projects.rb11
-rw-r--r--spec/factories/merge_requests.rb5
-rw-r--r--spec/factories/notes.rb1
-rw-r--r--spec/factories/projects.rb1
-rw-r--r--spec/features/atom/users_spec.rb2
-rw-r--r--spec/features/builds_spec.rb10
-rw-r--r--spec/features/issues_spec.rb4
-rw-r--r--spec/features/merge_requests/merge_when_build_succeeds_spec.rb85
-rw-r--r--spec/features/notes_on_merge_requests_spec.rb5
-rw-r--r--spec/helpers/application_helper_spec.rb2
-rw-r--r--spec/helpers/visibility_level_helper_spec.rb85
-rw-r--r--spec/javascripts/fixtures/merge_request_tabs.html.haml6
-rw-r--r--spec/lib/ci/ansi2html_spec.rb2
-rw-r--r--spec/lib/ci/charts_spec.rb2
-rw-r--r--spec/lib/ci/gitlab_ci_yaml_processor_spec.rb2
-rw-r--r--spec/lib/disable_email_interceptor_spec.rb2
-rw-r--r--spec/lib/extracts_path_spec.rb2
-rw-r--r--spec/lib/file_size_validator_spec.rb2
-rw-r--r--spec/lib/git_ref_validator_spec.rb2
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb2
-rw-r--r--spec/lib/gitlab/auth_spec.rb2
-rw-r--r--spec/lib/gitlab/backend/grack_auth_spec.rb2
-rw-r--r--spec/lib/gitlab/backend/shell_spec.rb4
-rw-r--r--spec/lib/gitlab/bitbucket_import/client_spec.rb2
-rw-r--r--spec/lib/gitlab/bitbucket_import/project_creator_spec.rb2
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb2
-rw-r--r--spec/lib/gitlab/color_schemes_spec.rb2
-rw-r--r--spec/lib/gitlab/database_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/file_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/parser_spec.rb2
-rw-r--r--spec/lib/gitlab/email/attachment_uploader_spec.rb2
-rw-r--r--spec/lib/gitlab/email/receiver_spec.rb2
-rw-r--r--spec/lib/gitlab/email/reply_parser_spec.rb2
-rw-r--r--spec/lib/gitlab/git_access_spec.rb2
-rw-r--r--spec/lib/gitlab/git_access_wiki_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/client_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/project_creator_spec.rb2
-rw-r--r--spec/lib/gitlab/gitlab_import/client_spec.rb2
-rw-r--r--spec/lib/gitlab/gitlab_import/project_creator_spec.rb2
-rw-r--r--spec/lib/gitlab/gitorious_import/project_creator_spec.rb2
-rw-r--r--spec/lib/gitlab/google_code_import/client_spec.rb2
-rw-r--r--spec/lib/gitlab/google_code_import/importer_spec.rb2
-rw-r--r--spec/lib/gitlab/google_code_import/project_creator_spec.rb2
-rw-r--r--spec/lib/gitlab/incoming_email_spec.rb2
-rw-r--r--spec/lib/gitlab/inline_diff_spec.rb2
-rw-r--r--spec/lib/gitlab/key_fingerprint_spec.rb2
-rw-r--r--spec/lib/gitlab/ldap/access_spec.rb7
-rw-r--r--spec/lib/gitlab/ldap/adapter_spec.rb2
-rw-r--r--spec/lib/gitlab/ldap/auth_hash_spec.rb2
-rw-r--r--spec/lib/gitlab/ldap/authentication_spec.rb2
-rw-r--r--spec/lib/gitlab/ldap/config_spec.rb2
-rw-r--r--spec/lib/gitlab/ldap/user_spec.rb2
-rw-r--r--spec/lib/gitlab/lfs/lfs_router_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/autolink_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/commit_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/cross_project_reference_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/emoji_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/external_link_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/issue_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/label_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/redactor_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/reference_gatherer_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/relative_link_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/sanitization_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/task_list_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/upload_link_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/user_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markup_helper_spec.rb2
-rw-r--r--spec/lib/gitlab/note_data_builder_spec.rb2
-rw-r--r--spec/lib/gitlab/o_auth/auth_hash_spec.rb2
-rw-r--r--spec/lib/gitlab/o_auth/user_spec.rb2
-rw-r--r--spec/lib/gitlab/popen_spec.rb2
-rw-r--r--spec/lib/gitlab/project_search_results_spec.rb2
-rw-r--r--spec/lib/gitlab/push_data_builder_spec.rb2
-rw-r--r--spec/lib/gitlab/reference_extractor_spec.rb2
-rw-r--r--spec/lib/gitlab/regex_spec.rb2
-rw-r--r--spec/lib/gitlab/sherlock/collection_spec.rb2
-rw-r--r--spec/lib/gitlab/sherlock/file_sample_spec.rb2
-rw-r--r--spec/lib/gitlab/sherlock/line_profiler_spec.rb2
-rw-r--r--spec/lib/gitlab/sherlock/line_sample_spec.rb2
-rw-r--r--spec/lib/gitlab/sherlock/location_spec.rb2
-rw-r--r--spec/lib/gitlab/sherlock/middleware_spec.rb2
-rw-r--r--spec/lib/gitlab/sherlock/query_spec.rb2
-rw-r--r--spec/lib/gitlab/sherlock/transaction_spec.rb2
-rw-r--r--spec/lib/gitlab/sql/union_spec.rb2
-rw-r--r--spec/lib/gitlab/themes_spec.rb2
-rw-r--r--spec/lib/gitlab/upgrader_spec.rb2
-rw-r--r--spec/lib/gitlab/uploads_transfer_spec.rb2
-rw-r--r--spec/lib/gitlab/url_builder_spec.rb2
-rw-r--r--spec/lib/gitlab/version_info_spec.rb2
-rw-r--r--spec/lib/repository_cache_spec.rb2
-rw-r--r--spec/models/application_setting_spec.rb16
-rw-r--r--spec/models/broadcast_message_spec.rb17
-rw-r--r--spec/models/build_spec.rb2
-rw-r--r--spec/models/ci/commit_spec.rb2
-rw-r--r--spec/models/ci/project_services/hip_chat_message_spec.rb2
-rw-r--r--spec/models/ci/project_services/hip_chat_service_spec.rb2
-rw-r--r--spec/models/ci/project_services/mail_service_spec.rb2
-rw-r--r--spec/models/ci/project_services/slack_message_spec.rb2
-rw-r--r--spec/models/ci/project_services/slack_service_spec.rb2
-rw-r--r--spec/models/ci/project_spec.rb2
-rw-r--r--spec/models/ci/runner_project_spec.rb2
-rw-r--r--spec/models/ci/runner_spec.rb2
-rw-r--r--spec/models/ci/service_spec.rb2
-rw-r--r--spec/models/ci/trigger_spec.rb2
-rw-r--r--spec/models/ci/variable_spec.rb2
-rw-r--r--spec/models/ci/web_hook_spec.rb2
-rw-r--r--spec/models/commit_range_spec.rb2
-rw-r--r--spec/models/commit_spec.rb2
-rw-r--r--spec/models/commit_status_spec.rb2
-rw-r--r--spec/models/concerns/case_sensitivity_spec.rb2
-rw-r--r--spec/models/deploy_key_spec.rb2
-rw-r--r--spec/models/deploy_keys_project_spec.rb2
-rw-r--r--spec/models/event_spec.rb2
-rw-r--r--spec/models/external_issue_spec.rb2
-rw-r--r--spec/models/external_wiki_service_spec.rb2
-rw-r--r--spec/models/generic_commit_status_spec.rb2
-rw-r--r--spec/models/global_milestone_spec.rb2
-rw-r--r--spec/models/group_spec.rb2
-rw-r--r--spec/models/hooks/project_hook_spec.rb2
-rw-r--r--spec/models/hooks/service_hook_spec.rb2
-rw-r--r--spec/models/hooks/system_hook_spec.rb2
-rw-r--r--spec/models/hooks/web_hook_spec.rb8
-rw-r--r--spec/models/issue_spec.rb2
-rw-r--r--spec/models/key_spec.rb2
-rw-r--r--spec/models/label_link_spec.rb2
-rw-r--r--spec/models/label_spec.rb2
-rw-r--r--spec/models/member_spec.rb2
-rw-r--r--spec/models/members/group_member_spec.rb2
-rw-r--r--spec/models/members/project_member_spec.rb2
-rw-r--r--spec/models/merge_request_spec.rb68
-rw-r--r--spec/models/milestone_spec.rb2
-rw-r--r--spec/models/namespace_spec.rb2
-rw-r--r--spec/models/note_spec.rb3
-rw-r--r--spec/models/project_security_spec.rb2
-rw-r--r--spec/models/project_services/buildkite_service_spec.rb2
-rw-r--r--spec/models/project_services/drone_ci_service_spec.rb2
-rw-r--r--spec/models/project_services/flowdock_service_spec.rb2
-rw-r--r--spec/models/project_services/gemnasium_service_spec.rb2
-rw-r--r--spec/models/project_services/gitlab_ci_service_spec.rb2
-rw-r--r--spec/models/project_services/gitlab_issue_tracker_service_spec.rb2
-rw-r--r--spec/models/project_services/hipchat_service_spec.rb2
-rw-r--r--spec/models/project_services/irker_service_spec.rb2
-rw-r--r--spec/models/project_services/jira_service_spec.rb2
-rw-r--r--spec/models/project_services/pushover_service_spec.rb2
-rw-r--r--spec/models/project_services/slack_service/issue_message_spec.rb2
-rw-r--r--spec/models/project_services/slack_service/merge_message_spec.rb2
-rw-r--r--spec/models/project_services/slack_service/note_message_spec.rb2
-rw-r--r--spec/models/project_services/slack_service/push_message_spec.rb2
-rw-r--r--spec/models/project_services/slack_service_spec.rb2
-rw-r--r--spec/models/project_snippet_spec.rb2
-rw-r--r--spec/models/project_spec.rb3
-rw-r--r--spec/models/project_team_spec.rb2
-rw-r--r--spec/models/project_wiki_spec.rb2
-rw-r--r--spec/models/protected_branch_spec.rb2
-rw-r--r--spec/models/repository_spec.rb2
-rw-r--r--spec/models/service_spec.rb2
-rw-r--r--spec/models/snippet_spec.rb2
-rw-r--r--spec/models/user_spec.rb21
-rw-r--r--spec/models/wiki_page_spec.rb2
-rw-r--r--spec/requests/api/groups_spec.rb59
-rw-r--r--spec/requests/api/labels_spec.rb10
-rw-r--r--spec/requests/api/merge_requests_spec.rb17
-rw-r--r--spec/requests/api/projects_spec.rb12
-rw-r--r--spec/requests/api/users_spec.rb4
-rw-r--r--spec/services/archive_repository_service_spec.rb2
-rw-r--r--spec/services/ci/create_commit_service_spec.rb2
-rw-r--r--spec/services/ci/create_trigger_request_service_spec.rb2
-rw-r--r--spec/services/ci/event_service_spec.rb2
-rw-r--r--spec/services/ci/image_for_build_service_spec.rb2
-rw-r--r--spec/services/ci/register_build_service_spec.rb2
-rw-r--r--spec/services/ci/web_hook_service_spec.rb2
-rw-r--r--spec/services/create_release_service_spec.rb2
-rw-r--r--spec/services/create_snippet_service_spec.rb2
-rw-r--r--spec/services/destroy_group_service_spec.rb2
-rw-r--r--spec/services/event_create_service_spec.rb2
-rw-r--r--spec/services/git_hooks_service_spec.rb2
-rw-r--r--spec/services/git_push_service_spec.rb2
-rw-r--r--spec/services/git_tag_push_service_spec.rb2
-rw-r--r--spec/services/issues/bulk_update_service_spec.rb2
-rw-r--r--spec/services/issues/close_service_spec.rb2
-rw-r--r--spec/services/issues/create_service_spec.rb2
-rw-r--r--spec/services/issues/update_service_spec.rb2
-rw-r--r--spec/services/merge_requests/close_service_spec.rb2
-rw-r--r--spec/services/merge_requests/create_service_spec.rb2
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb11
-rw-r--r--spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb84
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb8
-rw-r--r--spec/services/merge_requests/reopen_service_spec.rb2
-rw-r--r--spec/services/merge_requests/update_service_spec.rb2
-rw-r--r--spec/services/milestones/close_service_spec.rb2
-rw-r--r--spec/services/milestones/create_service_spec.rb2
-rw-r--r--spec/services/notes/create_service_spec.rb2
-rw-r--r--spec/services/notification_service_spec.rb2
-rw-r--r--spec/services/projects/create_service_spec.rb2
-rw-r--r--spec/services/projects/destroy_service_spec.rb2
-rw-r--r--spec/services/projects/download_service_spec.rb2
-rw-r--r--spec/services/projects/fork_service_spec.rb2
-rw-r--r--spec/services/projects/transfer_service_spec.rb2
-rw-r--r--spec/services/projects/update_service_spec.rb2
-rw-r--r--spec/services/projects/upload_service_spec.rb2
-rw-r--r--spec/services/search_service_spec.rb2
-rw-r--r--spec/services/system_hooks_service_spec.rb2
-rw-r--r--spec/services/system_note_service_spec.rb28
-rw-r--r--spec/services/test_hook_service_spec.rb2
-rw-r--r--spec/services/update_release_service_spec.rb2
-rw-r--r--spec/services/update_snippet_service_spec.rb2
-rw-r--r--spec/support/test_env.rb4
-rw-r--r--spec/support/wait_for_ajax.rb11
439 files changed, 4035 insertions, 1222 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e8290fb36b2..39d8f59cf07 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -24,6 +24,27 @@ spec:api:
- ruby
- mysql
+spec:models:
+ script:
+ - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models
+ tags:
+ - ruby
+ - mysql
+
+spec:lib:
+ script:
+ - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib
+ tags:
+ - ruby
+ - mysql
+
+spec:services:
+ script:
+ - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services
+ tags:
+ - ruby
+ - mysql
+
spec:benchmark:
script:
- RAILS_ENV=test bundle exec rake spec:benchmark
@@ -39,9 +60,16 @@ spec:other:
- ruby
- mysql
-spinach:project:
+spinach:project:half:
script:
- - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project
+ - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half
+ tags:
+ - ruby
+ - mysql
+
+spinach:project:rest:
+ script:
+ - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest
tags:
- ruby
- mysql
@@ -89,10 +117,9 @@ flay:
- mysql
bundler:audit:
- script:
+ script:
- "bundle exec bundle-audit update"
- "bundle exec bundle-audit check"
tags:
- ruby
- mysql
- allow_failure: true
diff --git a/CHANGELOG b/CHANGELOG
index 13207552e8e..01f4c386e1a 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,7 +1,14 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.3.0 (unreleased)
+ - Merge when build succeeds (Zeger-Jan van de Weg)
+ - Bump gollum-lib to 4.1.0 (Stan Hu)
+ - Fix broken group avatar upload under "New group" (Stan Hu)
+ - Update project repositorize size and commit count during import:repos task (Stan Hu)
+ - Fix API setting of 'public' attribute to false will make a project private (Stan Hu)
+ - Handle and report SSL errors in Web hook test (Stan Hu)
- Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera)
+ - Add rake tasks for git repository maintainance (Zeger-Jan van de Weg)
- Fix 500 error when update group member permission
- Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera)
- Recognize issue/MR/snippet/commit links as references
@@ -14,13 +21,19 @@ v 8.3.0 (unreleased)
- Expose events API with comment information and author info
- Fix: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583
- Run custom Git hooks when branch is created or deleted.
+ - Fix bug when simultaneously accepting multiple MRs results in MRs that are of "merged" status, but not merged to the target branch
+ - Add languages page to graphs
+ - Block LDAP user when they are no longer found in the LDAP server
+ - Improve wording on project visibility levels (Zeger-Jan van de Weg)
+ - Automatically select default clone protocol based on user preferences (Eirik Lygre)
v 8.2.3
- Fix application settings cache not expiring after changes (Stan Hu)
- Fix Error 500s when creating global milestones with Unicode characters (Stan Hu)
-
-v 8.2.3
+ - Update documentation for "Guest" permissions
+ - Properly convert Emoji-only comments into Award Emojis
- Webhook payload has an added, modified and removed properties for each commit
+ - Fix 500 error when creating a merge request that removes a submodule
v 8.2.2
- Fix 404 in redirection after removing a project (Stan Hu)
@@ -28,6 +41,7 @@ v 8.2.2
- Fix Error 500 when viewing user's personal projects from admin page (Stan Hu)
- Fix: Raw private snippets access workflow
- Prevent "413 Request entity too large" errors when pushing large files with LFS
+ - Fix: As an admin, cannot add oneself as a member to a group/project
- Fix invalid links within projects dashboard header
- Make current user the first user in assignee dropdown in issues detail page (Stan Hu)
- Fix: duplicate email notifications on issue comments
@@ -80,7 +94,6 @@ v 8.2.0
- Add email notification to former assignee upon unassignment (Adam Lieskovský)
- New design for project graphs page
- Remove deprecated dumped yaml file generated from previous job definitions
- - Fix incoming email config defaults
- Show specific runners from projects where user is master or owner
- MR target branch is now visible on a list view when it is different from project's default one
- Improve Continuous Integration graphs page
@@ -238,7 +251,6 @@ v 8.0.2
- Allow AWS S3 Server-Side Encryption with Amazon S3-Managed Keys for backups (Paul Beattie)
v 8.0.1
- - Remove git refs used internally by GitLab from network graph (Stan Hu)
- Improve CI migration procedure and documentation
v 8.0.0
diff --git a/Gemfile b/Gemfile
index 860e6bee47d..473770f53e5 100644
--- a/Gemfile
+++ b/Gemfile
@@ -52,7 +52,7 @@ gem "gitlab_git", '~> 7.2.20'
gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: "omniauth-ldap"
# Git Wiki
-gem 'gollum-lib', '~> 4.0.2'
+gem 'gollum-lib', '~> 4.1.0'
# Language detection
gem "github-linguist", "~> 4.7.0", require: "linguist"
@@ -93,13 +93,12 @@ gem 'html-pipeline', '~> 1.11.0'
gem 'task_list', '~> 1.0.2', require: 'task_list/railtie'
gem 'github-markup', '~> 1.3.1'
gem 'redcarpet', '~> 3.3.3'
-gem 'RedCloth', '~> 4.2.9'
gem 'rdoc', '~>3.6'
gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2'
-gem 'net-ssh', '~> 3.0.1'
+gem 'rouge', '~> 1.10.1'
# Diffs
gem 'diffy', '~> 3.0.3'
@@ -120,8 +119,8 @@ gem 'acts-as-taggable-on', '~> 3.4'
# Background jobs
gem 'sinatra', '~> 1.4.4', require: nil
-gem 'sidekiq', '3.3.0'
-gem 'sidetiq', '~> 0.6.3'
+gem 'sidekiq', '~> 3.5.0'
+gem 'sidekiq-cron', '~> 0.3.0'
# HTTP requests
gem "httparty", '~> 0.13.3'
@@ -205,6 +204,7 @@ gem 'raphael-rails', '~> 2.1.2'
gem 'request_store', '~> 1.2.0'
gem 'select2-rails', '~> 3.5.9'
gem 'virtus', '~> 1.0.1'
+gem 'net-ssh', '~> 3.0.1'
group :development do
gem "foreman"
diff --git a/Gemfile.lock b/Gemfile.lock
index be3e39d8e84..37ba22b7ceb 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -2,7 +2,6 @@ GEM
remote: https://rubygems.org/
specs:
CFPropertyList (2.3.2)
- RedCloth (4.2.9)
ace-rails-ap (2.0.1)
actionmailer (4.2.4)
actionpack (= 4.2.4)
@@ -117,8 +116,23 @@ GEM
activemodel (>= 3.2.0)
activesupport (>= 3.2.0)
json (>= 1.7)
- celluloid (0.16.0)
- timers (~> 4.0.0)
+ celluloid (0.17.2)
+ celluloid-essentials
+ celluloid-extras
+ celluloid-fsm
+ celluloid-pool
+ celluloid-supervision
+ timers (>= 4.1.1)
+ celluloid-essentials (0.20.5)
+ timers (>= 4.1.1)
+ celluloid-extras (0.20.5)
+ timers (>= 4.1.1)
+ celluloid-fsm (0.20.5)
+ timers (>= 4.1.1)
+ celluloid-pool (0.20.5)
+ timers (>= 4.1.1)
+ celluloid-supervision (0.20.5)
+ timers (>= 4.1.1)
charlock_holmes (0.7.3)
chunky_png (1.3.5)
cliver (0.3.2)
@@ -299,7 +313,7 @@ GEM
posix-spawn (~> 0.3)
gitlab_emoji (0.2.0)
gemojione (~> 2.1)
- gitlab_git (7.2.20)
+ gitlab_git (7.2.21)
activesupport (~> 4.0)
charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0)
@@ -314,11 +328,11 @@ GEM
activesupport (>= 4.1.0)
gollum-grit_adapter (1.0.0)
gitlab-grit (~> 2.7, >= 2.7.1)
- gollum-lib (4.0.3)
+ gollum-lib (4.1.0)
github-markup (~> 1.3.3)
gollum-grit_adapter (~> 1.0)
nokogiri (~> 1.6.4)
- rouge (~> 1.10.1)
+ rouge (~> 1.9)
sanitize (~> 2.1.0)
stringex (~> 2.5.1)
gon (6.0.1)
@@ -370,7 +384,6 @@ GEM
multi_xml (>= 0.5.2)
httpclient (2.7.0.1)
i18n (0.7.0)
- ice_cube (0.11.1)
ice_nine (0.11.1)
inflecto (0.0.2)
ipaddress (0.8.0)
@@ -641,6 +654,7 @@ GEM
sexp_processor (~> 4.1)
rubyntlm (0.5.2)
rubypants (0.2.0)
+ rufus-scheduler (3.1.10)
rugged (0.23.3)
safe_yaml (1.0.4)
sanitize (2.1.0)
@@ -668,16 +682,15 @@ GEM
rack
shoulda-matchers (2.8.0)
activesupport (>= 3.0.0)
- sidekiq (3.3.0)
- celluloid (>= 0.16.0)
- connection_pool (>= 2.0.0)
- json
- redis (>= 3.0.6)
- redis-namespace (>= 1.3.1)
- sidetiq (0.6.3)
- celluloid (>= 0.14.1)
- ice_cube (= 0.11.1)
- sidekiq (>= 3.0.0)
+ sidekiq (3.5.3)
+ celluloid (~> 0.17.2)
+ connection_pool (~> 2.2, >= 2.2.0)
+ json (~> 1.0)
+ redis (~> 3.2, >= 3.2.1)
+ redis-namespace (~> 1.5, >= 1.5.2)
+ sidekiq-cron (0.3.1)
+ rufus-scheduler (>= 2.0.24)
+ sidekiq (>= 2.17.3)
simple_oauth (0.1.9)
simplecov (0.10.0)
docile (~> 1.1.0)
@@ -743,7 +756,7 @@ GEM
thor (0.19.1)
thread_safe (0.3.5)
tilt (1.4.1)
- timers (4.0.4)
+ timers (4.1.1)
hitimes
timfel-krb5-auth (0.8.3)
tinder (1.10.1)
@@ -812,7 +825,6 @@ PLATFORMS
ruby
DEPENDENCIES
- RedCloth (~> 4.2.9)
ace-rails-ap (~> 2.0.1)
activerecord-deprecated_finders (~> 1.0.3)
activerecord-session_store (~> 0.1.0)
@@ -870,7 +882,7 @@ DEPENDENCIES
gitlab_git (~> 7.2.20)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
- gollum-lib (~> 4.0.2)
+ gollum-lib (~> 4.1.0)
gon (~> 6.0.1)
grape (~> 0.13.0)
grape-entity (~> 0.4.2)
@@ -926,6 +938,7 @@ DEPENDENCIES
request_store (~> 1.2.0)
rerun (~> 0.10.0)
responders (~> 2.0)
+ rouge (~> 1.10.1)
rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.3.0)
rubocop (~> 0.28.0)
@@ -938,8 +951,8 @@ DEPENDENCIES
settingslogic (~> 2.0.9)
sham_rack
shoulda-matchers (~> 2.8.0)
- sidekiq (= 3.3.0)
- sidetiq (~> 0.6.3)
+ sidekiq (~> 3.5.0)
+ sidekiq-cron (~> 0.3.0)
simplecov (~> 0.10.0)
sinatra (~> 1.4.4)
six (~> 0.2.0)
diff --git a/README.md b/README.md
index c59c8593eba..c459e67baa1 100644
--- a/README.md
+++ b/README.md
@@ -80,7 +80,7 @@ There are a lot of [third-party applications integrating with GitLab](https://ab
## GitLab release cycle
-Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases are published when needed. New features are detailed on the [blog](https://about.gitlab.com/blog/) and in the [changelog](CHANGELOG). For more information about the release process see the [release documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/release). Features that will likely be in the next releases can be found on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457).
+For more information about the release process see the [release documentation](http://doc.gitlab.com/ce/release/).
## Upgrading
diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee
index 9e5d594c861..746fa3cea87 100644
--- a/app/assets/javascripts/api.js.coffee
+++ b/app/assets/javascripts/api.js.coffee
@@ -2,6 +2,8 @@
groups_path: "/api/:version/groups.json"
group_path: "/api/:version/groups/:id.json"
namespaces_path: "/api/:version/namespaces.json"
+ group_projects_path: "/api/:version/groups/:id/projects.json"
+ projects_path: "/api/:version/projects.json"
group: (group_id, callback) ->
url = Api.buildUrl(Api.group_path)
@@ -44,6 +46,35 @@
).done (namespaces) ->
callback(namespaces)
+ # Return projects list. Filtered by query
+ projects: (query, callback) ->
+ url = Api.buildUrl(Api.projects_path)
+
+ $.ajax(
+ url: url
+ data:
+ private_token: gon.api_token
+ search: query
+ per_page: 20
+ dataType: "json"
+ ).done (projects) ->
+ callback(projects)
+
+ # Return group projects list. Filtered by query
+ groupProjects: (group_id, query, callback) ->
+ url = Api.buildUrl(Api.group_projects_path)
+ url = url.replace(':id', group_id)
+
+ $.ajax(
+ url: url
+ data:
+ private_token: gon.api_token
+ search: query
+ per_page: 20
+ dataType: "json"
+ ).done (projects) ->
+ callback(projects)
+
buildUrl: (url) ->
url = gon.relative_url_root + url if gon.relative_url_root?
return url.replace(':version', gon.api_version)
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index 4059fc39c67..599b4c49540 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -83,7 +83,7 @@ class Dispatcher
when 'projects:project_members:index'
new ProjectMembers()
new UsersSelect()
- when 'groups:new', 'groups:edit', 'admin:groups:edit'
+ when 'groups:new', 'groups:edit', 'admin:groups:edit', 'admin:groups:new'
new GroupAvatar()
when 'projects:tree:show'
new TreeView()
diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee
index 593a8f42130..b0eeb1db536 100644
--- a/app/assets/javascripts/merge_request_tabs.js.coffee
+++ b/app/assets/javascripts/merge_request_tabs.js.coffee
@@ -43,6 +43,7 @@
#
class @MergeRequestTabs
diffsLoaded: false
+ buildsLoaded: false
commitsLoaded: false
constructor: (@opts = {}) ->
@@ -54,6 +55,12 @@ class @MergeRequestTabs
bindEvents: ->
$(document).on 'shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', @tabShown
+ $(document).on 'click', '.js-show-tab', @showTab
+
+ showTab: (event) =>
+ event.preventDefault()
+
+ @activateTab $(event.target).data('action')
tabShown: (event) =>
$target = $(event.target)
@@ -63,6 +70,8 @@ class @MergeRequestTabs
@loadCommits($target.attr('href'))
else if action == 'diffs'
@loadDiff($target.attr('href'))
+ else if action == 'builds'
+ @loadBuilds($target.attr('href'))
@setCurrentAction(action)
@@ -101,7 +110,7 @@ class @MergeRequestTabs
action = 'notes' if action == 'show'
# Remove a trailing '/commits' or '/diffs'
- new_state = @_location.pathname.replace(/\/(commits|diffs)(\.html)?\/?$/, '')
+ new_state = @_location.pathname.replace(/\/(commits|diffs|builds)(\.html)?\/?$/, '')
# Append the new action if we're on a tab other than 'notes'
unless action == 'notes'
@@ -139,6 +148,17 @@ class @MergeRequestTabs
@diffsLoaded = true
@scrollToElement("#diffs")
+ loadBuilds: (source) ->
+ return if @buildsLoaded
+
+ @_get
+ url: "#{source}.json"
+ success: (data) =>
+ document.getElementById('builds').innerHTML = data.html
+ $('.js-timeago').timeago()
+ @buildsLoaded = true
+ @scrollToElement("#builds")
+
# Show or hide the loading spinner
#
# status - Boolean, true to show, false to hide
diff --git a/app/assets/javascripts/new_commit_form.js.coffee b/app/assets/javascripts/new_commit_form.js.coffee
index 2e561dea3e1..3c7b776155f 100644
--- a/app/assets/javascripts/new_commit_form.js.coffee
+++ b/app/assets/javascripts/new_commit_form.js.coffee
@@ -3,7 +3,7 @@ class @NewCommitForm
@newBranch = form.find('.js-new-branch')
@originalBranch = form.find('.js-original-branch')
@createMergeRequest = form.find('.js-create-merge-request')
- @createMergeRequestFormGroup = form.find('.js-create-merge-request-form-group')
+ @createMergeRequestContainer = form.find('.js-create-merge-request-container')
@renderDestination()
@newBranch.keyup @renderDestination
@@ -12,10 +12,10 @@ class @NewCommitForm
different = @newBranch.val() != @originalBranch.val()
if different
- @createMergeRequestFormGroup.show()
+ @createMergeRequestContainer.show()
@createMergeRequest.prop('checked', true) unless @wasDifferent
else
- @createMergeRequestFormGroup.hide()
+ @createMergeRequestContainer.hide()
@createMergeRequest.prop('checked', false)
@wasDifferent = different
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index dd6cbcfc70b..533d00bfb0c 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -369,8 +369,8 @@ class @Notes
note = $(this).closest(".note")
note.find(".note-attachment").remove()
note.find(".note-body > .note-text").show()
- note.find(".js-note-attachment-delete").hide()
- note.find(".note-edit-form").hide()
+ note.find(".note-header").show()
+ note.find(".current-note-edit-form").remove()
###
Called when clicking on the "reply" button for a diff line.
diff --git a/app/assets/javascripts/project_select.js.coffee b/app/assets/javascripts/project_select.js.coffee
new file mode 100644
index 00000000000..0ae274f3363
--- /dev/null
+++ b/app/assets/javascripts/project_select.js.coffee
@@ -0,0 +1,39 @@
+class @ProjectSelect
+ constructor: ->
+ $('.ajax-project-select').each (i, select) ->
+ @groupId = $(select).data('group-id')
+ @includeGroups = $(select).data('include-groups')
+
+ placeholder = "Search for project"
+ placeholder += " or group" if @includeGroups
+
+ $(select).select2
+ placeholder: placeholder
+ minimumInputLength: 0
+ query: (query) =>
+ finalCallback = (projects) ->
+ data = { results: projects }
+ query.callback(data)
+
+ if @includeGroups
+ projectsCallback = (projects) ->
+ groupsCallback = (groups) ->
+ data = groups.concat(projects)
+ finalCallback(data)
+
+ Api.groups query.term, false, groupsCallback
+ else
+ projectsCallback = finalCallback
+
+ if @groupId
+ Api.groupProjects @groupId, query.term, projectsCallback
+ else
+ Api.projects query.term, projectsCallback
+
+ id: (project) ->
+ project.web_url
+
+ text: (project) ->
+ project.name_with_namespace || project.name
+
+ dropdownCssClass: "ajax-project-dropdown"
diff --git a/app/assets/stylesheets/framework/callout.scss b/app/assets/stylesheets/framework/callout.scss
index f3ce4e3c219..20a9bfb9816 100644
--- a/app/assets/stylesheets/framework/callout.scss
+++ b/app/assets/stylesheets/framework/callout.scss
@@ -7,8 +7,8 @@
/* Common styles for all types */
.bs-callout {
- margin: 20px 0;
- padding: 20px;
+ margin: $gl-padding 0;
+ padding: $gl-padding;
border-left: 3px solid $border-color;
color: $text-color;
background: $background-color;
@@ -42,4 +42,3 @@
border-color: #5cA64d;
color: #3c763d;
}
-
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index d2f491daf78..2e8515668f6 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -333,7 +333,7 @@ table {
}
.well {
- margin-bottom: 0;
+ margin-bottom: $gl-padding;
}
.search_box {
@@ -379,9 +379,8 @@ table {
text-align: center;
margin-top: 5px;
margin-bottom: $gl-padding;
- height: 56px;
+ height: auto;
margin-top: -$gl-padding;
- padding-top: $gl-padding;
&.no-bottom {
margin-bottom: 0;
@@ -390,6 +389,18 @@ table {
&.no-top {
margin-top: 0;
}
+
+ li a {
+ display: inline-block;
+ padding-top: $gl-padding;
+ padding-bottom: 11px;
+ margin-bottom: -1px;
+ }
+
+ &.bottom-border {
+ border-bottom: 1px solid $border-color;
+ height: 57px;
+ }
}
.center-middle-menu {
@@ -437,3 +448,16 @@ table {
.alert, .progress {
margin-bottom: $gl-padding;
}
+
+.new-project-item-select-holder {
+ display: inline-block;
+ position: relative;
+
+ .new-project-item-select {
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 250px !important;
+ visibility: hidden;
+ }
+}
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index 6bf2857e83a..cbfd4bc29b6 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -21,7 +21,6 @@
position: relative;
background: $background-color;
border-bottom: 1px solid $border-color;
- text-shadow: 0 1px 1px #fff;
margin: 0;
text-align: left;
padding: 10px $gl-padding;
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index a798ae812e3..481e3f33a20 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -72,13 +72,6 @@
}
}
-ol, ul {
- &.styled {
- li {
- padding: 2px;
- }
- }
-}
/** light list with border-bottom between li **/
ul.bordered-list {
@@ -95,8 +88,14 @@ ul.bordered-list {
}
}
-li.task-list-item {
- list-style-type: none;
+ul.task-list {
+ li.task-list-item {
+ list-style-type: none;
+ }
+
+ ul:not(.task-list) {
+ padding-left: 1.3em;
+ }
}
ul.content-list {
@@ -124,6 +123,24 @@ ul.content-list {
padding: 10px 14px;
}
}
+
+ ul.controls {
+ list-style: none;
+
+ li {
+ float: left;
+ padding-right: 10px;
+
+ .author_link {
+ display: inline-block;
+
+ .avatar-inline {
+ margin-left: 0;
+ margin-right: 0;
+ }
+ }
+ }
+ }
}
}
diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss
index cea47fba192..6f44c323732 100644
--- a/app/assets/stylesheets/framework/mobile.scss
+++ b/app/assets/stylesheets/framework/mobile.scss
@@ -82,9 +82,6 @@
}
.center-top-menu {
- height: 45px;
- margin-bottom: 30px;
-
li a {
font-size: 14px;
padding: 19px 10px;
diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss
index 406aff3d72c..61053aff91a 100644
--- a/app/assets/stylesheets/framework/panels.scss
+++ b/app/assets/stylesheets/framework/panels.scss
@@ -1,9 +1,11 @@
.panel {
margin-bottom: $gl-padding;
-
+
.panel-heading {
- padding: 10px $gl-padding;
+ padding: 7px $gl-padding;
+ line-height: 42px !important;
}
+
.panel-body {
padding: $gl-padding;
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index aef338cfa56..c3e4ad0ad00 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -220,6 +220,7 @@ pre {
.monospace {
font-family: $monospace_font;
+ font-size: 90%;
}
code {
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index da9965f007a..3c2997c1d5a 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -67,9 +67,4 @@
color: #3084bb !important;
}
}
-
- .build-top-menu {
- margin-top: 0;
- margin-bottom: 2px;
- }
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index f21ad694d06..fc8c7161991 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -18,6 +18,7 @@
.accept-merge-holder {
.accept-action {
display: inline-block;
+ float: left;
.accept_merge_request {
&.ci-pending,
@@ -36,14 +37,15 @@
.accept-control {
display: inline-block;
+ float: left;
margin: 0;
margin-left: 20px;
padding: 5px;
+ padding-top: 12px;
line-height: 20px;
&.right {
float: right;
- padding-top: 12px;
a {
color: $gl-gray;
}
@@ -81,6 +83,10 @@
&.ci-error {
color: $gl-danger;
}
+
+ a.monospace {
+ color: inherit;
+ }
}
.mr-widget-body,
@@ -136,7 +142,7 @@
font-family: $monospace_font;
font-weight: bold;
overflow: hidden;
- font-size: 14px;
+ font-size: 90%;
margin: 0 3px;
}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 1980fe0d458..4dff87abaa4 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -109,13 +109,9 @@ ul.notes {
}
}
- // Reduce left padding of first task list ul element
- ul.task-list:first-child {
- padding-left: 10px;
-
- // sub-tasks should be padded normally
- ul {
- padding-left: 20px;
+ ul.task-list {
+ ul:not(.task-list) {
+ padding-left: 1.3em;
}
}
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index 1d6ca0dfc13..95fc26a608a 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -5,12 +5,6 @@
}
}
-.btn-build-token {
- float: left;
- padding: 6px 20px;
- margin-right: 12px;
-}
-
.profile-avatar-form-option {
hr {
margin: 10px 0;
diff --git a/app/assets/stylesheets/pages/ui_dev_kit.scss b/app/assets/stylesheets/pages/ui_dev_kit.scss
index 277afa1db9e..185f3622e64 100644
--- a/app/assets/stylesheets/pages/ui_dev_kit.scss
+++ b/app/assets/stylesheets/pages/ui_dev_kit.scss
@@ -1,9 +1,6 @@
.gitlab-ui-dev-kit {
> h2 {
- font-size: 27px;
- border-bottom: 1px solid #CCC;
- color: #666;
- margin: 30px 0;
+ margin: 35px 0 20px;
font-weight: bold;
}
}
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index d3f926b62bc..7d0d57858e0 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -21,14 +21,14 @@ class Projects::ApplicationController < ApplicationController
unless @repository.branch_names.include?(@ref)
redirect_to(
namespace_project_tree_path(@project.namespace, @project, @ref),
- notice: "This action is not allowed unless you are on top of a branch"
+ notice: "This action is not allowed unless you are on a branch"
)
end
end
private
- def ci_enabled
+ def builds_enabled
return render_404 unless @project.builds_enabled?
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 31a33bfd237..62163682936 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -162,12 +162,20 @@ class Projects::BlobController < Projects::ApplicationController
end
def sanitized_new_branch_name
- @new_branch ||= sanitize(strip_tags(params[:new_branch]))
+ sanitize(strip_tags(params[:new_branch]))
end
def editor_variables
@current_branch = @ref
- @new_branch = params[:new_branch].present? ? sanitized_new_branch_name : @ref
+
+ @new_branch =
+ if params[:new_branch].present?
+ sanitized_new_branch_name
+ elsif ::Gitlab::GitAccess.new(current_user, @project).can_push_to_branch?(@ref)
+ @ref
+ else
+ @repository.next_patch_branch
+ end
@file_path =
if action_name.to_s == 'create'
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 3f137440e28..e8af205b788 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -37,7 +37,7 @@ class Projects::CommitController < Projects::ApplicationController
def cancel_builds
ci_commit.builds.running_or_pending.each(&:cancel)
- redirect_to builds_namespace_project_commit_path(project.namespace, project, commit.sha)
+ redirect_back_or_default default: builds_namespace_project_commit_path(project.namespace, project, commit.sha)
end
def retry_builds
@@ -47,7 +47,7 @@ class Projects::CommitController < Projects::ApplicationController
end
end
- redirect_to builds_namespace_project_commit_path(project.namespace, project, commit.sha)
+ redirect_back_or_default default: builds_namespace_project_commit_path(project.namespace, project, commit.sha)
end
def branches
@@ -74,8 +74,8 @@ class Projects::CommitController < Projects::ApplicationController
end
@notes_count = commit.notes.count
-
- @builds = ci_commit.builds if ci_commit
+
+ @statuses = ci_commit.statuses if ci_commit
end
def authorize_manage_builds!
diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb
index 418b92040bc..a8f47069bb4 100644
--- a/app/controllers/projects/graphs_controller.rb
+++ b/app/controllers/projects/graphs_controller.rb
@@ -5,7 +5,7 @@ class Projects::GraphsController < Projects::ApplicationController
before_action :require_non_empty_project
before_action :assign_ref_vars
before_action :authorize_download_code!
- before_action :ci_enabled, only: :ci
+ before_action :builds_enabled, only: :ci
def show
respond_to do |format|
@@ -34,6 +34,26 @@ class Projects::GraphsController < Projects::ApplicationController
@charts[:build_times] = Ci::Charts::BuildTime.new(ci_project)
end
+ def languages
+ @languages = Linguist::Repository.new(@repository.rugged, @repository.rugged.head.target_id).languages
+ total = @languages.map(&:last).sum
+
+ @languages = @languages.map do |language|
+ name, share = language
+ color = Digest::SHA256.hexdigest(name)[0...6]
+ {
+ value: (share.to_f * 100 / total).round(2),
+ label: name,
+ color: "##{color}",
+ highlight: "##{color}"
+ }
+ end
+
+ @languages.sort! do |x, y|
+ y[:value] <=> x[:value]
+ end
+ end
+
private
def fetch_graph
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index c7569541899..6a62880cb71 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -25,13 +25,12 @@ class Projects::HooksController < Projects::ApplicationController
def test
if !@project.empty_repo?
- status = TestHookService.new.execute(hook, current_user)
+ status, message = TestHookService.new.execute(hook, current_user)
if status
flash[:notice] = 'Hook successfully executed.'
else
- flash[:alert] = 'Hook execution failed. '\
- 'Ensure hook URL is correct and service is up.'
+ flash[:alert] = "Hook execution failed: #{message}"
end
else
flash[:alert] = 'Hook execution failed. Ensure the project has commits.'
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 3f47f2ddb2c..530f3d3dcb8 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -1,13 +1,14 @@
class Projects::MergeRequestsController < Projects::ApplicationController
before_action :module_enabled
before_action :merge_request, only: [
- :edit, :update, :show, :diffs, :commits, :merge, :merge_check,
- :ci_status, :toggle_subscription
+ :edit, :update, :show, :diffs, :commits, :builds, :merge, :merge_check,
+ :ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds
]
- before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits]
- before_action :validates_merge_request, only: [:show, :diffs, :commits]
- before_action :define_show_vars, only: [:show, :diffs, :commits]
- before_action :ensure_ref_fetched, only: [:show, :commits, :diffs]
+ before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits, :builds]
+ before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds]
+ before_action :define_show_vars, only: [:show, :diffs, :commits, :builds]
+ before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds]
+ before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds]
# Allow read any merge_request
before_action :authorize_read_merge_request!
@@ -79,6 +80,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
end
+ def builds
+ @ci_project = @merge_request.source_project.gitlab_ci_project
+
+ respond_to do |format|
+ format.html { render 'show' }
+ format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_builds') } }
+ end
+ end
+
def new
params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
@merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute
@@ -91,20 +101,19 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@target_project = merge_request.target_project
@source_project = merge_request.source_project
- @commits = @merge_request.compare_commits
+ @commits = @merge_request.compare_commits.reverse
@commit = @merge_request.last_commit
@first_commit = @merge_request.first_commit
@diffs = @merge_request.compare_diffs
+
+ @ci_project = @source_project.gitlab_ci_project
+ @ci_commit = @merge_request.ci_commit
+ @statuses = @ci_commit.statuses if @ci_commit
+
@note_counts = Note.where(commit_id: @commits.map(&:id)).
group(:commit_id).count
end
- def edit
- @source_project = @merge_request.source_project
- @target_project = @merge_request.target_project
- @target_branches = @merge_request.target_project.repository.branch_names
- end
-
def create
@target_branches ||= []
@merge_request = MergeRequests::CreateService.new(project, current_user, merge_request_params).execute
@@ -118,6 +127,12 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
end
+ def edit
+ @source_project = @merge_request.source_project
+ @target_project = @merge_request.target_project
+ @target_branches = @merge_request.target_project.repository.branch_names
+ end
+
def update
@merge_request = MergeRequests::UpdateService.new(project, current_user, merge_request_params).execute(@merge_request)
@@ -150,15 +165,29 @@ class Projects::MergeRequestsController < Projects::ApplicationController
render partial: "projects/merge_requests/widget/show.html.haml", layout: false
end
+ def cancel_merge_when_build_succeeds
+ return access_denied! unless @merge_request.can_cancel_merge_when_build_succeeds?(current_user)
+
+ MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user).cancel(@merge_request)
+ end
+
def merge
return access_denied! unless @merge_request.can_be_merged_by?(current_user)
- if @merge_request.mergeable?
- @merge_request.update(merge_error: nil)
- MergeWorker.perform_async(@merge_request.id, current_user.id, params)
- @status = true
+ unless @merge_request.mergeable?
+ @status = :failed
+ return
+ end
+
+ @merge_request.update(merge_error: nil)
+
+ if params[:merge_when_build_succeeds] && @merge_request.ci_commit && @merge_request.ci_commit.active?
+ MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params)
+ .execute(@merge_request)
+ @status = :merge_when_build_succeeds
else
- @status = false
+ MergeWorker.perform_async(@merge_request.id, current_user.id, params)
+ @status = :success
end
end
@@ -264,12 +293,19 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request_diff = @merge_request.merge_request_diff
+ @ci_commit = @merge_request.ci_commit
+ @statuses = @ci_commit.statuses if @ci_commit
+
if @merge_request.locked_long_ago?
@merge_request.unlock_mr
@merge_request.close
end
end
+ def define_widget_vars
+ @ci_commit = @merge_request.ci_commit
+ end
+
def invalid_mr
# Render special view for MR with removed source or target branch
render 'invalid'
@@ -283,6 +319,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
)
end
+ def merge_params
+ params.permit(:should_remove_source_branch, :commit_message)
+ end
+
# Make sure merge requests created before 8.0
# have head file in refs/merge-requests/
def ensure_ref_fetched
diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb
index 07eb94e4f48..8364fc293b7 100644
--- a/app/controllers/projects/project_members_controller.rb
+++ b/app/controllers/projects/project_members_controller.rb
@@ -23,7 +23,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
@group_members = @group_members.where(user_id: users)
end
- @group_members = @group_members.order('access_level DESC').limit(20)
+ @group_members = @group_members.order('access_level DESC')
end
@project_member = @project.project_members.new
diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb
index d5ee6ac8663..be7d5c187fe 100644
--- a/app/controllers/projects/raw_controller.rb
+++ b/app/controllers/projects/raw_controller.rb
@@ -10,15 +10,13 @@ class Projects::RawController < Projects::ApplicationController
@blob = @repository.blob_at(@commit.id, @path)
if @blob
- type = get_blob_type
-
headers['X-Content-Type-Options'] = 'nosniff'
- send_data(
- @blob.data,
- type: type,
- disposition: 'inline'
- )
+ if @blob.lfs_pointer?
+ send_lfs_object
+ else
+ stream_data
+ end
else
render_404
end
@@ -35,4 +33,33 @@ class Projects::RawController < Projects::ApplicationController
'application/octet-stream'
end
end
+
+ def stream_data
+ type = get_blob_type
+
+ send_data(
+ @blob.data,
+ type: type,
+ disposition: 'inline'
+ )
+ end
+
+ def send_lfs_object
+ lfs_object = find_lfs_object
+
+ if lfs_object && lfs_object.project_allowed_access?(@project)
+ send_file lfs_object.file.path, filename: @blob.name, disposition: 'attachment'
+ else
+ render_404
+ end
+ end
+
+ def find_lfs_object
+ lfs_object = LfsObject.find_by_oid(@blob.lfs_oid)
+ if lfs_object && lfs_object.file.exists?
+ lfs_object
+ else
+ nil
+ end
+ end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 3230ff1b004..21f962df206 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -209,7 +209,7 @@ module ApplicationHelper
title: time.in_time_zone.stamp('Aug 21, 2011 9:23pm'),
data: { toggle: 'tooltip', placement: placement, container: 'body' }
- element += javascript_tag "$('.js-timeago').timeago()" unless skip_js
+ element += javascript_tag "$('.js-timeago').last().timeago()" unless skip_js
element
end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index df5f5fae23c..68e5d5be600 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -30,26 +30,24 @@ module BlobHelper
nil
end
- if blob && blob.text?
- text = 'Edit'
- after = options[:after] || ''
- from_mr = options[:from_merge_request_id]
- link_opts = {}
- link_opts[:from_merge_request_id] = from_mr if from_mr
- cls = 'btn btn-small'
- if allowed_tree_edit?(project, ref)
- link_to(text,
- namespace_project_edit_blob_path(project.namespace, project,
- tree_join(ref, path),
- link_opts),
- class: cls
- )
- else
- content_tag :span, text, class: cls + ' disabled'
- end + after.html_safe
- else
- ''
- end
+ return unless blob && blob.text? && blob_editable?(blob)
+
+ text = 'Edit'
+ after = options[:after] || ''
+ from_mr = options[:from_merge_request_id]
+ link_opts = {}
+ link_opts[:from_merge_request_id] = from_mr if from_mr
+ cls = 'btn btn-small'
+ link_to(text,
+ namespace_project_edit_blob_path(project.namespace, project,
+ tree_join(ref, path),
+ link_opts),
+ class: cls
+ ) + after.html_safe
+ end
+
+ def blob_editable?(blob, project = @project, ref = @ref)
+ !blob.lfs_pointer? && allowed_tree_edit?(project, ref)
end
def leave_edit_message
@@ -71,4 +69,16 @@ module BlobHelper
def blob_icon(mode, name)
icon("#{file_type_icon_class('file', mode, name)} fw")
end
+
+ def blob_viewable?(blob)
+ blob && blob.text? && !blob.lfs_pointer?
+ end
+
+ def blob_size(blob)
+ if blob.lfs_pointer?
+ blob.lfs_size
+ else
+ blob.size
+ end
+ end
end
diff --git a/app/helpers/branches_helper.rb b/app/helpers/branches_helper.rb
index d6eaa7d57bc..e39548e17e1 100644
--- a/app/helpers/branches_helper.rb
+++ b/app/helpers/branches_helper.rb
@@ -11,7 +11,7 @@ module BranchesHelper
def can_push_branch?(project, branch_name)
return false unless project.repository.branch_names.include?(branch_name)
-
+
::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name)
end
end
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
index 0ecf77bb45e..8e1f8f9ba6d 100644
--- a/app/helpers/ci_status_helper.rb
+++ b/app/helpers/ci_status_helper.rb
@@ -8,6 +8,10 @@ module CiStatusHelper
ci_icon_for_status(ci_commit.status)
end
+ def ci_status_label(ci_commit)
+ ci_label_for_status(ci_commit.status)
+ end
+
def ci_status_color(ci_commit)
case ci_commit.status
when 'success'
@@ -23,7 +27,15 @@ module CiStatusHelper
def ci_status_with_icon(status)
content_tag :span, class: "ci-status ci-#{status}" do
- ci_icon_for_status(status) + '&nbsp;'.html_safe + status
+ ci_icon_for_status(status) + '&nbsp;'.html_safe + ci_label_for_status(status)
+ end
+ end
+
+ def ci_label_for_status(status)
+ if status == 'success'
+ 'passed'
+ else
+ status
end
end
@@ -46,7 +58,7 @@ module CiStatusHelper
def render_ci_status(ci_commit)
link_to ci_status_path(ci_commit),
class: "c#{ci_status_color(ci_commit)}",
- title: "Build status: #{ci_commit.status}",
+ title: "Build #{ci_status_label(ci_commit)}",
data: { toggle: 'tooltip', placement: 'left' } do
ci_status_icon(ci_commit)
end
diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb
index 775cf5a3dd4..9bf750124b2 100644
--- a/app/helpers/page_layout_helper.rb
+++ b/app/helpers/page_layout_helper.rb
@@ -4,7 +4,8 @@ module PageLayoutHelper
@page_title.push(*titles.compact) if titles.any?
- @page_title.join(" | ")
+ # Segments are seperated by middot
+ @page_title.join(" \u00b7 ")
end
def header_title(title = nil, title_url = nil)
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 48729e5260e..d061136b7b8 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -175,11 +175,19 @@ module ProjectsHelper
end
def default_url_to_repo(project = @project)
- current_user ? project.url_to_repo : project.http_url_to_repo
+ if default_clone_protocol == "ssh"
+ project.ssh_url_to_repo
+ else
+ project.http_url_to_repo
+ end
end
def default_clone_protocol
- current_user ? "ssh" : "http"
+ if !current_user || current_user.require_ssh_key?
+ "http"
+ else
+ "ssh"
+ end
end
def project_last_activity(project)
diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb
index 7e175d0de8a..05386d790ca 100644
--- a/app/helpers/selects_helper.rb
+++ b/app/helpers/selects_helper.rb
@@ -48,6 +48,19 @@ module SelectsHelper
select2_tag(id, opts)
end
+ def project_select_tag(id, opts = {})
+ opts[:class] ||= ''
+ opts[:class] << ' ajax-project-select'
+
+ unless opts.delete(:scope) == :all
+ if @group
+ opts['data-group-id'] = @group.id
+ end
+ end
+
+ hidden_field_tag(id, opts[:selected], opts)
+ end
+
def select2_tag(id, opts = {})
css_class = ''
css_class << 'multiselect ' if opts[:multiple]
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index 03a49e119b8..886a1e734b5 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -46,12 +46,26 @@ module TreeHelper
File.join(*args)
end
+ def on_top_of_branch?(project = @project, ref = @ref)
+ project.repository.branch_names.include?(ref)
+ end
+
def allowed_tree_edit?(project = nil, ref = nil)
project ||= @project
ref ||= @ref
- return false unless project.repository.branch_names.include?(ref)
+ return false unless on_top_of_branch?(project, ref)
+
+ can?(current_user, :push_code, project)
+ end
- ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
+ def tree_edit_branch(project = @project, ref = @ref)
+ if allowed_tree_edit?(project, ref)
+ if can_push_branch?(project, ref)
+ ref
+ else
+ project.repository.next_patch_branch
+ end
+ end
end
def tree_breadcrumbs(tree, max_links = 2)
diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb
index 72c65030f94..2e69ce923a2 100644
--- a/app/helpers/visibility_level_helper.rb
+++ b/app/helpers/visibility_level_helper.rb
@@ -12,22 +12,22 @@ module VisibilityLevelHelper
# Return the description for the +level+ argument.
#
- # +level+ One of the Gitlab::VisibilityLevel constants
- # +form_model+ Either a model object (Project, Snippet, etc.) or the name of
- # a Project or Snippet class.
+ # +level+ One of the Gitlab::VisibilityLevel constants
+ # +form_model+ Either a model object (Project, Snippet, etc.) or the name of
+ # a Project or Snippet class.
def visibility_level_description(level, form_model)
- case form_model.is_a?(String) ? form_model : form_model.class.name
- when 'PersonalSnippet', 'ProjectSnippet', 'Snippet'
- snippet_visibility_level_description(level)
- when 'Project'
+ case form_model
+ when Project
project_visibility_level_description(level)
+ when Snippet
+ snippet_visibility_level_description(level, form_model)
end
end
def project_visibility_level_description(level)
case level
when Gitlab::VisibilityLevel::PRIVATE
- "Project access must be granted explicitly for each user."
+ "Project access must be granted explicitly to each user."
when Gitlab::VisibilityLevel::INTERNAL
"The project can be cloned by any logged in user."
when Gitlab::VisibilityLevel::PUBLIC
@@ -35,12 +35,16 @@ module VisibilityLevelHelper
end
end
- def snippet_visibility_level_description(level)
+ def snippet_visibility_level_description(level, snippet = nil)
case level
when Gitlab::VisibilityLevel::PRIVATE
- "The snippet is visible only for me."
+ if snippet.is_a? ProjectSnippet
+ "The snippet is visible only to project members."
+ else
+ "The snippet is visible only to me."
+ end
when Gitlab::VisibilityLevel::INTERNAL
- "The snippet is visible for any logged in user."
+ "The snippet is visible to any logged in user."
when Gitlab::VisibilityLevel::PUBLIC
"The snippet can be accessed without any authentication."
end
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 07f3a56ec7a..cd5ae0fb0fd 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -346,12 +346,10 @@ class Ability
unless group.last_owner?(target_user)
can_manage = group_abilities(user, group).include?(:admin_group_member)
- if can_manage && user != target_user
+ if can_manage
rules << :update_group_member
rules << :destroy_group_member
- end
-
- if user == target_user
+ elsif user == target_user
rules << :destroy_group_member
end
end
@@ -367,12 +365,10 @@ class Ability
unless target_user == project.owner
can_manage = project_abilities(user, project).include?(:admin_project_member)
- if can_manage && user != target_user
+ if can_manage
rules << :update_project_member
rules << :destroy_project_member
- end
-
- if user == target_user
+ elsif user == target_user
rules << :destroy_project_member
end
end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 5ddcf3d9a0b..1880ad9f33c 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -43,12 +43,12 @@ class ApplicationSetting < ActiveRecord::Base
validates :home_page_url,
allow_blank: true,
- format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" },
+ url: true,
if: :home_page_url_column_exist
validates :after_sign_out_path,
allow_blank: true,
- format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }
+ url: true
validates :admin_notification_email,
allow_blank: true,
diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb
index 05f5e979695..ad514706160 100644
--- a/app/models/broadcast_message.rb
+++ b/app/models/broadcast_message.rb
@@ -16,12 +16,12 @@
class BroadcastMessage < ActiveRecord::Base
include Sortable
- validates :message, presence: true
+ validates :message, presence: true
validates :starts_at, presence: true
- validates :ends_at, presence: true
+ validates :ends_at, presence: true
- validates :color, format: { with: /\A\#[0-9A-Fa-f]{3}{1,2}+\Z/ }, allow_blank: true
- validates :font, format: { with: /\A\#[0-9A-Fa-f]{3}{1,2}+\Z/ }, allow_blank: true
+ validates :color, allow_blank: true, color: true
+ validates :font, allow_blank: true, color: true
def self.current
where("ends_at > :now AND starts_at < :now", now: Time.zone.now).last
diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb
index 971e899de84..75465685e98 100644
--- a/app/models/ci/commit.rb
+++ b/app/models/ci/commit.rb
@@ -165,6 +165,14 @@ module Ci
status == 'canceled'
end
+ def active?
+ running? || pending?
+ end
+
+ def complete?
+ canceled? || success? || failed?
+ end
+
def duration
duration_array = latest_statuses.map(&:duration).compact
duration_array.reduce(:+).to_i
@@ -199,7 +207,7 @@ module Ci
end
def ci_yaml_file
- gl_project.repository.blob_at(sha, '.gitlab-ci.yml').data
+ @ci_yaml_file ||= gl_project.repository.blob_at(sha, '.gitlab-ci.yml').data
rescue
nil
end
diff --git a/app/models/ci/web_hook.rb b/app/models/ci/web_hook.rb
index 7ca16a1bde8..0dc15eb6683 100644
--- a/app/models/ci/web_hook.rb
+++ b/app/models/ci/web_hook.rb
@@ -20,8 +20,7 @@ module Ci
# HTTParty timeout
default_timeout 10
- validates :url, presence: true,
- format: { with: URI::regexp(%w(http https)), message: "should be a valid url" }
+ validates :url, presence: true, url: true
def execute(data)
parsed_url = URI.parse(url)
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index e70f4d37184..ff619965a57 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -1,34 +1,30 @@
# == Schema Information
#
-# Table name: ci_builds
-#
-# id :integer not null, primary key
-# project_id :integer
-# status :string(255)
-# finished_at :datetime
-# trace :text
-# created_at :datetime
-# updated_at :datetime
-# started_at :datetime
-# runner_id :integer
-# coverage :float
-# commit_id :integer
-# commands :text
-# job_id :integer
-# name :string(255)
-# deploy :boolean default(FALSE)
-# options :text
-# allow_failure :boolean default(FALSE), not null
-# stage :string(255)
-# trigger_request_id :integer
-# stage_idx :integer
-# tag :boolean
-# ref :string(255)
-# user_id :integer
-# type :string(255)
-# target_url :string(255)
-# description :string(255)
-# artifacts_file :text
+# project_id integer
+# status string
+# finished_at datetime
+# trace text
+# created_at datetime
+# updated_at datetime
+# started_at datetime
+# runner_id integer
+# coverage float
+# commit_id integer
+# commands text
+# job_id integer
+# name string
+# deploy boolean default: false
+# options text
+# allow_failure boolean default: false, null: false
+# stage string
+# trigger_request_id integer
+# stage_idx integer
+# tag boolean
+# ref string
+# user_id integer
+# type string
+# target_url string
+# description string
#
class CommitStatus < ActiveRecord::Base
@@ -79,6 +75,10 @@ class CommitStatus < ActiveRecord::Base
build.update_attributes finished_at: Time.now
end
+ after_transition [:pending, :running] => :success do |build, transition|
+ MergeRequests::MergeWhenBuildSucceedsService.new(build.commit.gl_project, nil).trigger(build)
+ end
+
state :pending, value: 'pending'
state :running, value: 'running'
state :failed, value: 'failed'
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index d6c6f415c4a..715ec5908b7 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -31,37 +31,38 @@ class WebHook < ActiveRecord::Base
# HTTParty timeout
default_timeout Gitlab.config.gitlab.webhook_timeout
- validates :url, presence: true,
- format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }
+ validates :url, presence: true, url: true
def execute(data, hook_name)
parsed_url = URI.parse(url)
if parsed_url.userinfo.blank?
- WebHook.post(url,
- body: data.to_json,
- headers: {
- "Content-Type" => "application/json",
- "X-Gitlab-Event" => hook_name.singularize.titleize
- },
- verify: enable_ssl_verification)
+ response = WebHook.post(url,
+ body: data.to_json,
+ headers: {
+ "Content-Type" => "application/json",
+ "X-Gitlab-Event" => hook_name.singularize.titleize
+ },
+ verify: enable_ssl_verification)
else
post_url = url.gsub("#{parsed_url.userinfo}@", "")
auth = {
username: URI.decode(parsed_url.user),
password: URI.decode(parsed_url.password),
}
- WebHook.post(post_url,
- body: data.to_json,
- headers: {
- "Content-Type" => "application/json",
- "X-Gitlab-Event" => hook_name.singularize.titleize
- },
- verify: enable_ssl_verification,
- basic_auth: auth)
+ response = WebHook.post(post_url,
+ body: data.to_json,
+ headers: {
+ "Content-Type" => "application/json",
+ "X-Gitlab-Event" => hook_name.singularize.titleize
+ },
+ verify: enable_ssl_verification,
+ basic_auth: auth)
end
- rescue SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout => e
+
+ [response.code == 200, ActionView::Base.full_sanitizer.sanitize(response.to_s)]
+ rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout => e
logger.error("WebHook Error => #{e}")
- false
+ [false, e.to_s]
end
def async_execute(data, hook_name)
diff --git a/app/models/label.rb b/app/models/label.rb
index bef6063fe88..220da10a6ab 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -27,9 +27,7 @@ class Label < ActiveRecord::Base
has_many :label_links, dependent: :destroy
has_many :issues, through: :label_links, source: :target, source_type: 'Issue'
- validates :color,
- format: { with: /\A#[0-9A-Fa-f]{6}\Z/ },
- allow_blank: false
+ validates :color, color: true, allow_blank: false
validates :project, presence: true, unless: Proc.new { |service| service.template? }
# Don't allow '?', '&', and ',' for label titles
diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb
index 3c1426f59d0..86b1b7e2f99 100644
--- a/app/models/lfs_object.rb
+++ b/app/models/lfs_object.rb
@@ -1,3 +1,15 @@
+# == Schema Information
+#
+# Table name: lfs_objects
+#
+# id :integer not null, primary key
+# oid :string(255) not null
+# size :integer not null
+# created_at :datetime
+# updated_at :datetime
+# file :string(255)
+#
+
class LfsObject < ActiveRecord::Base
has_many :lfs_objects_projects, dependent: :destroy
has_many :projects, through: :lfs_objects_projects
@@ -5,4 +17,16 @@ class LfsObject < ActiveRecord::Base
validates :oid, presence: true, uniqueness: true
mount_uploader :file, LfsObjectUploader
+
+ def storage_project(project)
+ if project && project.forked?
+ storage_project(project.forked_from_project)
+ else
+ project
+ end
+ end
+
+ def project_allowed_access?(project)
+ projects.exists?(storage_project(project).id)
+ end
end
diff --git a/app/models/lfs_objects_project.rb b/app/models/lfs_objects_project.rb
index 0fd5f089db9..890736bfc80 100644
--- a/app/models/lfs_objects_project.rb
+++ b/app/models/lfs_objects_project.rb
@@ -1,3 +1,14 @@
+# == Schema Information
+#
+# Table name: lfs_objects_projects
+#
+# id :integer not null, primary key
+# lfs_object_id :integer not null
+# project_id :integer not null
+# created_at :datetime
+# updated_at :datetime
+#
+
class LfsObjectsProject < ActiveRecord::Base
belongs_to :project
belongs_to :lfs_object
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 080b7f7fb88..f6f77a16267 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -2,25 +2,28 @@
#
# Table name: merge_requests
#
-# id :integer not null, primary key
-# target_branch :string(255) not null
-# source_branch :string(255) not null
-# source_project_id :integer not null
-# author_id :integer
-# assignee_id :integer
-# title :string(255)
-# created_at :datetime
-# updated_at :datetime
-# milestone_id :integer
-# state :string(255)
-# merge_status :string(255)
-# target_project_id :integer not null
-# iid :integer
-# description :text
-# position :integer default(0)
-# locked_at :datetime
-# updated_by_id :integer
-# merge_error :string(255)
+# id :integer not null, primary key
+# target_branch :string(255) not null
+# source_branch :string(255) not null
+# source_project_id :integer not null
+# author_id :integer
+# assignee_id :integer
+# title :string(255)
+# created_at :datetime
+# updated_at :datetime
+# milestone_id :integer
+# state :string(255)
+# merge_status :string(255)
+# target_project_id :integer not null
+# iid :integer
+# description :text
+# position :integer default(0)
+# locked_at :datetime
+# updated_by_id :integer
+# merge_error :string(255)
+# merge_params :text (serialized to hash)
+# merge_when_build_succeeds :boolean default(false), not null
+# merge_user_id :integer
#
require Rails.root.join("app/models/commit")
@@ -35,9 +38,12 @@ class MergeRequest < ActiveRecord::Base
belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project"
belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project"
+ belongs_to :merge_user, class_name: "User"
has_one :merge_request_diff, dependent: :destroy
+ serialize :merge_params, Hash
+
after_create :create_merge_request_diff
after_update :update_merge_request_diff
@@ -121,6 +127,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?
validate :validate_branches
validate :validate_fork
@@ -258,6 +265,16 @@ class MergeRequest < ActiveRecord::Base
end
end
+ def can_cancel_merge_when_build_succeeds?(current_user)
+ can_be_merged_by?(current_user) || self.author == current_user
+ end
+
+ def can_remove_source_branch?(current_user)
+ !source_project.protected_branch?(source_branch) &&
+ !source_project.root_ref?(source_branch) &&
+ Ability.abilities.allowed?(current_user, :push_code, source_project)
+ end
+
def mr_and_commit_notes
# Fetch comments only from last 100 commits
commits_for_notes_limit = 100
@@ -393,6 +410,16 @@ class MergeRequest < ActiveRecord::Base
message
end
+ def reset_merge_when_build_succeeds
+ return unless merge_when_build_succeeds?
+
+ self.merge_when_build_succeeds = false
+ self.merge_user = nil
+ self.merge_params = nil
+
+ self.save
+ end
+
# Return array of possible target branches
# depends on target project of MR
def target_branches
@@ -480,8 +507,10 @@ class MergeRequest < ActiveRecord::Base
end
def ci_commit
- if last_commit and source_project
- source_project.ci_commit(last_commit.id)
- end
+ @ci_commit ||= source_project.ci_commit(last_commit.id) if last_commit && source_project
+ end
+
+ def broken?
+ self.commits.blank? || branch_missing? || cannot_be_merged?
end
end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 20b92e68d61..1c4e101cc10 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -23,19 +23,17 @@ class Namespace < ActiveRecord::Base
validates :owner, presence: true, unless: ->(n) { n.type == "Group" }
validates :name,
- presence: true, uniqueness: true,
length: { within: 0..255 },
- format: { with: Gitlab::Regex.namespace_name_regex,
- message: Gitlab::Regex.namespace_name_regex_message }
+ namespace_name: true,
+ presence: true,
+ uniqueness: true
validates :description, length: { within: 0..255 }
validates :path,
- uniqueness: { case_sensitive: false },
- presence: true,
length: { within: 1..255 },
- exclusion: { in: Gitlab::Blacklist.path },
- format: { with: Gitlab::Regex.namespace_regex,
- message: Gitlab::Regex.namespace_regex_message }
+ namespace: true,
+ presence: true,
+ uniqueness: { case_sensitive: false }
delegate :name, to: :owner, allow_nil: true, prefix: true
diff --git a/app/models/note.rb b/app/models/note.rb
index 239a0f77f8e..98c29ddc4cd 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -16,6 +16,7 @@
# system :boolean default(FALSE), not null
# st_diff :text
# updated_by_id :integer
+# is_award :boolean default(FALSE), not null
#
require 'carrierwave/orm/activerecord'
@@ -44,7 +45,7 @@ class Note < ActiveRecord::Base
validates :note, :project, presence: true
validates :note, uniqueness: { scope: [:author, :noteable_type, :noteable_id] }, if: ->(n) { n.is_award }
validates :note, inclusion: { in: Emoji.emojis_names }, if: ->(n) { n.is_award }
- validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true
+ validates :line_code, line_code: true, allow_blank: true
# Attachments are deprecated and are handled by Markdown uploader
validates :attachment, file_size: { maximum: :max_attachment_size }
diff --git a/app/models/project.rb b/app/models/project.rb
index 6010770a5f2..cb965ce1b9e 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -28,6 +28,7 @@
# import_type :string(255)
# import_source :string(255)
# commit_count :integer default(0)
+# import_error :text
#
require 'carrierwave/orm/activerecord'
@@ -152,7 +153,7 @@ class Project < ActiveRecord::Base
validates_uniqueness_of :name, scope: :namespace_id
validates_uniqueness_of :path, scope: :namespace_id
validates :import_url,
- format: { with: /\A#{URI.regexp(%w(ssh git http https))}\z/, message: 'should be a valid url' },
+ url: { protocols: %w(ssh git http https) },
if: :external_import?
validates :star_count, numericality: { greater_than_or_equal_to: 0 }
validate :check_limit, on: :create
diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb
index d31b12f539e..0a61ad96a0e 100644
--- a/app/models/project_services/bamboo_service.rb
+++ b/app/models/project_services/bamboo_service.rb
@@ -23,10 +23,7 @@ class BambooService < CiService
prop_accessor :bamboo_url, :build_key, :username, :password
- validates :bamboo_url,
- presence: true,
- format: { with: /\A#{URI.regexp}\z/ },
- if: :activated?
+ validates :bamboo_url, presence: true, url: true, if: :activated?
validates :build_key, presence: true, if: :activated?
validates :username,
presence: true,
@@ -84,7 +81,7 @@ class BambooService < CiService
def supported_events
%w(push)
end
-
+
def build_info(sha)
url = URI.parse("#{bamboo_url}/rest/api/latest/result?label=#{sha}")
diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb
index 06c3922593c..08e5ccb3855 100644
--- a/app/models/project_services/drone_ci_service.rb
+++ b/app/models/project_services/drone_ci_service.rb
@@ -19,14 +19,11 @@
#
class DroneCiService < CiService
-
+
prop_accessor :drone_url, :token, :enable_ssl_verification
- validates :drone_url,
- presence: true,
- format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }, if: :activated?
- validates :token,
- presence: true,
- if: :activated?
+
+ validates :drone_url, presence: true, url: true, if: :activated?
+ validates :token, presence: true, if: :activated?
after_save :compose_service_hook, if: :activated?
@@ -58,16 +55,16 @@ class DroneCiService < CiService
end
def merge_request_status_path(iid, sha = nil, ref = nil)
- url = [drone_url,
- "gitlab/#{project.namespace.path}/#{project.path}/pulls/#{iid}",
+ url = [drone_url,
+ "gitlab/#{project.namespace.path}/#{project.path}/pulls/#{iid}",
"?access_token=#{token}"]
URI.join(*url).to_s
end
def commit_status_path(sha, ref)
- url = [drone_url,
- "gitlab/#{project.namespace.path}/#{project.path}/commits/#{sha}",
+ url = [drone_url,
+ "gitlab/#{project.namespace.path}/#{project.path}/commits/#{sha}",
"?branch=#{URI::encode(ref.to_s)}&access_token=#{token}"]
URI.join(*url).to_s
@@ -114,15 +111,15 @@ class DroneCiService < CiService
end
def merge_request_page(iid, sha, ref)
- url = [drone_url,
+ url = [drone_url,
"gitlab/#{project.namespace.path}/#{project.path}/redirect/pulls/#{iid}"]
URI.join(*url).to_s
end
def commit_page(sha, ref)
- url = [drone_url,
- "gitlab/#{project.namespace.path}/#{project.path}/redirect/commits/#{sha}",
+ url = [drone_url,
+ "gitlab/#{project.namespace.path}/#{project.path}/redirect/commits/#{sha}",
"?branch=#{URI::encode(ref.to_s)}"]
URI.join(*url).to_s
@@ -163,10 +160,10 @@ class DroneCiService < CiService
end
def push_valid?(data)
- opened_merge_requests = project.merge_requests.opened.where(source_project_id: project.id,
+ opened_merge_requests = project.merge_requests.opened.where(source_project_id: project.id,
source_branch: Gitlab::Git.ref_name(data[:ref]))
- opened_merge_requests.empty? && data[:total_commits_count] > 0 &&
+ opened_merge_requests.empty? && data[:total_commits_count] > 0 &&
!Gitlab::Git.blank_ref?(data[:after])
end
diff --git a/app/models/project_services/external_wiki_service.rb b/app/models/project_services/external_wiki_service.rb
index 9c46af7e721..74c57949b4d 100644
--- a/app/models/project_services/external_wiki_service.rb
+++ b/app/models/project_services/external_wiki_service.rb
@@ -22,10 +22,8 @@ class ExternalWikiService < Service
include HTTParty
prop_accessor :external_wiki_url
- validates :external_wiki_url,
- presence: true,
- format: { with: /\A#{URI.regexp}\z/ },
- if: :activated?
+
+ validates :external_wiki_url, presence: true, url: true, if: :activated?
def title
'External Wiki'
diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb
index 0b022461250..29d4236745a 100644
--- a/app/models/project_services/teamcity_service.rb
+++ b/app/models/project_services/teamcity_service.rb
@@ -23,16 +23,16 @@ class TeamcityService < CiService
prop_accessor :teamcity_url, :build_type, :username, :password
- validates :teamcity_url,
- presence: true,
- format: { with: /\A#{URI.regexp}\z/ }, if: :activated?
+ validates :teamcity_url, presence: true, url: true, if: :activated?
validates :build_type, presence: true, if: :activated?
validates :username,
presence: true,
- if: ->(service) { service.password? }, if: :activated?
+ if: ->(service) { service.password? },
+ if: :activated?
validates :password,
presence: true,
- if: ->(service) { service.username? }, if: :activated?
+ if: ->(service) { service.username? },
+ if: :activated?
attr_accessor :response
diff --git a/app/models/repository.rb b/app/models/repository.rb
index c304955b0b3..1edec52c09e 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -100,11 +100,11 @@ class Repository
end
def find_branch(name)
- branches.find { |branch| branch.name == name }
+ raw_repository.branches.find { |branch| branch.name == name }
end
def find_tag(name)
- tags.find { |tag| tag.name == name }
+ raw_repository.tags.find { |tag| tag.name == name }
end
def add_branch(user, branch_name, target)
@@ -329,6 +329,17 @@ class Repository
commit(sha)
end
+ def next_patch_branch
+ patch_branch_ids = self.branch_names.map do |n|
+ result = n.match(/\Apatch-([0-9]+)\z/)
+ result[1].to_i if result
+ end.compact
+
+ highest_patch_branch_id = patch_branch_ids.max || 0
+
+ "patch-#{highest_patch_branch_id + 1}"
+ end
+
# Remove archives older than 2 hours
def branches_sorted_by(value)
case value
diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb
index d8fe65b06f6..f36eda1531b 100644
--- a/app/models/sent_notification.rb
+++ b/app/models/sent_notification.rb
@@ -21,7 +21,7 @@ class SentNotification < ActiveRecord::Base
validates :reply_key, uniqueness: true
validates :noteable_id, presence: true, unless: :for_commit?
validates :commit_id, presence: true, if: :for_commit?
- validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true
+ validates :line_code, line_code: true, allow_blank: true
class << self
def reply_key
diff --git a/app/models/user.rb b/app/models/user.rb
index 719b49b16fe..7155dd2bea7 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -56,6 +56,7 @@
# project_view :integer default(0)
# consumed_timestep :integer
# layout :integer default(0)
+# hide_project_limit :boolean default(FALSE)
#
require 'carrierwave/orm/activerecord'
@@ -148,11 +149,9 @@ class User < ActiveRecord::Base
validates :bio, length: { maximum: 255 }, allow_blank: true
validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 }
validates :username,
+ namespace: true,
presence: true,
- uniqueness: { case_sensitive: false },
- exclusion: { in: Gitlab::Blacklist.path },
- format: { with: Gitlab::Regex.namespace_regex,
- message: Gitlab::Regex.namespace_regex_message }
+ uniqueness: { case_sensitive: false }
validates :notification_level, inclusion: { in: Notification.notification_levels }, presence: true
validate :namespace_uniq, if: ->(user) { user.username_changed? }
diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb
index f50aaf2eb52..9a67b160940 100644
--- a/app/services/files/base_service.rb
+++ b/app/services/files/base_service.rb
@@ -53,7 +53,7 @@ module Files
unless project.empty_repo?
unless repository.branch_names.include?(@current_branch)
- raise_error("You can only create files if you are on top of a branch")
+ raise_error("You can only create or edit files when you are on a branch")
end
if @current_branch != @target_branch
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index d619b72e3c2..cabc3d8fabb 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -6,15 +6,12 @@ module MergeRequests
# Executed when you do merge via GitLab UI
#
class MergeService < MergeRequests::BaseService
- attr_reader :merge_request, :commit_message
+ attr_reader :merge_request
- def execute(merge_request, commit_message)
- @commit_message = commit_message
+ def execute(merge_request)
@merge_request = merge_request
- unless @merge_request.mergeable?
- return error('Merge request is not mergeable')
- end
+ return error('Merge request is not mergeable') unless @merge_request.mergeable?
merge_request.in_locked_state do
if commit
@@ -32,7 +29,7 @@ module MergeRequests
committer = repository.user_to_committer(current_user)
options = {
- message: commit_message,
+ message: params[:commit_message] || merge_request.merge_commit_message,
author: committer,
committer: committer
}
@@ -46,6 +43,11 @@ module MergeRequests
def after_merge
MergeRequests::PostMergeService.new(project, current_user).execute(merge_request)
+
+ if params[:should_remove_source_branch]
+ DeleteBranchService.new(@merge_request.source_project, current_user).
+ execute(merge_request.source_branch)
+ end
end
end
end
diff --git a/app/services/merge_requests/merge_when_build_succeeds_service.rb b/app/services/merge_requests/merge_when_build_succeeds_service.rb
new file mode 100644
index 00000000000..5cf7404a493
--- /dev/null
+++ b/app/services/merge_requests/merge_when_build_succeeds_service.rb
@@ -0,0 +1,55 @@
+module MergeRequests
+ class MergeWhenBuildSucceedsService < MergeRequests::BaseService
+ # Marks the passed `merge_request` to be merged when the build 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?
+
+ unless already_approved
+ merge_request.merge_when_build_succeeds = true
+ merge_request.merge_user = @current_user
+
+ SystemNoteService.merge_when_build_succeeds(merge_request, @project, @current_user, merge_request.last_commit)
+ end
+
+ merge_request.save
+ end
+
+ # Triggers the automatic merge of merge_request once the build succeeds
+ def trigger(build)
+ merge_requests = merge_request_from(build)
+
+ merge_requests.each do |merge_request|
+ next unless merge_request.merge_when_build_succeeds?
+
+ if merge_request.ci_commit && merge_request.ci_commit.success? && merge_request.mergeable?
+ MergeWorker.perform_async(merge_request.id, merge_request.merge_user_id, merge_request.merge_params)
+ end
+ end
+ end
+
+ # 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)
+
+ success
+ else
+ error("Can't cancel the automatic merge", 406)
+ end
+ end
+
+ private
+
+ def merge_request_from(build)
+ merge_requests = @project.origin_merge_requests.opened.where(source_branch: build.ref).to_a
+ merge_requests += @project.fork_merge_requests.opened.where(source_branch: build.ref).to_a
+
+ merge_requests.uniq.select(&:source_project)
+ end
+ end
+end
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index e180edb4bf3..b26c7513f5b 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -11,6 +11,7 @@ module MergeRequests
# empty diff during a manual merge
close_merge_requests
reload_merge_requests
+ reset_merge_when_build_succeeds
# Leave a system note if a branch was deleted/added
if branch_added? || branch_removed?
@@ -57,7 +58,6 @@ module MergeRequests
merge_requests = filter_merge_requests(merge_requests)
merge_requests.each do |merge_request|
-
if merge_request.source_branch == @branch_name || force_push?
merge_request.reload_code
merge_request.mark_as_unchecked
@@ -76,6 +76,10 @@ module MergeRequests
end
end
+ def reset_merge_when_build_succeeds
+ merge_requests_for_source_branch.each(&:reset_merge_when_build_succeeds)
+ end
+
def find_new_commits
if branch_added?
@commits = []
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 09c159510cd..6975b2ee55b 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -130,6 +130,20 @@ class SystemNoteService
create_note(noteable: noteable, project: project, author: author, note: body)
end
+ # Called when 'merge when build succeeds' is executed
+ def self.merge_when_build_succeeds(noteable, project, author, last_commit)
+ body = "Enabled an automatic merge when the build for #{last_commit.to_reference(project)} succeeds"
+
+ create_note(noteable: noteable, project: project, author: author, note: body)
+ end
+
+ # Called when 'merge when build succeeds' is canceled
+ def self.cancel_merge_when_build_succeeds(noteable, project, author)
+ body = "Canceled the automatic merge"
+
+ create_note(noteable: noteable, project: project, author: author, note: body)
+ end
+
# Called when the title of a Noteable is changed
#
# noteable - Noteable object that responds to `title`
diff --git a/app/validators/color_validator.rb b/app/validators/color_validator.rb
new file mode 100644
index 00000000000..571d0007aa2
--- /dev/null
+++ b/app/validators/color_validator.rb
@@ -0,0 +1,20 @@
+# ColorValidator
+#
+# Custom validator for web color codes. It requires the leading hash symbol and
+# will accept RGB triplet or hexadecimal formats.
+#
+# Example:
+#
+# class User < ActiveRecord::Base
+# validates :background_color, allow_blank: true, color: true
+# end
+#
+class ColorValidator < ActiveModel::EachValidator
+ PATTERN = /\A\#[0-9A-Fa-f]{3}{1,2}+\Z/.freeze
+
+ def validate_each(record, attribute, value)
+ unless value =~ PATTERN
+ record.errors.add(attribute, "must be a valid color code")
+ end
+ end
+end
diff --git a/lib/email_validator.rb b/app/validators/email_validator.rb
index f509f0a5843..b35af100803 100644
--- a/lib/email_validator.rb
+++ b/app/validators/email_validator.rb
@@ -1,3 +1,5 @@
+# EmailValidator
+#
# Based on https://github.com/balexand/email_validator
#
# Extended to use only strict mode with following allowed characters:
@@ -6,15 +8,10 @@
# See http://www.remote.org/jochen/mail/info/chars.html
#
class EmailValidator < ActiveModel::EachValidator
- @@default_options = {}
-
- def self.default_options
- @@default_options
- end
+ PATTERN = /\A\s*([-a-z0-9+._']{1,64})@((?:[-a-z0-9]+\.)+[a-z]{2,})\s*\z/i.freeze
def validate_each(record, attribute, value)
- options = @@default_options.merge(self.options)
- unless value =~ /\A\s*([-a-z0-9+._']{1,64})@((?:[-a-z0-9]+\.)+[a-z]{2,})\s*\z/i
+ unless value =~ PATTERN
record.errors.add(attribute, options[:message] || :invalid)
end
end
diff --git a/app/validators/line_code_validator.rb b/app/validators/line_code_validator.rb
new file mode 100644
index 00000000000..ed29e5aeb67
--- /dev/null
+++ b/app/validators/line_code_validator.rb
@@ -0,0 +1,12 @@
+# LineCodeValidator
+#
+# Custom validator for GitLab line codes.
+class LineCodeValidator < ActiveModel::EachValidator
+ PATTERN = /\A[a-z0-9]+_\d+_\d+\z/.freeze
+
+ def validate_each(record, attribute, value)
+ unless value =~ PATTERN
+ record.errors.add(attribute, "must be a valid line code")
+ end
+ end
+end
diff --git a/app/validators/namespace_name_validator.rb b/app/validators/namespace_name_validator.rb
new file mode 100644
index 00000000000..2e51af2982d
--- /dev/null
+++ b/app/validators/namespace_name_validator.rb
@@ -0,0 +1,10 @@
+# NamespaceNameValidator
+#
+# Custom validator for GitLab namespace name strings.
+class NamespaceNameValidator < ActiveModel::EachValidator
+ def validate_each(record, attribute, value)
+ unless value =~ Gitlab::Regex.namespace_name_regex
+ record.errors.add(attribute, Gitlab::Regex.namespace_name_regex_message)
+ end
+ end
+end
diff --git a/app/validators/namespace_validator.rb b/app/validators/namespace_validator.rb
new file mode 100644
index 00000000000..10e35ce665a
--- /dev/null
+++ b/app/validators/namespace_validator.rb
@@ -0,0 +1,50 @@
+# NamespaceValidator
+#
+# Custom validator for GitLab namespace values.
+#
+# Values are checked for formatting and exclusion from a list of reserved path
+# names.
+class NamespaceValidator < ActiveModel::EachValidator
+ RESERVED = %w(
+ admin
+ all
+ assets
+ ci
+ dashboard
+ files
+ groups
+ help
+ hooks
+ issues
+ merge_requests
+ notes
+ profile
+ projects
+ public
+ repository
+ s
+ search
+ services
+ snippets
+ teams
+ u
+ unsubscribes
+ users
+ ).freeze
+
+ def validate_each(record, attribute, value)
+ unless value =~ Gitlab::Regex.namespace_regex
+ record.errors.add(attribute, Gitlab::Regex.namespace_regex_message)
+ end
+
+ if reserved?(value)
+ record.errors.add(attribute, "#{value} is a reserved name")
+ end
+ end
+
+ private
+
+ def reserved?(value)
+ RESERVED.include?(value)
+ end
+end
diff --git a/app/validators/url_validator.rb b/app/validators/url_validator.rb
new file mode 100644
index 00000000000..2848b9cd33d
--- /dev/null
+++ b/app/validators/url_validator.rb
@@ -0,0 +1,36 @@
+# UrlValidator
+#
+# Custom validator for URLs.
+#
+# By default, only URLs for the HTTP(S) protocols will be considered valid.
+# Provide a `:protocols` option to configure accepted protocols.
+#
+# Example:
+#
+# class User < ActiveRecord::Base
+# validates :personal_url, url: true
+#
+# validates :ftp_url, url: { protocols: %w(ftp) }
+#
+# validates :git_url, url: { protocols: %w(http https ssh git) }
+# end
+#
+class UrlValidator < ActiveModel::EachValidator
+ def validate_each(record, attribute, value)
+ unless valid_url?(value)
+ record.errors.add(attribute, "must be a valid URL")
+ end
+ end
+
+ private
+
+ def default_options
+ @default_options ||= { protocols: %w(http https) }
+ end
+
+ def valid_url?(value)
+ options = default_options.merge(self.options)
+
+ value =~ /\A#{URI.regexp(options[:protocols])}\z/
+ end
+end
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index ddaf0e0e8ff..6c355366948 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -14,11 +14,11 @@
.form-group.project-visibility-level-holder
= f.label :default_project_visibility, class: 'control-label col-sm-2'
.col-sm-10
- = render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: 'Project')
+ = render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: Project)
.form-group.project-visibility-level-holder
= f.label :default_snippet_visibility, class: 'control-label col-sm-2'
.col-sm-10
- = render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: 'Snippet')
+ = render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: PersonalSnippet)
.form-group
= f.label :restricted_visibility_levels, class: 'control-label col-sm-2'
.col-sm-10
diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml
index 991e67b1cd3..2e77afb7525 100644
--- a/app/views/dashboard/_projects_head.html.haml
+++ b/app/views/dashboard/_projects_head.html.haml
@@ -2,7 +2,7 @@
= render 'shared/project_limit'
%ul.center-top-menu
- = nav_link(path: ['projects#index', 'root#index']) do
+ = nav_link(page: [dashboard_projects_path, root_path]) do
= link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
Your Projects
= nav_link(page: starred_dashboard_projects_path) do
diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml
index cd602e897b7..2d3da01178a 100644
--- a/app/views/dashboard/issues.html.haml
+++ b/app/views/dashboard/issues.html.haml
@@ -4,14 +4,20 @@
- if current_user
= auto_discovery_link_tag(:atom, issues_dashboard_url(format: :atom, private_token: current_user.private_token), title: "#{current_user.name} issues")
+.project-issuable-filter
+ .controls
+ .pull-left
+ - if current_user
+ .hidden-xs.pull-left
+ = link_to issues_dashboard_url(format: :atom, private_token: current_user.private_token), class: 'btn' do
+ %i.fa.fa-rss
-.append-bottom-20
- .pull-right
- - if current_user
- .hidden-xs.pull-left.prepend-top-20
- = link_to issues_dashboard_url(format: :atom, private_token: current_user.private_token), class: '' do
- %i.fa.fa-rss
+ = render 'shared/new_project_item_select', path: 'issues/new', label: "New Issue"
= render 'shared/issuable/filter', type: :issues
-= render 'shared/issues'
+.gray-content-block.second-block
+ List all issues from all projects you have access to.
+
+.prepend-top-default
+ = render 'shared/issues'
diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml
index d1f332fa0d3..c5a5ec21f78 100644
--- a/app/views/dashboard/merge_requests.html.haml
+++ b/app/views/dashboard/merge_requests.html.haml
@@ -1,6 +1,14 @@
- page_title "Merge Requests"
- header_title "Merge Requests", merge_requests_dashboard_path(assignee_id: current_user.id)
-.append-bottom-20
+.project-issuable-filter
+ .controls
+ = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New Merge Request"
+
= render 'shared/issuable/filter', type: :merge_requests
-= render 'shared/merge_requests'
+
+.gray-content-block.second-block
+ List all merge requests from all projects you have access to.
+
+.prepend-top-default
+ = render 'shared/merge_requests'
diff --git a/app/views/dashboard/milestones/index.html.haml b/app/views/dashboard/milestones/index.html.haml
index 635251e2374..bec1692a4de 100644
--- a/app/views/dashboard/milestones/index.html.haml
+++ b/app/views/dashboard/milestones/index.html.haml
@@ -1,12 +1,14 @@
- page_title "Milestones"
-- header_title "Milestones", dashboard_milestones_path
+- header_title "Milestones", dashboard_milestones_path
+.project-issuable-filter
+ .controls
+ = render 'shared/new_project_item_select', path: 'milestones/new', label: "New Milestone", include_groups: true
-= render 'shared/milestones_filter'
+ = render 'shared/milestones_filter'
.gray-content-block
- .oneline
- List all milestones from all projects you have access to.
+ List all milestones from all projects you have access to.
.milestones
%ul.content-list
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index 08d97e418a3..90ade1e1680 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -4,21 +4,24 @@
- if current_user
= auto_discovery_link_tag(:atom, issues_group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} issues")
+.project-issuable-filter
+ .controls
+ .pull-left
+ - if current_user
+ .hidden-xs.pull-left
+ = link_to issues_group_url(@group, format: :atom, private_token: current_user.private_token), class: 'btn' do
+ %i.fa.fa-rss
+ = render 'shared/new_project_item_select', path: 'issues/new', label: "New Issue"
+
+ = render 'shared/issuable/filter', type: :issues
-= render 'shared/issuable/filter', type: :issues
.gray-content-block.second-block
- .pull-right
- - if current_user
- .hidden-xs.pull-left
- = link_to issues_group_url(@group, format: :atom, private_token: current_user.private_token) do
- %i.fa.fa-rss
- %div
- Only issues from
- %strong #{@group.name}
- group are listed here.
- - if current_user
- To see all issues you should visit #{link_to 'dashboard', issues_dashboard_path} page.
+ Only issues from
+ %strong #{@group.name}
+ group are listed here.
+ - if current_user
+ To see all issues you should visit #{link_to 'dashboard', issues_dashboard_path} page.
.prepend-top-default
= render 'shared/issues'
diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml
index 425ad8331bf..f662f5a8c17 100644
--- a/app/views/groups/merge_requests.html.haml
+++ b/app/views/groups/merge_requests.html.haml
@@ -1,13 +1,18 @@
- page_title "Merge Requests"
- header_title group_title(@group, "Merge Requests", merge_requests_group_path(@group))
-= render 'shared/issuable/filter', type: :merge_requests
+.project-issuable-filter
+ .controls
+ = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New Merge Request"
+
+ = render 'shared/issuable/filter', type: :merge_requests
+
.gray-content-block.second-block
- %div
- Only merge requests from
- %strong #{@group.name}
- group are listed here.
- - if current_user
- To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page.
+ Only merge requests from
+ %strong #{@group.name}
+ group are listed here.
+ - if current_user
+ To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page.
+
.prepend-top-default
= render 'shared/merge_requests'
diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml
index 84ec77c6188..b221d3a89a4 100644
--- a/app/views/groups/milestones/index.html.haml
+++ b/app/views/groups/milestones/index.html.haml
@@ -1,18 +1,22 @@
- page_title "Milestones"
- header_title group_title(@group, "Milestones", group_milestones_path(@group))
-= render 'shared/milestones_filter'
+.project-issuable-filter
+ .controls
+ - if can?(current_user, :admin_milestones, @group)
+ .pull-right
+ %span.pull-right.hidden-xs
+ = link_to new_group_milestone_path(@group), class: "btn btn-new" do
+ = icon('plus')
+ New Milestone
+
+ = render 'shared/milestones_filter'
+
.gray-content-block
- - if can?(current_user, :admin_milestones, @group)
- .pull-right
- %span.pull-right.hidden-xs
- = link_to new_group_milestone_path(@group), class: "btn btn-new" do
- New Milestone
+ Only milestones from
+ %strong #{@group.name}
+ group are listed here.
- .oneline
- Only milestones from
- %strong #{@group.name}
- group are listed here.
.milestones
%ul.content-list
- if @milestones.blank?
diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml
index 2169a821fb2..d9ffda884c8 100644
--- a/app/views/help/ui.html.haml
+++ b/app/views/help/ui.html.haml
@@ -31,11 +31,9 @@
%h2#blocks Blocks
- %h3
+ %h4
%code .gray-content-block
-
-
.gray-content-block.middle-block
%h4 Normal block inside content
= lorem
@@ -45,9 +43,28 @@
= lorem
+ %h4
+ %code .cover-block
+ %br
+ .cover-block
+ .avatar-holder
+ = image_tag avatar_icon('admin@example.com', 90), class: "avatar s90", alt: ''
+ .cover-title
+ John Smith
+
+ .cover-desc
+ = lorem
+
+ .cover-controls
+ = link_to '#', class: 'btn btn-gray' do
+ = icon('pencil')
+ &nbsp;
+ = link_to '#', class: 'btn btn-gray' do
+ = icon('rss')
+
%h2#lists Lists
- %h3
+ %h4
%code .content-list
%ul.content-list
%li
@@ -57,7 +74,7 @@
%li
One item
- %h3
+ %h4
%code .well-list
%ul.well-list
%li
@@ -67,7 +84,7 @@
%li
One item
- %h3
+ %h4
%code .panel .well-list
.panel.panel-default
@@ -80,7 +97,7 @@
%li
One item
- %h3
+ %h4
%code .bordered-list
%ul.bordered-list
%li
@@ -121,7 +138,7 @@
%h2#navs Navigation
- %h3
+ %h4
%code .center-top-menu
.example
%ul.center-top-menu
@@ -130,7 +147,7 @@
%li
%a Closed
- %h3
+ %h4
%code .btn-group.btn-group-next
.example
%div.btn-group.btn-group-next
@@ -138,7 +155,7 @@
%a.btn Closed
- %h3
+ %h4
%code .nav.nav-tabs
.example
%ul.nav.nav-tabs
@@ -204,7 +221,7 @@
%h2#forms Forms
- %h3
+ %h4
%code form.horizontal-form
%form.form-horizontal
@@ -226,7 +243,7 @@
.col-sm-offset-2.col-sm-10
%button.btn.btn-default{:type => "submit"} Sign in
- %h3
+ %h4
%code form
%form
@@ -243,7 +260,7 @@
%button.btn.btn-default{:type => "submit"} Sign in
%h2#file File
- %h3
+ %h4
%code .file-holder
- blob = Snippet.new(content: "Wow\nSuch\nFile")
@@ -254,13 +271,12 @@
.file-actions
.btn-group
%a.btn Edit
- %a.btn Remove
+ %a.btn.btn-danger Remove
.file-contenta.code
= render 'shared/file_highlight', blob: blob
-
%h2#markdown Markdown
- %h3
+ %h4
%code .md or .wiki and others
Markdown rendering has a bit different css and presented in next UI elements:
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index 319bdd57c39..17e47c622ce 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -26,11 +26,11 @@
- else
%span You don`t have one yet. Click generate to fix it.
- .form-actions
- - if current_user.private_token
- = f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default btn-build-token"
- - else
- = f.submit 'Generate', class: "btn btn-default btn-build-token"
+ .form-actions
+ - if current_user.private_token
+ = f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default"
+ - else
+ = f.submit 'Generate', class: "btn btn-default"
- unless current_user.ldap_user?
.panel.panel-default
diff --git a/app/views/profiles/keys/new.html.haml b/app/views/profiles/keys/new.html.haml
index 2bf207a3221..11166dc6d99 100644
--- a/app/views/profiles/keys/new.html.haml
+++ b/app/views/profiles/keys/new.html.haml
@@ -9,7 +9,7 @@
$('#key_key').on('focusout', function(){
var title = $('#key_title'),
val = $('#key_key').val(),
- comment = val.match(/^\S+ \S+ (.+)$/);
+ comment = val.match(/^\S+ \S+ (.+)\n?$/);
if( comment && comment.length > 1 && title.val() == '' ){
$('#key_title').val( comment[1] );
diff --git a/app/views/projects/_last_commit.html.haml b/app/views/projects/_last_commit.html.haml
index 7e1ee2b7fc1..386d72e7787 100644
--- a/app/views/projects/_last_commit.html.haml
+++ b/app/views/projects/_last_commit.html.haml
@@ -3,7 +3,7 @@
- if ci_commit
= link_to ci_status_path(ci_commit), class: "ci-status ci-#{ci_commit.status}" do
= ci_status_icon(ci_commit)
- = ci_commit.status
+ = ci_status_label(ci_commit)
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
= link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit), class: "commit-row-message"
diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml
index ba3e0c3c590..b1df8d19938 100644
--- a/app/views/projects/blob/_actions.html.haml
+++ b/app/views/projects/blob/_actions.html.haml
@@ -1,9 +1,8 @@
.btn-group.tree-btn-group
- = edit_blob_link(@project, @ref, @path)
= 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
- - if @blob.text?
+ - if blob_viewable?(@blob)
- if current_page? namespace_project_blame_path(@project.namespace, @project, @id)
= link_to 'Normal View', namespace_project_blob_path(@project.namespace, @project, @id),
class: 'btn btn-sm'
@@ -12,11 +11,16 @@
class: 'btn btn-sm' unless @blob.empty?
= link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id),
class: 'btn btn-sm'
- - if @ref != @commit.sha
- = link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project,
- tree_join(@commit.sha, @path)), class: 'btn btn-sm'
+ = link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project,
+ tree_join(@commit.sha, @path)), class: 'btn btn-sm'
-- if allowed_tree_edit?
+- if blob_editable?(@blob)
.btn-group{ role: "group" }
+ = edit_blob_link(@project, @ref, @path)
%button.btn.btn-default{ 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } Replace
%button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Delete
+- elsif !on_top_of_branch?
+ .btn-group{ role: "group" }
+ %button.btn.btn-default.disabled.has_tooltip{title: "You can only edit files when you are on a branch.", data: {container: 'body'}} Edit
+ %button.btn.btn-default.disabled.has_tooltip{title: "You can only replace files when you are on a branch.", data: {container: 'body'}} Replace
+ %button.btn.btn-remove.disabled.has_tooltip{title: "You can only delete files when you are on a branch.", data: {container: 'body'}} Delete
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index 42f632b38ef..2a3315da3db 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -29,10 +29,12 @@
%strong
= blob.name
%small
- = number_to_human_size(blob.size)
+ = number_to_human_size(blob_size(blob))
.file-actions.hidden-xs
= render "actions"
- - if blob.text?
+ - if blob.lfs_pointer?
+ = render "download", blob: blob
+ - elsif blob.text?
= render "text", blob: blob
- elsif blob.image?
= render "image", blob: blob
diff --git a/app/views/projects/blob/_download.html.haml b/app/views/projects/blob/_download.html.haml
index f2c5e95ecf4..7908fcae3de 100644
--- a/app/views/projects/blob/_download.html.haml
+++ b/app/views/projects/blob/_download.html.haml
@@ -4,4 +4,4 @@
%h1.light
%i.fa.fa-download
%h4
- Download (#{number_to_human_size blob.size})
+ Download (#{number_to_human_size blob_size(blob)})
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index b7276868ce6..3f8d11ed8c8 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -6,7 +6,7 @@
%div#tree-holder.tree-holder
= render 'blob', blob: @blob
-- if allowed_tree_edit?
+- if blob_editable?(@blob)
= render 'projects/blob/remove'
- title = "Replace #{@blob.name}"
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index 3f95e2a1bf6..5081bae6801 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -3,17 +3,17 @@
%div
= link_to namespace_project_tree_path(@project.namespace, @project, branch.name) do
%strong.str-truncated= branch.name
- &nbsp;
- - if branch.name == @repository.root_ref
- %span.label.label-primary default
- - elsif @repository.merged_to_root_ref? branch.name
- %span.label.label-info.has_tooltip(title="Merged into #{@repository.root_ref}")
- merged
+ &nbsp;
+ - if branch.name == @repository.root_ref
+ %span.label.label-primary default
+ - elsif @repository.merged_to_root_ref? branch.name
+ %span.label.label-info.has_tooltip(title="Merged into #{@repository.root_ref}")
+ merged
- - if @project.protected_branch? branch.name
- %span.label.label-success
- %i.fa.fa-lock
- protected
+ - if @project.protected_branch? branch.name
+ %span.label.label-success
+ %i.fa.fa-lock
+ protected
.controls.hidden-xs
- if create_mr_button?(@repository.root_ref, branch.name)
= link_to create_mr_path(@repository.root_ref, branch.name), class: 'btn btn-grouped btn-xs' do
@@ -26,7 +26,7 @@
Compare
- if can_remove_branch?(@project, branch.name)
- = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row', method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?" }, remote: true do
+ = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has_tooltip', title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do
= icon("trash-o")
- if commit
diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml
index dab7164153f..742676305a9 100644
--- a/app/views/projects/builds/index.html.haml
+++ b/app/views/projects/builds/index.html.haml
@@ -3,10 +3,10 @@
.project-issuable-filter
.controls
- - if @ci_project && current_user && can?(current_user, :manage_builds, @project)
+ - if @ci_project && can?(current_user, :manage_builds, @project)
.pull-left.hidden-xs
- if @all_builds.running_or_pending.any?
- = link_to 'Cancel all', cancel_all_namespace_project_builds_path(@project.namespace, @project), data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
+ = link_to 'Cancel running', cancel_all_namespace_project_builds_path(@project.namespace, @project), data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
%ul.center-top-menu
%li{class: ('active' if @scope.nil?)}
@@ -50,4 +50,3 @@
= render 'projects/commit_statuses/commit_status', commit_status: build, commit_sha: true, stage: true, allow_retry: true
= paginate @builds, theme: 'gitlab'
-
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index 907e1ce10bd..d5e81f84b56 100644
--- a/app/views/projects/builds/show.html.haml
+++ b/app/views/projects/builds/show.html.haml
@@ -1,17 +1,16 @@
-- page_title "#{@build.name} (#{@build.id})", "Builds"
+- page_title "#{@build.name} (##{@build.id})", "Builds"
= render "header_title"
.build-page
- .gray-content-block
+ .gray-content-block.top-block
Build ##{@build.id} for commit
- %strong.monospace
- = link_to @build.commit.short_sha, ci_status_path(@build.commit)
+ %strong.monospace= link_to @build.commit.short_sha, ci_status_path(@build.commit)
from
= link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref)
#up-build-trace
- if @commit.matrix_for_ref?(@build.ref)
- %ul.center-top-menu.build-top-menu
+ %ul.center-top-menu.no-top.no-bottom
- @commit.latest_builds_for_ref(@build.ref).each do |build|
%li{class: ('active' if build == @build) }
= link_to namespace_project_build_path(@project.namespace, @project, build) do
@@ -22,7 +21,6 @@
- else
= build.id
-
- if @build.retried?
%li.active
%a
@@ -31,7 +29,7 @@
%i.fa.fa-warning
This build was retried.
- .gray-content-block.second-block
+ .gray-content-block.middle-block
.build-head
.clearfix
= ci_status_with_icon(@build.status)
@@ -140,7 +138,7 @@
%h4.title
Commit
.pull-right
- %small
+ %small
= link_to @build.commit.short_sha, ci_status_path(@build.commit), class: "monospace"
%p
%span.attr-name Branch:
@@ -162,7 +160,7 @@
- if @builds.present?
.build-widget
- %h4.title #{pluralize(@builds.count(:id), "other build")} for
+ %h4.title #{pluralize(@builds.count(:id), "other build")} for
= succeed ":" do
= link_to @build.commit.short_sha, ci_status_path(@build.commit), class: "monospace"
%table.table.builds
diff --git a/app/views/projects/commit/_builds.html.haml b/app/views/projects/commit/_builds.html.haml
new file mode 100644
index 00000000000..e4d81182c1a
--- /dev/null
+++ b/app/views/projects/commit/_builds.html.haml
@@ -0,0 +1,67 @@
+.gray-content-block.middle-block
+ .pull-right
+ - if @ci_project && can?(current_user, :manage_builds, @ci_commit.gl_project)
+ - if @ci_commit.builds.latest.failed.any?(&:retryable?)
+ = link_to "Retry failed", retry_builds_namespace_project_commit_path(@ci_commit.gl_project.namespace, @ci_commit.gl_project, @ci_commit.sha), class: 'btn btn-grouped btn-primary', method: :post
+
+ - if @ci_commit.builds.running_or_pending.any?
+ = link_to "Cancel running", cancel_builds_namespace_project_commit_path(@ci_commit.gl_project.namespace, @ci_commit.gl_project, @ci_commit.sha), data: { confirm: 'Are you sure?' }, class: 'btn btn-grouped btn-danger', method: :post
+
+ .oneline
+ = pluralize @statuses.count(:id), "build"
+ - if defined?(link_to_commit) && link_to_commit
+ for commit
+ = link_to @ci_commit.short_sha, namespace_project_commit_path(@ci_commit.gl_project.namespace, @ci_commit.gl_project, @ci_commit.sha), class: "monospace"
+ - if @ci_commit.duration > 0
+ in
+ = time_interval_in_words @ci_commit.duration
+
+- if @ci_commit.yaml_errors.present?
+ .bs-callout.bs-callout-danger
+ %h4 Found errors in your .gitlab-ci.yml:
+ %ul
+ - @ci_commit.yaml_errors.split(",").each do |error|
+ %li= error
+
+- if @ci_commit.gl_project.builds_enabled? && !@ci_commit.ci_yaml_file
+ .bs-callout.bs-callout-warning
+ \.gitlab-ci.yml not found in this commit
+
+.table-holder
+ %table.table.builds
+ %thead
+ %tr
+ %th Status
+ %th Build ID
+ %th Ref
+ %th Stage
+ %th Name
+ %th Duration
+ %th Finished at
+ - if @ci_project && @ci_project.coverage_enabled?
+ %th Coverage
+ %th
+ - @ci_commit.refs.each do |ref|
+ = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.statuses.for_ref(ref).latest.ordered,
+ locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true, allow_retry: true }
+
+- if @ci_commit.retried.any?
+ .gray-content-block.second-block
+ Retried builds
+
+ .table-holder
+ %table.table.builds
+ %thead
+ %tr
+ %th Status
+ %th Build ID
+ %th Ref
+ %th Stage
+ %th Name
+ %th Duration
+ %th Finished at
+ - if @ci_project && @ci_project.coverage_enabled?
+ %th Coverage
+ %th
+ = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried,
+ locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true }
diff --git a/app/views/projects/commit/_ci_menu.html.haml b/app/views/projects/commit/_ci_menu.html.haml
index 76dc87a8824..f74f8b427ec 100644
--- a/app/views/projects/commit/_ci_menu.html.haml
+++ b/app/views/projects/commit/_ci_menu.html.haml
@@ -6,4 +6,4 @@
= nav_link(path: 'commit#builds') do
= link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id) do
Builds
- %span.badge= @builds.count(:id)
+ %span.badge= @statuses.count
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index d8bfe6a07ac..bb37e4a7049 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -20,7 +20,8 @@
%p
%span.light Commit
- = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace"
+ = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace", data: { clipboard_text: @commit.id }
+ = clipboard_button
.commit-info-row
%span.light Authored by
%strong
@@ -44,7 +45,7 @@
= link_to ci_status_path(@ci_commit), class: "ci-status ci-#{@ci_commit.status}" do
= ci_status_icon(@ci_commit)
build:
- = @ci_commit.status
+ = ci_status_label(@ci_commit)
.commit-info-row.branches
%i.fa.fa-spinner.fa-spin
diff --git a/app/views/projects/commit/builds.html.haml b/app/views/projects/commit/builds.html.haml
index 00cf9c76102..99d62503a94 100644
--- a/app/views/projects/commit/builds.html.haml
+++ b/app/views/projects/commit/builds.html.haml
@@ -3,70 +3,4 @@
= render "commit_box"
= render "ci_menu"
-
-- if @ci_commit.yaml_errors.present?
- .bs-callout.bs-callout-danger
- %h4 Found errors in your .gitlab-ci.yml:
- %ul
- - @ci_commit.yaml_errors.split(",").each do |error|
- %li= error
-
-- unless @ci_commit.ci_yaml_file
- .bs-callout.bs-callout-warning
- \.gitlab-ci.yml not found in this commit
-
-.gray-content-block.second-block
- Latest builds
-
- .pull-right
- - if @ci_commit.duration > 0
- %i.fa.fa-time
- #{time_interval_in_words @ci_commit.duration}
-
- &nbsp;
-
- - if @ci_project && current_user && can?(current_user, :manage_builds, @project)
- - if @ci_commit.builds.latest.failed.any?(&:retryable?)
- = link_to "Retry failed", retry_builds_namespace_project_commit_path(@project.namespace, @project, @commit.sha), class: 'btn btn-xs btn-primary', method: :post
-
- - if @ci_commit.builds.running_or_pending.any?
- = link_to "Cancel running", cancel_builds_namespace_project_commit_path(@project.namespace, @project, @commit.sha), class: 'btn btn-xs btn-danger', method: :post
-
-.table-holder
- %table.table.builds
- %thead
- %tr
- %th Status
- %th Build ID
- %th Ref
- %th Stage
- %th Name
- %th Duration
- %th Finished at
- - if @ci_project && @ci_project.coverage_enabled?
- %th Coverage
- %th
- - @ci_commit.refs.each do |ref|
- = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.statuses.for_ref(ref).latest.ordered,
- locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true, allow_retry: true }
-
-- if @ci_commit.retried.any?
- .gray-content-block.second-block
- Retried builds
-
- .table-holder
- %table.table.builds
- %thead
- %tr
- %th Status
- %th Build ID
- %th Ref
- %th Stage
- %th Name
- %th Duration
- %th Finished at
- - if @ci_project && @ci_project.coverage_enabled?
- %th Coverage
- %th
- = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried,
- locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true }
+= render "builds"
diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml
index 9a0e7bff3f1..a527bb2f84a 100644
--- a/app/views/projects/commit_statuses/_commit_status.html.haml
+++ b/app/views/projects/commit_statuses/_commit_status.html.haml
@@ -1,13 +1,18 @@
%tr.commit_status
%td.status
- = ci_status_with_icon(commit_status.status)
+ - if commit_status.target_url
+ = link_to commit_status.target_url, class: "ci-status ci-#{commit_status.status}" do
+ = ci_icon_for_status(commit_status.status)
+ = commit_status.status
+ - else
+ = ci_status_with_icon(commit_status.status)
%td.commit_status-link
- if commit_status.target_url
= link_to commit_status.target_url do
- %strong Build ##{commit_status.id}
+ %strong ##{commit_status.id}
- else
- %strong Build ##{commit_status.id}
+ %strong ##{commit_status.id}
- if commit_status.show_warning?
%i.fa.fa-warning.text-warning
diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml
index b3392d00e01..327e7d9245a 100644
--- a/app/views/projects/diffs/_file.html.haml
+++ b/app/views/projects/diffs/_file.html.haml
@@ -3,9 +3,8 @@
- if diff_file.diff.submodule?
%span
= icon('archive fw')
- - submodule_item = project.repository.blob_at(@commit.id, diff_file.file_path)
%strong
- = submodule_link(submodule_item, @commit.id, project.repository)
+ = submodule_link(blob, @commit.id, project.repository)
- else
%span
= blob_icon blob.mode, blob.name
@@ -25,7 +24,7 @@
= "#{diff_file.diff.a_mode} → #{diff_file.diff.b_mode}"
.diff-controls
- - if blob.text?
+ - if blob_viewable?(blob)
= link_to '#', class: 'js-toggle-diff-comments btn btn-sm active has_tooltip', title: "Toggle comments for this file" do
%i.fa.fa-comments
&nbsp;
@@ -40,7 +39,7 @@
.diff-content.diff-wrap-lines
-# Skipp all non non-supported blobs
- return unless blob.respond_to?('text?')
- - if blob.text?
+ - if blob_viewable?(blob)
- if diff_view == 'parallel'
= render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i
- else
diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml
index 03d0733f913..a47643bd09c 100644
--- a/app/views/projects/graphs/_head.html.haml
+++ b/app/views/projects/graphs/_head.html.haml
@@ -3,6 +3,8 @@
= 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.builds_enabled?
= nav_link(action: :ci) do
= link_to ci_namespace_project_graph_path do
diff --git a/app/views/projects/graphs/languages.html.haml b/app/views/projects/graphs/languages.html.haml
new file mode 100644
index 00000000000..a7fab5b6d72
--- /dev/null
+++ b/app/views/projects/graphs/languages.html.haml
@@ -0,0 +1,32 @@
+- page_title "Languages", "Graphs"
+= render "header_title"
+= render 'head'
+
+.gray-content-block.append-bottom-default
+ .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/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml
index b5f522f2079..f2011542ca7 100644
--- a/app/views/projects/issues/_discussion.html.haml
+++ b/app/views/projects/issues/_discussion.html.haml
@@ -12,15 +12,12 @@
.col-md-9
.votes-holder.pull-right
#votes= render 'votes/votes_block', votable: @issue
- .participants
- %span= pluralize(@participants.count, 'participant')
- - @participants.each do |participant|
- = link_to_member(@project, participant, name: false, size: 24)
+ = render "shared/issuable/participants"
.col-md-3
.input-group.cross-project-reference
%span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'}
= cross_project_reference(@project, @issue)
- = clipboard_button(clipboard_target: '#cross-project-reference')
+ = clipboard_button(clipboard_target: 'span#cross-project-reference')
.row
%section.col-md-9
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index 1eb71990e55..f9cf4910df3 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -6,23 +6,26 @@
.issue-title
%span.issue-title-text
= link_to_gfm issue.title, issue_path(issue), class: "row_title"
- .pull-right.light
+ %ul.controls.light
- if issue.closed?
- %span
+ %li
CLOSED
+
- if issue.assignee
- = link_to_member(@project, issue.assignee, name: false, title: "Assigned to :name")
+ %li
+ = link_to_member(@project, issue.assignee, name: false, title: "Assigned to :name")
+
- note_count = issue.notes.user.count
- if note_count > 0
- &nbsp;
- = link_to issue_path(issue) + "#notes" do
- = icon('comments')
- = note_count
+ %li
+ = link_to issue_path(issue) + "#notes" do
+ = icon('comments')
+ = note_count
- else
- &nbsp;
- = link_to issue_path(issue) + "#notes", class: "issue-no-comments" do
- = icon('comments')
- = 0
+ %li
+ = link_to issue_path(issue) + "#notes", class: "issue-no-comments" do
+ = icon('comments')
+ = note_count
.issue-info
#{issue.to_reference} &middot;
diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml
index ea462561668..d64b19ae91a 100644
--- a/app/views/projects/merge_requests/_discussion.html.haml
+++ b/app/views/projects/merge_requests/_discussion.html.haml
@@ -12,16 +12,16 @@
.col-md-9
.votes-holder.pull-right
#votes= render 'votes/votes_block', votable: @merge_request
- = render "projects/merge_requests/show/participants"
+ = render "shared/issuable/participants"
.col-md-3
.input-group.cross-project-reference
%span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'}
= cross_project_reference(@project, @merge_request)
- = clipboard_button(clipboard_target: '#cross-project-reference')
+ = clipboard_button(clipboard_target: 'span#cross-project-reference')
.row
%section.col-md-9
- = render "projects/notes/notes_with_form"
+ .voting_notes#notes= render "projects/notes/notes_with_form"
%aside.col-md-3
.issuable-affix
.context
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 1d4c9b66c42..cf9570f7c7e 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -1,33 +1,41 @@
-- ci_commit = merge_request.ci_commit
%li{ class: mr_css_classes(merge_request) }
.merge-request-title
%span.merge-request-title-text
= link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title"
- .pull-right.light
- - if ci_commit
- = render_ci_status(ci_commit)
+ %ul.controls.light
- if merge_request.merged?
- %span
+ %li
= icon('check')
MERGED
- elsif merge_request.closed?
- %span
+ %li
= icon('ban')
CLOSED
- - note_count = merge_request.mr_and_commit_notes.user.count
+
+ - if merge_request.ci_commit
+ %li
+ = render_ci_status(merge_request.ci_commit)
+
+ - if merge_request.open? && merge_request.broken?
+ %li
+ = link_to merge_request_path(merge_request), class: "has_tooltip", title: "Cannot be merged automatically", data: {container: 'body'} do
+ = icon('exclamation-triangle')
+
- if merge_request.assignee
- &nbsp;
- = link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name")
+ %li
+ = link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name")
+
+ - note_count = merge_request.mr_and_commit_notes.user.count
- if note_count > 0
- &nbsp;
- = link_to merge_request_path(merge_request) + "#notes" do
- = icon('comments')
- = note_count
+ %li
+ = link_to merge_request_path(merge_request) + "#notes" do
+ = icon('comments')
+ = note_count
- else
- &nbsp;
- = link_to merge_request_path(merge_request) + "#notes", class: "merge-request-no-comments" do
- = icon('comments')
- = 0
+ %li
+ = link_to merge_request_path(merge_request) + "#notes", class: "merge-request-no-comments" do
+ = icon('comments')
+ = note_count
.merge-request-info
\##{merge_request.iid} &middot;
diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml
index 156922cea41..4172d5a4e88 100644
--- a/app/views/projects/merge_requests/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/_new_submit.html.haml
@@ -20,13 +20,18 @@
.mr-compare.merge-request
%ul.merge-request-tabs.center-top-menu.no-top.no-bottom
%li.commits-tab
- = link_to url_for(params), data: {target: '#commits', action: 'commits', toggle: 'tab'} do
+ = link_to url_for(params), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do
Commits
%span.badge= @commits.size
%li.diffs-tab.active
- = link_to url_for(params), data: {target: '#diffs', action: 'diffs', toggle: 'tab'} do
+ = link_to url_for(params), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do
Changes
%span.badge= @diffs.size
+ - if @ci_commit
+ %li.builds-tab.active
+ = link_to url_for(params), data: {target: 'div#builds', action: 'builds', toggle: 'tab'} do
+ Builds
+ %span.badge= @statuses.size
.tab-content
#commits.commits.tab-pane
@@ -42,6 +47,9 @@
.alert.alert-danger
%h4 This comparison includes a huge diff.
%p To preserve performance the line changes are not shown.
+ - if @ci_commit
+ #builds.builds.tab-pane
+ = render "projects/merge_requests/show/builds"
:javascript
$('.assign-to-me-link').on('click', function(e){
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index f5aff0877e7..960d1561e73 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -26,8 +26,7 @@
%li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff)
.normal
%span Request to merge
- %span.label-branch
- = source_branch_with_namespace(@merge_request)
+ %span.label-branch= source_branch_with_namespace(@merge_request)
%span into
= link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do
= @merge_request.target_branch
@@ -44,17 +43,22 @@
- if @commits.present?
%ul.merge-request-tabs.center-top-menu.no-top.no-bottom
%li.notes-tab
- = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#notes', action: 'notes', toggle: 'tab'} do
+ = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#notes', action: 'notes', toggle: 'tab'} do
Discussion
%span.badge= @merge_request.mr_and_commit_notes.user.count
%li.commits-tab
- = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#commits', action: 'commits', toggle: 'tab'} do
+ = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do
Commits
%span.badge= @commits.size
%li.diffs-tab
- = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#diffs', action: 'diffs', toggle: 'tab'} do
+ = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do
Changes
%span.badge= @merge_request.diffs.size
+ - if @ci_commit
+ %li.builds-tab
+ = link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#builds', action: 'builds', toggle: 'tab'} do
+ Builds
+ %span.badge= @statuses.size
.tab-content
#notes.notes.tab-pane.voting_notes
@@ -63,6 +67,8 @@
- # This tab is always loaded via AJAX
#diffs.diffs.tab-pane
- # This tab is always loaded via AJAX
+ #builds.builds.tab-pane
+ - # This tab is always loaded via AJAX
.mr-loading-status
= spinner
diff --git a/app/views/projects/merge_requests/cancel_merge_when_build_succeeds.js.haml b/app/views/projects/merge_requests/cancel_merge_when_build_succeeds.js.haml
new file mode 100644
index 00000000000..eab5be488b5
--- /dev/null
+++ b/app/views/projects/merge_requests/cancel_merge_when_build_succeeds.js.haml
@@ -0,0 +1,2 @@
+:plain
+ $('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/accept'))}");
diff --git a/app/views/projects/merge_requests/merge.js.haml b/app/views/projects/merge_requests/merge.js.haml
index 518ecb9f00f..92ce479d463 100644
--- a/app/views/projects/merge_requests/merge.js.haml
+++ b/app/views/projects/merge_requests/merge.js.haml
@@ -1,6 +1,10 @@
-- if @status
+- case @status
+- when :success
:plain
merge_request_widget.mergeInProgress(#{params[:should_remove_source_branch] == '1'});
+- when :merge_when_build_succeeds
+ :plain
+ $('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/merge_when_build_succeeds'))}");
- else
:plain
$('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/reload'))}");
diff --git a/app/views/projects/merge_requests/show/_builds.html.haml b/app/views/projects/merge_requests/show/_builds.html.haml
new file mode 100644
index 00000000000..307a75d02ca
--- /dev/null
+++ b/app/views/projects/merge_requests/show/_builds.html.haml
@@ -0,0 +1 @@
+= render "projects/commit/builds", link_to_commit: true
diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml
index ba5ad22bca7..b05ab869215 100644
--- a/app/views/projects/merge_requests/widget/_heading.html.haml
+++ b/app/views/projects/merge_requests/widget/_heading.html.haml
@@ -1,30 +1,33 @@
-- ci_commit = @merge_request.ci_commit
-- if ci_commit
- - status = ci_commit.status
+- if @ci_commit
.mr-widget-heading
- .ci_widget{class: "ci-#{status}"}
- = ci_status_icon(ci_commit)
- %span CI build #{status}
- for #{@merge_request.last_commit_short_sha}.
+ .ci_widget{class: "ci-#{@ci_commit.status}"}
+ = ci_status_icon(@ci_commit)
+ %span
+ Build
+ = ci_status_label(@ci_commit)
+ for
+ = succeed "." do
+ = link_to @ci_commit.short_sha, namespace_project_commit_path(@merge_request.source_project.namespace, @merge_request.source_project, @ci_commit.sha), class: "monospace"
%span.ci-coverage
- = link_to "View build details", ci_status_path(ci_commit)
+ = link_to "View details", builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "js-show-tab", data: {action: 'builds'}
- elsif @merge_request.has_ci?
- # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX
- # Remove in later versions when services like Jenkins will set CI status via Commit status API
.mr-widget-heading
- - [:success, :skipped, :canceled, :failed, :running, :pending].each do |status|
+ - %w[success skipped canceled failed running pending].each do |status|
.ci_widget{class: "ci-#{status}", style: "display:none"}
- - if status == :success
- - status = "passed"
- = icon("check-circle")
- - else
- = icon("circle")
- %span CI build #{status}
- for #{@merge_request.last_commit_short_sha}.
+ = ci_icon_for_status(status)
+ %span
+ CI build
+ = ci_label_for_status(status)
+ for
+ - commit = @merge_request.last_commit
+ = succeed "." do
+ = link_to commit.short_id, namespace_project_commit_path(@merge_request.source_project.namespace, @merge_request.source_project, commit), class: "monospace"
%span.ci-coverage
- - if ci_build_details_path(@merge_request)
- = link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
+ - if details_path = ci_build_details_path(@merge_request)
+ = link_to "View details", details_path, :"data-no-turbolink" => "data-no-turbolink"
.ci_widget
= icon("spinner spin")
diff --git a/app/views/projects/merge_requests/widget/_merged.html.haml b/app/views/projects/merge_requests/widget/_merged.html.haml
index 5c6fece8c5c..8c2b5366a06 100644
--- a/app/views/projects/merge_requests/widget/_merged.html.haml
+++ b/app/views/projects/merge_requests/widget/_merged.html.haml
@@ -14,7 +14,7 @@
= @merge_request.target_branch
The source branch has been removed.
- - elsif can_remove_branch?(@merge_request.source_project, @merge_request.source_branch)
+ - elsif @merge_request.can_remove_source_branch?(current_user)
.remove_source_branch_widget
%p
= succeed '.' do
@@ -50,5 +50,3 @@
$('.remove_source_branch_in_progress').hide();
$('.remove_source_branch_widget.failed').show();
});
-
-
diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml
index 0aad9bb3e88..e0013fb769a 100644
--- a/app/views/projects/merge_requests/widget/_open.html.haml
+++ b/app/views/projects/merge_requests/widget/_open.html.haml
@@ -13,6 +13,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.can_be_merged_by?(current_user)
= render 'projects/merge_requests/widget/open/not_allowed'
- elsif @merge_request.can_be_merged?
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 9b31014b581..c6bc4ca5beb 100644
--- a/app/views/projects/merge_requests/widget/open/_accept.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml
@@ -1,28 +1,62 @@
-- status_class = @merge_request.ci_commit ? " ci-#{@merge_request.ci_commit.status}" : nil
+- status_class = @ci_commit ? " ci-#{@ci_commit.status}" : nil
= form_for [:merge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-requires-input' } do |f|
= hidden_field_tag :authenticity_token, form_authenticity_token
.accept-merge-holder.clearfix.js-toggle-container
- .accept-action
- = f.button class: "btn btn-create accept_merge_request#{status_class}" do
- Accept Merge Request
- - if can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && !@merge_request.for_fork?
- .accept-control.checkbox
- = label_tag :should_remove_source_branch, class: "remove_source_checkbox" do
- = check_box_tag :should_remove_source_branch
- Remove source branch
- .accept-control.right
- = link_to "#", class: "modify-merge-commit-link js-toggle-button" do
- = icon('edit')
- Modify commit message
- .js-toggle-content.hide.prepend-top-20
+ .clearfix
+ .accept-action
+ - if @ci_commit && @ci_commit.active?
+ %span.btn-group
+ = link_to "#", class: "btn btn-create merge_when_build_succeeds" do
+ Merge When Build Succeeds
+ %a.btn.btn-success.dropdown-toggle{ 'data-toggle' => 'dropdown' }
+ %span.caret
+ %span.sr-only
+ Select Merge Moment
+ %ul.dropdown-menu.dropdown-menu-right{ role: 'menu' }
+ %li
+ = link_to "#", class: "merge_when_build_succeeds" do
+ = icon('check fw')
+ Merge When Build Succeeds
+ %li
+ = link_to "#", class: "accept_merge_request" do
+ = icon('warning fw')
+ Merge Immediately
+ - else
+ = f.button class: "btn btn-create btn-grouped accept_merge_request #{status_class}" do
+ Accept Merge Request
+ - if @merge_request.can_remove_source_branch?(current_user)
+ .accept-control.checkbox
+ = label_tag :should_remove_source_branch, class: "remove_source_checkbox" do
+ = check_box_tag :should_remove_source_branch
+ Remove source branch
+ .accept-control.right
+ = link_to "#", class: "modify-merge-commit-link js-toggle-button" do
+ = icon('edit')
+ Modify commit message
+ .js-toggle-content.hide.prepend-top-default
= render 'shared/commit_message_container', params: params,
text: @merge_request.merge_commit_message,
rows: 14, hint: true
+ = hidden_field_tag :merge_when_build_succeeds, "", autocomplete: "off"
+
:javascript
- $('.accept-mr-form').on('ajax:before', function() {
- var btn = $('.accept_merge_request');
- btn.disable();
- btn.html("<i class='fa fa-spinner fa-spin'></i> Merge in progress");
+ $('.accept_merge_request').on('click', function() {
+ $(this).html("<i class='fa fa-spinner fa-spin'></i> Merge in progress");
+ });
+
+ $('.accept-mr-form').on('ajax:send', function() {
+ $(".accept-mr-form :input").disable();
+ });
+
+ $('a.accept_merge_request').on('click', function(e) {
+ e.preventDefault();
+ $(this).closest("form").submit();
+ });
+
+ $('a.merge_when_build_succeeds').on('click', function(e) {
+ e.preventDefault();
+ $("#merge_when_build_succeeds").val("1");
+ $(this).closest("form").submit();
});
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_build_succeeds.html.haml
new file mode 100644
index 00000000000..08af124274b
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml
@@ -0,0 +1,26 @@
+%h4
+ Set by #{link_to_member(@project, @merge_request.merge_user, avatar: true)}
+ to be merged automatically when the build succeeds.
+%div
+ - should_remove_source_branch = @merge_request.merge_params["should_remove_source_branch"].present?
+ %p
+ = succeed '.' do
+ The changes will be merged into
+ %span.label-branch= @merge_request.target_branch
+ - if should_remove_source_branch
+ The source branch will be removed.
+ - else
+ The source branch will not be removed.
+
+ - remove_source_branch_button = @merge_request.can_remove_source_branch?(current_user) && !should_remove_source_branch
+ - user_can_cancel_automatic_merge = @merge_request.can_cancel_merge_when_build_succeeds?(current_user)
+ - if remove_source_branch_button || user_can_cancel_automatic_merge
+ .clearfix.prepend-top-10
+ - if remove_source_branch_button
+ = link_to merge_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request, merge_when_build_succeeds: true, should_remove_source_branch: true), remote: true, method: :post, class: "btn btn-grouped btn-primary btn-sm remove_source_branch" do
+ = icon('times')
+ 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-warning btn-sm" do
+ Cancel Automatic Merge
diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml
index a207385bd43..114b06457a5 100644
--- a/app/views/projects/milestones/index.html.haml
+++ b/app/views/projects/milestones/index.html.haml
@@ -1,15 +1,18 @@
- page_title "Milestones"
= render "header_title"
-= render 'shared/milestones_filter'
-.gray-content-block
- .pull-right
- - if can? current_user, :admin_milestone, @project
+
+.project-issuable-filter
+ .controls
+ - if can?(current_user, :admin_milestone, @project)
= link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "pull-right btn btn-new", title: "New Milestone" do
%i.fa.fa-plus
New Milestone
- .oneline
- Milestone allows you to group issues and set due date for it
+
+ = render 'shared/milestones_filter'
+
+.gray-content-block
+ Milestone allows you to group issues and set due date for it
.milestones
%ul.content-list
diff --git a/app/views/projects/network/_head.html.haml b/app/views/projects/network/_head.html.haml
index 415c98ec6a6..9e0e0dc6bb0 100644
--- a/app/views/projects/network/_head.html.haml
+++ b/app/views/projects/network/_head.html.haml
@@ -1,3 +1,6 @@
-.append-bottom-20
- = render partial: 'shared/ref_switcher', locals: {destination: 'graph'}
- .pull-right.visible-lg.light You can move around the graph by using the arrow keys.
+.gray-content-block.top-block.append-bottom-default
+ .tree-ref-holder
+ = render partial: 'shared/ref_switcher', locals: {destination: 'graph'}
+
+ .oneline
+ You can move around the graph by using the arrow keys.
diff --git a/app/views/projects/project_members/_group_members.html.haml b/app/views/projects/project_members/_group_members.html.haml
index 0c73d7e34ac..1c2458fa144 100644
--- a/app/views/projects/project_members/_group_members.html.haml
+++ b/app/views/projects/project_members/_group_members.html.haml
@@ -4,12 +4,13 @@
group members
%small
(#{members.count})
- .pull-right
- = link_to group_group_members_path(@group), class: 'btn' do
- = icon('pencil-square-o')
- Edit group members
+ - if can?(current_user, :admin_group_member, @group)
+ .pull-right
+ = link_to group_group_members_path(@group), class: 'btn' do
+ = icon('pencil-square-o')
+ Manage group members
%ul.content-list
- - members.each do |member|
+ - members.limit(20).each do |member|
= render 'groups/group_members/group_member', member: member, show_controls: false
- if members.count > 20
%li
diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml
index 2541105b007..cfd7e1534ca 100644
--- a/app/views/projects/protected_branches/index.html.haml
+++ b/app/views/projects/protected_branches/index.html.haml
@@ -3,7 +3,7 @@
%p.light Keep stable branches secure and force developers to use Merge Requests
%hr
-.well.append-bottom-20
+.well
%p Protected branches are designed to
%ul
%li prevent pushes from everybody except #{link_to "masters", help_page_path("permissions", "permissions"), class: "vlink"}
diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml
index e2c5178185e..28b706c5c7e 100644
--- a/app/views/projects/tags/_tag.html.haml
+++ b/app/views/projects/tags/_tag.html.haml
@@ -11,11 +11,17 @@
= strip_gpg_signature(tag.message)
.controls
- = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn' do
- = icon("pencil")
- - if can? current_user, :download_code, @project
+ - if can?(current_user, :download_code, @project)
= render 'projects/tags/download', ref: tag.name, project: @project
+ - if can?(current_user, :push_code, @project)
+ = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn has_tooltip', title: "Edit release notes" do
+ = icon("pencil")
+
+ - if can?(current_user, :admin_project, @project)
+ = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has_tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do
+ = icon("trash-o")
+
- if commit
= render 'projects/branches/commit', commit: commit, project: @project
- else
diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml
index 879c6c7d310..b594d4f1f27 100644
--- a/app/views/projects/tags/show.html.haml
+++ b/app/views/projects/tags/show.html.haml
@@ -5,17 +5,17 @@
.gray-content-block
.pull-right
- if can?(current_user, :push_code, @project)
- = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn', title: 'Edit release notes' do
+ = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn has_tooltip', title: 'Edit release notes' do
= icon("pencil")
- = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped', title: 'Browse source code' do
+ = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has_tooltip', title: 'Browse files' do
= icon('files-o')
- = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped', title: 'Browse commits' do
+ = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has_tooltip', title: 'Browse commits' do
= icon('history')
- if can? current_user, :download_code, @project
= render 'projects/tags/download', ref: @tag.name, project: @project
- if can?(current_user, :admin_project, @project)
.pull-right
- = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped', method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do
+ = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has_tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do
%i.fa.fa-trash-o
.title
%strong= @tag.name
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index 1115ca6b4ca..cefe33e581f 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -14,7 +14,7 @@
- if allowed_tree_edit?
%li
%span.dropdown
- %a.dropdown-toggle.btn.add-to-tree{href: '#', "data-toggle" => "dropdown"}
+ %a.dropdown-toggle.btn.btn-sm.add-to-tree{href: '#', "data-toggle" => "dropdown"}
= icon('plus')
%ul.dropdown-menu
%li
@@ -30,3 +30,7 @@
= link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
= icon('folder fw')
New directory
+ - elsif !on_top_of_branch?
+ %li
+ %span.btn.btn-sm.add-to-tree.disabled.has_tooltip{title: "You can only add files when you are on a branch.", data: {container: 'body'}}
+ = icon('plus')
diff --git a/app/views/shared/_new_commit_form.html.haml b/app/views/shared/_new_commit_form.html.haml
index 31b02ed93d0..111219f2064 100644
--- a/app/views/shared/_new_commit_form.html.haml
+++ b/app/views/shared/_new_commit_form.html.haml
@@ -4,14 +4,13 @@
.form-group.branch
= label_tag 'new_branch', 'Target branch', class: 'control-label'
.col-sm-10
- = text_field_tag 'new_branch', @new_branch || @ref, required: true, class: "form-control js-new-branch"
+ = text_field_tag 'new_branch', @new_branch || tree_edit_branch, required: true, class: "form-control js-new-branch"
- .form-group.js-create-merge-request-form-group
- .col-sm-offset-2.col-sm-10
- .checkbox
- - nonce = SecureRandom.hex
- = label_tag "create_merge_request-#{nonce}" do
- = check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: "create_merge_request-#{nonce}"
- Start a <strong>new merge request</strong> with this commit
+ .js-create-merge-request-container
+ .checkbox
+ - nonce = SecureRandom.hex
+ = label_tag "create_merge_request-#{nonce}" do
+ = check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: "create_merge_request-#{nonce}"
+ Start a <strong>new merge request</strong> with these changes
= hidden_field_tag 'original_branch', @ref, class: 'js-original-branch'
diff --git a/app/views/shared/_new_project_item_select.html.haml b/app/views/shared/_new_project_item_select.html.haml
new file mode 100644
index 00000000000..c4431d66927
--- /dev/null
+++ b/app/views/shared/_new_project_item_select.html.haml
@@ -0,0 +1,20 @@
+- if @projects.any?
+ .prepend-left-10.new-project-item-select-holder
+ = project_select_tag :project_path, class: "new-project-item-select", data: { include_groups: local_assigns[:include_groups] }
+ %a.btn.btn-new.new-project-item-select-button
+ = icon('plus')
+ = local_assigns[:label]
+ %b.caret
+
+ :javascript
+ $('.new-project-item-select-button').on('click', function() {
+ $('.new-project-item-select').select2('open');
+ });
+
+ var relativePath = '#{local_assigns[:path]}';
+
+ $('.new-project-item-select').on('click', function() {
+ window.location = $(this).val() + '/' + relativePath;
+ });
+
+ new ProjectSelect()
diff --git a/app/views/projects/merge_requests/show/_participants.html.haml b/app/views/shared/issuable/_participants.html.haml
index c67afe963e7..b4e0def48b6 100644
--- a/app/views/projects/merge_requests/show/_participants.html.haml
+++ b/app/views/shared/issuable/_participants.html.haml
@@ -1,4 +1,5 @@
.participants
- %span #{@participants.count} participants
+ %span
+ = pluralize @participants.count, "participant"
- @participants.each do |participant|
= link_to_member(@project, participant, name: false, size: 24)
diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml
index 89c1d7122b0..eb0fd21c2d4 100644
--- a/app/views/shared/snippets/_header.html.haml
+++ b/app/views/shared/snippets/_header.html.haml
@@ -1,6 +1,6 @@
.issuable-details
.page-title
- .snippet-box.has_tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level), data: { container: 'body' }}
+ .snippet-box.has_tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: 'body' }}
= visibility_level_icon(@snippet.visibility_level, fw: false)
= visibility_level_label(@snippet.visibility_level)
Snippet ##{@snippet.id}
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index d5a92cb816a..a0a6e2d9810 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -73,7 +73,7 @@
.user-calendar-activities
-%ul.center-middle-menu
+%ul.center-top-menu.no-top.no-bottom.bottom-border
%li.active
= link_to "#activity", 'data-toggle' => 'tab' do
Activity
diff --git a/app/workers/merge_worker.rb b/app/workers/merge_worker.rb
index 5d1a8555b7d..c87c0a252b1 100644
--- a/app/workers/merge_worker.rb
+++ b/app/workers/merge_worker.rb
@@ -8,16 +8,7 @@ class MergeWorker
current_user = User.find(current_user_id)
merge_request = MergeRequest.find(merge_request_id)
- result = MergeRequests::MergeService.new(merge_request.target_project, current_user).
- execute(merge_request, params[:commit_message])
-
- if result[:status] == :success && params[:should_remove_source_branch].present?
- DeleteBranchService.new(merge_request.source_project, current_user).
- execute(merge_request.source_branch)
-
- merge_request.source_project.repository.expire_branch_names
- end
-
- result
+ MergeRequests::MergeService.new(merge_request.target_project, current_user, params).
+ execute(merge_request)
end
end
diff --git a/app/workers/stuck_ci_builds_worker.rb b/app/workers/stuck_ci_builds_worker.rb
index 4e5eddbaba1..ca594e77e7c 100644
--- a/app/workers/stuck_ci_builds_worker.rb
+++ b/app/workers/stuck_ci_builds_worker.rb
@@ -1,11 +1,8 @@
class StuckCiBuildsWorker
include Sidekiq::Worker
- include Sidetiq::Schedulable
BUILD_STUCK_TIMEOUT = 1.day
- recurrence { daily }
-
def perform
Rails.logger.info 'Cleaning stuck builds'
diff --git a/bin/parallel-rsync-repos b/bin/parallel-rsync-repos
new file mode 100755
index 00000000000..21921148fa0
--- /dev/null
+++ b/bin/parallel-rsync-repos
@@ -0,0 +1,54 @@
+#!/usr/bin/env bash
+# this script should run as the 'git' user, not root, because 'root' should not
+# own intermediate directories created by rsync.
+#
+# Example invocation:
+# find /var/opt/gitlab/git-data/repositories -maxdepth 2 | \
+# parallel-rsync-repos transfer-success.log /var/opt/gitlab/git-data/repositories /mnt/gitlab/repositories
+#
+# You can also rsync to a remote destination.
+#
+# parallel-rsync-repos transfer-success.log /var/opt/gitlab/git-data/repositories user@host:/mnt/gitlab/repositories
+#
+# If you need to pass extra options to rsync, set the RSYNC variable
+#
+# env RSYNC='rsync --rsh="foo bar"' parallel-rsync-repos transfer-success.log /src dest
+#
+
+LOGFILE=$1
+SRC=$2
+DEST=$3
+
+if [ -z "$LOGFILE" ] || [ -z "$SRC" ] || [ -z "$DEST" ] ; then
+ echo "Usage: $0 LOGFILE SRC DEST"
+ exit 1
+fi
+
+if [ -z "$JOBS" ] ; then
+ JOBS=10
+fi
+
+if [ -z "$RSYNC" ] ; then
+ RSYNC=rsync
+fi
+
+if ! cd $SRC ; then
+ echo "cd $SRC failed"
+ exit 1
+fi
+
+rsyncjob() {
+ relative_dir="./${1#$SRC}"
+
+ if ! $RSYNC --delete --relative -a "$relative_dir" "$DEST" ; then
+ echo "rsync $1 failed"
+ return 1
+ fi
+
+ echo "$1" >> $LOGFILE
+}
+
+export LOGFILE SRC DEST RSYNC
+export -f rsyncjob
+
+parallel -j$JOBS --progress rsyncjob
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 1da42ab38f3..db378118f85 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -76,7 +76,7 @@ production: &base
# This happens when the commit is pushed or merged into the default branch of a project.
# When not specified the default issue_closing_pattern as specified below will be used.
# Tip: you can test your closing pattern at http://rubular.com.
- # issue_closing_pattern: '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)'
+ # issue_closing_pattern: '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)'
## Default project features settings
default_projects_features:
diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb
index 70ed10e8275..4c164119fff 100644
--- a/config/initializers/omniauth.rb
+++ b/config/initializers/omniauth.rb
@@ -16,7 +16,7 @@ OmniAuth.config.allowed_request_methods = [:post]
#In case of auto sign-in, the GET method is used (users don't get to click on a button)
OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_sign_in_with_provider.present?
OmniAuth.config.before_request_phase do |env|
- OmniAuth::RequestForgeryProtection.new(env).call
+ OmniAuth::RequestForgeryProtection.call(env)
end
if Gitlab.config.omniauth.enabled
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index e856499732e..6e5701e33da 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -17,6 +17,12 @@ Sidekiq.configure_server do |config|
chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger if ENV['SIDEKIQ_LOG_ARGUMENTS']
chain.add Gitlab::SidekiqMiddleware::MemoryKiller if ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS']
end
+
+ # Sidekiq-cron: load recurring jobs from schedule.yml
+ schedule_file = 'config/schedule.yml'
+ if File.exists?(schedule_file)
+ Sidekiq::Cron::Job.load_from_hash YAML.load_file(schedule_file)
+ end
end
Sidekiq.configure_client do |config|
diff --git a/config/routes.rb b/config/routes.rb
index fdd387fd184..061a8fd5da4 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,4 +1,5 @@
require 'sidekiq/web'
+require 'sidekiq/cron/web'
require 'api/api'
Rails.application.routes.draw do
@@ -499,6 +500,7 @@ Rails.application.routes.draw do
member do
get :commits
get :ci
+ get :languages
end
end
@@ -568,10 +570,12 @@ Rails.application.routes.draw do
resources :merge_requests, constraints: { id: /\d+/ }, except: [:destroy] do
member do
- get :diffs
get :commits
- post :merge
+ get :diffs
+ get :builds
get :merge_check
+ post :merge
+ post :cancel_merge_when_build_succeeds
get :ci_status
post :toggle_subscription
end
diff --git a/config/schedule.yml b/config/schedule.yml
new file mode 100644
index 00000000000..993a95fef56
--- /dev/null
+++ b/config/schedule.yml
@@ -0,0 +1,10 @@
+# Here is a list of jobs that are scheduled to run periodically.
+# We use a UNIX cron notation to specify execution schedule.
+#
+# Please read here for more information:
+# https://github.com/ondrejbartas/sidekiq-cron#adding-cron-job
+
+stuck_ci_builds_worker:
+ cron: "0 0 * * *"
+ class: "StuckCiBuildsWorker"
+ queue: "default"
diff --git a/db/migrate/20151028152939_add_merge_when_build_succeeds_to_merge_request.rb b/db/migrate/20151028152939_add_merge_when_build_succeeds_to_merge_request.rb
new file mode 100644
index 00000000000..ceb52f0c222
--- /dev/null
+++ b/db/migrate/20151028152939_add_merge_when_build_succeeds_to_merge_request.rb
@@ -0,0 +1,7 @@
+class AddMergeWhenBuildSucceedsToMergeRequest < ActiveRecord::Migration
+ def change
+ add_column :merge_requests, :merge_params, :text
+ add_column :merge_requests, :merge_when_build_succeeds, :boolean, default: false, null: false
+ add_column :merge_requests, :merge_user_id, :integer
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index fb59e187625..94b87040d88 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -476,9 +476,9 @@ ActiveRecord::Schema.define(version: 20151203162133) do
add_index "merge_request_diffs", ["merge_request_id"], name: "index_merge_request_diffs_on_merge_request_id", unique: true, using: :btree
create_table "merge_requests", force: :cascade do |t|
- t.string "target_branch", null: false
- t.string "source_branch", null: false
- t.integer "source_project_id", null: false
+ t.string "target_branch", null: false
+ t.string "source_branch", null: false
+ t.integer "source_project_id", null: false
t.integer "author_id"
t.integer "assignee_id"
t.string "title"
@@ -487,13 +487,16 @@ ActiveRecord::Schema.define(version: 20151203162133) do
t.integer "milestone_id"
t.string "state"
t.string "merge_status"
- t.integer "target_project_id", null: false
+ t.integer "target_project_id", null: false
t.integer "iid"
t.text "description"
- t.integer "position", default: 0
+ t.integer "position", default: 0
t.datetime "locked_at"
t.integer "updated_by_id"
t.string "merge_error"
+ t.text "merge_params"
+ t.boolean "merge_when_build_succeeds", default: false, null: false
+ t.integer "merge_user_id"
end
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
diff --git a/doc/README.md b/doc/README.md
index 58ab5dd08e0..a3098094210 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -24,9 +24,21 @@
- [Using Docker Images](ci/docker/using_docker_images.md)
- [Using Docker Build](ci/docker/using_docker_build.md)
- [Using Variables](ci/variables/README.md)
+- [Using SSH keys](ci/ssh_keys/README.md)
- [User permissions](ci/permissions/README.md)
- [API](ci/api/README.md)
+### CI Languages
+
++ [Testing PHP](ci/languages/php.md)
+
+### CI Services
+
++ [Using MySQL](ci/services/mysql.md)
++ [Using PostgreSQL](ci/services/postgres.md)
++ [Using Redis](ci/services/redis.md)
++ [Using Other Services](ci/docker/using_docker_images.md#how-to-use-other-images-as-services)
+
### CI Examples
- [Test and deploy Ruby applications to Heroku](ci/examples/test-and-deploy-ruby-application-to-heroku.md)
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 0b9f6406d8d..808675d8605 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -1,6 +1,6 @@
# Groups
-## List project groups
+## List groups
Get a list of groups. (As user: my groups, as admin: all groups)
@@ -21,6 +21,70 @@ GET /groups
You can search for groups by name or path, see below.
+
+## List a group's projects
+
+Get a list of projects in this group.
+
+```
+GET /groups/:id/projects
+```
+
+Parameters:
+
+- `archived` (optional) - if passed, limit by archived status
+- `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, `updated_at` or `last_activity_at` fields. Default is `created_at`
+- `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
+- `search` (optional) - Return list of authorized projects according to a search criteria
+- `ci_enabled_first` - Return projects ordered by ci_enabled flag. Projects with enabled GitLab CI go first
+
+```json
+[
+ {
+ "id": 4,
+ "description": null,
+ "default_branch": "master",
+ "public": false,
+ "visibility_level": 0,
+ "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",
+ "tag_list": [
+ "example",
+ "disapora client"
+ ],
+ "owner": {
+ "id": 3,
+ "name": "Diaspora",
+ "created_at": "2013-09-30T13: 46: 02Z"
+ },
+ "name": "Diaspora Client",
+ "name_with_namespace": "Diaspora / Diaspora Client",
+ "path": "diaspora-client",
+ "path_with_namespace": "diaspora/diaspora-client",
+ "issues_enabled": true,
+ "merge_requests_enabled": true,
+ "builds_enabled": true,
+ "wiki_enabled": true,
+ "snippets_enabled": false,
+ "created_at": "2013-09-30T13: 46: 02Z",
+ "last_activity_at": "2013-09-30T13: 46: 02Z",
+ "creator_id": 3,
+ "namespace": {
+ "created_at": "2013-09-30T13: 46: 02Z",
+ "description": "",
+ "id": 3,
+ "name": "Diaspora",
+ "owner_id": 1,
+ "path": "diaspora",
+ "updated_at": "2013-09-30T13: 46: 02Z"
+ },
+ "archived": false,
+ "avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png"
+ }
+]
+```
+
## Details of a group
Get all details of a group.
@@ -186,7 +250,7 @@ To get more (up to 100), pass the following as an argument to the API call:
/groups?per_page=100
```
-And to switch pages add:
+And to switch pages add:
```
/groups?per_page=100&page=2
-``` \ No newline at end of file
+```
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 82f2cef969f..366a1f8abec 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -335,9 +335,57 @@ PUT /projects/:id/merge_request/:merge_request_id/merge
Parameters:
-- `id` (required) - The ID of a project
-- `merge_request_id` (required) - ID of MR
-- `merge_commit_message` (optional) - Custom merge commit message
+- `id` (required) - The ID of a project
+- `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
+- `merged_when_build_succeeds` (optional) - if `true` the MR is merge when the build succeeds
+
+```json
+{
+ "id": 1,
+ "target_branch": "master",
+ "source_branch": "test1",
+ "project_id": 3,
+ "title": "test1",
+ "state": "merged",
+ "upvotes": 0,
+ "downvotes": 0,
+ "author": {
+ "id": 1,
+ "username": "admin",
+ "email": "admin@example.com",
+ "name": "Administrator",
+ "state": "active",
+ "created_at": "2012-04-29T08:46:00Z"
+ },
+ "assignee": {
+ "id": 1,
+ "username": "admin",
+ "email": "admin@example.com",
+ "name": "Administrator",
+ "state": "active",
+ "created_at": "2012-04-29T08:46:00Z"
+ }
+}
+```
+
+## Cancel Merge When Build Succeeds
+
+If successful you'll get `200 OK`.
+
+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.
+```
+PUT /projects/:id/merge_request/:merge_request_id/cancel_merge_when_build_succeeds
+```
+Parameters:
+
+- `id` (required) - The ID of a project
+- `merge_request_id` (required) - ID of MR
```json
{
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 42919a312ae..43a50a9a810 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -398,7 +398,6 @@ Parameters:
- `user_id` (required) - user_id of owner
- `name` (required) - new project name
- `description` (optional) - short project description
-- `default_branch` (optional) - 'master' by default
- `issues_enabled` (optional)
- `merge_requests_enabled` (optional)
- `builds_enabled` (optional)
diff --git a/doc/ci/README.md b/doc/ci/README.md
index 97325069ceb..5d9d7a81db3 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -9,6 +9,18 @@
+ [Using Docker Images](docker/using_docker_images.md)
+ [Using Docker Build](docker/using_docker_build.md)
+ [Using Variables](variables/README.md)
++ [Using SSH keys](ssh_keys/README.md)
+
+### Languages
+
++ [Testing PHP](languages/php.md)
+
+### Services
+
++ [Using MySQL](services/mysql.md)
++ [Using PostgreSQL](services/postgres.md)
++ [Using Redis](services/redis.md)
++ [Using Other Services](docker/using_docker_images.md#how-to-use-other-images-as-services)
### Examples
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index 1feae62b1c7..8d4bd44053e 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -1,19 +1,29 @@
# Using Docker Images
-GitLab CI can use [Docker Engine](https://www.docker.com/) to build projects.
-Docker is an open-source project that allows to use predefined images to run applications
-in independent "containers" that are run within a single Linux instance.
-[Docker Hub](https://registry.hub.docker.com/) have rich database of built images that can be used to build applications.
+GitLab CI in conjuction with [GitLab Runner](../runners/README.md) can use
+[Docker Engine](https://www.docker.com/) to test and build any application.
-Docker when used with GitLab CI runs each build in separate and isolated container using predefined image and always from scratch.
-It makes it easier to have simple and reproducible build environment that can also be run on your workstation.
-This allows you to test all commands from your shell, rather than having to test them on a CI server.
+Docker is an open-source project that allows you to use predefined images to
+run applications in independent "containers" that are run within a single Linux
+instance. [Docker Hub][hub] has a rich database of prebuilt images that can be
+used to test and build your applications.
-### Register Docker runner
-To use GitLab Runner with Docker you need to register new runner to use `docker` executor:
+Docker, when used with GitLab CI, runs each build in a separate and isolated
+container using the predefined image that is set up in
+[`.gitlab-ci.yml`](../yaml/README.md).
+
+This makes it easier to have a simple and reproducible build environment that
+can also run on your workstation. The added benefit is that you can test all
+the commands that we will explore later from your shell, rather than having to
+test them on a dedicated CI server.
+
+## Register docker runner
+
+To use GitLab Runner with docker you need to register a new runner to use the
+`docker` executor:
```bash
-gitlab-ci-multi-runner register \
+gitlab-runner register \
--url "https://gitlab.com/" \
--registration-token "PROJECT_REGISTRATION_TOKEN" \
--description "docker-ruby-2.1" \
@@ -23,101 +33,79 @@ gitlab-ci-multi-runner register \
--docker-mysql latest
```
-**The registered runner will use `ruby:2.1` image and will run two services (`postgres:latest` and `mysql:latest`) that will be accessible for time of the build.**
+The registered runner will use the `ruby:2.1` docker image and will run two
+services, `postgres:latest` and `mysql:latest`, both of which will be
+accessible during the build process.
-### What is image?
-The image is the name of any repository that is present in local Docker Engine or any repository that can be found at [Docker Hub](https://registry.hub.docker.com/).
-For more information about the image and Docker Hub please read the [Docker Fundamentals](https://docs.docker.com/introduction/understanding-docker/).
+## What is image
-### What is service?
-Service is just another image that is run for time of your build and is linked to your build. This allows you to access the service image during build time.
-The service image can run any application, but most common use case is to run some database container, ie.: `mysql`.
-It's easier and faster to use existing image, run it as additional container than install `mysql` every time project is built.
+The `image` keyword is the name of the docker image that is present in the
+local Docker Engine (list all images with `docker images`) or any image that
+can be found at [Docker Hub][hub]. For more information about images and Docker
+Hub please read the [Docker Fundamentals][] documentation.
-#### How is service linked to the build?
-There's good document that describes how Docker linking works: [Linking containers together](https://docs.docker.com/userguide/dockerlinks/).
-To summarize: if you add `mysql` as service to your application, the image will be used to create container that is linked to build container.
-The service container for MySQL will be accessible under hostname `mysql`.
-So, **to access your database service you have to connect to host: `mysql` instead of socket or `localhost`**.
+In short, with `image` we refer to the docker image, which will be used to
+create a container on which your build will run.
-### How to use other images as services?
-You are not limited to have only database services.
-You can hand modify `config.toml` to add any image as service found at [Docker Hub](https://registry.hub.docker.com/).
-Look for `[runners.docker]` section:
-```
-[runners.docker]
- image = "ruby:2.1"
- services = ["mysql:latest", "postgres:latest"]
-```
+## What is service
-For example you need `wordpress` instance to test some API integration with `Wordpress`.
-You can for example use this image: [tutum/wordpress](https://registry.hub.docker.com/u/tutum/wordpress/).
-This is image that have fully preconfigured `wordpress` and have `MySQL` server built-in:
-```
-[runners.docker]
- image = "ruby:2.1"
- services = ["mysql:latest", "postgres:latest", "tutum/wordpress:latest"]
-```
+The `services` keyword defines just another docker image that is run during
+your build and is linked to the docker image that the `image` keyword defines.
+This allows you to access the service image during build time.
-Next time when you run your application the `tutum/wordpress` will be started
-and you will have access to it from your build container under hostname: `tutum__wordpress`.
+The service image can run any application, but the most common use case is to
+run a database container, eg. `mysql`. It's easier and faster to use an
+existing image and run it as an additional container than install `mysql` every
+time the project is built.
-Alias hostname for the service is made from the image name:
-1. Everything after `:` is stripped,
-2. '/' is replaced with `__`.
+You can see some widely used services examples in the relevant documentation of
+[CI services examples](../services/README.md).
-### Configuring services
-Many services accept environment variables, which allow you to easily change database names or set account names depending on the environment.
+### How is service linked to the build
-GitLab Runner 0.5.0 and up passes all YAML-defined variables to created service containers.
+To better understand how the container linking works, read
+[Linking containers together](https://docs.docker.com/userguide/dockerlinks/).
-1. To configure database name for [postgres](https://registry.hub.docker.com/u/library/postgres/) service,
-you need to set POSTGRES_DB.
+To summarize, if you add `mysql` as service to your application, the image will
+then be used to create a container that is linked to the build container.
- ```yaml
- services:
- - postgres
-
- variables:
- POSTGRES_DB: gitlab
- ```
+The service container for MySQL will be accessible under the hostname `mysql`.
+So, in order to access your database service you have to connect to the host
+named `mysql` instead of a socket or `localhost`.
-1. To use [mysql](https://registry.hub.docker.com/u/library/mysql/) service with empty password for time of build,
-you need to set MYSQL_ALLOW_EMPTY_PASSWORD.
+## Overwrite image and services
- ```yaml
- services:
- - mysql
-
- variables:
- MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
- ```
+See [How to use other images as services](#how-to-use-other-images-as-services).
-For other possible configuration variables check the
-https://registry.hub.docker.com/u/library/mysql/ or https://registry.hub.docker.com/u/library/postgres/
-or README page for any other Docker image.
+## How to use other images as services
-**Note: All variables will passed to all service containers. It's not designed to distinguish which variable should go where.**
+You are not limited to have only database services. You can add as many
+services you need to `.gitlab-ci.yml` or manually modify `config.toml`.
+Any image found at [Docker Hub][hub] can be used as a service.
-### Overwrite image and services
-It's possible to overwrite `docker-image` and specify services from `.gitlab-ci.yml`.
-If you add to your YAML the `image` and the `services` these parameters
-be used instead of the ones that were specified during runner's registration.
-```
+## Define image and services from `.gitlab-ci.yml`
+
+You can simply define an image that will be used for all jobs and a list of
+services that you want to use during build time.
+
+```yaml
image: ruby:2.2
+
services:
- postgres:9.3
-before_install:
+
+before_script:
- bundle install
-
+
test:
script:
- bundle exec rake spec
```
-It's possible to define image and service per-job:
-```
-before_install:
+It is also possible to define different images and services per job:
+
+```yaml
+before_script:
- bundle install
test:2.1:
@@ -135,34 +123,73 @@ test:2.2:
- bundle exec rake spec
```
-#### How to enable overwriting?
-To enable overwriting you have to **enable it first** (it's disabled by default for security reasons).
-You can do that by hand modifying runner configuration: `config.toml`.
-Please go to section where is `[runners.docker]` definition for your runner.
-Add `allowed_images` and `allowed_services` to specify what images are allowed to be picked from `.gitlab-ci.yml`:
+## Define image and services in `config.toml`
+
+Look for the `[runners.docker]` section:
+
```
[runners.docker]
image = "ruby:2.1"
- allowed_images = ["ruby:*", "python:*"]
- allowed_services = ["mysql:*", "redis:*"]
+ services = ["mysql:latest", "postgres:latest"]
```
-This enables you to use in your `.gitlab-ci.yml` any image that matches above wildcards.
-You will be able to pick only `ruby` and `python` images.
-The same rule can be applied to limit services.
-If you are courageous enough, you can make it fully open and accept everything:
-```
-[runners.docker]
- image = "ruby:2.1"
- allowed_images = ["*", "*/*"]
- allowed_services = ["*", "*/*"]
+The image and services defined this way will be added to all builds run by
+that runner.
+
+## Accessing the services
+
+Let's say that you need a Wordpress instance to test some API integration with
+your application.
+
+You can then use for example the [tutum/wordpress][] image in your
+`.gitlab-ci.yml`:
+
+```yaml
+services:
+- tutum/wordpress:latest
```
-**It the feature is not enabled, or image isn't allowed the error message will be put into the build log.**
+When the build is run, `tutum/wordpress` will be started and you will have
+access to it from your build container under the hostname `tutum__wordpress`.
+
+The alias hostname for the service is made from the image name following these
+rules:
+
+1. Everything after `:` is stripped
+2. Backslash (`/`) is replaced with double underscores (`__`)
+
+## Configuring services
+
+Many services accept environment variables which allow you to easily change
+database names or set account names depending on the environment.
+
+GitLab Runner 0.5.0 and up passes all YAML-defined variables to the created
+service containers.
+
+For all possible configuration variables check the documentation of each image
+provided in their corresponding Docker hub page.
+
+*Note: All variables will be passed to all services containers. It's not
+designed to distinguish which variable should go where.*
+
+### PostgreSQL service example
+
+See the specific documentation for
+[using PostgreSQL as a service](../services/postgres.md).
+
+### MySQL service example
+
+See the specific documentation for
+[using MySQL as a service](../services/mysql.md).
+
+## How Docker integration works
+
+Below is a high level overview of the steps performed by docker during build
+time.
-### How Docker integration works
1. Create any service container: `mysql`, `postgresql`, `mongodb`, `redis`.
-1. Create cache container to store all volumes as defined in `config.toml` and `Dockerfile` of build image (`ruby:2.1` as in above example).
+1. Create cache container to store all volumes as defined in `config.toml` and
+ `Dockerfile` of build image (`ruby:2.1` as in above example).
1. Create build container and link any service container to build container.
1. Start build container and send build script to the container.
1. Run build script.
@@ -171,33 +198,63 @@ If you are courageous enough, you can make it fully open and accept everything:
1. Check exit status of build script.
1. Remove build container and all created service containers.
-### How to debug a build locally
-1. Create a file with build script:
+## How to debug a build locally
+
+*Note: The following commands are run without root privileges. You should be
+able to run docker with your regular user account.*
+
+First start with creating a file named `build script`:
+
```bash
-$ cat <<EOF > build_script
+cat <<EOF > build_script
git clone https://gitlab.com/gitlab-org/gitlab-ci-multi-runner.git /builds/gitlab-org/gitlab-ci-multi-runner
cd /builds/gitlab-org/gitlab-ci-multi-runner
-make <- or any other build step
+make
EOF
```
-1. Create service containers:
+Here we use as an example the GitLab Runner repository which contains a
+Makefile, so running `make` will execute the commands defined in the Makefile.
+Your mileage may vary, so instead of `make` you could run the command which
+is specific to your project.
+
+Then create some service containers:
+
```
-$ docker run -d -n service-mysql mysql:latest
-$ docker run -d -n service-postgres postgres:latest
+docker run -d -n service-mysql mysql:latest
+docker run -d -n service-postgres postgres:latest
```
-This will create two service containers (MySQL and PostgreSQL).
-1. Create a build container and execute script in its context:
+This will create two service containers, named `service-mysql` and
+`service-postgres` which use the latest MySQL and PostgreSQL images
+respectively. They will both run in the background (`-d`).
+
+Finally, create a build container by executing the `build_script` file we
+created earlier:
+
```
-$ docker run --name build -i --link=service-mysql:mysql --link=service-postgres:postgres ruby:2.1 /bin/bash < build_script
+docker run --name build -i --link=service-mysql:mysql --link=service-postgres:postgres ruby:2.1 /bin/bash < build_script
```
-This will create build container that has two service containers linked.
-The build_script is piped using STDIN to bash interpreter which executes the build script in container.
-1. At the end remove all containers:
+The above command will create a container named `build` that is spawned from
+the `ruby:2.1` image and has two services linked to it. The `build_script` is
+piped using STDIN to the bash interpreter which in turn executes the
+`build_script` in the `build` container.
+
+When you finish testing and no longer need the containers, you can remove them
+with:
+
```
docker rm -f -v build service-mysql service-postgres
```
-This will forcefully (the `-f` switch) remove build container and service containers
-and all volumes (the `-v` switch) that were created with the container creation.
+
+This will forcefully (`-f`) remove the `build` container, the two service
+containers as well as all volumes (`-v`) that were created with the container
+creation.
+
+[Docker Fundamentals]: https://docs.docker.com/engine/introduction/understanding-docker/
+[hub]: https://hub.docker.com/
+[linking-containers]: https://docs.docker.com/engine/userguide/networking/default_network/dockerlinks/
+[tutum/wordpress]: https://registry.hub.docker.com/u/tutum/wordpress/
+[postgres-hub]: https://registry.hub.docker.com/u/library/postgres/
+[mysql-hub]: https://registry.hub.docker.com/u/library/mysql/
diff --git a/doc/ci/languages/README.md b/doc/ci/languages/README.md
new file mode 100644
index 00000000000..54b2343e08b
--- /dev/null
+++ b/doc/ci/languages/README.md
@@ -0,0 +1,7 @@
+### Languages
+
+This is a list of languages you can test with GitLab CI. Each section has
+comprehensive documentation and comes with a test repository hosted on
+GitLab.com
+
++ [Testing PHP](php.md)
diff --git a/doc/ci/languages/php.md b/doc/ci/languages/php.md
new file mode 100644
index 00000000000..dacb67fa3ff
--- /dev/null
+++ b/doc/ci/languages/php.md
@@ -0,0 +1,284 @@
+# Testing PHP projects
+
+This guide covers basic building instructions for PHP projects.
+
+There are covered two cases: testing using the Docker executor and testing
+using the Shell executor.
+
+## Test PHP projects using the Docker executor
+
+While it is possible to test PHP apps on any system, this would require manual
+configuration from the developer. To overcome this we will be using the
+official [PHP docker image][php-hub] that can be found in Docker Hub.
+
+This will allow us to test PHP projects against different versions of PHP.
+However, not everything is plug 'n' play, you still need to onfigure some
+things manually.
+
+As with every build, you need to create a valid `.gitlab-ci.yml` describing the
+build environment.
+
+Let's first specify the PHP image that will be used for the build process
+(you can read more about what an image means in the Runner's lingo reading
+about [Using Docker images](../docker/using_docker_images.md#what-is-image)).
+
+Start by adding the image to your `.gitlab-ci.yml`:
+
+```yaml
+image: php:5.6
+```
+
+The official images are great, but they lack a few useful tools for testing.
+We need to first prepare the build environment. A way to overcome this is to
+create a script which installs all prerequisites prior the actual testing is
+done.
+
+Let's create a `ci/docker_install.sh` file in the root directory of our
+repository with the following content:
+
+```bash
+#!/bin/bash
+
+# We need to install dependencies only for Docker
+[[ ! -e /.dockerinit ]] && exit 0
+
+set -xe
+
+# Install git (the php image doesn't have it) which is required by composer
+apt-get update -yqq
+apt-get install git -yqq
+
+# Install phpunit, the tool that we will use for testing
+curl -o /usr/local/bin/phpunit https://phar.phpunit.de/phpunit.phar
+chmod +x /usr/local/bin/phpunit
+
+# Install mysql driver
+# Here you can install any other extension that you need
+docker-php-ext-install pdo_mysql
+```
+
+You might wonder what `docker-php-ext-install` is. In short, it is a script
+provided by the official php docker image that you can use to easilly install
+extensions. For more information read the the documentation at
+<https://hub.docker.com/_/php/>.
+
+Now that we created the script that contains all prerequisites for our build
+environment, let's add it in `.gitlab-ci.yml`:
+
+```yaml
+...
+
+before_script:
+- bash ci/docker_install.sh > /dev/null
+
+...
+```
+
+Last step, run the actual tests using `phpunit`:
+
+```yaml
+...
+
+test:app:
+ script:
+ - phpunit --configuration phpunit_myapp.xml
+
+...
+```
+
+Finally, commit your files and push them to GitLab to see your build succeeding
+(or failing).
+
+The final `.gitlab-ci.yml` should look similar to this:
+
+```yaml
+# Select image from https://hub.docker.com/_/php/
+image: php:5.6
+
+before_script:
+# Install dependencies
+- ci/docker_install.sh > /dev/null
+
+test:app:
+ script:
+ - phpunit --configuration phpunit_myapp.xml
+```
+
+### Test against different PHP versions in Docker builds
+
+Testing against multiple versions of PHP is super easy. Just add another job
+with a different docker image version and the runner will do the rest:
+
+```yaml
+before_script:
+# Install dependencies
+- ci/docker_install.sh > /dev/null
+
+# We test PHP5.6
+test:5.6:
+ image: php:5.6
+ script:
+ - phpunit --configuration phpunit_myapp.xml
+
+# We test PHP7.0 (good luck with that)
+test:7.0:
+ image: php:7.0
+ script:
+ - phpunit --configuration phpunit_myapp.xml
+```
+
+### Custom PHP configuration in Docker builds
+
+There are times where you will need to customise your PHP environment by
+putting your `.ini` file into `/usr/local/etc/php/conf.d/`. For that purpose
+add a `before_script` action:
+
+```yaml
+before_script:
+- cp my_php.ini /usr/local/etc/php/conf.d/test.ini
+```
+
+Of course, `my_php.ini` must be present in the root directory of your repository.
+
+## Test PHP projects using the Shell executor
+
+The shell executor runs your builds in a terminal session on your server.
+Thus, in order to test your projects you first need to make sure that all
+dependencies are installed.
+
+For example, in a VM running Debian 8 we first update the cache, then we
+install `phpunit` and `php5-mysql`:
+
+```bash
+sudo apt-get update -y
+sudo apt-get install -y phpunit php5-mysql
+```
+
+Next, add the following snippet to your `.gitlab-ci.yml`:
+
+```yaml
+test:app:
+ script:
+ - phpunit --configuration phpunit_myapp.xml
+```
+
+Finally, push to GitLab and let the tests begin!
+
+### Test against different PHP versions in Shell builds
+
+The [phpenv][] project allows you to easily manage different versions of PHP
+each with its own config. This is specially usefull when testing PHP projects
+with the Shell executor.
+
+You will have to install it on your build machine under the `gitlab-runner`
+user following [the upstream installation guide][phpenv-installation].
+
+Using phpenv also allows to easily configure the PHP environment with:
+
+```
+phpenv config-add my_config.ini
+```
+
+*__Important note:__ It seems `phpenv/phpenv`
+ [is abandoned](https://github.com/phpenv/phpenv/issues/57). There is a fork
+ at [madumlao/phpenv](https://github.com/madumlao/phpenv) that tries to bring
+ the project back to life. [CHH/phpenv](https://github.com/CHH/phpenv) also
+ seems like a good alternative. Picking any of the mentioned tools will work
+ with the basic phpenv commands. Guiding you to choose the right phpenv is out
+ of the scope of this tutorial.*
+
+### Install custom extensions
+
+Since this is a pretty bare installation of the PHP environment, you may need
+some extensions that are not currently present on the build machine.
+
+To install additional extensions simply execute:
+
+```bash
+pecl install <extension>
+```
+
+It's not advised to add this to `.gitlab-ci.yml`. You should execute this
+command once, only to setup the build environment.
+
+## Extend your tests
+
+### Using atoum
+
+Instead of PHPUnit, you can use any other tool to run unit tests. For example
+you can use [atoum](https://github.com/atoum/atoum):
+
+```yaml
+before_script:
+- wget http://downloads.atoum.org/nightly/mageekguy.atoum.phar
+
+test:atoum:
+ script:
+ - php mageekguy.atoum.phar
+```
+
+### Using Composer
+
+The majority of the PHP projects use Composer for managing their PHP packages.
+In order to execute Composer before running your tests, simply add the
+following in your `.gitlab-ci.yml`:
+
+```yaml
+...
+
+# Composer stores all downloaded packages in the vendor/ directory.
+# Do not use the following if the vendor/ directory is commited to
+# your git repository.
+cache:
+ paths:
+ - vendor/
+
+before_script:
+# Install composer dependencies
+- curl -sS https://getcomposer.org/installer | php
+- php composer.phar install
+
+...
+```
+
+## Access private packages / dependencies
+
+If your test suite needs to access a private repository, you need to configure
+[the SSH keys](../ssh_keys/README.md) in order to be able to clone it.
+
+## Use databases or other services
+
+Most of the time you will need a running database in order for your tests to
+run. If you are using the Docker executor you can leverage Docker's ability to
+link to other containers. In GitLab Runner lingo, this can be achieved by
+defining a `service`.
+
+This functionality is covered in [the CI services](../services/README.md)
+documentation.
+
+## Testing things locally
+
+With GitLab Runner 1.0 you can also test any changes locally. From your
+terminal execute:
+
+```bash
+# Check using docker executor
+gitlab-runner exec docker test:app
+
+# Check using shell executor
+gitlab-runner exec shell test:app
+```
+
+## Example project
+
+We have set up an [Example PHP Project][php-example-repo] for your convenience
+that runs on [GitLab.com](https://gitlab.com) using our publicly available
+[shared runners](../runners/README.md).
+
+Want to hack on it? Simply fork it, commit and push your changes. Within a few
+moments the changes will be picked by a public runner and the build will begin.
+
+[php-hub]: https://hub.docker.com/_/php/
+[phpenv]: https://github.com/phpenv/phpenv
+[phpenv-installation]: https://github.com/phpenv/phpenv#installation
+[php-example-repo]: https://gitlab.com/gitlab-examples/php
diff --git a/doc/ci/services/README.md b/doc/ci/services/README.md
new file mode 100644
index 00000000000..1ebb0a4a250
--- /dev/null
+++ b/doc/ci/services/README.md
@@ -0,0 +1,9 @@
+## GitLab CI Services
+
+GitLab CI uses the `services` keyword to define what docker containers should be
+linked with your base image. Below is a list of examples you may use.
+
++ [Using MySQL](mysql.md)
++ [Using PostgreSQL](postgres.md)
++ [Using Redis](redis.md)
++ [Using Other Services](../docker/using_docker_images.md#how-to-use-other-images-as-services)
diff --git a/doc/ci/services/docker-services.md b/doc/ci/services/docker-services.md
new file mode 100644
index 00000000000..df36ebaf7d4
--- /dev/null
+++ b/doc/ci/services/docker-services.md
@@ -0,0 +1,5 @@
+## GitLab CI Services
+
++ [Using MySQL](mysql.md)
++ [Using PostgreSQL](postgres.md)
++ [Using Redis](redis.md)
diff --git a/doc/ci/services/mysql.md b/doc/ci/services/mysql.md
new file mode 100644
index 00000000000..c66d77122b2
--- /dev/null
+++ b/doc/ci/services/mysql.md
@@ -0,0 +1,118 @@
+# Using MySQL
+
+As many applications depend on MySQL as their database, you will eventually
+need it in order for your tests to run. Below you are guided how to do this
+with the Docker and Shell executors of GitLab Runner.
+
+## Use MySQL with the Docker executor
+
+If you are using [GitLab Runner](../runners/README.md) with the Docker executor
+you basically have everything set up already.
+
+First, in your `.gitlab-ci.yml` add:
+
+```yaml
+services:
+ - mysql:latest
+
+variables:
+ # Configure mysql environment variables (https://hub.docker.com/_/mysql/)
+ MYSQL_DATABASE: el_duderino
+ MYSQL_ROOT_PASSWORD: mysql_strong_password
+```
+
+And then configure your application to use the database, for example:
+
+```yaml
+Host: mysql
+User: root
+Password: mysql_strong_password
+Database: el_duderino
+```
+
+If you are wondering why we used `mysql` for the `Host`, read more at
+[How is service linked to the build](../docker/using_docker_images.md#how-is-service-linked-to-the-build).
+
+You can also use any other docker image available on [Docker Hub][hub-mysql].
+For example, to use MySQL 5.5 the service becomes `mysql:5.5`.
+
+The `mysql` image can accept some environment variables. For more details
+check the documentation on [Docker Hub][hub-mysql].
+
+## Use MySQL with the Shell executor
+
+You can also use MySQL on manually configured servers that are using
+GitLab Runner with the Shell executor.
+
+First install the MySQL server:
+
+```bash
+sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
+```
+
+Pick a MySQL root password (can be anything), and type it twice when asked.
+
+*Note: As a security measure you can run `mysql_secure_installation` to
+remove anonymous users, drop the test database and disable remote logins with
+the root user.*
+
+The next step is to create a user, so login to MySQL as root:
+
+```bash
+mysql -u root -p
+```
+
+Then create a user (in our case `runner`) which will be used by your
+application. Change `$password` in the command below to a real strong password.
+
+*Note: Do not type `mysql>`, this is part of the MySQL prompt.*
+
+```bash
+mysql> CREATE USER 'runner'@'localhost' IDENTIFIED BY '$password';
+```
+
+Create the database:
+
+```bash
+mysql> CREATE DATABASE IF NOT EXISTS `el_duderino` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`;
+```
+
+Grant the necessary permissions on the database:
+
+```bash
+mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, CREATE TEMPORARY TABLES, DROP, INDEX, ALTER, LOCK TABLES ON `el_duderino`.* TO 'runner'@'localhost';
+```
+
+If all went well you can now quit the database session:
+
+```bash
+mysql> \q
+```
+
+Now, try to connect to the newly created database to check that everything is
+in place:
+
+```bash
+mysql -u runner -p -D el_duderino
+```
+
+As a final step, configure your application to use the database, for example:
+
+```bash
+Host: localhost
+User: runner
+Password: $password
+Database: el_duderino
+```
+
+## Example project
+
+We have set up an [Example MySQL Project][mysql-example-repo] for your
+convenience that runs on [GitLab.com](https://gitlab.com) using our publicly
+available [shared runners](../runners/README.md).
+
+Want to hack on it? Simply fork it, commit and push your changes. Within a few
+moments the changes will be picked by a public runner and the build will begin.
+
+[hub-mysql]: https://hub.docker.com/_/mysql/
+[mysql-example-repo]: https://gitlab.com/gitlab-examples/mysql
diff --git a/doc/ci/services/postgres.md b/doc/ci/services/postgres.md
new file mode 100644
index 00000000000..17d21dbda1c
--- /dev/null
+++ b/doc/ci/services/postgres.md
@@ -0,0 +1,114 @@
+# Using PostgreSQL
+
+As many applications depend on PostgreSQL as their database, you will
+eventually need it in order for your tests to run. Below you are guided how to
+do this with the Docker and Shell executors of GitLab Runner.
+
+## Use PostgreSQL with the Docker executor
+
+If you are using [GitLab Runner](../runners/README.md) with the Docker executor
+you basically have everything set up already.
+
+First, in your `.gitlab-ci.yml` add:
+
+```yaml
+services:
+ - postgres:latest
+
+variables:
+ POSTGRES_DB: nice_marmot
+ POSTGRES_USER: runner
+ POSTGRES_PASSWORD: ""
+```
+
+And then configure your application to use the database, for example:
+
+```yaml
+Host: postgres
+User: runner
+Password:
+Database: nice_marmot
+```
+
+If you are wondering why we used `postgres` for the `Host`, read more at
+[How is service linked to the build](../docker/using_docker_images.md#how-is-service-linked-to-the-build).
+
+You can also use any other docker image available on [Docker Hub][hub-pg].
+For example, to use PostgreSQL 9.3 the service becomes `postgres:9.3`.
+
+The `postgres` image can accept some environment variables. For more details
+check the documentation on [Docker Hub][hub-pg].
+
+## Use PostgreSQL with the Shell executor
+
+You can also use PostgreSQL on manually configured servers that are using
+GitLab Runner with the Shell executor.
+
+First install the PostgreSQL server:
+
+```bash
+sudo apt-get install -y postgresql postgresql-client libpq-dev
+```
+
+The next step is to create a user, so login to PostgreSQL:
+
+```bash
+sudo -u postgres psql -d template1
+```
+
+Then create a user (in our case `runner`) which will be used by your
+application. Change `$password` in the command below to a real strong password.
+
+*__Note:__ Do not type `template1=#`, this is part of the PostgreSQL prompt.*
+
+```bash
+template1=# CREATE USER runner WITH PASSWORD '$password' CREATEDB;
+```
+
+*__Note:__ Notice that we created the user with the privilege to be able to
+create databases (`CREATEDB`). In the following steps we will create a database
+explicitly for that user but having that privilege can be useful if in your
+testing framework you have tools that drop and create databases.*
+
+Create the database and grant all privileges on it for the user `runner`:
+
+```bash
+template1=# CREATE DATABASE nice_marmot OWNER runner;
+```
+
+If all went well you can now quit the database session:
+
+```bash
+template1=# \q
+```
+
+Now, try to connect to the newly created database with the user `runner` to
+check that everything is in place.
+
+```bash
+psql -U runner -h localhost -d nice_marmot -W
+```
+
+*__Note:__ We are explicitly telling `psql` to connect to localhost in order
+to use the md5 authentication. If you omit this step you will be denied access.*
+
+Finally, configure your application to use the database, for example:
+
+```yaml
+Host: localhost
+User: runner
+Password: $password
+Database: nice_marmot
+```
+
+## Example project
+
+We have set up an [Example PostgreSQL Project][postgres-example-repo] for your
+convenience that runs on [GitLab.com](https://gitlab.com) using our publicly
+available [shared runners](../runners/README.md).
+
+Want to hack on it? Simply fork it, commit and push your changes. Within a few
+moments the changes will be picked by a public runner and the build will begin.
+
+[hub-pg]: https://hub.docker.com/_/postgres/
+[postgres-example-repo]: https://gitlab.com/gitlab-examples/postgres
diff --git a/doc/ci/services/redis.md b/doc/ci/services/redis.md
new file mode 100644
index 00000000000..b281e8f9f60
--- /dev/null
+++ b/doc/ci/services/redis.md
@@ -0,0 +1,69 @@
+# Using Redis
+
+As many applications depend on Redis as their key-value store, you will
+eventually need it in order for your tests to run. Below you are guided how to
+do this with the Docker and Shell executors of GitLab Runner.
+
+## Use Redis with the Docker executor
+
+If you are using [GitLab Runner](../runners/README.md) with the Docker executor
+you basically have everything set up already.
+
+First, in your `.gitlab-ci.yml` add:
+
+```yaml
+services:
+ - redis:latest
+```
+
+Then you need to configure your application to use the Redis database, for
+example:
+
+```yaml
+Host: redis
+```
+
+And that's it. Redis will now be available to be used within your testing
+framework.
+
+You can also use any other docker image available on [Docker Hub][hub-redis].
+For example, to use Redis 2.8 the service becomes `redis:2.8`.
+
+## Use Redis with the Shell executor
+
+Redis can also be used on manually configured servers that are using GitLab
+Runner with the Shell executor.
+
+In your build machine install the Redis server:
+
+```bash
+sudo apt-get install redis-server
+```
+
+Verify that you can connect to the server with the `gitlab-runner` user:
+
+```bash
+# Try connecting the the Redis server
+sudo -u gitlab-runner -H redis-cli
+
+# Quit the session
+127.0.0.1:6379> quit
+```
+
+Finally, configure your application to use the database, for example:
+
+```yaml
+Host: localhost
+```
+
+## Example project
+
+We have set up an [Example Redis Project][redis-example-repo] for your convenience
+that runs on [GitLab.com](https://gitlab.com) using our publicly available
+[shared runners](../runners/README.md).
+
+Want to hack on it? Simply fork it, commit and push your changes. Within a few
+moments the changes will be picked by a public runner and the build will begin.
+
+[hub-redis]: https://hub.docker.com/_/redis/
+[redis-example-repo]: https://gitlab.com/gitlab-examples/redis
diff --git a/doc/ci/ssh_keys/README.md b/doc/ci/ssh_keys/README.md
new file mode 100644
index 00000000000..210f9c3e849
--- /dev/null
+++ b/doc/ci/ssh_keys/README.md
@@ -0,0 +1,109 @@
+# Using SSH keys
+
+GitLab currently doesn't have built-in support for managing SSH keys in a build
+environment.
+
+The SSH keys can be useful when:
+
+1. You want to checkout internal submodules
+2. You want to download private packages using your package manager (eg. bundler)
+3. You want to deploy your application to eg. Heroku or your own server
+4. You want to execute SSH commands from the build server to the remote server
+5. You want to rsync files from your build server to the remote server
+
+If anything of the above rings a bell, then you most likely need an SSH key.
+
+## Inject keys in your build server
+
+The most widely supported method is to inject an SSH key into your build
+environment by extending your `.gitlab-ci.yml`.
+
+This is the universal solution which works with any type of executor
+(docker, shell, etc.).
+
+### How it works
+
+1. Create a new SSH key pair with [ssh-keygen][]
+2. Add the private key as a **Secret Variable** to the project
+3. Run the [ssh-agent][] during build to load the private key.
+
+## SSH keys when using the Docker executor
+
+You will first need to create an SSH key pair. For more information, follow the
+instructions to [generate an SSH key](../ssh/README.md).
+
+Then, create a new **Secret Variable** in your project settings on GitLab
+following **Settings > Variables**. As **Key** add the name `SSH_PRIVATE_KEY`
+and in the **Value** field paste the content of your _private_ key that you
+created earlier.
+
+Next you need to modify your `.gitlab-ci.yml` with a `before_script` action.
+Add it to the top:
+
+```
+before_script:
+ # Install ssh-agent if not already installed, it is required by Docker.
+ # (change apt-get to yum if you use a CentOS-based image)
+ - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
+
+ # Run ssh-agent (inside the build environment)
+ - eval $(ssh-agent -s)
+
+ # Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store
+ - ssh-add <(echo "$SSH_PRIVATE_KEY")
+
+ # For Docker builds disable host key checking. Be aware that by adding that
+ # you are suspectible to man-in-the-middle attacks.
+ # WARNING: Use this only with the Docker executor, if you use it with shell
+ # you will overwrite your user's SSH config.
+ - mkdir -p ~/.ssh
+ - '[[ -f /.dockerinit ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config`
+```
+
+As a final step, add the _public_ key from the one you created earlier to the
+services that you want to have an access to from within the build environment.
+If you are accessing a private GitLab repository you need to add it as a
+[deploy key](../ssh/README.md#deploy-keys).
+
+That's it! You can now have access to private servers or repositories in your
+build environment.
+
+## SSH keys when using the Shell executor
+
+If you are using the Shell executor and not Docker, it is easier to set up an
+SSH key.
+
+You can generate the SSH key from the machine that GitLab Runner is installed
+on, and use that key for all projects that are run on this machine.
+
+First, you need to login to the server that runs your builds.
+
+Then from the terminal login as the `gitlab-runner` user and generate the SSH
+key pair as described in the [SSH keys documentation](../ssh/README.md).
+
+As a final step, add the _public_ key from the one you created earlier to the
+services that you want to have an access to from within the build environment.
+If you are accessing a private GitLab repository you need to add it as a
+[deploy key](../ssh/README.md#deploy-keys).
+
+Once done, try to login to the remote server in order to accept the fingerprint:
+
+```bash
+ssh <address-of-my-server>
+```
+
+For accessing repositories on GitLab.com, the `<address-of-my-server>` would be
+`git@gitlab.com`.
+
+## Example project
+
+We have set up an [Example SSH Project][ssh-example-repo] for your convenience
+that runs on [GitLab.com](https://gitlab.com) using our publicly available
+[shared runners](../runners/README.md).
+
+Want to hack on it? Simply fork it, commit and push your changes. Within a few
+moments the changes will be picked by a public runner and the build will begin.
+
+[ssh-keygen]: http://linux.die.net/man/1/ssh-keygen
+[ssh-agent]: http://linux.die.net/man/1/ssh-agent
+[ssh-example-repo]: https://gitlab.com/gitlab-examples/ssh-private-key/
diff --git a/doc/customization/issue_closing.md b/doc/customization/issue_closing.md
index 64f128f5a63..00edfc97ed9 100644
--- a/doc/customization/issue_closing.md
+++ b/doc/customization/issue_closing.md
@@ -1,38 +1,39 @@
# Issue closing pattern
-Here's how to close multiple issues in one commit message:
+When a commit or merge request resolves one or more issues, it is possible to automatically have these issues closed when the commit or merge request lands in the project's default branch.
-If a commit message matches the regular expression below, all issues referenced from
-the matched text will be closed. This happens when the commit is pushed or merged
-into the default branch of a project.
+If a commit message or merge request description contains a sentence matching the regular expression below, all issues referenced from
+the matched text will be closed. This happens when the commit is pushed to a project's default branch, or when a commit or merge request is merged into there.
-When not specified, the default issue_closing_pattern as shown below will be used:
+When not specified, the default `issue_closing_pattern` as shown below will be used:
```bash
-((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)
+((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)
```
+Here, `%{issue_ref}` is a complex regular expression defined inside GitLab, that matches a reference to a local issue (`#123`), cross-project issue (`group/project#123`) or a link to an issue (`https://gitlab.example.com/group/project/issues/123`).
+
For example:
```
-git commit -m "Awesome commit message (Fix #20, Fixes #21 and Closes #22). This commit is also related to #17 and fixes #18, #19 and #23."
+git commit -m "Awesome commit message (Fix #20, Fixes #21 and Closes group/otherproject#2). This commit is also related to #17 and fixes #18, #19 and https://gitlab.example.com/group/otherproject/issues/23."
```
-will close `#20`, `#21`, `#22`, `#18`, `#19` and `#23`, but `#17` won't be closed
-as it does not match the pattern. It also works with multiline commit messages.
+will close `#18`, `#19`, `#20`, and `#21` in the project this commit is pushed to, as well as `#22` and `#23` in group/otherproject. `#17` won't be closed as it does not match the pattern. It also works with multiline commit messages.
Tip: you can test this closing pattern at [http://rubular.com][1]. Use this site
to test your own patterns.
+Because Rubular doesn't understand `%{issue_ref}`, you can replace this by `#\d+` in testing, which matches only local issue references like `#123`.
## Change the pattern
For Omnibus installs you can change the default pattern in `/etc/gitlab/gitlab.rb`:
```
-issue_closing_pattern: '((?:[Cc]los(?:e[sd]|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)'
+issue_closing_pattern: '((?:[Cc]los(?:e[sd]|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)'
```
-For manual installs you can customize the pattern in [gitlab.yml][0].
+For manual installs you can customize the pattern in [gitlab.yml][0] using the `issue_closing_pattern` key.
-[0]: https://gitlab.com/gitlab-org/gitlab-ce/blob/40c3675372320febf5264061c9bcd63db2dfd13c/config/gitlab.yml.example#L65
-[1]: http://rubular.com/r/Xmbexed1OJ \ No newline at end of file
+[0]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/gitlab.yml.example
+[1]: http://rubular.com/r/Xmbexed1OJ
diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md
index 6a0fa4ce015..63432b04432 100644
--- a/doc/integration/bitbucket.md
+++ b/doc/integration/bitbucket.md
@@ -30,7 +30,7 @@ Bitbucket will generate an application ID and secret key for you to use.
sudo editor /etc/gitlab/gitlab.rb
```
- For instalations from source:
+ For installations from source:
```sh
cd /home/git/gitlab
diff --git a/doc/integration/crowd.md b/doc/integration/crowd.md
index 2ecc8795ac1..40d93aef2a9 100644
--- a/doc/integration/crowd.md
+++ b/doc/integration/crowd.md
@@ -10,7 +10,7 @@ To enable the Crowd OmniAuth provider you must register your application with Cr
sudo editor /etc/gitlab/gitlab.rb
```
- For instalations from source:
+ For installations from source:
```sh
cd /home/git/gitlab
diff --git a/doc/integration/github.md b/doc/integration/github.md
index b64501c2aaa..a789d2c814f 100644
--- a/doc/integration/github.md
+++ b/doc/integration/github.md
@@ -32,7 +32,7 @@ GitHub will generate an application ID and secret key for you to use.
sudo editor /etc/gitlab/gitlab.rb
```
- For instalations from source:
+ For installations from source:
```sh
cd /home/git/gitlab
diff --git a/doc/integration/gitlab.md b/doc/integration/gitlab.md
index 216f1f11a9b..80e3c0142a0 100644
--- a/doc/integration/gitlab.md
+++ b/doc/integration/gitlab.md
@@ -38,7 +38,7 @@ GitLab.com will generate an application ID and secret key for you to use.
sudo editor /etc/gitlab/gitlab.rb
```
- For instalations from source:
+ For installations from source:
```sh
cd /home/git/gitlab
diff --git a/doc/integration/google.md b/doc/integration/google.md
index e1c14c7c948..91e9b2495cc 100644
--- a/doc/integration/google.md
+++ b/doc/integration/google.md
@@ -35,7 +35,7 @@ To enable the Google OAuth2 OmniAuth provider you must register your application
sudo editor /etc/gitlab/gitlab.rb
```
- For instalations from source:
+ For installations from source:
```sh
cd /home/git/gitlab
diff --git a/doc/integration/ldap.md b/doc/integration/ldap.md
index 7e2920b8865..845f588f913 100644
--- a/doc/integration/ldap.md
+++ b/doc/integration/ldap.md
@@ -13,6 +13,12 @@ An LDAP user who is allowed to change their email on the LDAP server can [take o
We recommend against using GitLab LDAP integration if your LDAP users are allowed to change their 'mail', 'email' or 'userPrincipalName' attribute on the LDAP server.
+If a user is deleted from the LDAP server, they will be blocked in GitLab as well.
+Users will be immediately blocked from logging in. However, there is an LDAP check
+cache time of one hour. The means users that are already logged in or are using Git
+over SSH will still be able to access GitLab for up to one hour. Manually block
+the user in the GitLab Admin area to immediately block all access.
+
## Configuring GitLab for LDAP integration
To enable GitLab LDAP integration you need to add your LDAP server settings in `/etc/gitlab/gitlab.rb` or `/home/git/gitlab/config/gitlab.yml`.
@@ -192,4 +198,4 @@ Not supported by GitLab's configuration options.
When setting `method: ssl`, the underlying authentication method used by
`omniauth-ldap` is `simple_tls`. This method establishes TLS encryption with
the LDAP server before any LDAP-protocol data is exchanged but no validation of
-the LDAP server's SSL certificate is performed. \ No newline at end of file
+the LDAP server's SSL certificate is performed.
diff --git a/doc/integration/saml.md b/doc/integration/saml.md
index 4aa6dbe758a..1b8c28dd0f4 100644
--- a/doc/integration/saml.md
+++ b/doc/integration/saml.md
@@ -14,7 +14,7 @@ First configure SAML 2.0 support in GitLab, then register the GitLab application
sudo editor /etc/gitlab/gitlab.rb
```
- For instalations from source:
+ For installations from source:
```sh
cd /home/git/gitlab
diff --git a/doc/integration/twitter.md b/doc/integration/twitter.md
index 1350c8f693c..52ed4a22339 100644
--- a/doc/integration/twitter.md
+++ b/doc/integration/twitter.md
@@ -37,7 +37,7 @@ To enable the Twitter OmniAuth provider you must register your application with
sudo editor /etc/gitlab/gitlab.rb
```
- For instalations from source:
+ For installations from source:
```sh
cd /home/git/gitlab
diff --git a/doc/operations/moving_repositories.md b/doc/operations/moving_repositories.md
new file mode 100644
index 00000000000..39086b7a251
--- /dev/null
+++ b/doc/operations/moving_repositories.md
@@ -0,0 +1,180 @@
+# Moving repositories managed by GitLab
+
+Sometimes you need to move all repositories managed by GitLab to
+another filesystem or another server. In this document we will look
+at some of the ways you can copy all your repositories from
+`/var/opt/gitlab/git-data/repositories` to `/mnt/gitlab/repositories`.
+
+We will look at three scenarios: the target directory is empty, the
+target directory contains an outdated copy of the repositories, and
+how to deal with thousands of repositories.
+
+**Each of the approaches we list can/will overwrite data in the
+target directory `/mnt/gitlab/repositories`. Do not mix up the
+source and the target.**
+
+## Target directory is empty: use a tar pipe
+
+If the target directory `/mnt/gitlab/repositories` is empty the
+simplest thing to do is to use a tar pipe. This method has low
+overhead and tar is almost always already installed on your system.
+However, it is not possible to resume an interrupted tar pipe: if
+that happens then all data must be copied again.
+
+```
+# As the git user
+tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\
+ tar -C /mnt/gitlab/repositories -xf -
+```
+
+If you want to see progress, replace `-xf` with `-xvf`.
+
+### Tar pipe to another server
+
+You can also use a tar pipe to copy data to another server. If your
+'git' user has SSH access to the newserver as 'git@newserver', you
+can pipe the data through SSH.
+
+```
+# As the git user
+tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\
+ ssh git@newserver tar -C /mnt/gitlab/repositories -xf -
+```
+
+If you want to compress the data before it goes over the network
+(which will cost you CPU cycles) you can replace `ssh` with `ssh -C`.
+
+## The target directory contains an outdated copy of the repositories: use rsync
+
+If the target directory already contains a partial / outdated copy
+of the repositories it may be wasteful to copy all the data again
+with tar. In this scenario it is better to use rsync. This utility
+is either already installed on your system or easily installable
+via apt, yum etc.
+
+```
+# As the 'git' user
+rsync -a --delete /var/opt/gitlab/git-data/repositories/. \
+ /mnt/gitlab/repositories
+```
+
+The `/.` in the command above is very important, without it you can
+easily get the wrong directory structure in the target directory.
+If you want to see progress, replace `-a` with `-av`.
+
+### Single rsync to another server
+
+If the 'git' user on your source system has SSH access to the target
+server you can send the repositories over the network with rsync.
+
+```
+# As the 'git' user
+rsync -a --delete /var/opt/gitlab/git-data/repositories/. \
+ git@newserver:/mnt/gitlab/repositories
+```
+
+## Thousands of Git repositories: use one rsync per repository
+
+Every time you start an rsync job it has to inspect all files in
+the source directory, all files in the target directory, and then
+decide what files to copy or not. If the source or target directory
+has many contents this startup phase of rsync can become a burden
+for your GitLab server. In cases like this you can make rsync's
+life easier by dividing its work in smaller pieces, and sync one
+repository at a time.
+
+In addition to rsync we will use [GNU
+Parallel](http://www.gnu.org/software/parallel/). This utility is
+not included in GitLab so you need to install it yourself with apt
+or yum. Also note that the GitLab scripts we used below were added
+in GitLab 8.1.
+
+** This process does not clean up repositories at the target location that no
+longer exist at the source. ** If you start using your GitLab instance with
+`/mnt/gitlab/repositories`, you need to run `gitlab-rake gitlab:cleanup:repos`
+after switching to the new repository storage directory.
+
+### Parallel rsync for all repositories known to GitLab
+
+This will sync repositories with 10 rsync processes at a time. We keep
+track of progress so that the transfer can be restarted if necessary.
+
+First we create a new directory, owned by 'git', to hold transfer
+logs. We assume the directory is empty before we start the transfer
+procedure, and that we are the only ones writing files in it.
+
+```
+# Omnibus
+sudo mkdir /var/opt/gitlab/transfer-logs
+sudo chown git:git /var/opt/gitlab/transfer-logs
+
+# Source
+sudo -u git -H mkdir /home/git/transfer-logs
+```
+
+We seed the process with a list of the directories we want to copy.
+
+```
+# Omnibus
+sudo -u git sh -c 'gitlab-rake gitlab:list_repos > /var/opt/gitlab/transfer-logs/all-repos-$(date +%s).txt'
+
+# Source
+cd /home/git/gitlab
+sudo -u git -H sh -c 'bundle exec rake gitlab:list_repos > /home/git/transfer-logs/all-repos-$(date +%s).txt'
+```
+
+Now we can start the transfer. The command below is idempotent, and
+the number of jobs done by GNU Parallel should converge to zero. If it
+does not some repositories listed in all-repos-1234.txt may have been
+deleted/renamed before they could be copied.
+
+```
+# Omnibus
+sudo -u git sh -c '
+cat /var/opt/gitlab/transfer-logs/* | sort | uniq -u |\
+ /usr/bin/env JOBS=10 \
+ /opt/gitlab/embedded/service/gitlab-rails/bin/parallel-rsync-repos \
+ /var/opt/gitlab/transfer-logs/succes-$(date +%s).log \
+ /var/opt/gitlab/git-data/repositories \
+ /mnt/gitlab/repositories
+'
+
+# Source
+cd /home/git/gitlab
+sudo -u git -H sh -c '
+cat /home/git/transfer-logs/* | sort | uniq -u |\
+ /usr/bin/env JOBS=10 \
+ bin/parallel-rsync-repos \
+ /home/git/transfer-logs/succes-$(date +%s).log \
+ /home/git/repositories \
+ /mnt/gitlab/repositories
+`
+```
+
+### Parallel rsync only for repositories with recent activity
+
+Suppose you have already done one sync that started after 2015-10-1 12:00 UTC.
+Then you might only want to sync repositories that were changed via GitLab
+_after_ that time. You can use the 'SINCE' variable to tell 'rake
+gitlab:list_repos' to only print repositories with recent activity.
+
+```
+# Omnibus
+sudo gitlab-rake gitlab:list_repos SINCE='2015-10-1 12:00 UTC' |\
+ sudo -u git \
+ /usr/bin/env JOBS=10 \
+ /opt/gitlab/embedded/service/gitlab-rails/bin/parallel-rsync-repos \
+ succes-$(date +%s).log \
+ /var/opt/gitlab/git-data/repositories \
+ /mnt/gitlab/repositories
+
+# Source
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:list_repos SINCE='2015-10-1 12:00 UTC' |\
+ sudo -u git -H \
+ /usr/bin/env JOBS=10 \
+ bin/parallel-rsync-repos \
+ succes-$(date +%s).log \
+ /home/git/repositories \
+ /mnt/gitlab/repositories
+```
diff --git a/doc/raketasks/list_repos.md b/doc/raketasks/list_repos.md
new file mode 100644
index 00000000000..476428eb4f5
--- /dev/null
+++ b/doc/raketasks/list_repos.md
@@ -0,0 +1,30 @@
+# Listing repository directories
+
+You can print a list of all Git repositories on disk managed by
+GitLab with the following command:
+
+```
+# Omnibus
+sudo gitlab-rake gitlab:list_repos
+
+# Source
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:list_repos RAILS_ENV=production
+```
+
+If you only want to list projects with recent activity you can pass
+a date with the 'SINCE' environment variable. The time you specify
+is parsed by the Rails [TimeZone#parse
+function](http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html#method-i-parse).
+
+```
+# Omnibus
+sudo gitlab-rake gitlab:list_repos SINCE='Sep 1 2015'
+
+# Source
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:list_repos RAILS_ENV=production SINCE='Sep 1 2015'
+```
+
+Note that the projects listed are NOT sorted by activity; they use
+the default ordering of the GitLab Rails application.
diff --git a/doc/release/README.md b/doc/release/README.md
index 1342b90f3b3..52eca7c02a6 100644
--- a/doc/release/README.md
+++ b/doc/release/README.md
@@ -1,4 +1,8 @@
-GitLab has the following updates:
+## Release cycle
+
+Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases are published when needed. New features are detailed on the [blog](https://about.gitlab.com/blog/) and in the [changelog](CHANGELOG). Features that will likely be in the next releases can be found on the [direction page](https://about.gitlab.com/direction/).
+
+## Release process documentation
- [Monthly release](monthly.md), every month on the 22nd.
- [Patch release](patch.md), if there are serious regressions.
diff --git a/doc/update/8.1-to-8.2.md b/doc/update/8.1-to-8.2.md
index 7b228d6a22f..b08a79ca0aa 100644
--- a/doc/update/8.1-to-8.2.md
+++ b/doc/update/8.1-to-8.2.md
@@ -85,11 +85,10 @@ sudo -u git -H git checkout 0.4.2
sudo -u git -H make
```
-Update the GitLab init script and 'default' file.
+Update the GitLab 'default' file.
```
cd /home/git/gitlab
-sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
test -e /etc/default/gitlab && \
sudo sed -i.pre-8.2 's/^\([^=]*\)gitlab_git_http_server/\1gitlab_workhorse/' /etc/default/gitlab
```
diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md
index 957354decb7..c19ee49f9e0 100644
--- a/doc/update/patch_versions.md
+++ b/doc/update/patch_versions.md
@@ -47,7 +47,7 @@ sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION` -b v`ca
```bash
cd /home/git/gitlab-workhorse
sudo -u git -H git fetch
-sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION` -b v`cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION`
+sudo -u git -H git checkout `cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION` -b `cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION`
```
### 5. Install libs, migrations, etc.
diff --git a/doc/workflow/README.md b/doc/workflow/README.md
index a6b4d951188..d2642495c9a 100644
--- a/doc/workflow/README.md
+++ b/doc/workflow/README.md
@@ -17,4 +17,5 @@
- [Milestones](milestones.md)
- [Merge Requests](merge_requests.md)
- ["Work In Progress" Merge Requests](wip_merge_requests.md)
+- [Merge When Build Succeeds](merge_when_build_succeeds.md)
- [Manage large binaries with Git LFS](lfs/manage_large_binaries_with_git_lfs.md)
diff --git a/doc/workflow/merge_when_build_succeeds.md b/doc/workflow/merge_when_build_succeeds.md
new file mode 100644
index 00000000000..75e1fdff2b2
--- /dev/null
+++ b/doc/workflow/merge_when_build_succeeds.md
@@ -0,0 +1,15 @@
+# Merge When Build Succeeds
+
+When reviewing a merge request that looks ready to merge but still has one or more CI builds running, you can set it to be merged automatically when all builds succeed. This way, you don't have to wait for the builds to finish and remember to merge the request manually.
+
+![Enable](merge_when_build_succeeds/enable.png)
+
+When you hit the "Merge When Build Succeeds" button, the status of the merge request will be updated to represent the impending merge. If you cannot wait for the build to succeed and want to merge immediately, this option is available in the dropdown menu on the right of the main button.
+
+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](merge_when_build_succeeds/status.png)
+
+When the build succeeds, the merge request will automatically be merged. When the build fails, the author gets a chance to retry any failed builds, or to push new commits to fix the failure.
+
+When the builds are retried and succeed on the second try, the merge request will automatically be merged after all. When the merge request is updated with new commits, the automatic merge is automatically canceled to allow the new changes to be reviewed.
diff --git a/doc/workflow/merge_when_build_succeeds/enable.png b/doc/workflow/merge_when_build_succeeds/enable.png
new file mode 100644
index 00000000000..633efa1246f
--- /dev/null
+++ b/doc/workflow/merge_when_build_succeeds/enable.png
Binary files differ
diff --git a/doc/workflow/merge_when_build_succeeds/status.png b/doc/workflow/merge_when_build_succeeds/status.png
new file mode 100644
index 00000000000..c856c7d14dc
--- /dev/null
+++ b/doc/workflow/merge_when_build_succeeds/status.png
Binary files differ
diff --git a/features/admin/groups.feature b/features/admin/groups.feature
index 973918086a3..2edb3964f70 100644
--- a/features/admin/groups.feature
+++ b/features/admin/groups.feature
@@ -33,3 +33,19 @@ Feature: Admin Groups
When I visit admin group page
When I select user "johndoe@gitlab.com" from user list as "Reporter"
Then I should see "johndoe@gitlab.com" in team list in every project as "Reporter"
+
+ @javascript
+ Scenario: Signed in admin should be able to add himself to a group
+ Given "John Doe" is owner of group "Owned"
+ When I visit group "Owned" members page
+ When I select current user as "Developer"
+ Then I should see current user as "Developer"
+
+ @javascript
+ Scenario: Signed in admin should be able to remove himself from group
+ Given current user is developer of group "Owned"
+ When I visit group "Owned" members page
+ Then I should see current user as "Developer"
+ When I click on the "Remove User From Group" button for current user
+ When I visit group "Owned" members page
+ Then I should not see current user as "Developer"
diff --git a/features/admin/projects.feature b/features/admin/projects.feature
index f7cec04eb75..c5ee80136c8 100644
--- a/features/admin/projects.feature
+++ b/features/admin/projects.feature
@@ -27,3 +27,19 @@ Feature: Admin Projects
And I visit admin project page
When I transfer project to group 'Web'
Then I should see project transfered
+
+ @javascript
+ Scenario: Signed in admin should be able to add himself to a project
+ Given "John Doe" owns private project "Enterprise"
+ When I visit project "Enterprise" members page
+ When I select current user as "Developer"
+ Then I should see current user as "Developer"
+
+ @javascript
+ Scenario: Signed in admin should be able to remove himself from a project
+ Given "John Doe" owns private project "Enterprise"
+ And current user is developer of project "Enterprise"
+ When I visit project "Enterprise" members page
+ Then I should see current user as "Developer"
+ When I click on the "Remove User From Project" button for current user
+ Then I should not see current user as "Developer"
diff --git a/features/explore/projects.feature b/features/explore/projects.feature
index 5d3870827f5..629859e960d 100644
--- a/features/explore/projects.feature
+++ b/features/explore/projects.feature
@@ -31,8 +31,17 @@ Feature: Explore Projects
Then I should see empty public project details
And I should see empty public project details with http clone info
- Scenario: I visit an empty public project page as user
+ Scenario: I visit an empty public project page as user with no ssh-keys
Given I sign in as a user
+ And I have no ssh keys
+ And public empty project "Empty Public Project"
+ When I visit empty project page
+ Then I should see empty public project details
+ And I should see empty public project details with http clone info
+
+ Scenario: I visit an empty public project page as user with an ssh-key
+ Given I sign in as a user
+ And I have an ssh key
And public empty project "Empty Public Project"
When I visit empty project page
Then I should see empty public project details
@@ -57,8 +66,16 @@ Feature: Explore Projects
Then I should see project "Community" home page
And I should see an http link to the repository
- Scenario: I visit public project page as user
+ Scenario: I visit public project page as user with no ssh-keys
+ Given I sign in as a user
+ And I have no ssh keys
+ When I visit project "Community" page
+ Then I should see project "Community" home page
+ And I should see an http link to the repository
+
+ Scenario: I visit public project page as user with an ssh-key
Given I sign in as a user
+ And I have an ssh key
When I visit project "Community" page
Then I should see project "Community" home page
And I should see an ssh link to the repository
diff --git a/features/project/commits/branches.feature b/features/project/commits/branches.feature
index 65d8e48b9b3..5103ca12947 100644
--- a/features/project/commits/branches.feature
+++ b/features/project/commits/branches.feature
@@ -1,3 +1,4 @@
+@project_commits
Feature: Project Commits Branches
Background:
Given I sign in as a user
diff --git a/features/project/commits/comments.feature b/features/project/commits/comments.feature
index 320f008abb6..fafb54b183a 100644
--- a/features/project/commits/comments.feature
+++ b/features/project/commits/comments.feature
@@ -1,3 +1,4 @@
+@project_commits
Feature: Project Commits Comments
Background:
Given I sign in as a user
diff --git a/features/project/commits/commits.feature b/features/project/commits/commits.feature
index e4beeb59adc..21367fd76a0 100644
--- a/features/project/commits/commits.feature
+++ b/features/project/commits/commits.feature
@@ -1,3 +1,4 @@
+@project_commits
Feature: Project Commits
Background:
Given I sign in as a user
diff --git a/features/project/commits/diff_comments.feature b/features/project/commits/diff_comments.feature
index d6e0c84537e..2bde4c8a99b 100644
--- a/features/project/commits/diff_comments.feature
+++ b/features/project/commits/diff_comments.feature
@@ -1,3 +1,4 @@
+@project_commits
Feature: Project Commits Diff Comments
Background:
Given I sign in as a user
diff --git a/features/project/commits/tags.feature b/features/project/commits/tags.feature
index 56ee091acc0..a4be39b2d40 100644
--- a/features/project/commits/tags.feature
+++ b/features/project/commits/tags.feature
@@ -1,3 +1,4 @@
+@project_commits
Feature: Project Commits Tags
Background:
Given I sign in as a user
diff --git a/features/project/commits/user_lookup.feature b/features/project/commits/user_lookup.feature
index db51d4a6cfa..c18f4e070f3 100644
--- a/features/project/commits/user_lookup.feature
+++ b/features/project/commits/user_lookup.feature
@@ -1,3 +1,4 @@
+@project_commits
Feature: Project Commits User Lookup
Background:
Given I sign in as a user
diff --git a/features/project/create.feature b/features/project/create.feature
index e9dc4fe6b3c..a86079143e5 100644
--- a/features/project/create.feature
+++ b/features/project/create.feature
@@ -7,6 +7,7 @@ Feature: Project Create
Scenario: User create a project
Given I sign in as a user
When I visit new project page
+ And I have an ssh key
And fill project form with valid data
Then I should see project page
And I should see empty project instuctions
@@ -14,6 +15,7 @@ Feature: Project Create
@javascript
Scenario: Empty project instructions
Given I sign in as a user
+ And I have an ssh key
When I visit new project page
And fill project form with valid data
Then I see empty project instuctions
diff --git a/features/project/graph.feature b/features/project/graph.feature
index 2acd65aea5f..63793d6f989 100644
--- a/features/project/graph.feature
+++ b/features/project/graph.feature
@@ -18,3 +18,8 @@ Feature: Project Graph
Given project "Shop" has CI enabled
When I visit project "Shop" CI graph page
Then page should have CI graphs
+
+ @javascript
+ Scenario: I should see project languages graphs
+ When I visit project "Shop" languages graph page
+ Then page should have languages graphs
diff --git a/features/project/issues/award_emoji.feature b/features/project/issues/award_emoji.feature
index 2609f129d07..0ce99e855c6 100644
--- a/features/project/issues/award_emoji.feature
+++ b/features/project/issues/award_emoji.feature
@@ -1,3 +1,4 @@
+@project_issues
Feature: Award Emoji
Background:
Given I sign in as a user
diff --git a/features/project/issues/filter_labels.feature b/features/project/issues/filter_labels.feature
index e316f519861..e07f8053fb7 100644
--- a/features/project/issues/filter_labels.feature
+++ b/features/project/issues/filter_labels.feature
@@ -1,3 +1,4 @@
+@project_issues
Feature: Project Issues Filter Labels
Background:
Given I sign in as a user
diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature
index 28cc43ef710..f08b30e0b88 100644
--- a/features/project/issues/issues.feature
+++ b/features/project/issues/issues.feature
@@ -1,3 +1,4 @@
+@project_issues
Feature: Project Issues
Background:
Given I sign in as a user
@@ -196,4 +197,3 @@ Feature: Project Issues
And I should not see labels field
And I submit new issue "500 error on profile"
Then I should see issue "500 error on profile"
-
diff --git a/features/project/issues/labels.feature b/features/project/issues/labels.feature
index 039a7d83cb1..45de57f18e3 100644
--- a/features/project/issues/labels.feature
+++ b/features/project/issues/labels.feature
@@ -1,3 +1,4 @@
+@project_issues
Feature: Project Issues Labels
Background:
Given I sign in as a user
diff --git a/features/project/issues/milestones.feature b/features/project/issues/milestones.feature
index c1a20e9b488..1af05b3c326 100644
--- a/features/project/issues/milestones.feature
+++ b/features/project/issues/milestones.feature
@@ -1,3 +1,4 @@
+@project_issues
Feature: Project Issues Milestones
Background:
Given I sign in as a user
diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature
index 6cd081c868e..35ace948888 100644
--- a/features/project/merge_requests.feature
+++ b/features/project/merge_requests.feature
@@ -1,3 +1,4 @@
+@project_merge_requests
Feature: Project Merge Requests
Background:
Given I sign in as a user
diff --git a/features/project/merge_requests/accept.feature b/features/project/merge_requests/accept.feature
index 3e6e59a3808..d5e4f2b0bd8 100644
--- a/features/project/merge_requests/accept.feature
+++ b/features/project/merge_requests/accept.feature
@@ -1,3 +1,4 @@
+@project_merge_requests
Feature: Project Merge Requests Acceptance
Background:
Given There is an open Merge Request
@@ -8,10 +9,12 @@ Feature: Project Merge Requests Acceptance
Given I am on the Merge Request detail page
When I click on "Remove source branch" option
And I click on Accept Merge Request
- Then I should not see the Remove Source Branch button
+ Then I should see merge request merged
+ And I should not see the Remove Source Branch button
@javascript
Scenario: Accepting the Merge Request without removing the source branch
Given I am on the Merge Request detail page
When I click on Accept Merge Request
- Then I should see the Remove Source Branch button
+ Then I should see merge request merged
+ And I should see the Remove Source Branch button
diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature
index e545ea63ca8..439787f2641 100644
--- a/features/project/source/browse_files.feature
+++ b/features/project/source/browse_files.feature
@@ -110,12 +110,6 @@ Feature: Project Source Browse Files
Given I visit a binary file in the repo
Then I cannot see the edit button
- Scenario: If I don't have edit permission the edit link is disabled
- Given public project "Community"
- And I visit project "Community" source page
- And I click on ".gitignore" file in repo
- Then The edit button is disabled
-
@javascript
Scenario: I can edit and commit file
Given I click on ".gitignore" file in repo
@@ -221,3 +215,9 @@ Feature: Project Source Browse Files
Given I switch ref to fix
And I visit the fix tree
Then I see the commit data for a directory with a leading dot
+
+ Scenario: I browse LFS object
+ Given I click on "files/lfs/lfs_object.iso" file in repo
+ Then I should see download link and object size
+ And I should not see lfs pointer details
+ And I should see buttons for allowed commands
diff --git a/features/steps/admin/groups.rb b/features/steps/admin/groups.rb
index d27634858a2..43fd91d0d4c 100644
--- a/features/steps/admin/groups.rb
+++ b/features/steps/admin/groups.rb
@@ -1,5 +1,6 @@
class Spinach::Features::AdminGroups < Spinach::FeatureSteps
include SharedAuthentication
+ include SharedGroup
include SharedPaths
include SharedUser
include SharedActiveTab
@@ -88,6 +89,34 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps
end
end
+ step 'I select current user as "Developer"' do
+ page.within ".users-group-form" do
+ select2(current_user.id, from: "#user_ids", multiple: true)
+ select "Developer", from: "access_level"
+ end
+
+ click_button "Add users to group"
+ end
+
+ step 'I should see current user as "Developer"' do
+ page.within '.content-list' do
+ expect(page).to have_content(current_user.name)
+ expect(page).to have_content('Developer')
+ end
+ end
+
+ step 'I click on the "Remove User From Group" button for current user' do
+ find(:css, 'li', text: current_user.name).find(:css, 'a.btn-remove').click
+ # poltergeist always confirms popups.
+ end
+
+ step 'I should not see current user as "Developer"' do
+ page.within '.content-list' do
+ expect(page).not_to have_content(current_user.name)
+ expect(page).not_to have_content('Developer')
+ end
+ end
+
protected
def current_group
diff --git a/features/steps/admin/labels.rb b/features/steps/admin/labels.rb
index 2ea5dffdc66..55ddcc25085 100644
--- a/features/steps/admin/labels.rb
+++ b/features/steps/admin/labels.rb
@@ -71,7 +71,7 @@ class Spinach::Features::AdminIssuesLabels < Spinach::FeatureSteps
step 'I should see label color error message' do
page.within '.label-form' do
- expect(page).to have_content 'Color is invalid'
+ expect(page).to have_content 'Color must be a valid color code'
end
end
diff --git a/features/steps/admin/projects.rb b/features/steps/admin/projects.rb
index 5a1cc9aa151..a7a28755a6c 100644
--- a/features/steps/admin/projects.rb
+++ b/features/steps/admin/projects.rb
@@ -3,6 +3,8 @@ class Spinach::Features::AdminProjects < Spinach::FeatureSteps
include SharedPaths
include SharedAdmin
include SharedProject
+ include SharedUser
+ include Select2Helper
step 'I should see all non-archived projects' do
Project.non_archived.each do |p|
@@ -56,6 +58,41 @@ class Spinach::Features::AdminProjects < Spinach::FeatureSteps
expect(page).to have_content 'Namespace: Web'
end
+ step 'I visit project "Enterprise" members page' do
+ project = Project.find_by!(name: "Enterprise")
+ visit namespace_project_project_members_path(project.namespace, project)
+ end
+
+ step 'I select current user as "Developer"' do
+ page.within ".users-project-form" do
+ select2(current_user.id, from: "#user_ids", multiple: true)
+ select "Developer", from: "access_level"
+ end
+
+ click_button "Add users to project"
+ end
+
+ step 'I should see current user as "Developer"' do
+ page.within '.content-list' do
+ expect(page).to have_content(current_user.name)
+ expect(page).to have_content('Developer')
+ end
+ end
+
+ step 'current user is developer of project "Enterprise"' do
+ project = Project.find_by!(name: "Enterprise")
+ project.team << [current_user, :developer]
+ end
+
+ step 'I click on the "Remove User From Project" button for current user' do
+ find(:css, 'li', text: current_user.name).find(:css, 'a.btn-remove').click
+ # poltergeist always confirms popups.
+ end
+
+ step 'I should not see current_user as "Developer"' do
+ expect(page).not_to have_selector(:css, '.content-list')
+ end
+
def project
@project ||= Project.first
end
diff --git a/features/steps/dashboard/dashboard.rb b/features/steps/dashboard/dashboard.rb
index f0fbd8a826a..63f0ec2b6e8 100644
--- a/features/steps/dashboard/dashboard.rb
+++ b/features/steps/dashboard/dashboard.rb
@@ -12,7 +12,7 @@ class Spinach::Features::Dashboard < Spinach::FeatureSteps
end
step 'I should see "Shop" project CI status' do
- expect(page).to have_link "Build status: skipped"
+ expect(page).to have_link "Build skipped"
end
step 'I should see last push widget' do
diff --git a/features/steps/explore/projects.rb b/features/steps/explore/projects.rb
index 8b498e7b4a6..f819dec2192 100644
--- a/features/steps/explore/projects.rb
+++ b/features/steps/explore/projects.rb
@@ -2,6 +2,7 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include SharedProject
+ include SharedUser
step 'I should see project "Empty Public Project"' do
expect(page).to have_content "Empty Public Project"
diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb
index e5b3f27135d..0d6a9a8fc66 100644
--- a/features/steps/project/commits/commits.rb
+++ b/features/steps/project/commits/commits.rb
@@ -118,6 +118,6 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
step 'I see builds list' do
expect(page).to have_content "build: pending"
- expect(page).to have_content "Latest builds"
+ expect(page).to have_content "1 build"
end
end
diff --git a/features/steps/project/create.rb b/features/steps/project/create.rb
index 0d39e1997b5..f90218f3791 100644
--- a/features/steps/project/create.rb
+++ b/features/steps/project/create.rb
@@ -1,6 +1,7 @@
class Spinach::Features::ProjectCreate < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
+ include SharedUser
step 'fill project form with valid data' do
fill_in 'project_path', with: 'Empty'
diff --git a/features/steps/project/graph.rb b/features/steps/project/graph.rb
index 98f31f3b76a..b09ec86e5df 100644
--- a/features/steps/project/graph.rb
+++ b/features/steps/project/graph.rb
@@ -14,6 +14,15 @@ class Spinach::Features::ProjectGraph < Spinach::FeatureSteps
visit commits_namespace_project_graph_path(project.namespace, project, "master")
end
+ step 'I visit project "Shop" languages graph page' do
+ visit languages_namespace_project_graph_path(project.namespace, project, "master")
+ end
+
+ step 'page should have languages graphs' do
+ expect(page).to have_content "Ruby 66.63 %"
+ expect(page).to have_content "JavaScript 22.96 %"
+ end
+
step 'page should have commits graphs' do
expect(page).to have_content "Commit statistics for master"
expect(page).to have_content "Commits per day of month"
diff --git a/features/steps/project/hooks.rb b/features/steps/project/hooks.rb
index df4a23a3716..be4db770948 100644
--- a/features/steps/project/hooks.rb
+++ b/features/steps/project/hooks.rb
@@ -70,8 +70,6 @@ class Spinach::Features::ProjectHooks < Spinach::FeatureSteps
step 'I should see hook service down error message' do
expect(page).to have_selector '.flash-alert',
- text: 'Hook execution failed. '\
- 'Ensure hook URL is correct and '\
- 'service is up.'
+ text: 'Hook execution failed: Exception from'
end
end
diff --git a/features/steps/project/issues/labels.rb b/features/steps/project/issues/labels.rb
index e273bb391b3..2ab8956867b 100644
--- a/features/steps/project/issues/labels.rb
+++ b/features/steps/project/issues/labels.rb
@@ -55,7 +55,7 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps
step 'I should see label color error message' do
page.within '.label-form' do
- expect(page).to have_content 'Color is invalid'
+ expect(page).to have_content 'Color must be a valid color code'
end
end
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index 822cf0ffe1c..0107d9d8486 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -367,7 +367,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should see merge request "Bug NS-05" with CI status' do
page.within ".mr-list" do
- expect(page).to have_link "Build status: pending"
+ expect(page).to have_link "Build pending"
end
end
diff --git a/features/steps/project/merge_requests/acceptance.rb b/features/steps/project/merge_requests/acceptance.rb
index 6adecaa8385..383c055c4ef 100644
--- a/features/steps/project/merge_requests/acceptance.rb
+++ b/features/steps/project/merge_requests/acceptance.rb
@@ -32,4 +32,8 @@ class Spinach::Features::ProjectMergeRequestsAcceptance < Spinach::FeatureSteps
step 'I am signed in as a developer of the project' do
login_as(@user)
end
+
+ step 'I should see merge request merged' do
+ expect(page).to have_content('The changes were merged into')
+ end
end
diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb
index 05d1346d006..f2b95764267 100644
--- a/features/steps/project/source/browse_files.rb
+++ b/features/steps/project/source/browse_files.rb
@@ -53,10 +53,6 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
expect(page).not_to have_link 'edit'
end
- step 'The edit button is disabled' do
- expect(page).to have_css '.disabled', text: 'Edit'
- end
-
step 'I can edit code' do
set_new_content
expect(evaluate_script('blob.editor.getValue()')).to eq new_gitignore_content
@@ -305,6 +301,33 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
expect(page).not_to have_content('Loading commit data...')
end
+ step 'I click on "files/lfs/lfs_object.iso" file in repo' do
+ visit namespace_project_tree_path(@project.namespace, @project, "lfs")
+ click_link 'files'
+ click_link "lfs"
+ click_link "lfs_object.iso"
+ end
+
+ step 'I should see download link and object size' do
+ expect(page).to have_content 'Download (1.5 MB)'
+ end
+
+ step 'I should not see lfs pointer details' do
+ expect(page).not_to have_content 'version https://git-lfs.github.com/spec/v1'
+ expect(page).not_to have_content 'oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897'
+ expect(page).not_to have_content 'size 1575078'
+ end
+
+ step 'I should see buttons for allowed commands' do
+ expect(page).to have_content 'Raw'
+ expect(page).to have_content 'History'
+ expect(page).to have_content 'Permalink'
+ expect(page).not_to have_content 'Edit'
+ expect(page).not_to have_content 'Blame'
+ expect(page).not_to have_content 'Delete'
+ expect(page).not_to have_content 'Replace'
+ end
+
private
def set_new_content
diff --git a/features/steps/shared/group.rb b/features/steps/shared/group.rb
index 58581653f28..fe6736dacd4 100644
--- a/features/steps/shared/group.rb
+++ b/features/steps/shared/group.rb
@@ -1,6 +1,10 @@
module SharedGroup
include Spinach::DSL
+ step 'current user is developer of group "Owned"' do
+ is_member_of(current_user.name, "Owned", Gitlab::Access::DEVELOPER)
+ end
+
step '"John Doe" is owner of group "Owned"' do
is_member_of("John Doe", "Owned", Gitlab::Access::OWNER)
end
diff --git a/features/steps/shared/user.rb b/features/steps/shared/user.rb
index 250cc5b94f3..f0721094ee3 100644
--- a/features/steps/shared/user.rb
+++ b/features/steps/shared/user.rb
@@ -18,4 +18,12 @@ module SharedUser
def user_exists(name, options = {})
User.find_by(name: name) || create(:user, { name: name, admin: false }.merge(options))
end
+
+ step 'I have an ssh key' do
+ create(:personal_key, user: @user)
+ end
+
+ step 'I have no ssh keys' do
+ @user.keys.delete_all
+ end
end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 96b73df6af9..81bf7a8222b 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -171,6 +171,7 @@ module API
expose :description
expose :work_in_progress?, as: :work_in_progress
expose :milestone, using: Entities::Milestone
+ expose :merge_when_build_succeeds
end
class MergeRequestChanges < MergeRequest
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 024aeec2e14..1a14d870a4a 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -65,6 +65,18 @@ module API
DestroyGroupService.new(group, current_user).execute
end
+ # Get a list of projects in this group
+ #
+ # Example Request:
+ # GET /groups/:id/projects
+ get ":id/projects" do
+ group = find_group(params[:id])
+ projects = group.projects
+ projects = filter_projects(projects)
+ projects = paginate projects
+ present projects, with: Entities::Project
+ end
+
# Transfer a project to the Group namespace
#
# Parameters:
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index e7c5f808aea..3c1c6bda260 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -195,46 +195,54 @@ module API
# Merge MR
#
# Parameters:
- # id (required) - The ID of a project
- # merge_request_id (required) - ID of MR
- # merge_commit_message (optional) - Custom merge commit message
+ # id (required) - The ID of a project
+ # merge_request_id (required) - ID of MR
+ # merge_commit_message (optional) - Custom merge commit message
+ # should_remove_source_branch (optional) - When true, the source branch will be deleted if possible
+ # merge_when_build_succeeds (optional) - When true, this MR will be merged when the build succeeds
# Example:
# PUT /projects/:id/merge_request/:merge_request_id/merge
#
put ":id/merge_request/:merge_request_id/merge" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
- allowed = ::Gitlab::GitAccess.new(current_user, user_project).
- can_push_to_branch?(merge_request.target_branch)
+ # Merge request can not be merged
+ # because user dont have permissions to push into target branch
+ unauthorized! unless merge_request.can_be_merged_by?(current_user)
+ not_allowed! if !merge_request.open? || merge_request.work_in_progress?
- if allowed
- if merge_request.unchecked?
- merge_request.check_if_can_be_merged
- end
+ merge_request.check_if_can_be_merged if merge_request.unchecked?
- if merge_request.open? && !merge_request.work_in_progress?
- if merge_request.can_be_merged?
- commit_message = params[:merge_commit_message] || merge_request.merge_commit_message
-
- ::MergeRequests::MergeService.new(merge_request.target_project, current_user).
- execute(merge_request, commit_message)
-
- present merge_request, with: Entities::MergeRequest
- else
- render_api_error!('Branch cannot be merged', 405)
- end
- else
- # Merge request can not be merged
- # because it is already closed/merged or marked as WIP
- not_allowed!
- end
+ render_api_error!('Branch cannot be merged', 406) unless merge_request.can_be_merged?
+
+ merge_params = {
+ commit_message: params[:merge_commit_message],
+ should_remove_source_branch: params[:should_remove_source_branch]
+ }
+
+ if parse_boolean(params[:merge_when_build_succeeds]) && merge_request.ci_commit && merge_request.ci_commit.active?
+ ::MergeRequests::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user, merge_params).
+ execute(merge_request)
else
- # Merge request can not be merged
- # because user dont have permissions to push into target branch
- unauthorized!
+ ::MergeRequests::MergeService.new(merge_request.target_project, current_user, merge_params).
+ execute(merge_request)
end
+
+ present merge_request, with: Entities::MergeRequest
end
+ # Cancel Merge if Merge When build succeeds is enabled
+ # Parameters:
+ # id (required) - The ID of a project
+ # merge_request_id (required) - ID of MR
+ #
+ post ":id/merge_request/:merge_request_id/cancel_merge_when_build_succeeds" do
+ merge_request = user_project.merge_requests.find(params[:merge_request_id])
+
+ unauthorized! unless merge_request.can_cancel_merge_when_build_succeeds?(current_user)
+
+ ::MergeRequest::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user).cancel(merge_request)
+ end
# Get a merge request's comments
#
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 2b4ada6e2eb..6928fe0eb9d 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -7,8 +7,12 @@ module API
helpers do
def map_public_to_visibility_level(attrs)
publik = attrs.delete(:public)
- publik = parse_boolean(publik)
- attrs[:visibility_level] = Gitlab::VisibilityLevel::PUBLIC if !attrs[:visibility_level].present? && publik == true
+ if publik.present? && !attrs[:visibility_level].present?
+ publik = parse_boolean(publik)
+ # Since setting the public attribute to private could mean either
+ # private or internal, use the more conservative option, private.
+ attrs[:visibility_level] = (publik == true) ? Gitlab::VisibilityLevel::PUBLIC : Gitlab::VisibilityLevel::PRIVATE
+ end
attrs
end
end
diff --git a/lib/gitlab/blacklist.rb b/lib/gitlab/blacklist.rb
deleted file mode 100644
index 43145e0ee1b..00000000000
--- a/lib/gitlab/blacklist.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-module Gitlab
- module Blacklist
- extend self
-
- def path
- %w(
- admin
- dashboard
- files
- groups
- help
- profile
- projects
- search
- public
- assets
- u
- s
- teams
- merge_requests
- issues
- users
- snippets
- services
- repository
- hooks
- notes
- unsubscribes
- all
- ci
- )
- end
- end
-end
diff --git a/lib/gitlab/ldap/access.rb b/lib/gitlab/ldap/access.rb
index 16ff03c38d4..c438a3d167b 100644
--- a/lib/gitlab/ldap/access.rb
+++ b/lib/gitlab/ldap/access.rb
@@ -37,13 +37,15 @@ module Gitlab
# Block user in GitLab if he/she was blocked in AD
if Gitlab::LDAP::Person.disabled_via_active_directory?(user.ldap_identity.extern_uid, adapter)
- user.block unless user.blocked?
+ user.block
false
else
user.activate if user.blocked? && !ldap_config.block_auto_created_users
true
end
else
+ # Block the user if they no longer exist in LDAP/AD
+ user.block
false
end
rescue
diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb
index 9be9a65671b..9d9617761b3 100644
--- a/lib/gitlab/lfs/response.rb
+++ b/lib/gitlab/lfs/response.rb
@@ -220,7 +220,7 @@ module Gitlab
def storage_project(project)
if project.forked?
- project.forked_from_project
+ storage_project(project.forked_from_project)
else
project
end
diff --git a/lib/gitlab/markdown/commit_reference_filter.rb b/lib/gitlab/markdown/commit_reference_filter.rb
index b4036578e60..e3066a89b04 100644
--- a/lib/gitlab/markdown/commit_reference_filter.rb
+++ b/lib/gitlab/markdown/commit_reference_filter.rb
@@ -47,6 +47,17 @@ module Gitlab
def object_link_title(commit)
commit.link_title
end
+
+ def object_link_text_extras(object, matches)
+ extras = super
+
+ path = matches[:path] if matches.names.include?("path")
+ if path == '/builds'
+ extras.unshift "builds"
+ end
+
+ extras
+ end
end
end
end
diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb
index de71fc76a9b..2eb77c46da7 100644
--- a/lib/gitlab/markdown/merge_request_reference_filter.rb
+++ b/lib/gitlab/markdown/merge_request_reference_filter.rb
@@ -24,8 +24,14 @@ module Gitlab
def object_link_text_extras(object, matches)
extras = super
- if matches.names.include?("path") && matches[:path] && matches[:path] == '/diffs'
+ path = matches[:path] if matches.names.include?("path")
+ case path
+ when '/diffs'
extras.unshift "diffs"
+ when '/commits'
+ extras.unshift "commits"
+ when '/builds'
+ extras.unshift "builds"
end
extras
diff --git a/lib/omni_auth/request_forgery_protection.rb b/lib/omni_auth/request_forgery_protection.rb
index 3557522d3c9..69155131d8d 100644
--- a/lib/omni_auth/request_forgery_protection.rb
+++ b/lib/omni_auth/request_forgery_protection.rb
@@ -1,66 +1,21 @@
# Protects OmniAuth request phase against CSRF.
module OmniAuth
- # Based on ActionController::RequestForgeryProtection.
- class RequestForgeryProtection
- def initialize(env)
- @env = env
- end
-
- def request
- @request ||= ActionDispatch::Request.new(@env)
- end
-
- def session
- request.session
- end
-
- def reset_session
- request.reset_session
- end
-
- def params
- request.params
- end
-
- def call
- verify_authenticity_token
- end
+ module RequestForgeryProtection
+ class Controller < ActionController::Base
+ protect_from_forgery with: :exception
- def verify_authenticity_token
- if !verified_request?
- Rails.logger.warn "Can't verify CSRF token authenticity" if Rails.logger
- handle_unverified_request
+ def index
+ head :ok
end
end
- private
-
- def protect_against_forgery?
- ApplicationController.allow_forgery_protection
- end
-
- def request_forgery_protection_token
- ApplicationController.request_forgery_protection_token
- end
-
- def forgery_protection_strategy
- ApplicationController.forgery_protection_strategy
- end
-
- def verified_request?
- !protect_against_forgery? || request.get? || request.head? ||
- form_authenticity_token == params[request_forgery_protection_token] ||
- form_authenticity_token == request.headers['X-CSRF-Token']
- end
-
- def handle_unverified_request
- forgery_protection_strategy.new(self).handle_unverified_request
+ def self.app
+ @app ||= Controller.action(:index)
end
- # Sets the token value for the current session.
- def form_authenticity_token
- session[:_csrf_token] ||= SecureRandom.base64(32)
+ def self.call(env)
+ app.call(env)
end
end
end
diff --git a/lib/tasks/gitlab/git.rake b/lib/tasks/gitlab/git.rake
new file mode 100644
index 00000000000..65ee430d550
--- /dev/null
+++ b/lib/tasks/gitlab/git.rake
@@ -0,0 +1,55 @@
+namespace :gitlab do
+ namespace :git do
+
+ desc "GitLab | Git | Repack"
+ task repack: :environment do
+ failures = perform_git_cmd(%W(git repack -a --quiet), "Repacking repo")
+ if failures.empty?
+ puts "Done".green
+ else
+ output_failures(failures)
+ end
+ end
+
+ desc "GitLab | Git | Run garbage collection on all repos"
+ task gc: :environment do
+ failures = perform_git_cmd(%W(git gc --auto --quiet), "Garbage Collecting")
+ if failures.empty?
+ puts "Done".green
+ else
+ output_failures(failures)
+ end
+ end
+
+ desc "GitLab | Git | Prune all repos"
+ task prune: :environment do
+ failures = perform_git_cmd(%W(git prune), "Git Prune")
+ if failures.empty?
+ puts "Done".green
+ else
+ output_failures(failures)
+ end
+ end
+
+ def perform_git_cmd(cmd, message)
+ puts "Starting #{message} on all repositories"
+
+ failures = []
+ all_repos do |repo|
+ if system(*cmd, chdir: repo)
+ puts "Performed #{message} at #{repo}"
+ else
+ failures << repo
+ end
+ end
+
+ failures
+ end
+
+ def output_failures(failures)
+ puts "The following repositories reported errors:".red
+ failures.each { |f| puts "- #{f}" }
+ end
+
+ end
+end
diff --git a/lib/tasks/gitlab/import.rake b/lib/tasks/gitlab/import.rake
index c1ee271ae2b..1c04f47f08f 100644
--- a/lib/tasks/gitlab/import.rake
+++ b/lib/tasks/gitlab/import.rake
@@ -64,6 +64,8 @@ namespace :gitlab do
if project.persisted?
puts " * Created #{project.name} (#{repo_path})".green
+ project.update_repository_size
+ project.update_commit_count
else
puts " * Failed trying to create #{project.name} (#{repo_path})".red
puts " Errors: #{project.errors.messages}".red
diff --git a/lib/tasks/gitlab/list_repos.rake b/lib/tasks/gitlab/list_repos.rake
new file mode 100644
index 00000000000..c7596e7abcb
--- /dev/null
+++ b/lib/tasks/gitlab/list_repos.rake
@@ -0,0 +1,17 @@
+namespace :gitlab do
+ task list_repos: :environment do
+ scope = Project
+ if ENV['SINCE']
+ date = Time.parse(ENV['SINCE'])
+ warn "Listing repositories with activity or changes since #{date}"
+ project_ids = Project.where('last_activity_at > ? OR updated_at > ?', date, date).pluck(:id).sort
+ namespace_ids = Namespace.where(['updated_at > ?', date]).pluck(:id).sort
+ scope = scope.where('id IN (?) OR namespace_id in (?)', project_ids, namespace_ids)
+ end
+ scope.find_each do |project|
+ base = File.join(Gitlab.config.gitlab_shell.repos_path, project.path_with_namespace)
+ puts base + '.git'
+ puts base + '.wiki.git'
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake
index efb863a8764..ebe516ec879 100644
--- a/lib/tasks/gitlab/task_helpers.rake
+++ b/lib/tasks/gitlab/task_helpers.rake
@@ -118,4 +118,12 @@ namespace :gitlab do
false
end
end
+
+ def all_repos
+ IO.popen(%W(find #{Gitlab.config.gitlab_shell.repos_path} -mindepth 2 -maxdepth 2 -type d -name *.git)) do |find|
+ find.each_line do |path|
+ yield path.chomp
+ end
+ end
+ end
end
diff --git a/lib/tasks/spec.rake b/lib/tasks/spec.rake
index 365ff2defd4..0985ef3a669 100644
--- a/lib/tasks/spec.rake
+++ b/lib/tasks/spec.rake
@@ -19,6 +19,33 @@ namespace :spec do
run_commands(cmds)
end
+ desc 'GitLab | Rspec | Run model specs'
+ task :models do
+ cmds = [
+ %W(rake gitlab:setup),
+ %W(rspec spec --tag @models)
+ ]
+ run_commands(cmds)
+ end
+
+ desc 'GitLab | Rspec | Run service specs'
+ task :services do
+ cmds = [
+ %W(rake gitlab:setup),
+ %W(rspec spec --tag @services)
+ ]
+ run_commands(cmds)
+ end
+
+ desc 'GitLab | Rspec | Run lib specs'
+ task :lib do
+ cmds = [
+ %W(rake gitlab:setup),
+ %W(rspec spec --tag @lib)
+ ]
+ run_commands(cmds)
+ end
+
desc 'GitLab | Rspec | Run benchmark specs'
task :benchmark do
cmds = [
@@ -32,7 +59,7 @@ namespace :spec do
task :other do
cmds = [
%W(rake gitlab:setup),
- %W(rspec spec --tag ~@api --tag ~@feature --tag ~@benchmark)
+ %W(rspec spec --tag ~@api --tag ~@feature --tag ~@models --tag ~@lib --tag ~@services --tag ~@benchmark)
]
run_commands(cmds)
end
diff --git a/lib/tasks/spinach.rake b/lib/tasks/spinach.rake
index d5a96fd38f4..3acfc6e2075 100644
--- a/lib/tasks/spinach.rake
+++ b/lib/tasks/spinach.rake
@@ -1,11 +1,31 @@
Rake::Task["spinach"].clear if Rake::Task.task_defined?('spinach')
namespace :spinach do
+ namespace :project do
+ desc "GitLab | Spinach | Run project commits, issues and merge requests spinach features"
+ task :half do
+ cmds = [
+ %W(rake gitlab:setup),
+ %W(spinach --tags @project_commits,@project_issues,@project_merge_requests),
+ ]
+ run_commands(cmds)
+ end
+
+ desc "GitLab | Spinach | Run remaining project spinach features"
+ task :rest do
+ cmds = [
+ %W(rake gitlab:setup),
+ %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets,~@project_commits,~@project_issues,~@project_merge_requests),
+ ]
+ run_commands(cmds)
+ end
+ end
+
desc "GitLab | Spinach | Run project spinach features"
task :project do
cmds = [
%W(rake gitlab:setup),
- %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets,~@commits),
+ %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets),
]
run_commands(cmds)
end
@@ -14,7 +34,7 @@ namespace :spinach do
task :other do
cmds = [
%W(rake gitlab:setup),
- %W(spinach --tags @admin,@dashboard,@profile,@public,@snippets,@commits),
+ %W(spinach --tags @admin,@dashboard,@profile,@public,@snippets),
]
run_commands(cmds)
end
@@ -33,4 +53,4 @@ def run_commands(cmds)
cmds.each do |cmd|
system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd) or raise("#{cmd} failed!")
end
-end \ No newline at end of file
+end
diff --git a/shared/lfs-objects/.gitkeep b/shared/lfs-objects/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/shared/lfs-objects/.gitkeep
diff --git a/spec/controllers/commit_controller_spec.rb b/spec/controllers/commit_controller_spec.rb
index 5337a69e84b..7793bf1e421 100644
--- a/spec/controllers/commit_controller_spec.rb
+++ b/spec/controllers/commit_controller_spec.rb
@@ -110,6 +110,26 @@ describe Projects::CommitController do
expect(response.body).to match(/^diff --git/)
end
end
+
+ context 'commit that removes a submodule' do
+ render_views
+
+ let(:fork_project) { create(:forked_project_with_submodules) }
+ let(:commit) { fork_project.commit('remove-submodule') }
+
+ before do
+ fork_project.team << [user, :master]
+ end
+
+ it 'renders it' do
+ get(:show,
+ namespace_id: fork_project.namespace.to_param,
+ project_id: fork_project.to_param,
+ id: commit.id)
+
+ expect(response).to be_success
+ end
+ end
end
describe "#branches" do
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 3e5e1fa87ae..6aaec224f6e 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -10,6 +10,30 @@ describe Projects::MergeRequestsController do
project.team << [user, :master]
end
+ describe '#new' do
+ context 'merge request that removes a submodule' do
+ render_views
+
+ let(:fork_project) { create(:forked_project_with_submodules) }
+
+ before do
+ fork_project.team << [user, :master]
+ end
+
+ it 'renders it' do
+ get :new,
+ namespace_id: fork_project.namespace.to_param,
+ project_id: fork_project.to_param,
+ merge_request: {
+ source_branch: 'remove-submodule',
+ target_branch: 'master'
+ }
+
+ expect(response).to be_success
+ end
+ end
+ end
+
describe "#show" do
shared_examples "export merge as" do |format|
it "should generally work" do
diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb
index c114f342021..1caa476d37d 100644
--- a/spec/controllers/projects/raw_controller_spec.rb
+++ b/spec/controllers/projects/raw_controller_spec.rb
@@ -33,5 +33,39 @@ describe Projects::RawController do
expect(response.header['Content-Type']).to eq('image/jpeg')
end
end
+
+ context 'lfs object' do
+ let(:id) { 'be93687/files/lfs/lfs_object.iso' }
+ let!(:lfs_object) { create(:lfs_object, oid: '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', size: '1575078') }
+
+ context 'when project has access' do
+ before do
+ public_project.lfs_objects << lfs_object
+ allow_any_instance_of(LfsObjectUploader).to receive(:exists?).and_return(true)
+ allow(controller).to receive(:send_file) { controller.render nothing: true }
+ end
+
+ it 'serves the file' do
+ expect(controller).to receive(:send_file).with("#{Gitlab.config.shared.path}/lfs-objects/91/ef/f75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897", filename: "lfs_object.iso", disposition: 'attachment')
+ get(:show,
+ namespace_id: public_project.namespace.to_param,
+ project_id: public_project.to_param,
+ id: id)
+
+ expect(response.status).to eq(200)
+ end
+ end
+
+ context 'when project does not have access' do
+ it 'does not serve the file' do
+ get(:show,
+ namespace_id: public_project.namespace.to_param,
+ project_id: public_project.to_param,
+ id: id)
+
+ expect(response.status).to eq(404)
+ end
+ end
+ end
end
end
diff --git a/spec/factories/ci/commits.rb b/spec/factories/ci/commits.rb
index 79e000b7ccb..70e3fa319c6 100644
--- a/spec/factories/ci/commits.rb
+++ b/spec/factories/ci/commits.rb
@@ -2,17 +2,18 @@
#
# Table name: commits
#
-# id :integer not null, primary key
-# project_id :integer
-# ref :string(255)
-# sha :string(255)
-# before_sha :string(255)
-# push_data :text
-# created_at :datetime
-# updated_at :datetime
-# tag :boolean default(FALSE)
-# yaml_errors :text
-# committed_at :datetime
+# id :integer not null, primary key
+# project_id :integer
+# ref :string(255)
+# sha :string(255)
+# before_sha :string(255)
+# push_data :text
+# created_at :datetime
+# updated_at :datetime
+# tag :boolean default(FALSE)
+# yaml_errors :text
+# committed_at :datetime
+# gl_project_id :integer
#
# Read about factories at https://github.com/thoughtbot/factory_girl
diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb
index 52de437052d..8898b71e2a3 100644
--- a/spec/factories/commit_statuses.rb
+++ b/spec/factories/commit_statuses.rb
@@ -5,7 +5,7 @@ FactoryGirl.define do
name 'default'
status 'success'
description 'commit status'
- commit factory: :ci_commit
+ commit factory: :ci_commit_with_one_job
factory :generic_commit_status, class: GenericCommitStatus do
name 'generic'
diff --git a/spec/factories/lfs_objects.rb b/spec/factories/lfs_objects.rb
index 7fb2d77ca32..2da107ba24b 100644
--- a/spec/factories/lfs_objects.rb
+++ b/spec/factories/lfs_objects.rb
@@ -1,3 +1,15 @@
+# == Schema Information
+#
+# Table name: lfs_objects
+#
+# id :integer not null, primary key
+# oid :string(255) not null
+# size :integer not null
+# created_at :datetime
+# updated_at :datetime
+# file :string(255)
+#
+
# Read about factories at https://github.com/thoughtbot/factory_girl
FactoryGirl.define do
diff --git a/spec/factories/lfs_objects_projects.rb b/spec/factories/lfs_objects_projects.rb
index 93de6607df8..3772236a77a 100644
--- a/spec/factories/lfs_objects_projects.rb
+++ b/spec/factories/lfs_objects_projects.rb
@@ -1,3 +1,14 @@
+# == Schema Information
+#
+# Table name: lfs_objects_projects
+#
+# id :integer not null, primary key
+# lfs_object_id :integer not null
+# project_id :integer not null
+# created_at :datetime
+# updated_at :datetime
+#
+
# Read about factories at https://github.com/thoughtbot/factory_girl
FactoryGirl.define do
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index 729a49c9f72..5b4d7f41bc4 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -65,6 +65,11 @@ FactoryGirl.define do
target_branch "master"
end
+ trait :merge_when_build_succeeds do
+ merge_when_build_succeeds true
+ merge_user author
+ end
+
factory :closed_merge_request, traits: [:closed]
factory :reopened_merge_request, traits: [:reopened]
factory :merge_request_with_diffs, traits: [:with_diffs]
diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb
index 9d777ddfccd..35a20adeef3 100644
--- a/spec/factories/notes.rb
+++ b/spec/factories/notes.rb
@@ -16,6 +16,7 @@
# system :boolean default(FALSE), not null
# st_diff :text
# updated_by_id :integer
+# is_award :boolean default(FALSE), not null
#
require_relative '../support/repo_helpers'
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 1d500a11ad7..112213377ff 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -28,6 +28,7 @@
# import_type :string(255)
# import_source :string(255)
# commit_count :integer default(0)
+# import_error :text
#
FactoryGirl.define do
diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb
index dc41be8246f..52134556339 100644
--- a/spec/features/atom/users_spec.rb
+++ b/spec/features/atom/users_spec.rb
@@ -79,6 +79,6 @@ describe "User Feed", feature: true do
end
def safe_name
- html_escape(user.name)
+ CGI.escapeHTML(user.name)
end
end
diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb
index 5213ce1099f..1f99a808f87 100644
--- a/spec/features/builds_spec.rb
+++ b/spec/features/builds_spec.rb
@@ -19,7 +19,7 @@ describe "Builds" do
end
it { expect(page).to have_content 'Running' }
- it { expect(page).to have_content 'Cancel all' }
+ it { expect(page).to have_content 'Cancel running' }
it { expect(page).to have_content @build.short_sha }
it { expect(page).to have_content @build.ref }
it { expect(page).to have_content @build.name }
@@ -32,7 +32,7 @@ describe "Builds" do
end
it { expect(page).to have_content 'No builds to show' }
- it { expect(page).to have_content 'Cancel all' }
+ it { expect(page).to have_content 'Cancel running' }
end
context "All builds" do
@@ -45,7 +45,7 @@ describe "Builds" do
it { expect(page).to have_content @build.short_sha }
it { expect(page).to have_content @build.ref }
it { expect(page).to have_content @build.name }
- it { expect(page).to_not have_content 'Cancel all' }
+ it { expect(page).to_not have_content 'Cancel running' }
end
end
@@ -53,11 +53,11 @@ describe "Builds" do
before do
@build.run!
visit namespace_project_builds_path(@gl_project.namespace, @gl_project)
- click_link "Cancel all"
+ click_link "Cancel running"
end
it { expect(page).to have_content 'No builds to show' }
- it { expect(page).to_not have_content 'Cancel all' }
+ it { expect(page).to_not have_content 'Cancel running' }
end
describe "GET /:project/builds/:id" do
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index 0af5e6fc1a6..3d6d87e764a 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -293,10 +293,10 @@ describe 'Issues', feature: true do
end
def first_issue
- page.all('ul.issues-list li').first.text
+ page.all('ul.issues-list > li').first.text
end
def last_issue
- page.all('ul.issues-list li').last.text
+ page.all('ul.issues-list > li').last.text
end
end
diff --git a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb
new file mode 100644
index 00000000000..a674124aab7
--- /dev/null
+++ b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb
@@ -0,0 +1,85 @@
+require 'spec_helper'
+
+feature 'Merge When Build Succeeds', feature: true, js: true do
+ let(:user) { create(:user) }
+
+ let(:project) { create(:project, :public) }
+ let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user, title: "Bug NS-04") }
+
+ before do
+ project.team << [user, :master]
+ project.enable_ci
+ end
+
+ context "Active build for Merge Request" do
+ let!(:ci_commit) { create(:ci_commit, gl_project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) }
+ let!(:ci_build) { create(:ci_build, commit: ci_commit) }
+
+ before do
+ login_as user
+ visit_merge_request(merge_request)
+ end
+
+ it 'displays the Merge When Build Succeeds button' do
+ expect(page).to have_link "Merge When Build Succeeds"
+ end
+
+ context "Merge When Build succeeds enabled" do
+ before do
+ click_link "Merge When Build Succeeds"
+ end
+
+ it 'activates Merge When Build Succeeds feature' do
+ expect(page).to have_link "Cancel Automatic Merge"
+
+ expect(page).to have_content "Set by #{user.name} to be merged automatically when the build succeeds."
+ expect(page).to have_content "The source branch will not be removed."
+
+ visit_merge_request(merge_request) # Needed to refresh the page
+ expect(page).to have_content /Enabled an automatic merge when the build for [0-9a-f]{8} succeeds/i
+ end
+ end
+ end
+
+ context 'When it is enabled' do
+ let(:merge_request) do
+ create(:merge_request_with_diffs, :simple, source_project: project, author: user,
+ merge_user: user, title: "MepMep", merge_when_build_succeeds: true)
+ end
+
+ let!(:ci_commit) { create(:ci_commit, gl_project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) }
+ let!(:ci_build) { create(:ci_build, commit: ci_commit) }
+
+ before do
+ login_as user
+ visit_merge_request(merge_request)
+ end
+
+ it 'cancels the automatic merge' do
+ click_link "Cancel Automatic Merge"
+
+ expect(page).to have_link "Merge When Build Succeeds"
+
+ visit_merge_request(merge_request) # Needed to refresh the page
+ expect(page).to have_content "Canceled the automatic merge"
+ end
+
+ it "allows the user to remove the source branch" do
+ expect(page).to have_link "Remove Source Branch When Merged"
+
+ click_link "Remove Source Branch When Merged"
+ expect(page).to have_content "The source branch will be removed"
+ end
+ end
+
+ context 'Build is not active' do
+ it "should not allow for enabling" do
+ visit_merge_request(merge_request)
+ expect(page).not_to have_link "Merge When Build Succeeds"
+ end
+ end
+
+ def visit_merge_request(merge_request)
+ visit namespace_project_merge_request_path(merge_request.project.namespace, merge_request.project, merge_request)
+ end
+end
diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb
index d7cb3b2e86e..f0fc6916c4d 100644
--- a/spec/features/notes_on_merge_requests_spec.rb
+++ b/spec/features/notes_on_merge_requests_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
describe 'Comments', feature: true do
include RepoHelpers
+ include WaitForAjax
describe 'On a merge request', js: true, feature: true do
let!(:merge_request) { create(:merge_request) }
@@ -123,8 +124,8 @@ describe 'Comments', feature: true do
it 'removes the attachment div and resets the edit form' do
find('.js-note-attachment-delete').click
is_expected.not_to have_css('.note-attachment')
- expect(find('.current-note-edit-form', visible: false)).
- not_to be_visible
+ is_expected.not_to have_css('.current-note-edit-form')
+ wait_for_ajax
end
end
end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 0a64b70d6a6..5568f06639c 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -278,7 +278,7 @@ describe ApplicationHelper do
el = element.next_element
expect(el.name).to eq 'script'
- expect(el.text).to include "$('.js-timeago').timeago()"
+ expect(el.text).to include "$('.js-timeago').last().timeago()"
end
it 'allows the script tag to be excluded' do
diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb
index c4f7693329c..aafc24397a9 100644
--- a/spec/helpers/visibility_level_helper_spec.rb
+++ b/spec/helpers/visibility_level_helper_spec.rb
@@ -7,69 +7,52 @@ describe VisibilityLevelHelper do
init_haml_helpers
end
- let(:project) { create(:project) }
+ let(:project) { build(:project) }
+ let(:personal_snippet) { build(:personal_snippet) }
+ let(:project_snippet) { build(:project_snippet) }
describe 'visibility_level_description' do
- shared_examples 'a visibility level description' do
- let(:desc) do
- visibility_level_description(Gitlab::VisibilityLevel::PRIVATE,
- form_model)
- end
-
- let(:expected_class) do
- class_name = case form_model.class.name
- when 'String'
- form_model
- else
- form_model.class.name
- end
-
- class_name.match(/(project|snippet)$/i)[0]
- end
-
- it 'should refer to the correct class' do
- expect(desc).to match(/#{expected_class}/i)
+ context 'used with a Project' do
+ it 'delegates projects to #project_visibility_level_description' do
+ expect(visibility_level_description(Gitlab::VisibilityLevel::PRIVATE, project))
+ .to match /project/i
end
end
- context 'form_model argument is a String' do
- context 'model object is a personal snippet' do
- it_behaves_like 'a visibility level description' do
- let(:form_model) { 'PersonalSnippet' }
- end
+ context 'called with a Snippet' do
+ it 'delegates snippets to #snippet_visibility_level_description' do
+ expect(visibility_level_description(Gitlab::VisibilityLevel::INTERNAL, project_snippet))
+ .to match /snippet/i
end
+ end
+ end
- context 'model object is a project snippet' do
- it_behaves_like 'a visibility level description' do
- let(:form_model) { 'ProjectSnippet' }
- end
- end
+ describe "#project_visibility_level_description" do
+ it "describes private projects" do
+ expect(project_visibility_level_description(Gitlab::VisibilityLevel::PRIVATE))
+ .to eq "Project access must be granted explicitly to each user."
+ end
- context 'model object is a project' do
- it_behaves_like 'a visibility level description' do
- let(:form_model) { 'Project' }
- end
- end
+ it "describes public projects" do
+ expect(project_visibility_level_description(Gitlab::VisibilityLevel::PUBLIC))
+ .to eq "The project can be cloned without any authentication."
end
+ end
- context 'form_model argument is a model object' do
- context 'model object is a personal snippet' do
- it_behaves_like 'a visibility level description' do
- let(:form_model) { create(:personal_snippet) }
- end
- end
+ describe "#snippet_visibility_level_description" do
+ it 'describes visibility only for me' do
+ expect(snippet_visibility_level_description(Gitlab::VisibilityLevel::PRIVATE, personal_snippet))
+ .to eq "The snippet is visible only to me."
+ end
- context 'model object is a project snippet' do
- it_behaves_like 'a visibility level description' do
- let(:form_model) { create(:project_snippet, project: project) }
- end
- end
+ it 'describes visibility for project members' do
+ expect(snippet_visibility_level_description(Gitlab::VisibilityLevel::PRIVATE, project_snippet))
+ .to eq "The snippet is visible only to project members."
+ end
- context 'model object is a project' do
- it_behaves_like 'a visibility level description' do
- let(:form_model) { project }
- end
- end
+ it 'defaults to personal snippet' do
+ expect(snippet_visibility_level_description(Gitlab::VisibilityLevel::PRIVATE))
+ .to eq "The snippet is visible only to me."
end
end
diff --git a/spec/javascripts/fixtures/merge_request_tabs.html.haml b/spec/javascripts/fixtures/merge_request_tabs.html.haml
index 7624a713948..68678c3d7e3 100644
--- a/spec/javascripts/fixtures/merge_request_tabs.html.haml
+++ b/spec/javascripts/fixtures/merge_request_tabs.html.haml
@@ -1,12 +1,12 @@
%ul.nav.nav-tabs.merge-request-tabs
%li.notes-tab
- %a{href: '/foo/bar/merge_requests/1', data: {target: '#notes', action: 'notes', toggle: 'tab'}}
+ %a{href: '/foo/bar/merge_requests/1', data: {target: 'div#notes', action: 'notes', toggle: 'tab'}}
Discussion
%li.commits-tab
- %a{href: '/foo/bar/merge_requests/1/commits', data: {target: '#commits', action: 'commits', toggle: 'tab'}}
+ %a{href: '/foo/bar/merge_requests/1/commits', data: {target: 'div#commits', action: 'commits', toggle: 'tab'}}
Commits
%li.diffs-tab
- %a{href: '/foo/bar/merge_requests/1/diffs', data: {target: '#diffs', action: 'diffs', toggle: 'tab'}}
+ %a{href: '/foo/bar/merge_requests/1/diffs', data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'}}
Diffs
.tab-content
diff --git a/spec/lib/ci/ansi2html_spec.rb b/spec/lib/ci/ansi2html_spec.rb
index 75c023bbc43..3a2b568f4c7 100644
--- a/spec/lib/ci/ansi2html_spec.rb
+++ b/spec/lib/ci/ansi2html_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Ci::Ansi2html do
+describe Ci::Ansi2html, lib: true do
subject { Ci::Ansi2html }
it "prints non-ansi as-is" do
diff --git a/spec/lib/ci/charts_spec.rb b/spec/lib/ci/charts_spec.rb
index 83e2ad220b8..50a77308cde 100644
--- a/spec/lib/ci/charts_spec.rb
+++ b/spec/lib/ci/charts_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe "Charts" do
+describe Ci::Charts, lib: true do
context "build_times" do
before do
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index 6f287719ba6..ce4a5244bd0 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Ci
- describe GitlabCiYamlProcessor do
+ describe GitlabCiYamlProcessor, lib: true do
let(:path) { 'path' }
describe "#builds_for_ref" do
diff --git a/spec/lib/disable_email_interceptor_spec.rb b/spec/lib/disable_email_interceptor_spec.rb
index a9624e9a2b7..c2a7b20b84d 100644
--- a/spec/lib/disable_email_interceptor_spec.rb
+++ b/spec/lib/disable_email_interceptor_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe DisableEmailInterceptor do
+describe DisableEmailInterceptor, lib: true do
before do
ActionMailer::Base.register_interceptor(DisableEmailInterceptor)
end
diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb
index 48bc60eed16..f38fadda9ba 100644
--- a/spec/lib/extracts_path_spec.rb
+++ b/spec/lib/extracts_path_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe ExtractsPath do
+describe ExtractsPath, lib: true do
include ExtractsPath
include RepoHelpers
include Gitlab::Application.routes.url_helpers
diff --git a/spec/lib/file_size_validator_spec.rb b/spec/lib/file_size_validator_spec.rb
index 12ccc051c74..fda6f9a6c88 100644
--- a/spec/lib/file_size_validator_spec.rb
+++ b/spec/lib/file_size_validator_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Gitlab::FileSizeValidatorSpec' do
+describe FileSizeValidator, lib: true do
let(:validator) { FileSizeValidator.new(options) }
let(:attachment) { AttachmentUploader.new }
let(:note) { create(:note) }
diff --git a/spec/lib/git_ref_validator_spec.rb b/spec/lib/git_ref_validator_spec.rb
index 4633b6f3934..dc57e94f193 100644
--- a/spec/lib/git_ref_validator_spec.rb
+++ b/spec/lib/git_ref_validator_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::GitRefValidator do
+describe Gitlab::GitRefValidator, lib: true do
it { expect(Gitlab::GitRefValidator.validate('feature/new')).to be_truthy }
it { expect(Gitlab::GitRefValidator.validate('implement_@all')).to be_truthy }
it { expect(Gitlab::GitRefValidator.validate('my_new_feature')).to be_truthy }
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index 03e36fd3552..7d985fb6f1e 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
require 'nokogiri'
module Gitlab
- describe Asciidoc do
+ describe Asciidoc, lib: true do
let(:input) { '<b>ascii</b>' }
let(:context) { {} }
diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb
index 72806bebe1f..aad291c03cd 100644
--- a/spec/lib/gitlab/auth_spec.rb
+++ b/spec/lib/gitlab/auth_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Auth do
+describe Gitlab::Auth, lib: true do
let(:gl_auth) { Gitlab::Auth.new }
describe :find do
diff --git a/spec/lib/gitlab/backend/grack_auth_spec.rb b/spec/lib/gitlab/backend/grack_auth_spec.rb
index dfa0e10318a..73458735ad9 100644
--- a/spec/lib/gitlab/backend/grack_auth_spec.rb
+++ b/spec/lib/gitlab/backend/grack_auth_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe Grack::Auth do
+describe Grack::Auth, lib: true do
let(:user) { create(:user) }
let(:project) { create(:project) }
diff --git a/spec/lib/gitlab/backend/shell_spec.rb b/spec/lib/gitlab/backend/shell_spec.rb
index b60e23454d6..fd869f48b5c 100644
--- a/spec/lib/gitlab/backend/shell_spec.rb
+++ b/spec/lib/gitlab/backend/shell_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Shell do
+describe Gitlab::Shell, lib: true do
let(:project) { double('Project', id: 7, path: 'diaspora') }
let(:gitlab_shell) { Gitlab::Shell.new }
@@ -16,7 +16,7 @@ describe Gitlab::Shell do
it { expect(gitlab_shell.url_to_repo('diaspora')).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + "diaspora.git") }
- describe Gitlab::Shell::KeyAdder do
+ describe Gitlab::Shell::KeyAdder, lib: true do
describe '#add_key' do
it 'normalizes space characters in the key' do
io = spy
diff --git a/spec/lib/gitlab/bitbucket_import/client_spec.rb b/spec/lib/gitlab/bitbucket_import/client_spec.rb
index dfe58637eee..aa0699f2ebf 100644
--- a/spec/lib/gitlab/bitbucket_import/client_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/client_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::BitbucketImport::Client do
+describe Gitlab::BitbucketImport::Client, lib: true do
let(:token) { '123456' }
let(:secret) { 'secret' }
let(:client) { Gitlab::BitbucketImport::Client.new(token, secret) }
diff --git a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb
index 0e826a319e0..e1c60e07b4d 100644
--- a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::BitbucketImport::ProjectCreator do
+describe Gitlab::BitbucketImport::ProjectCreator, lib: true do
let(:user) { create(:user) }
let(:repo) do
{
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index fe1b94a484e..99288da1e43 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::ClosingIssueExtractor do
+describe Gitlab::ClosingIssueExtractor, lib: true do
let(:project) { create(:project) }
let(:project2) { create(:project) }
let(:issue) { create(:issue, project: project) }
diff --git a/spec/lib/gitlab/color_schemes_spec.rb b/spec/lib/gitlab/color_schemes_spec.rb
index c7be45dbcd3..0a1ec66f199 100644
--- a/spec/lib/gitlab/color_schemes_spec.rb
+++ b/spec/lib/gitlab/color_schemes_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::ColorSchemes do
+describe Gitlab::ColorSchemes, lib: true do
describe '.body_classes' do
it 'returns a space-separated list of class names' do
css = described_class.body_classes
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 7cdebdf209a..8461e8ce50d 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Database do
+describe Gitlab::Database, lib: true do
# These are just simple smoke tests to check if the methods work (regardless
# of what they may return).
describe '.mysql?' do
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index 8b7946f3117..c7cdf8691d6 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Diff::File do
+describe Gitlab::Diff::File, lib: true do
include RepoHelpers
let(:project) { create(:project) }
diff --git a/spec/lib/gitlab/diff/parser_spec.rb b/spec/lib/gitlab/diff/parser_spec.rb
index 4d5d1431683..ba577bd28e5 100644
--- a/spec/lib/gitlab/diff/parser_spec.rb
+++ b/spec/lib/gitlab/diff/parser_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Diff::Parser do
+describe Gitlab::Diff::Parser, lib: true do
include RepoHelpers
let(:project) { create(:project) }
diff --git a/spec/lib/gitlab/email/attachment_uploader_spec.rb b/spec/lib/gitlab/email/attachment_uploader_spec.rb
index 8fb432367b6..476a21bf996 100644
--- a/spec/lib/gitlab/email/attachment_uploader_spec.rb
+++ b/spec/lib/gitlab/email/attachment_uploader_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe Gitlab::Email::AttachmentUploader do
+describe Gitlab::Email::AttachmentUploader, lib: true do
describe "#execute" do
let(:project) { build(:project) }
let(:message_raw) { fixture_file("emails/attachment.eml") }
diff --git a/spec/lib/gitlab/email/receiver_spec.rb b/spec/lib/gitlab/email/receiver_spec.rb
index e470b7cd5f5..b535413bbd4 100644
--- a/spec/lib/gitlab/email/receiver_spec.rb
+++ b/spec/lib/gitlab/email/receiver_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe Gitlab::Email::Receiver do
+describe Gitlab::Email::Receiver, lib: true do
before do
stub_incoming_email_setting(enabled: true, address: "reply+%{key}@appmail.adventuretime.ooo")
end
diff --git a/spec/lib/gitlab/email/reply_parser_spec.rb b/spec/lib/gitlab/email/reply_parser_spec.rb
index 7cae1da8050..6f8e9a4be64 100644
--- a/spec/lib/gitlab/email/reply_parser_spec.rb
+++ b/spec/lib/gitlab/email/reply_parser_spec.rb
@@ -1,7 +1,7 @@
require "spec_helper"
# Inspired in great part by Discourse's Email::Receiver
-describe Gitlab::Email::ReplyParser do
+describe Gitlab::Email::ReplyParser, lib: true do
describe '#execute' do
def test_parse_body(mail_string)
described_class.new(Mail::Message.new(mail_string)).execute
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index c7291689e32..9b3a0e3a75f 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::GitAccess do
+describe Gitlab::GitAccess, lib: true do
let(:access) { Gitlab::GitAccess.new(actor, project) }
let(:project) { create(:project) }
let(:user) { create(:user) }
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index 4cb91094cb3..77ecfce6f17 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::GitAccessWiki do
+describe Gitlab::GitAccessWiki, lib: true do
let(:access) { Gitlab::GitAccessWiki.new(user, project) }
let(:project) { create(:project) }
let(:user) { create(:user) }
diff --git a/spec/lib/gitlab/github_import/client_spec.rb b/spec/lib/gitlab/github_import/client_spec.rb
index 26618120316..49d8cdf4314 100644
--- a/spec/lib/gitlab/github_import/client_spec.rb
+++ b/spec/lib/gitlab/github_import/client_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::GithubImport::Client do
+describe Gitlab::GithubImport::Client, lib: true do
let(:token) { '123456' }
let(:client) { Gitlab::GithubImport::Client.new(token) }
diff --git a/spec/lib/gitlab/github_import/project_creator_spec.rb b/spec/lib/gitlab/github_import/project_creator_spec.rb
index ca61d3c5234..c93a3ebdaec 100644
--- a/spec/lib/gitlab/github_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/github_import/project_creator_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::GithubImport::ProjectCreator do
+describe Gitlab::GithubImport::ProjectCreator, lib: true do
let(:user) { create(:user) }
let(:repo) do
OpenStruct.new(
diff --git a/spec/lib/gitlab/gitlab_import/client_spec.rb b/spec/lib/gitlab/gitlab_import/client_spec.rb
index c511c515474..e6831e7c383 100644
--- a/spec/lib/gitlab/gitlab_import/client_spec.rb
+++ b/spec/lib/gitlab/gitlab_import/client_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::GitlabImport::Client do
+describe Gitlab::GitlabImport::Client, lib: true do
let(:token) { '123456' }
let(:client) { Gitlab::GitlabImport::Client.new(token) }
diff --git a/spec/lib/gitlab/gitlab_import/project_creator_spec.rb b/spec/lib/gitlab/gitlab_import/project_creator_spec.rb
index 2d8923d14bb..483f65cd053 100644
--- a/spec/lib/gitlab/gitlab_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/gitlab_import/project_creator_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::GitlabImport::ProjectCreator do
+describe Gitlab::GitlabImport::ProjectCreator, lib: true do
let(:user) { create(:user) }
let(:repo) do
{
diff --git a/spec/lib/gitlab/gitorious_import/project_creator_spec.rb b/spec/lib/gitlab/gitorious_import/project_creator_spec.rb
index c1125ca6357..946712ca38e 100644
--- a/spec/lib/gitlab/gitorious_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/gitorious_import/project_creator_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::GitoriousImport::ProjectCreator do
+describe Gitlab::GitoriousImport::ProjectCreator, lib: true do
let(:user) { create(:user) }
let(:repo) { Gitlab::GitoriousImport::Repository.new('foo/bar-baz-qux') }
let(:namespace){ create(:group, owner: user) }
diff --git a/spec/lib/gitlab/google_code_import/client_spec.rb b/spec/lib/gitlab/google_code_import/client_spec.rb
index 37985c062b4..85949ae8dc4 100644
--- a/spec/lib/gitlab/google_code_import/client_spec.rb
+++ b/spec/lib/gitlab/google_code_import/client_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe Gitlab::GoogleCodeImport::Client do
+describe Gitlab::GoogleCodeImport::Client, lib: true do
let(:raw_data) { JSON.parse(fixture_file("GoogleCodeProjectHosting.json")) }
subject { described_class.new(raw_data) }
diff --git a/spec/lib/gitlab/google_code_import/importer_spec.rb b/spec/lib/gitlab/google_code_import/importer_spec.rb
index 65ad7524cc2..647631271e0 100644
--- a/spec/lib/gitlab/google_code_import/importer_spec.rb
+++ b/spec/lib/gitlab/google_code_import/importer_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe Gitlab::GoogleCodeImport::Importer do
+describe Gitlab::GoogleCodeImport::Importer, lib: true do
let(:mapped_user) { create(:user, username: "thilo123") }
let(:raw_data) { JSON.parse(fixture_file("GoogleCodeProjectHosting.json")) }
let(:client) { Gitlab::GoogleCodeImport::Client.new(raw_data) }
diff --git a/spec/lib/gitlab/google_code_import/project_creator_spec.rb b/spec/lib/gitlab/google_code_import/project_creator_spec.rb
index 35549b48687..499a896ee76 100644
--- a/spec/lib/gitlab/google_code_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/google_code_import/project_creator_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::GoogleCodeImport::ProjectCreator do
+describe Gitlab::GoogleCodeImport::ProjectCreator, lib: true do
let(:user) { create(:user) }
let(:repo) do
Gitlab::GoogleCodeImport::Repository.new(
diff --git a/spec/lib/gitlab/incoming_email_spec.rb b/spec/lib/gitlab/incoming_email_spec.rb
index 5fdb9c723b1..bcdba8d4c12 100644
--- a/spec/lib/gitlab/incoming_email_spec.rb
+++ b/spec/lib/gitlab/incoming_email_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe Gitlab::IncomingEmail do
+describe Gitlab::IncomingEmail, lib: true do
describe "self.enabled?" do
context "when reply by email is enabled" do
before do
diff --git a/spec/lib/gitlab/inline_diff_spec.rb b/spec/lib/gitlab/inline_diff_spec.rb
index 2e0a05088cc..c690c195112 100644
--- a/spec/lib/gitlab/inline_diff_spec.rb
+++ b/spec/lib/gitlab/inline_diff_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::InlineDiff do
+describe Gitlab::InlineDiff, lib: true do
describe '#processing' do
let(:diff) do
<<eos
diff --git a/spec/lib/gitlab/key_fingerprint_spec.rb b/spec/lib/gitlab/key_fingerprint_spec.rb
index 266eab6e793..d09f51f3bfc 100644
--- a/spec/lib/gitlab/key_fingerprint_spec.rb
+++ b/spec/lib/gitlab/key_fingerprint_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe Gitlab::KeyFingerprint do
+describe Gitlab::KeyFingerprint, lib: true do
let(:key) { "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" }
let(:fingerprint) { "3f:a2:ee:de:b5:de:53:c3:aa:2f:9c:45:24:4c:47:7b" }
diff --git a/spec/lib/gitlab/ldap/access_spec.rb b/spec/lib/gitlab/ldap/access_spec.rb
index c38f212b405..a628d0c0157 100644
--- a/spec/lib/gitlab/ldap/access_spec.rb
+++ b/spec/lib/gitlab/ldap/access_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::LDAP::Access do
+describe Gitlab::LDAP::Access, lib: true do
let(:access) { Gitlab::LDAP::Access.new user }
let(:user) { create(:omniauth_user) }
@@ -13,6 +13,11 @@ describe Gitlab::LDAP::Access do
end
it { is_expected.to be_falsey }
+
+ it 'should block user in GitLab' do
+ access.allowed?
+ expect(user).to be_blocked
+ end
end
context 'when the user is found' do
diff --git a/spec/lib/gitlab/ldap/adapter_spec.rb b/spec/lib/gitlab/ldap/adapter_spec.rb
index 38076602df9..4847b5f3b0e 100644
--- a/spec/lib/gitlab/ldap/adapter_spec.rb
+++ b/spec/lib/gitlab/ldap/adapter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::LDAP::Adapter do
+describe Gitlab::LDAP::Adapter, lib: true do
let(:adapter) { Gitlab::LDAP::Adapter.new 'ldapmain' }
describe '#dn_matches_filter?' do
diff --git a/spec/lib/gitlab/ldap/auth_hash_spec.rb b/spec/lib/gitlab/ldap/auth_hash_spec.rb
index 7d8268536a4..6a53ed1db64 100644
--- a/spec/lib/gitlab/ldap/auth_hash_spec.rb
+++ b/spec/lib/gitlab/ldap/auth_hash_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::LDAP::AuthHash do
+describe Gitlab::LDAP::AuthHash, lib: true do
let(:auth_hash) do
Gitlab::LDAP::AuthHash.new(
OmniAuth::AuthHash.new(
diff --git a/spec/lib/gitlab/ldap/authentication_spec.rb b/spec/lib/gitlab/ldap/authentication_spec.rb
index 6e3de914a45..b8f3290e84c 100644
--- a/spec/lib/gitlab/ldap/authentication_spec.rb
+++ b/spec/lib/gitlab/ldap/authentication_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::LDAP::Authentication do
+describe Gitlab::LDAP::Authentication, lib: true do
let(:user) { create(:omniauth_user, extern_uid: dn) }
let(:dn) { 'uid=john,ou=people,dc=example,dc=com' }
let(:login) { 'john' }
diff --git a/spec/lib/gitlab/ldap/config_spec.rb b/spec/lib/gitlab/ldap/config_spec.rb
index 3548d647c84..835853a83a4 100644
--- a/spec/lib/gitlab/ldap/config_spec.rb
+++ b/spec/lib/gitlab/ldap/config_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::LDAP::Config do
+describe Gitlab::LDAP::Config, lib: true do
let(:config) { Gitlab::LDAP::Config.new provider }
let(:provider) { 'ldapmain' }
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index b5b56a34952..3bba5e2efa2 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::LDAP::User do
+describe Gitlab::LDAP::User, lib: true do
let(:ldap_user) { Gitlab::LDAP::User.new(auth_hash) }
let(:gl_user) { ldap_user.gl_user }
let(:info) do
diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb
index 5b13d65c7c0..5852b31ab3a 100644
--- a/spec/lib/gitlab/lfs/lfs_router_spec.rb
+++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Lfs::Router do
+describe Gitlab::Lfs::Router, lib: true do
let(:project) { create(:project) }
let(:public_project) { create(:project, :public) }
let(:forked_project) { fork_project(public_project, user) }
diff --git a/spec/lib/gitlab/markdown/autolink_filter_spec.rb b/spec/lib/gitlab/markdown/autolink_filter_spec.rb
index 26332ba5217..7cfec554a45 100644
--- a/spec/lib/gitlab/markdown/autolink_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/autolink_filter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe AutolinkFilter do
+ describe AutolinkFilter, lib: true do
include FilterSpecHelper
let(:link) { 'http://about.gitlab.com/' }
diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
index 9ce63f9af46..6aecba5a676 100644
--- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe CommitRangeReferenceFilter do
+ describe CommitRangeReferenceFilter, lib: true do
include FilterSpecHelper
let(:project) { create(:project, :public) }
diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
index 462a41b4756..9ac76b3228b 100644
--- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe CommitReferenceFilter do
+ describe CommitReferenceFilter, lib: true do
include FilterSpecHelper
let(:project) { create(:project, :public) }
diff --git a/spec/lib/gitlab/markdown/cross_project_reference_spec.rb b/spec/lib/gitlab/markdown/cross_project_reference_spec.rb
index 8d4f9e403a6..e7774897780 100644
--- a/spec/lib/gitlab/markdown/cross_project_reference_spec.rb
+++ b/spec/lib/gitlab/markdown/cross_project_reference_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe CrossProjectReference do
+ describe CrossProjectReference, lib: true do
include described_class
describe '#project_from_ref' do
diff --git a/spec/lib/gitlab/markdown/emoji_filter_spec.rb b/spec/lib/gitlab/markdown/emoji_filter_spec.rb
index 11efd9bb4cd..fb98aac3340 100644
--- a/spec/lib/gitlab/markdown/emoji_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/emoji_filter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe EmojiFilter do
+ describe EmojiFilter, lib: true do
include FilterSpecHelper
before do
diff --git a/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
index d8c2970b6bd..cd92007e047 100644
--- a/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe ExternalIssueReferenceFilter do
+ describe ExternalIssueReferenceFilter, lib: true do
include FilterSpecHelper
def helper
diff --git a/spec/lib/gitlab/markdown/external_link_filter_spec.rb b/spec/lib/gitlab/markdown/external_link_filter_spec.rb
index a040b34577b..493c19a287d 100644
--- a/spec/lib/gitlab/markdown/external_link_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/external_link_filter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe ExternalLinkFilter do
+ describe ExternalLinkFilter, lib: true do
include FilterSpecHelper
it 'ignores elements without an href attribute' do
diff --git a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
index 078ff3ed4b2..dee0826336b 100644
--- a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe IssueReferenceFilter do
+ describe IssueReferenceFilter, lib: true do
include FilterSpecHelper
def helper
diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
index ef6dd524aba..799dbe6f2db 100644
--- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
require 'html/pipeline'
module Gitlab::Markdown
- describe LabelReferenceFilter do
+ describe LabelReferenceFilter, lib: true do
include FilterSpecHelper
let(:project) { create(:empty_project, :public) }
diff --git a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
index 4a232051127..ed70d5c4fa2 100644
--- a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe MergeRequestReferenceFilter do
+ describe MergeRequestReferenceFilter, lib: true do
include FilterSpecHelper
let(:project) { create(:project, :public) }
diff --git a/spec/lib/gitlab/markdown/redactor_filter_spec.rb b/spec/lib/gitlab/markdown/redactor_filter_spec.rb
index eea3f1cf370..27a3d8ec4ef 100644
--- a/spec/lib/gitlab/markdown/redactor_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/redactor_filter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe RedactorFilter do
+ describe RedactorFilter, lib: true do
include ActionView::Helpers::UrlHelper
include FilterSpecHelper
diff --git a/spec/lib/gitlab/markdown/reference_gatherer_filter_spec.rb b/spec/lib/gitlab/markdown/reference_gatherer_filter_spec.rb
index 4fa473ad191..375dd689472 100644
--- a/spec/lib/gitlab/markdown/reference_gatherer_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/reference_gatherer_filter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe ReferenceGathererFilter do
+ describe ReferenceGathererFilter, lib: true do
include ActionView::Helpers::UrlHelper
include FilterSpecHelper
diff --git a/spec/lib/gitlab/markdown/relative_link_filter_spec.rb b/spec/lib/gitlab/markdown/relative_link_filter_spec.rb
index 027336ceb73..7f193004e32 100644
--- a/spec/lib/gitlab/markdown/relative_link_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/relative_link_filter_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe RelativeLinkFilter do
+ describe RelativeLinkFilter, lib: true do
def filter(doc, contexts = {})
contexts.reverse_merge!({
commit: project.commit,
diff --git a/spec/lib/gitlab/markdown/sanitization_filter_spec.rb b/spec/lib/gitlab/markdown/sanitization_filter_spec.rb
index 27cd00e8054..522481b45a5 100644
--- a/spec/lib/gitlab/markdown/sanitization_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/sanitization_filter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe SanitizationFilter do
+ describe SanitizationFilter, lib: true do
include FilterSpecHelper
describe 'default whitelist' do
diff --git a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
index 3a9acc9d6d4..279244c5ac2 100644
--- a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe SnippetReferenceFilter do
+ describe SnippetReferenceFilter, lib: true do
include FilterSpecHelper
let(:project) { create(:empty_project, :public) }
diff --git a/spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb b/spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb
index 6a490673728..f9be5ad2a6e 100644
--- a/spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe SyntaxHighlightFilter do
+ describe SyntaxHighlightFilter, lib: true do
include FilterSpecHelper
it 'highlights valid code blocks' do
diff --git a/spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb b/spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb
index ddf583a72c1..8c3c6e50b75 100644
--- a/spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe TableOfContentsFilter do
+ describe TableOfContentsFilter, lib: true do
include FilterSpecHelper
def header(level, text)
diff --git a/spec/lib/gitlab/markdown/task_list_filter_spec.rb b/spec/lib/gitlab/markdown/task_list_filter_spec.rb
index 94f39cc966e..cbcfd92956f 100644
--- a/spec/lib/gitlab/markdown/task_list_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/task_list_filter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe TaskListFilter do
+ describe TaskListFilter, lib: true do
include FilterSpecHelper
it 'does not apply `task-list` class to non-task lists' do
diff --git a/spec/lib/gitlab/markdown/upload_link_filter_spec.rb b/spec/lib/gitlab/markdown/upload_link_filter_spec.rb
index 9ae45a6f559..9087f823d7f 100644
--- a/spec/lib/gitlab/markdown/upload_link_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/upload_link_filter_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe UploadLinkFilter do
+ describe UploadLinkFilter, lib: true do
def filter(doc, contexts = {})
contexts.reverse_merge!({
project: project
diff --git a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
index 25379f0670e..3dca68edfa8 100644
--- a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Gitlab::Markdown
- describe UserReferenceFilter do
+ describe UserReferenceFilter, lib: true do
include FilterSpecHelper
let(:project) { create(:empty_project, :public) }
diff --git a/spec/lib/gitlab/markup_helper_spec.rb b/spec/lib/gitlab/markup_helper_spec.rb
index e610fab05da..93b91b849f2 100644
--- a/spec/lib/gitlab/markup_helper_spec.rb
+++ b/spec/lib/gitlab/markup_helper_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::MarkupHelper do
+describe Gitlab::MarkupHelper, lib: true do
describe '#markup?' do
%w(textile rdoc org creole wiki
mediawiki rst adoc ad asciidoc mdown md markdown).each do |type|
diff --git a/spec/lib/gitlab/note_data_builder_spec.rb b/spec/lib/gitlab/note_data_builder_spec.rb
index 448cd0c6880..6cbdae737f4 100644
--- a/spec/lib/gitlab/note_data_builder_spec.rb
+++ b/spec/lib/gitlab/note_data_builder_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Gitlab::NoteDataBuilder' do
+describe 'Gitlab::NoteDataBuilder', lib: true do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:data) { Gitlab::NoteDataBuilder.build(note, user) }
diff --git a/spec/lib/gitlab/o_auth/auth_hash_spec.rb b/spec/lib/gitlab/o_auth/auth_hash_spec.rb
index 5632f2306ec..a4f8b44e38e 100644
--- a/spec/lib/gitlab/o_auth/auth_hash_spec.rb
+++ b/spec/lib/gitlab/o_auth/auth_hash_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::OAuth::AuthHash do
+describe Gitlab::OAuth::AuthHash, lib: true do
let(:auth_hash) do
Gitlab::OAuth::AuthHash.new(
OmniAuth::AuthHash.new(
diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb
index fd3ab1fb7c8..925bc442a90 100644
--- a/spec/lib/gitlab/o_auth/user_spec.rb
+++ b/spec/lib/gitlab/o_auth/user_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::OAuth::User do
+describe Gitlab::OAuth::User, lib: true do
let(:oauth_user) { Gitlab::OAuth::User.new(auth_hash) }
let(:gl_user) { oauth_user.gl_user }
let(:uid) { 'my-uid' }
diff --git a/spec/lib/gitlab/popen_spec.rb b/spec/lib/gitlab/popen_spec.rb
index e53efec6c67..795cf241278 100644
--- a/spec/lib/gitlab/popen_spec.rb
+++ b/spec/lib/gitlab/popen_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Gitlab::Popen', no_db: true do
+describe 'Gitlab::Popen', lib: true, no_db: true do
let(:path) { Rails.root.join('tmp').to_s }
before do
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index 19327ac8ce0..efc2e5f4ef1 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::ProjectSearchResults do
+describe Gitlab::ProjectSearchResults, lib: true do
let(:project) { create(:project) }
let(:query) { 'hello world' }
diff --git a/spec/lib/gitlab/push_data_builder_spec.rb b/spec/lib/gitlab/push_data_builder_spec.rb
index 2170399ab5c..3ef61685398 100644
--- a/spec/lib/gitlab/push_data_builder_spec.rb
+++ b/spec/lib/gitlab/push_data_builder_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Gitlab::PushDataBuilder' do
+describe 'Gitlab::PushDataBuilder', lib: true do
let(:project) { create(:project) }
let(:user) { create(:user) }
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index ad84d2274e8..66dc5d4911d 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::ReferenceExtractor do
+describe Gitlab::ReferenceExtractor, lib: true do
let(:project) { create(:project) }
subject { Gitlab::ReferenceExtractor.new(project, project.creator) }
diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb
index 7fdc8fa600d..d67ee423b9b 100644
--- a/spec/lib/gitlab/regex_spec.rb
+++ b/spec/lib/gitlab/regex_spec.rb
@@ -1,7 +1,7 @@
# coding: utf-8
require 'spec_helper'
-describe Gitlab::Regex do
+describe Gitlab::Regex, lib: true do
describe 'project path regex' do
it { expect('gitlab-ce').to match(Gitlab::Regex.project_path_regex) }
it { expect('gitlab_git').to match(Gitlab::Regex.project_path_regex) }
diff --git a/spec/lib/gitlab/sherlock/collection_spec.rb b/spec/lib/gitlab/sherlock/collection_spec.rb
index a8a9d6fc7bc..de6bb86c5dd 100644
--- a/spec/lib/gitlab/sherlock/collection_spec.rb
+++ b/spec/lib/gitlab/sherlock/collection_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Sherlock::Collection do
+describe Gitlab::Sherlock::Collection, lib: true do
let(:collection) { described_class.new }
let(:transaction) do
diff --git a/spec/lib/gitlab/sherlock/file_sample_spec.rb b/spec/lib/gitlab/sherlock/file_sample_spec.rb
index f05a59f56f6..cadf8bbce78 100644
--- a/spec/lib/gitlab/sherlock/file_sample_spec.rb
+++ b/spec/lib/gitlab/sherlock/file_sample_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Sherlock::FileSample do
+describe Gitlab::Sherlock::FileSample, lib: true do
let(:sample) { described_class.new(__FILE__, [], 150.4, 2) }
describe '#id' do
diff --git a/spec/lib/gitlab/sherlock/line_profiler_spec.rb b/spec/lib/gitlab/sherlock/line_profiler_spec.rb
index 8f2e1299714..d57627bba2b 100644
--- a/spec/lib/gitlab/sherlock/line_profiler_spec.rb
+++ b/spec/lib/gitlab/sherlock/line_profiler_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Sherlock::LineProfiler do
+describe Gitlab::Sherlock::LineProfiler, lib: true do
let(:profiler) { described_class.new }
describe '#profile' do
diff --git a/spec/lib/gitlab/sherlock/line_sample_spec.rb b/spec/lib/gitlab/sherlock/line_sample_spec.rb
index 5f02f6a3213..f9b61f8684e 100644
--- a/spec/lib/gitlab/sherlock/line_sample_spec.rb
+++ b/spec/lib/gitlab/sherlock/line_sample_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Sherlock::LineSample do
+describe Gitlab::Sherlock::LineSample, lib: true do
let(:sample) { described_class.new(150.0, 4) }
describe '#duration' do
diff --git a/spec/lib/gitlab/sherlock/location_spec.rb b/spec/lib/gitlab/sherlock/location_spec.rb
index b295a624b35..5739afa6b1e 100644
--- a/spec/lib/gitlab/sherlock/location_spec.rb
+++ b/spec/lib/gitlab/sherlock/location_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Sherlock::Location do
+describe Gitlab::Sherlock::Location, lib: true do
let(:location) { described_class.new(__FILE__, 1) }
describe 'from_ruby_location' do
diff --git a/spec/lib/gitlab/sherlock/middleware_spec.rb b/spec/lib/gitlab/sherlock/middleware_spec.rb
index aa74fc53a79..2bbeb25ce98 100644
--- a/spec/lib/gitlab/sherlock/middleware_spec.rb
+++ b/spec/lib/gitlab/sherlock/middleware_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Sherlock::Middleware do
+describe Gitlab::Sherlock::Middleware, lib: true do
let(:app) { double(:app) }
let(:middleware) { described_class.new(app) }
diff --git a/spec/lib/gitlab/sherlock/query_spec.rb b/spec/lib/gitlab/sherlock/query_spec.rb
index a9afef5dc1d..05da915ccfd 100644
--- a/spec/lib/gitlab/sherlock/query_spec.rb
+++ b/spec/lib/gitlab/sherlock/query_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Sherlock::Query do
+describe Gitlab::Sherlock::Query, lib: true do
let(:started_at) { Time.utc(2015, 1, 1) }
let(:finished_at) { started_at + 5 }
diff --git a/spec/lib/gitlab/sherlock/transaction_spec.rb b/spec/lib/gitlab/sherlock/transaction_spec.rb
index fb80c62c794..7553f2a045f 100644
--- a/spec/lib/gitlab/sherlock/transaction_spec.rb
+++ b/spec/lib/gitlab/sherlock/transaction_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Sherlock::Transaction do
+describe Gitlab::Sherlock::Transaction, lib: true do
let(:transaction) { described_class.new('POST', '/cat_pictures') }
describe '#id' do
diff --git a/spec/lib/gitlab/sql/union_spec.rb b/spec/lib/gitlab/sql/union_spec.rb
index 9e1cd4419e0..0cdbab87544 100644
--- a/spec/lib/gitlab/sql/union_spec.rb
+++ b/spec/lib/gitlab/sql/union_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::SQL::Union do
+describe Gitlab::SQL::Union, lib: true do
describe '#to_sql' do
it 'returns a String joining relations together using a UNION' do
rel1 = User.where(email: 'alice@example.com')
diff --git a/spec/lib/gitlab/themes_spec.rb b/spec/lib/gitlab/themes_spec.rb
index e554458e41c..7a140518dd2 100644
--- a/spec/lib/gitlab/themes_spec.rb
+++ b/spec/lib/gitlab/themes_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Themes do
+describe Gitlab::Themes, lib: true do
describe '.body_classes' do
it 'returns a space-separated list of class names' do
css = described_class.body_classes
diff --git a/spec/lib/gitlab/upgrader_spec.rb b/spec/lib/gitlab/upgrader_spec.rb
index 8df84665e16..e958e087a80 100644
--- a/spec/lib/gitlab/upgrader_spec.rb
+++ b/spec/lib/gitlab/upgrader_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Upgrader do
+describe Gitlab::Upgrader, lib: true do
let(:upgrader) { Gitlab::Upgrader.new }
let(:current_version) { Gitlab::VERSION }
diff --git a/spec/lib/gitlab/uploads_transfer_spec.rb b/spec/lib/gitlab/uploads_transfer_spec.rb
index 260364a513e..4092f7fb638 100644
--- a/spec/lib/gitlab/uploads_transfer_spec.rb
+++ b/spec/lib/gitlab/uploads_transfer_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::UploadsTransfer do
+describe Gitlab::UploadsTransfer, lib: true do
before do
@root_dir = File.join(Rails.root, "public", "uploads")
@upload_transfer = Gitlab::UploadsTransfer.new
diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb
index 5153ed15af3..f023be6ae45 100644
--- a/spec/lib/gitlab/url_builder_spec.rb
+++ b/spec/lib/gitlab/url_builder_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::UrlBuilder do
+describe Gitlab::UrlBuilder, lib: true do
describe 'When asking for an issue' do
it 'returns the issue url' do
issue = create(:issue)
diff --git a/spec/lib/gitlab/version_info_spec.rb b/spec/lib/gitlab/version_info_spec.rb
index 18f71b40fe0..706ee9bec58 100644
--- a/spec/lib/gitlab/version_info_spec.rb
+++ b/spec/lib/gitlab/version_info_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Gitlab::VersionInfo', no_db: true do
+describe 'Gitlab::VersionInfo', lib: true, no_db: true do
before do
@unknown = Gitlab::VersionInfo.new
@v0_0_1 = Gitlab::VersionInfo.new(0, 0, 1)
diff --git a/spec/lib/repository_cache_spec.rb b/spec/lib/repository_cache_spec.rb
index 37240d51310..63b5292b098 100644
--- a/spec/lib/repository_cache_spec.rb
+++ b/spec/lib/repository_cache_spec.rb
@@ -1,6 +1,6 @@
require_relative '../../lib/repository_cache'
-describe RepositoryCache do
+describe RepositoryCache, lib: true do
let(:backend) { double('backend').as_null_object }
let(:cache) { RepositoryCache.new('example', backend) }
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index dfbac7b4004..b67b84959d9 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -36,6 +36,22 @@ describe ApplicationSetting, models: true do
it { expect(setting).to be_valid }
+ describe 'validations' do
+ let(:http) { 'http://example.com' }
+ let(:https) { 'https://example.com' }
+ let(:ftp) { 'ftp://example.com' }
+
+ it { is_expected.to allow_value(nil).for(:home_page_url) }
+ it { is_expected.to allow_value(http).for(:home_page_url) }
+ it { is_expected.to allow_value(https).for(:home_page_url) }
+ it { is_expected.not_to allow_value(ftp).for(:home_page_url) }
+
+ it { is_expected.to allow_value(nil).for(:after_sign_out_path) }
+ it { is_expected.to allow_value(http).for(:after_sign_out_path) }
+ it { is_expected.to allow_value(https).for(:after_sign_out_path) }
+ it { is_expected.not_to allow_value(ftp).for(:after_sign_out_path) }
+ end
+
context 'restricted signup domains' do
it 'set single domain' do
setting.restricted_signup_domains_raw = 'example.com'
diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb
index d80748f23a4..e4cac105110 100644
--- a/spec/models/broadcast_message_spec.rb
+++ b/spec/models/broadcast_message_spec.rb
@@ -15,11 +15,26 @@
require 'spec_helper'
-describe BroadcastMessage do
+describe BroadcastMessage, models: true do
subject { create(:broadcast_message) }
it { is_expected.to be_valid }
+ describe 'validations' do
+ let(:triplet) { '#000' }
+ let(:hex) { '#AABBCC' }
+
+ it { is_expected.to allow_value(nil).for(:color) }
+ it { is_expected.to allow_value(triplet).for(:color) }
+ it { is_expected.to allow_value(hex).for(:color) }
+ it { is_expected.not_to allow_value('000').for(:color) }
+
+ it { is_expected.to allow_value(nil).for(:font) }
+ it { is_expected.to allow_value(triplet).for(:font) }
+ it { is_expected.to allow_value(hex).for(:font) }
+ it { is_expected.not_to allow_value('000').for(:font) }
+ end
+
describe :current do
it "should return last message if time match" do
broadcast_message = create(:broadcast_message, starts_at: Time.now.yesterday, ends_at: Time.now.tomorrow)
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index 839b4c6b16e..70c831b7cbe 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -25,7 +25,7 @@
require 'spec_helper'
-describe Ci::Build do
+describe Ci::Build, models: true do
let(:project) { FactoryGirl.create :ci_project }
let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project }
let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb
index a13f6458cac..89813cdf7fc 100644
--- a/spec/models/ci/commit_spec.rb
+++ b/spec/models/ci/commit_spec.rb
@@ -18,7 +18,7 @@
require 'spec_helper'
-describe Ci::Commit do
+describe Ci::Commit, models: true do
let(:project) { FactoryGirl.create :ci_project }
let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project }
let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
diff --git a/spec/models/ci/project_services/hip_chat_message_spec.rb b/spec/models/ci/project_services/hip_chat_message_spec.rb
index e23d6ae2c28..7d54b6cf84c 100644
--- a/spec/models/ci/project_services/hip_chat_message_spec.rb
+++ b/spec/models/ci/project_services/hip_chat_message_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Ci::HipChatMessage do
+describe Ci::HipChatMessage, models: true do
subject { Ci::HipChatMessage.new(build) }
let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) }
diff --git a/spec/models/ci/project_services/hip_chat_service_spec.rb b/spec/models/ci/project_services/hip_chat_service_spec.rb
index d9ccc855edf..714f1e17e0b 100644
--- a/spec/models/ci/project_services/hip_chat_service_spec.rb
+++ b/spec/models/ci/project_services/hip_chat_service_spec.rb
@@ -15,7 +15,7 @@
require 'spec_helper'
-describe Ci::HipChatService do
+describe Ci::HipChatService, models: true do
describe "Validations" do
diff --git a/spec/models/ci/project_services/mail_service_spec.rb b/spec/models/ci/project_services/mail_service_spec.rb
index c03be3ef75f..638d9a4a626 100644
--- a/spec/models/ci/project_services/mail_service_spec.rb
+++ b/spec/models/ci/project_services/mail_service_spec.rb
@@ -14,7 +14,7 @@
require 'spec_helper'
-describe Ci::MailService do
+describe Ci::MailService, models: true do
describe "Associations" do
it { is_expected.to belong_to :project }
end
diff --git a/spec/models/ci/project_services/slack_message_spec.rb b/spec/models/ci/project_services/slack_message_spec.rb
index 8adda6c86cc..226032b4cda 100644
--- a/spec/models/ci/project_services/slack_message_spec.rb
+++ b/spec/models/ci/project_services/slack_message_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Ci::SlackMessage do
+describe Ci::SlackMessage, models: true do
subject { Ci::SlackMessage.new(commit) }
let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) }
diff --git a/spec/models/ci/project_services/slack_service_spec.rb b/spec/models/ci/project_services/slack_service_spec.rb
index 1ac7dfe568d..e7d7d5d6f4c 100644
--- a/spec/models/ci/project_services/slack_service_spec.rb
+++ b/spec/models/ci/project_services/slack_service_spec.rb
@@ -14,7 +14,7 @@
require 'spec_helper'
-describe Ci::SlackService do
+describe Ci::SlackService, models: true do
describe "Associations" do
it { is_expected.to belong_to :project }
end
diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb
index ac7e38bbcb0..346471aa9b5 100644
--- a/spec/models/ci/project_spec.rb
+++ b/spec/models/ci/project_spec.rb
@@ -27,7 +27,7 @@
require 'spec_helper'
-describe Ci::Project do
+describe Ci::Project, models: true do
let(:project) { FactoryGirl.create :ci_project }
let(:gl_project) { project.gl_project }
subject { project }
diff --git a/spec/models/ci/runner_project_spec.rb b/spec/models/ci/runner_project_spec.rb
index 37682c6ea0c..da8491357a5 100644
--- a/spec/models/ci/runner_project_spec.rb
+++ b/spec/models/ci/runner_project_spec.rb
@@ -11,6 +11,6 @@
require 'spec_helper'
-describe Ci::RunnerProject do
+describe Ci::RunnerProject, models: true do
pending "add some examples to (or delete) #{__FILE__}"
end
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 9a1233b9095..6ebb5e86863 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -19,7 +19,7 @@
require 'spec_helper'
-describe Ci::Runner do
+describe Ci::Runner, models: true do
describe '#display_name' do
it 'should return the description if it has a value' do
runner = FactoryGirl.build(:ci_runner, description: 'Linux/Ruby-1.9.3-p448')
diff --git a/spec/models/ci/service_spec.rb b/spec/models/ci/service_spec.rb
index 36cda988eb4..34e3af7f810 100644
--- a/spec/models/ci/service_spec.rb
+++ b/spec/models/ci/service_spec.rb
@@ -14,7 +14,7 @@
require 'spec_helper'
-describe Ci::Service do
+describe Ci::Service, models: true do
describe "Associations" do
it { is_expected.to belong_to :project }
diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb
index b8aa3c1e777..61eb3c08296 100644
--- a/spec/models/ci/trigger_spec.rb
+++ b/spec/models/ci/trigger_spec.rb
@@ -12,7 +12,7 @@
require 'spec_helper'
-describe Ci::Trigger do
+describe Ci::Trigger, models: true do
let(:project) { FactoryGirl.create :ci_project }
describe 'before_validation' do
diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb
index a515f5881ff..31b56953a13 100644
--- a/spec/models/ci/variable_spec.rb
+++ b/spec/models/ci/variable_spec.rb
@@ -13,7 +13,7 @@
require 'spec_helper'
-describe Ci::Variable do
+describe Ci::Variable, models: true do
subject { Ci::Variable.new }
let(:secret_value) { 'secret' }
diff --git a/spec/models/ci/web_hook_spec.rb b/spec/models/ci/web_hook_spec.rb
index 2865482a212..1a4edec9d4f 100644
--- a/spec/models/ci/web_hook_spec.rb
+++ b/spec/models/ci/web_hook_spec.rb
@@ -11,7 +11,7 @@
require 'spec_helper'
-describe Ci::WebHook do
+describe Ci::WebHook, models: true do
describe "Associations" do
it { is_expected.to belong_to :project }
end
diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb
index 3c1009a2eb0..9307d97e214 100644
--- a/spec/models/commit_range_spec.rb
+++ b/spec/models/commit_range_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe CommitRange do
+describe CommitRange, models: true do
describe 'modules' do
subject { described_class }
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index 38a3dc1f4a6..ecf37b40c58 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Commit do
+describe Commit, models: true do
let(:project) { create(:project) }
let(:commit) { project.commit }
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index dca0715eed8..5e311ead28b 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -33,7 +33,7 @@
require 'spec_helper'
-describe CommitStatus do
+describe CommitStatus, models: true do
let(:commit) { FactoryGirl.create :ci_commit }
let(:commit_status) { FactoryGirl.create :commit_status, commit: commit }
diff --git a/spec/models/concerns/case_sensitivity_spec.rb b/spec/models/concerns/case_sensitivity_spec.rb
index f7ed30f8198..25b3f4e50da 100644
--- a/spec/models/concerns/case_sensitivity_spec.rb
+++ b/spec/models/concerns/case_sensitivity_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe CaseSensitivity do
+describe CaseSensitivity, models: true do
describe '.iwhere' do
let(:connection) { ActiveRecord::Base.connection }
let(:model) { Class.new { include CaseSensitivity } }
diff --git a/spec/models/deploy_key_spec.rb b/spec/models/deploy_key_spec.rb
index 95729932459..64ba778afea 100644
--- a/spec/models/deploy_key_spec.rb
+++ b/spec/models/deploy_key_spec.rb
@@ -15,7 +15,7 @@
require 'spec_helper'
-describe DeployKey do
+describe DeployKey, models: true do
let(:project) { create(:project) }
let(:deploy_key) { create(:deploy_key, projects: [project]) }
diff --git a/spec/models/deploy_keys_project_spec.rb b/spec/models/deploy_keys_project_spec.rb
index 0eb22599d18..8aedbfb8636 100644
--- a/spec/models/deploy_keys_project_spec.rb
+++ b/spec/models/deploy_keys_project_spec.rb
@@ -11,7 +11,7 @@
require 'spec_helper'
-describe DeployKeysProject do
+describe DeployKeysProject, models: true do
describe "Associations" do
it { is_expected.to belong_to(:deploy_key) }
it { is_expected.to belong_to(:project) }
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index ae53f7a536b..071582b0282 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -16,7 +16,7 @@
require 'spec_helper'
-describe Event do
+describe Event, models: true do
describe "Associations" do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:target) }
diff --git a/spec/models/external_issue_spec.rb b/spec/models/external_issue_spec.rb
index 7744610db78..6ec6b9037a4 100644
--- a/spec/models/external_issue_spec.rb
+++ b/spec/models/external_issue_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe ExternalIssue do
+describe ExternalIssue, models: true do
let(:project) { double('project', to_reference: 'namespace1/project1') }
let(:issue) { described_class.new('EXT-1234', project) }
diff --git a/spec/models/external_wiki_service_spec.rb b/spec/models/external_wiki_service_spec.rb
index 4bd5b0be61c..b198aa77526 100644
--- a/spec/models/external_wiki_service_spec.rb
+++ b/spec/models/external_wiki_service_spec.rb
@@ -20,7 +20,7 @@
require 'spec_helper'
-describe ExternalWikiService do
+describe ExternalWikiService, models: true do
include ExternalWikiHelper
describe "Associations" do
it { should belong_to :project }
diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb
index c86314c454c..d61c1c96bde 100644
--- a/spec/models/generic_commit_status_spec.rb
+++ b/spec/models/generic_commit_status_spec.rb
@@ -33,7 +33,7 @@
require 'spec_helper'
-describe GenericCommitStatus do
+describe GenericCommitStatus, models: true do
let(:commit) { FactoryGirl.create :ci_commit }
let(:generic_commit_status) { FactoryGirl.create :generic_commit_status, commit: commit }
diff --git a/spec/models/global_milestone_spec.rb b/spec/models/global_milestone_spec.rb
index 6eeff30b20e..ba03e6aabd0 100644
--- a/spec/models/global_milestone_spec.rb
+++ b/spec/models/global_milestone_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe GlobalMilestone do
+describe GlobalMilestone, models: true do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:group) { create(:group) }
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 6f166b5ab75..646f767e6fe 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -16,7 +16,7 @@
require 'spec_helper'
-describe Group do
+describe Group, models: true do
let!(:group) { create(:group) }
describe 'associations' do
diff --git a/spec/models/hooks/project_hook_spec.rb b/spec/models/hooks/project_hook_spec.rb
index a2dc66fce3e..645ee0b929a 100644
--- a/spec/models/hooks/project_hook_spec.rb
+++ b/spec/models/hooks/project_hook_spec.rb
@@ -18,7 +18,7 @@
require 'spec_helper'
-describe ProjectHook do
+describe ProjectHook, models: true do
describe '.push_hooks' do
it 'should return hooks for push events only' do
hook = create(:project_hook, push_events: true)
diff --git a/spec/models/hooks/service_hook_spec.rb b/spec/models/hooks/service_hook_spec.rb
index 16641c12124..1455661485b 100644
--- a/spec/models/hooks/service_hook_spec.rb
+++ b/spec/models/hooks/service_hook_spec.rb
@@ -18,7 +18,7 @@
require "spec_helper"
-describe ServiceHook do
+describe ServiceHook, models: true do
describe "Associations" do
it { is_expected.to belong_to :service }
end
diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index 02d2cc2c77a..138b87a9a06 100644
--- a/spec/models/hooks/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -18,7 +18,7 @@
require "spec_helper"
-describe SystemHook do
+describe SystemHook, models: true do
describe "execute" do
before(:each) do
@system_hook = create(:system_hook)
diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb
index 2fdc49f02ee..2d90b0793cc 100644
--- a/spec/models/hooks/web_hook_spec.rb
+++ b/spec/models/hooks/web_hook_spec.rb
@@ -18,7 +18,7 @@
require 'spec_helper'
-describe ProjectHook do
+describe ProjectHook, models: true do
describe "Associations" do
it { is_expected.to belong_to :project }
end
@@ -71,5 +71,11 @@ describe ProjectHook do
expect { @project_hook.execute(@data, 'push_hooks') }.to raise_error(RuntimeError)
end
+
+ it "handles SSL exceptions" do
+ expect(WebHook).to receive(:post).and_raise(OpenSSL::SSL::SSLError.new('SSL error'))
+
+ expect(@project_hook.execute(@data, 'push_hooks')).to eq([false, 'SSL error'])
+ end
end
end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index c9aa1b063c6..52271c7c8c6 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -20,7 +20,7 @@
require 'spec_helper'
-describe Issue do
+describe Issue, models: true do
describe "Associations" do
it { is_expected.to belong_to(:milestone) }
end
diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb
index 2f819f60cbb..d7fe01976d8 100644
--- a/spec/models/key_spec.rb
+++ b/spec/models/key_spec.rb
@@ -15,7 +15,7 @@
require 'spec_helper'
-describe Key do
+describe Key, models: true do
describe "Associations" do
it { is_expected.to belong_to(:user) }
end
diff --git a/spec/models/label_link_spec.rb b/spec/models/label_link_spec.rb
index 8c240826582..dc7510b1de3 100644
--- a/spec/models/label_link_spec.rb
+++ b/spec/models/label_link_spec.rb
@@ -12,7 +12,7 @@
require 'spec_helper'
-describe LabelLink do
+describe LabelLink, models: true do
let(:label) { create(:label_link) }
it { expect(label).to be_valid }
diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb
index 511ee8cbd96..696fbf7e0aa 100644
--- a/spec/models/label_spec.rb
+++ b/spec/models/label_spec.rb
@@ -13,7 +13,7 @@
require 'spec_helper'
-describe Label do
+describe Label, models: true do
let(:label) { create(:label) }
describe 'associations' do
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index 57f840c1e91..2aedca20df2 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -19,7 +19,7 @@
require 'spec_helper'
-describe Member do
+describe Member, models: true do
describe "Associations" do
it { is_expected.to belong_to(:user) }
end
diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb
index 652026729bb..5424c9b9cba 100644
--- a/spec/models/members/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
@@ -19,7 +19,7 @@
require 'spec_helper'
-describe GroupMember do
+describe GroupMember, models: true do
context 'notification' do
describe "#after_create" do
it "should send email to user" do
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index ee912bf12a2..9f26d9eb5ce 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -19,7 +19,7 @@
require 'spec_helper'
-describe ProjectMember do
+describe ProjectMember, models: true do
describe :import_team do
before do
@abilities = Six.new
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 567c911425c..1aeba9b2b3b 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -25,13 +25,13 @@
require 'spec_helper'
-describe MergeRequest do
+describe MergeRequest, models: true do
subject { create(:merge_request) }
describe 'associations' do
it { is_expected.to belong_to(:target_project).with_foreign_key(:target_project_id).class_name('Project') }
it { is_expected.to belong_to(:source_project).with_foreign_key(:source_project_id).class_name('Project') }
-
+ it { is_expected.to belong_to(:merge_user).class_name("User") }
it { is_expected.to have_one(:merge_request_diff).dependent(:destroy) }
end
@@ -48,12 +48,32 @@ describe MergeRequest do
describe 'validation' do
it { is_expected.to validate_presence_of(:target_branch) }
it { is_expected.to validate_presence_of(:source_branch) }
+
+ context "Validation of merge user with Merge When Build succeeds" do
+ it "allows user to be nil when the feature is disabled" do
+ expect(subject).to be_valid
+ end
+
+ it "is invalid without merge user" do
+ subject.merge_when_build_succeeds = true
+ expect(subject).not_to be_valid
+ end
+
+ it "is valid with merge user" do
+ subject.merge_when_build_succeeds = true
+ subject.merge_user = build(:user)
+
+ expect(subject).to be_valid
+ end
+ end
end
describe 'respond to' do
it { is_expected.to respond_to(:unchecked?) }
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) }
end
describe '#to_reference' do
@@ -172,6 +192,50 @@ describe MergeRequest do
end
end
+ describe '#can_remove_source_branch?' do
+ let(:user) { create(:user) }
+ let(:user2) { create(:user) }
+
+ before do
+ subject.source_project.team << [user, :master]
+
+ subject.source_branch = "feature"
+ subject.target_branch = "master"
+ subject.save!
+ end
+
+ it "can't be removed when its a protected branch" do
+ allow(subject.source_project).to receive(:protected_branch?).and_return(true)
+ expect(subject.can_remove_source_branch?(user)).to be_falsey
+ end
+
+ it "cant remove a root ref" do
+ subject.source_branch = "master"
+ subject.target_branch = "feature"
+
+ expect(subject.can_remove_source_branch?(user)).to be_falsey
+ end
+
+ it "is unable to remove the source branch for a project the user cannot push to" do
+ expect(subject.can_remove_source_branch?(user2)).to be_falsey
+ end
+
+ it "is can be removed in all other cases" do
+ expect(subject.can_remove_source_branch?(user)).to be_truthy
+ end
+ end
+
+ describe "#reset_merge_when_build_succeeds" do
+ let(:merge_if_green) { create :merge_request, merge_when_build_succeeds: true, merge_user: create(:user) }
+
+ it "sets the item to false" do
+ merge_if_green.reset_merge_when_build_succeeds
+ merge_if_green.reload
+
+ expect(merge_if_green.merge_when_build_succeeds).to be_falsey
+ end
+ end
+
describe "#hook_attrs" do
it "has all the required keys" do
attrs = subject.hook_attrs
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index 77c58627322..30a71987d86 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -15,7 +15,7 @@
require 'spec_helper'
-describe Milestone do
+describe Milestone, models: true do
describe "Associations" do
it { is_expected.to belong_to(:project) }
it { is_expected.to have_many(:issues) }
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index a98b9cb7321..4fa2d2bc4d2 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -16,7 +16,7 @@
require 'spec_helper'
-describe Namespace do
+describe Namespace, models: true do
let!(:namespace) { create(:namespace) }
it { is_expected.to have_many :projects }
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index f347f537550..cd3c868ecc5 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -16,11 +16,12 @@
# system :boolean default(FALSE), not null
# st_diff :text
# updated_by_id :integer
+# is_award :boolean default(FALSE), not null
#
require 'spec_helper'
-describe Note do
+describe Note, models: true do
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:noteable) }
diff --git a/spec/models/project_security_spec.rb b/spec/models/project_security_spec.rb
index f600a240c46..3643ad1b052 100644
--- a/spec/models/project_security_spec.rb
+++ b/spec/models/project_security_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Project do
+describe Project, models: true do
describe :authorization do
before do
@p1 = create(:project)
diff --git a/spec/models/project_services/buildkite_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb
index 230807ea672..88cd624877a 100644
--- a/spec/models/project_services/buildkite_service_spec.rb
+++ b/spec/models/project_services/buildkite_service_spec.rb
@@ -20,7 +20,7 @@
require 'spec_helper'
-describe BuildkiteService do
+describe BuildkiteService, models: true do
describe 'Associations' do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb
index e9967f5fe0b..a2cf68a9e38 100644
--- a/spec/models/project_services/drone_ci_service_spec.rb
+++ b/spec/models/project_services/drone_ci_service_spec.rb
@@ -20,7 +20,7 @@
require 'spec_helper'
-describe DroneCiService do
+describe DroneCiService, models: true do
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to have_one(:service_hook) }
diff --git a/spec/models/project_services/flowdock_service_spec.rb b/spec/models/project_services/flowdock_service_spec.rb
index 16296607a94..ff7fbcaa004 100644
--- a/spec/models/project_services/flowdock_service_spec.rb
+++ b/spec/models/project_services/flowdock_service_spec.rb
@@ -20,7 +20,7 @@
require 'spec_helper'
-describe FlowdockService do
+describe FlowdockService, models: true do
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
diff --git a/spec/models/project_services/gemnasium_service_spec.rb b/spec/models/project_services/gemnasium_service_spec.rb
index 9e156472316..ecb3ccb1673 100644
--- a/spec/models/project_services/gemnasium_service_spec.rb
+++ b/spec/models/project_services/gemnasium_service_spec.rb
@@ -20,7 +20,7 @@
require 'spec_helper'
-describe GemnasiumService do
+describe GemnasiumService, models: true do
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb
index b9006b693b2..835bf364050 100644
--- a/spec/models/project_services/gitlab_ci_service_spec.rb
+++ b/spec/models/project_services/gitlab_ci_service_spec.rb
@@ -20,7 +20,7 @@
require 'spec_helper'
-describe GitlabCiService do
+describe GitlabCiService, models: true do
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to have_one(:service_hook) }
diff --git a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
index e34ca09bffc..3518dbd1728 100644
--- a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
+++ b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
@@ -20,7 +20,7 @@
require 'spec_helper'
-describe GitlabIssueTrackerService do
+describe GitlabIssueTrackerService, models: true do
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb
index f67d7b30980..c96ab548149 100644
--- a/spec/models/project_services/hipchat_service_spec.rb
+++ b/spec/models/project_services/hipchat_service_spec.rb
@@ -20,7 +20,7 @@
require 'spec_helper'
-describe HipchatService do
+describe HipchatService, models: true do
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/project_services/irker_service_spec.rb
index 7d483a44c53..b783b1a576e 100644
--- a/spec/models/project_services/irker_service_spec.rb
+++ b/spec/models/project_services/irker_service_spec.rb
@@ -22,7 +22,7 @@ require 'spec_helper'
require 'socket'
require 'json'
-describe IrkerService do
+describe IrkerService, models: true do
describe 'Associations' do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index 576f5fc79eb..7d91ebe9ce6 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -20,7 +20,7 @@
require 'spec_helper'
-describe JiraService do
+describe JiraService, models: true do
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
diff --git a/spec/models/project_services/pushover_service_spec.rb b/spec/models/project_services/pushover_service_spec.rb
index ac10ffbd39b..96039f9491b 100644
--- a/spec/models/project_services/pushover_service_spec.rb
+++ b/spec/models/project_services/pushover_service_spec.rb
@@ -20,7 +20,7 @@
require 'spec_helper'
-describe PushoverService do
+describe PushoverService, models: true do
describe 'Associations' do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
diff --git a/spec/models/project_services/slack_service/issue_message_spec.rb b/spec/models/project_services/slack_service/issue_message_spec.rb
index b78d92f23a1..97e6f03e308 100644
--- a/spec/models/project_services/slack_service/issue_message_spec.rb
+++ b/spec/models/project_services/slack_service/issue_message_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe SlackService::IssueMessage do
+describe SlackService::IssueMessage, models: true do
subject { SlackService::IssueMessage.new(args) }
let(:args) do
diff --git a/spec/models/project_services/slack_service/merge_message_spec.rb b/spec/models/project_services/slack_service/merge_message_spec.rb
index 581c50d6c88..dae8bd90922 100644
--- a/spec/models/project_services/slack_service/merge_message_spec.rb
+++ b/spec/models/project_services/slack_service/merge_message_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe SlackService::MergeMessage do
+describe SlackService::MergeMessage, models: true do
subject { SlackService::MergeMessage.new(args) }
let(:args) do
diff --git a/spec/models/project_services/slack_service/note_message_spec.rb b/spec/models/project_services/slack_service/note_message_spec.rb
index 21fb575480b..ebf8837570e 100644
--- a/spec/models/project_services/slack_service/note_message_spec.rb
+++ b/spec/models/project_services/slack_service/note_message_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe SlackService::NoteMessage do
+describe SlackService::NoteMessage, models: true do
let(:color) { '#345' }
before do
diff --git a/spec/models/project_services/slack_service/push_message_spec.rb b/spec/models/project_services/slack_service/push_message_spec.rb
index ddc290820d1..cda9ee670b0 100644
--- a/spec/models/project_services/slack_service/push_message_spec.rb
+++ b/spec/models/project_services/slack_service/push_message_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe SlackService::PushMessage do
+describe SlackService::PushMessage, models: true do
subject { SlackService::PushMessage.new(args) }
let(:args) do
diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb
index 97b60e19e40..a9e0afad90f 100644
--- a/spec/models/project_services/slack_service_spec.rb
+++ b/spec/models/project_services/slack_service_spec.rb
@@ -20,7 +20,7 @@
require 'spec_helper'
-describe SlackService do
+describe SlackService, models: true do
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
diff --git a/spec/models/project_snippet_spec.rb b/spec/models/project_snippet_spec.rb
index 3e8f106d27f..cc92eb0bd9f 100644
--- a/spec/models/project_snippet_spec.rb
+++ b/spec/models/project_snippet_spec.rb
@@ -17,7 +17,7 @@
require 'spec_helper'
-describe ProjectSnippet do
+describe ProjectSnippet, models: true do
describe "Associations" do
it { is_expected.to belong_to(:project) }
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 06a02c13bf1..6ddb0e2b8f7 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -28,11 +28,12 @@
# import_type :string(255)
# import_source :string(255)
# commit_count :integer default(0)
+# import_error :text
#
require 'spec_helper'
-describe Project do
+describe Project, models: true do
describe 'associations' do
it { is_expected.to belong_to(:group) }
it { is_expected.to belong_to(:namespace) }
diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb
index 26e8fdae472..5cd5ae327bf 100644
--- a/spec/models/project_team_spec.rb
+++ b/spec/models/project_team_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe ProjectTeam do
+describe ProjectTeam, models: true do
let(:master) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index 3b889144447..876b927eaea 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe ProjectWiki do
+describe ProjectWiki, models: true do
let(:project) { create(:empty_project) }
let(:repository) { project.repository }
let(:user) { project.owner }
diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb
index 1e6937b536c..7e956cf6779 100644
--- a/spec/models/protected_branch_spec.rb
+++ b/spec/models/protected_branch_spec.rb
@@ -12,7 +12,7 @@
require 'spec_helper'
-describe ProtectedBranch do
+describe ProtectedBranch, models: true do
describe 'Associations' do
it { is_expected.to belong_to(:project) }
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index fa261e64c35..e6c415da267 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Repository do
+describe Repository, models: true do
include RepoHelpers
let(:repository) { create(:project).repository }
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index 692e5fda3ba..0ca82365b98 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -20,7 +20,7 @@
require 'spec_helper'
-describe Service do
+describe Service, models: true do
describe "Associations" do
it { is_expected.to belong_to :project }
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index 81581838675..eb2dbbdc5a4 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -17,7 +17,7 @@
require 'spec_helper'
-describe Snippet do
+describe Snippet, models: true do
describe 'modules' do
subject { described_class }
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 4631b12faf1..daa9d1087bf 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -56,11 +56,12 @@
# project_view :integer default(0)
# consumed_timestep :integer
# layout :integer default(0)
+# hide_project_limit :boolean default(FALSE)
#
require 'spec_helper'
-describe User do
+describe User, models: true do
include Gitlab::CurrentSettings
describe 'modules' do
@@ -91,7 +92,23 @@ describe User do
end
describe 'validations' do
- it { is_expected.to validate_presence_of(:username) }
+ describe 'username' do
+ it 'validates presence' do
+ expect(subject).to validate_presence_of(:username)
+ end
+
+ it 'rejects blacklisted names' do
+ user = build(:user, username: 'dashboard')
+
+ expect(user).not_to be_valid
+ expect(user.errors.values).to eq [['dashboard is a reserved name']]
+ end
+
+ it 'validates uniqueness' do
+ expect(subject).to validate_uniqueness_of(:username)
+ end
+ end
+
it { is_expected.to validate_presence_of(:projects_limit) }
it { is_expected.to validate_numericality_of(:projects_limit) }
it { is_expected.to allow_value(0).for(:projects_limit) }
diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb
index d7802d1734f..c1b03838aa9 100644
--- a/spec/models/wiki_page_spec.rb
+++ b/spec/models/wiki_page_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe WikiPage do
+describe WikiPage, models: true do
let(:project) { create(:empty_project) }
let(:user) { project.owner }
let(:wiki) { ProjectWiki.new(project, user) }
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 13cced81875..4cfa49d1566 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -10,6 +10,8 @@ describe API::API, api: true do
let(:avatar_file_path) { File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') }
let!(:group1) { create(:group, avatar: File.open(avatar_file_path)) }
let!(:group2) { create(:group) }
+ let!(:project1) { create(:project, namespace: group1) }
+ let!(:project2) { create(:project, namespace: group2) }
before do
group1.add_owner(user1)
@@ -67,7 +69,7 @@ describe API::API, api: true do
it "should return any existing group" do
get api("/groups/#{group2.id}", admin)
expect(response.status).to eq(200)
- json_response['name'] == group2.name
+ expect(json_response['name']).to eq(group2.name)
end
it "should not return a non existing group" do
@@ -80,7 +82,7 @@ describe API::API, api: true do
it 'should return any existing group' do
get api("/groups/#{group1.path}", admin)
expect(response.status).to eq(200)
- json_response['name'] == group2.name
+ expect(json_response['name']).to eq(group1.name)
end
it 'should not return a non existing group' do
@@ -95,6 +97,59 @@ describe API::API, api: true do
end
end
+ describe "GET /groups/:id/projects" do
+ context "when authenticated as user" do
+ it "should return the group's projects" do
+ get api("/groups/#{group1.id}/projects", user1)
+ expect(response.status).to eq(200)
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['name']).to eq(project1.name)
+ end
+
+ it "should not return a non existing group" do
+ get api("/groups/1328/projects", user1)
+ expect(response.status).to eq(404)
+ end
+
+ it "should not return a group not attached to user1" do
+ get api("/groups/#{group2.id}/projects", user1)
+ expect(response.status).to eq(403)
+ end
+ end
+
+ context "when authenticated as admin" do
+ it "should return any existing group" do
+ get api("/groups/#{group2.id}/projects", admin)
+ expect(response.status).to eq(200)
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['name']).to eq(project2.name)
+ end
+
+ it "should not return a non existing group" do
+ get api("/groups/1328/projects", admin)
+ expect(response.status).to eq(404)
+ end
+ end
+
+ context 'when using group path in URL' do
+ it 'should return any existing group' do
+ get api("/groups/#{group1.path}/projects", admin)
+ expect(response.status).to eq(200)
+ expect(json_response.first['name']).to eq(project1.name)
+ end
+
+ it 'should not return a non existing group' do
+ get api('/groups/unknown/projects', admin)
+ expect(response.status).to eq(404)
+ end
+
+ it 'should not return a group not attached to user1' do
+ get api("/groups/#{group2.path}/projects", user1)
+ expect(response.status).to eq(403)
+ end
+ end
+ end
+
describe "POST /groups" do
context "when authenticated as user without group permissions" do
it "should not create group" do
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index aff109a9424..667f0dbea5c 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -47,7 +47,7 @@ describe API::API, api: true do
name: 'Foo',
color: '#FFAA'
expect(response.status).to eq(400)
- expect(json_response['message']['color']).to eq(['is invalid'])
+ expect(json_response['message']['color']).to eq(['must be a valid color code'])
end
it 'should return 400 for too long color code' do
@@ -55,7 +55,7 @@ describe API::API, api: true do
name: 'Foo',
color: '#FFAAFFFF'
expect(response.status).to eq(400)
- expect(json_response['message']['color']).to eq(['is invalid'])
+ expect(json_response['message']['color']).to eq(['must be a valid color code'])
end
it 'should return 400 for invalid name' do
@@ -151,12 +151,12 @@ describe API::API, api: true do
expect(json_response['message']['title']).to eq(['is invalid'])
end
- it 'should return 400 for invalid name' do
+ it 'should return 400 when color code is too short' do
put api("/projects/#{project.id}/labels", user),
name: 'label1',
color: '#FF'
expect(response.status).to eq(400)
- expect(json_response['message']['color']).to eq(['is invalid'])
+ expect(json_response['message']['color']).to eq(['must be a valid color code'])
end
it 'should return 400 for too long color code' do
@@ -164,7 +164,7 @@ describe API::API, api: true do
name: 'Foo',
color: '#FFAAFFFF'
expect(response.status).to eq(400)
- expect(json_response['message']['color']).to eq(['is invalid'])
+ expect(json_response['message']['color']).to eq(['must be a valid color code'])
end
end
end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index c6d3aef0af9..a91fa735321 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -320,19 +320,21 @@ describe API::API, api: true do
end
describe "PUT /projects/:id/merge_request/:merge_request_id/merge" do
+ let(:ci_commit) { create(:ci_commit_without_jobs) }
+
it "should return merge_request in case of success" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
expect(response.status).to eq(200)
end
- it "should return 405 if branch can't be merged" do
+ it "should return 406 if branch can't be merged" do
allow_any_instance_of(MergeRequest).
to receive(:can_be_merged?).and_return(false)
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
- expect(response.status).to eq(405)
+ expect(response.status).to eq(406)
expect(json_response['message']).to eq('Branch cannot be merged')
end
@@ -357,6 +359,17 @@ describe API::API, api: true do
expect(response.status).to eq(401)
expect(json_response['message']).to eq('401 Unauthorized')
end
+
+ it "enables merge when build succeeds if the ci is active" do
+ allow_any_instance_of(MergeRequest).to receive(:ci_commit).and_return(ci_commit)
+ allow(ci_commit).to receive(:active?).and_return(true)
+
+ put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user), merge_when_build_succeeds: true
+
+ expect(response.status).to eq(200)
+ expect(json_response['title']).to eq('Test')
+ expect(json_response['merge_when_build_succeeds']).to eq(true)
+ end
end
describe "PUT /projects/:id/merge_request/:merge_request_id" do
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index c59ee7af8ab..24b765f4979 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -742,6 +742,18 @@ describe API::API, api: true do
end
end
+ it 'should update visibility_level from public to private' do
+ project3.update_attributes({ visibility_level: Gitlab::VisibilityLevel::PUBLIC })
+
+ project_param = { public: false }
+ put api("/projects/#{project3.id}", user), project_param
+ expect(response.status).to eq(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)
+ end
+
it 'should not update name to existing name' do
project_param = { name: project3.name }
put api("/projects/#{project.id}", user), project_param
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index a9ef2fe5885..2f609c63330 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -153,7 +153,7 @@ describe API::API, api: true do
expect(json_response['message']['projects_limit']).
to eq(['must be greater than or equal to 0'])
expect(json_response['message']['username']).
- to eq([Gitlab::Regex.send(:namespace_regex_message)])
+ to eq([Gitlab::Regex.namespace_regex_message])
end
it "shouldn't available for non admin users" do
@@ -296,7 +296,7 @@ describe API::API, api: true do
expect(json_response['message']['projects_limit']).
to eq(['must be greater than or equal to 0'])
expect(json_response['message']['username']).
- to eq([Gitlab::Regex.send(:namespace_regex_message)])
+ to eq([Gitlab::Regex.namespace_regex_message])
end
context "with existing user" do
diff --git a/spec/services/archive_repository_service_spec.rb b/spec/services/archive_repository_service_spec.rb
index f7a36cd9670..bd871605c66 100644
--- a/spec/services/archive_repository_service_spec.rb
+++ b/spec/services/archive_repository_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe ArchiveRepositoryService do
+describe ArchiveRepositoryService, services: true do
let(:project) { create(:project) }
subject { ArchiveRepositoryService.new(project, "master", "zip") }
diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb
index e0ede1d58b7..c2fafca2ad2 100644
--- a/spec/services/ci/create_commit_service_spec.rb
+++ b/spec/services/ci/create_commit_service_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Ci
- describe CreateCommitService do
+ describe CreateCommitService, services: true do
let(:service) { CreateCommitService.new }
let(:project) { FactoryGirl.create(:ci_project) }
let(:user) { nil }
diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb
index 2ef4bb50a57..c80cb58163a 100644
--- a/spec/services/ci/create_trigger_request_service_spec.rb
+++ b/spec/services/ci/create_trigger_request_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Ci::CreateTriggerRequestService do
+describe Ci::CreateTriggerRequestService, services: true do
let(:service) { Ci::CreateTriggerRequestService.new }
let(:gl_project) { create(:project) }
let(:project) { gl_project.ensure_gitlab_ci_project }
diff --git a/spec/services/ci/event_service_spec.rb b/spec/services/ci/event_service_spec.rb
index 1264e17ff5e..32516c75cf3 100644
--- a/spec/services/ci/event_service_spec.rb
+++ b/spec/services/ci/event_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Ci::EventService do
+describe Ci::EventService, services: true do
let(:project) { FactoryGirl.create :ci_project }
let(:user) { double(username: "root", id: 1) }
diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb
index cda7d0c4a51..b43cabb4ee4 100644
--- a/spec/services/ci/image_for_build_service_spec.rb
+++ b/spec/services/ci/image_for_build_service_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Ci
- describe ImageForBuildService do
+ describe ImageForBuildService, services: true do
let(:service) { ImageForBuildService.new }
let(:project) { FactoryGirl.create(:ci_project) }
let(:gl_project) { FactoryGirl.create(:project, gitlab_ci_project: project) }
diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb
index b370dfbe113..379e07982fb 100644
--- a/spec/services/ci/register_build_service_spec.rb
+++ b/spec/services/ci/register_build_service_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
module Ci
- describe RegisterBuildService do
+ describe RegisterBuildService, services: true do
let!(:service) { RegisterBuildService.new }
let!(:gl_project) { FactoryGirl.create :empty_project }
let!(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
diff --git a/spec/services/ci/web_hook_service_spec.rb b/spec/services/ci/web_hook_service_spec.rb
index aa48fcbcbfd..e7d8ab30652 100644
--- a/spec/services/ci/web_hook_service_spec.rb
+++ b/spec/services/ci/web_hook_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Ci::WebHookService do
+describe Ci::WebHookService, services: true do
let(:project) { FactoryGirl.create :ci_project }
let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project }
let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
diff --git a/spec/services/create_release_service_spec.rb b/spec/services/create_release_service_spec.rb
index 26d7f365bbb..61e5ae72f51 100644
--- a/spec/services/create_release_service_spec.rb
+++ b/spec/services/create_release_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe CreateReleaseService do
+describe CreateReleaseService, services: true do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:tag_name) { project.repository.tag_names.first }
diff --git a/spec/services/create_snippet_service_spec.rb b/spec/services/create_snippet_service_spec.rb
index 8edabe9450b..c800dea04fa 100644
--- a/spec/services/create_snippet_service_spec.rb
+++ b/spec/services/create_snippet_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe CreateSnippetService do
+describe CreateSnippetService, services: true do
before do
@user = create :user
@admin = create :user, admin: true
diff --git a/spec/services/destroy_group_service_spec.rb b/spec/services/destroy_group_service_spec.rb
index e28564b3866..afa89b84175 100644
--- a/spec/services/destroy_group_service_spec.rb
+++ b/spec/services/destroy_group_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe DestroyGroupService do
+describe DestroyGroupService, services: true do
let!(:user) { create(:user) }
let!(:group) { create(:group) }
let!(:project) { create(:project, namespace: group) }
diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb
index 7756b973ecd..f6dc9d4008f 100644
--- a/spec/services/event_create_service_spec.rb
+++ b/spec/services/event_create_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe EventCreateService do
+describe EventCreateService, services: true do
let(:service) { EventCreateService.new }
describe 'Issues' do
diff --git a/spec/services/git_hooks_service_spec.rb b/spec/services/git_hooks_service_spec.rb
index 7e018d3c9fe..2bb9c3b3db3 100644
--- a/spec/services/git_hooks_service_spec.rb
+++ b/spec/services/git_hooks_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe GitHooksService do
+describe GitHooksService, services: true do
include RepoHelpers
let(:user) { create :user }
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index 17015d29e51..a04c242cf0e 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe GitPushService do
+describe GitPushService, services: true do
include RepoHelpers
let(:user) { create :user }
diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb
index eed50c7ebac..e2d15f1a83d 100644
--- a/spec/services/git_tag_push_service_spec.rb
+++ b/spec/services/git_tag_push_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe GitTagPushService do
+describe GitTagPushService, services: true do
include RepoHelpers
let(:user) { create :user }
diff --git a/spec/services/issues/bulk_update_service_spec.rb b/spec/services/issues/bulk_update_service_spec.rb
index 4c62fbafd73..6a7ea4b2f44 100644
--- a/spec/services/issues/bulk_update_service_spec.rb
+++ b/spec/services/issues/bulk_update_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Issues::BulkUpdateService do
+describe Issues::BulkUpdateService, services: true do
let(:issue) { create(:issue, project: @project) }
before do
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index d711e3da104..3a8daf28f5e 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Issues::CloseService do
+describe Issues::CloseService, services: true do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:issue) { create(:issue, assignee: user2) }
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index 7f1ebcb3198..2148d091a57 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Issues::CreateService do
+describe Issues::CreateService, services: true do
let(:project) { create(:empty_project) }
let(:user) { create(:user) }
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 73d0b7f7abe..87da0e9618b 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Issues::UpdateService do
+describe Issues::UpdateService, services: true do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:user3) { create(:user) }
diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb
index 272f3897938..50d0c288790 100644
--- a/spec/services/merge_requests/close_service_spec.rb
+++ b/spec/services/merge_requests/close_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe MergeRequests::CloseService do
+describe MergeRequests::CloseService, services: true do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:merge_request) { create(:merge_request, assignee: user2) }
diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb
index cc64d69361e..be8f1676eeb 100644
--- a/spec/services/merge_requests/create_service_spec.rb
+++ b/spec/services/merge_requests/create_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe MergeRequests::CreateService do
+describe MergeRequests::CreateService, services: true do
let(:project) { create(:project) }
let(:user) { create(:user) }
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index c0961ceb11e..ceb3f97280e 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe MergeRequests::MergeService do
+describe MergeRequests::MergeService, services: true do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:merge_request) { create(:merge_request, assignee: user2) }
@@ -13,12 +13,13 @@ describe MergeRequests::MergeService do
describe :execute do
context 'valid params' do
- let(:service) { MergeRequests::MergeService.new(project, user, {}) }
+ let(:service) { MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') }
before do
allow(service).to receive(:execute_hooks)
+
perform_enqueued_jobs do
- service.execute(merge_request, 'Awesome message')
+ service.execute(merge_request)
end
end
@@ -38,14 +39,14 @@ describe MergeRequests::MergeService do
end
context "error handling" do
- let(:service) { MergeRequests::MergeService.new(project, user, {}) }
+ let(:service) { MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') }
it 'saves error if there is an exception' do
allow(service).to receive(:repository).and_raise("error")
allow(service).to receive(:execute_hooks)
- service.execute(merge_request, 'Awesome message')
+ service.execute(merge_request)
expect(merge_request.merge_error).to eq("Something went wrong during merge")
end
diff --git a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb
new file mode 100644
index 00000000000..188fda6211f
--- /dev/null
+++ b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb
@@ -0,0 +1,84 @@
+require 'spec_helper'
+
+describe MergeRequests::MergeWhenBuildSucceedsService do
+ let(:user) { create(:user) }
+ let(:merge_request) { create(:merge_request) }
+
+ let(:mr_merge_if_green_enabled) do
+ create(:merge_request, merge_when_build_succeeds: true, merge_user: user,
+ source_branch: "source_branch", target_branch: project.default_branch,
+ source_project: project, target_project: project, state: "opened")
+ end
+
+ let(:project) { create(:project) }
+ let(:ci_commit) { create(:ci_commit_with_one_job, ref: mr_merge_if_green_enabled.source_branch, gl_project: project) }
+ let(:service) { MergeRequests::MergeWhenBuildSucceedsService.new(project, user, commit_message: 'Awesome message') }
+
+ describe "#execute" do
+ context 'first time enabling' do
+ before do
+ allow(merge_request).to receive(:ci_commit).and_return(ci_commit)
+ service.execute(merge_request)
+ end
+
+ 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_params).to eq commit_message: 'Awesome message'
+ expect(merge_request.merge_user).to be user
+ end
+
+ it 'creates a system note' do
+ note = merge_request.notes.last
+ expect(note.note).to match /Enabled an automatic merge when the build for (\w+\/\w+@)?[0-9a-z]{8}/
+ end
+ end
+
+ context 'already approved' do
+ let(:service) { MergeRequests::MergeWhenBuildSucceedsService.new(project, user, new_key: true) }
+ let(:build) { create(:ci_build, ref: mr_merge_if_green_enabled.source_branch) }
+
+ before do
+ allow(mr_merge_if_green_enabled).to receive(:ci_commit).and_return(ci_commit)
+ allow(mr_merge_if_green_enabled).to receive(:mergeable?).and_return(true)
+ allow(ci_commit).to receive(:success?).and_return(true)
+ end
+
+ it 'updates the merge params' do
+ expect(SystemNoteService).not_to receive(:merge_when_build_succeeds)
+
+ service.execute(mr_merge_if_green_enabled)
+ expect(mr_merge_if_green_enabled.merge_params).to have_key(:new_key)
+ end
+ end
+ end
+
+ describe "#trigger" do
+ let(:build) { create(:ci_build, ref: mr_merge_if_green_enabled.source_branch, status: "success") }
+
+ it "merges all merge requests with merge when build succeeds enabled" do
+ allow_any_instance_of(MergeRequest).to receive(:ci_commit).and_return(ci_commit)
+ allow(ci_commit).to receive(:success?).and_return(true)
+
+ expect(MergeWorker).to receive(:perform_async)
+ service.trigger(build)
+ end
+ end
+
+ describe "#cancel" do
+ before 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
+ expect(mr_merge_if_green_enabled.merge_params).to eq({})
+ expect(mr_merge_if_green_enabled.merge_user).to be nil
+ end
+
+ it 'Posts a system note' do
+ note = mr_merge_if_green_enabled.notes.last
+ expect(note.note).to include 'Canceled the automatic merge'
+ end
+ end
+end
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 7ee4488521d..450250ba032 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe MergeRequests::RefreshService do
+describe MergeRequests::RefreshService, services: true do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:service) { MergeRequests::RefreshService }
@@ -17,7 +17,9 @@ describe MergeRequests::RefreshService do
source_project: @project,
source_branch: 'master',
target_branch: 'feature',
- target_project: @project)
+ target_project: @project,
+ merge_when_build_succeeds: true,
+ merge_user: @user)
@fork_merge_request = create(:merge_request,
source_project: @fork_project,
@@ -46,6 +48,7 @@ describe MergeRequests::RefreshService 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(@fork_merge_request).to be_open }
it { expect(@fork_merge_request.notes).to be_empty }
end
@@ -146,6 +149,7 @@ describe MergeRequests::RefreshService do
end
end
+
def reload_mrs
@merge_request.reload
@fork_merge_request.reload
diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb
index 05146bf43f4..ac0221998f5 100644
--- a/spec/services/merge_requests/reopen_service_spec.rb
+++ b/spec/services/merge_requests/reopen_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe MergeRequests::ReopenService do
+describe MergeRequests::ReopenService, services: true do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:merge_request) { create(:merge_request, assignee: user2) }
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index d899b1f01d1..2e9e6e0870d 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe MergeRequests::UpdateService do
+describe MergeRequests::UpdateService, services: true do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:user3) { create(:user) }
diff --git a/spec/services/milestones/close_service_spec.rb b/spec/services/milestones/close_service_spec.rb
index 034c0f22e12..1cd6eb2ab38 100644
--- a/spec/services/milestones/close_service_spec.rb
+++ b/spec/services/milestones/close_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Milestones::CloseService do
+describe Milestones::CloseService, services: true do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:milestone) { create(:milestone, title: "Milestone v1.2", project: project) }
diff --git a/spec/services/milestones/create_service_spec.rb b/spec/services/milestones/create_service_spec.rb
index 757c9a226d8..c793026e300 100644
--- a/spec/services/milestones/create_service_spec.rb
+++ b/spec/services/milestones/create_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Milestones::CreateService do
+describe Milestones::CreateService, services: true do
let(:project) { create(:empty_project) }
let(:user) { create(:user) }
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index cc38d257792..a797a2fe4aa 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Notes::CreateService do
+describe Notes::CreateService, services: true do
let(:project) { create(:empty_project) }
let(:issue) { create(:issue, project: project) }
let(:user) { create(:user) }
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 35fa412ed80..d7a898e85ff 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe NotificationService do
+describe NotificationService, services: true do
let(:notification) { NotificationService.new }
around(:each) do |example|
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index e81c4edb7d8..2d8c316e38d 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Projects::CreateService do
+describe Projects::CreateService, services: true do
describe :create_by_user do
before do
@user = create :user
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index e83eef0b1a2..1ec27077717 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Projects::DestroyService do
+describe Projects::DestroyService, services: true do
let!(:user) { create(:user) }
let!(:project) { create(:project, namespace: user.namespace) }
let!(:path) { project.repository.path_to_repo }
diff --git a/spec/services/projects/download_service_spec.rb b/spec/services/projects/download_service_spec.rb
index ddee2e62dfc..5ceed5af9a5 100644
--- a/spec/services/projects/download_service_spec.rb
+++ b/spec/services/projects/download_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Projects::DownloadService do
+describe Projects::DownloadService, services: true do
describe 'File service' do
before do
@user = create :user
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index 1feba6ce048..d1ee60a0aea 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Projects::ForkService do
+describe Projects::ForkService, services: true do
describe :fork_by_user do
before do
@from_namespace = create(:namespace)
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 47755bfc990..c46259431aa 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Projects::TransferService do
+describe Projects::TransferService, services: true do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, namespace: user.namespace) }
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index b347fa15f87..c36d4581989 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Projects::UpdateService do
+describe Projects::UpdateService, services: true do
describe :update_by_user do
before do
@user = create :user
diff --git a/spec/services/projects/upload_service_spec.rb b/spec/services/projects/upload_service_spec.rb
index 1b1a80d1fe7..9268a9fb1a2 100644
--- a/spec/services/projects/upload_service_spec.rb
+++ b/spec/services/projects/upload_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Projects::UploadService do
+describe Projects::UploadService, services: true do
describe 'File service' do
before do
@user = create :user
diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb
index f57bfaea879..7b3a9a75d7c 100644
--- a/spec/services/search_service_spec.rb
+++ b/spec/services/search_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Search::GlobalService' do
+describe 'Search::GlobalService', services: true do
let(:user) { create(:user) }
let(:public_user) { create(:user) }
let(:internal_user) { create(:user) }
diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb
index a31fc1e4b07..febc78d2784 100644
--- a/spec/services/system_hooks_service_spec.rb
+++ b/spec/services/system_hooks_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe SystemHooksService do
+describe SystemHooksService, services: true do
let(:user) { create :user }
let(:project) { create :project }
let(:project_member) { create :project_member }
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index a45130bd473..0a4f9b230e8 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe SystemNoteService do
+describe SystemNoteService, services: true do
let(:project) { create(:project) }
let(:author) { create(:user) }
let(:noteable) { create(:issue, project: project) }
@@ -207,6 +207,32 @@ describe SystemNoteService do
end
end
+ describe '.merge_when_build_succeeds' do
+ let(:ci_commit) { build :ci_commit_without_jobs }
+ let(:noteable) { create :merge_request }
+
+ subject { described_class.merge_when_build_succeeds(noteable, project, author, noteable.last_commit) }
+
+ it_behaves_like 'a system note'
+
+ it "posts the Merge When Build Succeeds system note" do
+ expect(subject.note).to match /Enabled an automatic merge when the build for (\w+\/\w+@)?[0-9a-f]{40} succeeds/
+ end
+ end
+
+ describe '.cancel_merge_when_build_succeeds' do
+ let(:ci_commit) { build :ci_commit_without_jobs }
+ let(:noteable) { create :merge_request }
+
+ subject { described_class.cancel_merge_when_build_succeeds(noteable, project, author) }
+
+ it_behaves_like 'a system note'
+
+ it "posts the Merge When Build Succeeds system note" do
+ expect(subject.note).to eq "Canceled the automatic merge"
+ end
+ end
+
describe '.change_title' do
subject { described_class.change_title(noteable, project, author, 'Old title') }
diff --git a/spec/services/test_hook_service_spec.rb b/spec/services/test_hook_service_spec.rb
index 226196eedae..f034f251ba4 100644
--- a/spec/services/test_hook_service_spec.rb
+++ b/spec/services/test_hook_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe TestHookService do
+describe TestHookService, services: true do
let(:user) { create :user }
let(:project) { create :project }
let(:hook) { create :project_hook, project: project }
diff --git a/spec/services/update_release_service_spec.rb b/spec/services/update_release_service_spec.rb
index 93368c45b88..bba211089a8 100644
--- a/spec/services/update_release_service_spec.rb
+++ b/spec/services/update_release_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe UpdateReleaseService do
+describe UpdateReleaseService, services: true do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:tag_name) { project.repository.tag_names.first }
diff --git a/spec/services/update_snippet_service_spec.rb b/spec/services/update_snippet_service_spec.rb
index d7c516e3934..124bb76e678 100644
--- a/spec/services/update_snippet_service_spec.rb
+++ b/spec/services/update_snippet_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe UpdateSnippetService do
+describe UpdateSnippetService, services: true do
before do
@user = create :user
@admin = create :user, admin: true
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index 787670e9297..4f4743bff6d 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -12,6 +12,7 @@ module TestEnv
'fix' => '48f0be4',
'improve/awesome' => '5937ac0',
'markdown' => '0ed8c6c',
+ 'lfs' => 'be93687',
'master' => '5937ac0',
"'test'" => 'e56497b',
}
@@ -21,7 +22,8 @@ module TestEnv
# We currently only need a subset of the branches
FORKED_BRANCH_SHA = {
'add-submodule-version-bump' => '3f547c08',
- 'master' => '5937ac0'
+ 'master' => '5937ac0',
+ 'remove-submodule' => '2a33e0c0'
}
# Test environment
diff --git a/spec/support/wait_for_ajax.rb b/spec/support/wait_for_ajax.rb
new file mode 100644
index 00000000000..692d219e9f1
--- /dev/null
+++ b/spec/support/wait_for_ajax.rb
@@ -0,0 +1,11 @@
+module WaitForAjax
+ def wait_for_ajax
+ Timeout.timeout(Capybara.default_wait_time) do
+ loop until finished_all_ajax_requests?
+ end
+ end
+
+ def finished_all_ajax_requests?
+ page.evaluate_script('jQuery.active').zero?
+ end
+end