summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.rubocop.yml42
-rw-r--r--CHANGELOG13
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile9
-rw-r--r--Gemfile.lock61
-rw-r--r--app/assets/javascripts/issuable_context.js.coffee2
-rw-r--r--app/assets/javascripts/issue.js.coffee8
-rw-r--r--app/assets/javascripts/merge_request.js.coffee8
-rw-r--r--app/assets/stylesheets/framework/common.scss5
-rw-r--r--app/assets/stylesheets/framework/issue_box.scss10
-rw-r--r--app/assets/stylesheets/framework/lists.scss6
-rw-r--r--app/assets/stylesheets/framework/markdown_area.scss2
-rw-r--r--app/assets/stylesheets/framework/panels.scss5
-rw-r--r--app/assets/stylesheets/framework/timeline.scss3
-rw-r--r--app/assets/stylesheets/framework/variables.scss2
-rw-r--r--app/assets/stylesheets/pages/detail_page.scss33
-rw-r--r--app/assets/stylesheets/pages/issuable.scss45
-rw-r--r--app/assets/stylesheets/pages/issues.scss5
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss2
-rw-r--r--app/assets/stylesheets/pages/note_form.scss1
-rw-r--r--app/assets/stylesheets/pages/status.scss17
-rw-r--r--app/controllers/application_controller.rb15
-rw-r--r--app/controllers/dashboard/snippets_controller.rb3
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb16
-rw-r--r--app/controllers/projects/merge_requests_controller.rb11
-rw-r--r--app/controllers/projects/notes_controller.rb5
-rw-r--r--app/controllers/projects/protected_branches_controller.rb2
-rw-r--r--app/controllers/projects/services_controller.rb5
-rw-r--r--app/helpers/application_helper.rb10
-rw-r--r--app/helpers/button_helper.rb4
-rw-r--r--app/helpers/ci_status_helper.rb22
-rw-r--r--app/helpers/external_wiki_helper.rb2
-rw-r--r--app/helpers/gitlab_markdown_helper.rb9
-rw-r--r--app/helpers/issues_helper.rb12
-rw-r--r--app/helpers/labels_helper.rb2
-rw-r--r--app/helpers/projects_helper.rb7
-rw-r--r--app/helpers/tree_helper.rb2
-rw-r--r--app/mailers/notify.rb2
-rw-r--r--app/models/application_setting.rb12
-rw-r--r--app/models/ci/build.rb13
-rw-r--r--app/models/ci/commit.rb10
-rw-r--r--app/models/concerns/mentionable.rb18
-rw-r--r--app/models/concerns/participable.rb31
-rw-r--r--app/models/concerns/token_authenticatable.rb4
-rw-r--r--app/models/issue.rb10
-rw-r--r--app/models/jira_issue.rb2
-rw-r--r--app/models/merge_request.rb6
-rw-r--r--app/models/namespace.rb4
-rw-r--r--app/models/note.rb4
-rw-r--r--app/models/project.rb22
-rw-r--r--app/models/project_services/bamboo_service.rb6
-rw-r--r--app/models/project_services/flowdock_service.rb2
-rw-r--r--app/models/project_services/gemnasium_service.rb2
-rw-r--r--app/models/project_services/jira_service.rb241
-rw-r--r--app/models/project_services/teamcity_service.rb8
-rw-r--r--app/models/user.rb7
-rw-r--r--app/services/create_commit_builds_service.rb18
-rw-r--r--app/services/issues/close_service.rb5
-rw-r--r--app/services/merge_requests/refresh_service.rb2
-rw-r--r--app/services/system_note_service.rb9
-rw-r--r--app/views/admin/dashboard/index.html.haml4
-rw-r--r--app/views/dashboard/milestones/show.html.haml20
-rw-r--r--app/views/devise/mailer/unlock_instructions.html.erb7
-rw-r--r--app/views/devise/mailer/unlock_instructions.html.haml10
-rw-r--r--app/views/devise/unlocks/new.html.erb12
-rw-r--r--app/views/devise/unlocks/new.html.haml14
-rw-r--r--app/views/groups/milestones/show.html.haml32
-rw-r--r--app/views/notify/_note_message.html.haml2
-rw-r--r--app/views/projects/builds/show.html.haml4
-rw-r--r--app/views/projects/commit/_commit_box.html.haml6
-rw-r--r--app/views/projects/commit_statuses/_commit_status.html.haml8
-rw-r--r--app/views/projects/commits/_commit.html.haml2
-rw-r--r--app/views/projects/issues/_closed_by_box.html.haml3
-rw-r--r--app/views/projects/issues/_discussion.html.haml3
-rw-r--r--app/views/projects/issues/_merge_requests.html.haml7
-rw-r--r--app/views/projects/issues/show.html.haml99
-rw-r--r--app/views/projects/merge_requests/_discussion.html.haml3
-rw-r--r--app/views/projects/merge_requests/_new_submit.html.haml8
-rw-r--r--app/views/projects/merge_requests/_show.html.haml135
-rw-r--r--app/views/projects/merge_requests/show/_how_to_merge.html.haml12
-rw-r--r--app/views/projects/merge_requests/show/_mr_box.html.haml4
-rw-r--r--app/views/projects/merge_requests/show/_mr_title.html.haml13
-rw-r--r--app/views/projects/merge_requests/widget/_merged.html.haml12
-rw-r--r--app/views/projects/merge_requests/widget/open/_accept.html.haml24
-rw-r--r--app/views/projects/milestones/show.html.haml68
-rw-r--r--app/views/projects/notes/_notes_with_form.html.haml2
-rw-r--r--app/views/projects/tree/_tree_header.html.haml12
-rw-r--r--app/views/shared/issuable/_form.html.haml2
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml16
-rw-r--r--app/views/shared/snippets/_header.html.haml44
-rw-r--r--app/views/users/show.html.haml2
-rw-r--r--config/gitlab.yml.example13
-rw-r--r--config/initializers/1_settings.rb6
-rw-r--r--config/initializers/carrierwave.rb10
-rw-r--r--config/initializers/devise.rb14
-rw-r--r--config/routes.rb2
-rw-r--r--db/migrate/20151012173029_set_jira_service_api_url.rb50
-rw-r--r--db/migrate/20151210030143_add_unlock_token_to_user.rb5
-rw-r--r--db/migrate/20151210125930_migrate_ci_to_project.rb3
-rw-r--r--db/schema.rb1
-rw-r--r--doc/README.md1
-rw-r--r--doc/api/projects.md18
-rw-r--r--doc/ci/docker/using_docker_images.md23
-rw-r--r--doc/ci/variables/README.md2
-rw-r--r--doc/ci/yaml/README.md346
-rw-r--r--doc/install/installation.md2
-rw-r--r--doc/integration/README.md2
-rw-r--r--doc/integration/cas.md62
-rw-r--r--doc/integration/jira.md113
-rw-r--r--doc/integration/jira_issue_reference.pngbin0 -> 39942 bytes
-rw-r--r--doc/integration/jira_project_name.pngbin0 -> 60598 bytes
-rw-r--r--doc/integration/jira_service.pngbin0 -> 59082 bytes
-rw-r--r--doc/integration/jira_service_close_issue.pngbin0 -> 88433 bytes
-rw-r--r--doc/integration/jira_service_page.pngbin0 -> 162449 bytes
-rw-r--r--doc/integration/jira_workflow_screenshot.pngbin0 -> 121534 bytes
-rw-r--r--doc/raketasks/README.md3
-rw-r--r--doc/raketasks/check.md63
-rw-r--r--doc/raketasks/check_repos_output.pngbin0 -> 73786 bytes
-rw-r--r--doc/update/8.2-to-8.3.md22
-rw-r--r--doc/workflow/README.md1
-rw-r--r--doc/workflow/importing/README.md20
-rw-r--r--doc/workflow/importing/migrating_from_svn.md79
-rw-r--r--features/project/issues/issues.feature6
-rw-r--r--features/project/service.feature6
-rw-r--r--features/steps/explore/groups.rb8
-rw-r--r--features/steps/explore/projects.rb12
-rw-r--r--features/steps/groups.rb2
-rw-r--r--features/steps/profile/profile.rb2
-rw-r--r--features/steps/project/forked_merge_requests.rb3
-rw-r--r--features/steps/project/issues/issues.rb10
-rw-r--r--features/steps/project/merge_requests.rb4
-rw-r--r--features/steps/project/project.rb2
-rw-r--r--features/steps/project/services.rb18
-rw-r--r--features/steps/project/snippets.rb2
-rw-r--r--features/steps/project/source/browse_files.rb12
-rw-r--r--features/steps/shared/diff_note.rb2
-rw-r--r--features/steps/shared/paths.rb8
-rw-r--r--features/steps/snippets/snippets.rb2
-rw-r--r--features/support/capybara.rb2
-rw-r--r--lib/api/entities.rb3
-rw-r--r--lib/api/projects.rb11
-rw-r--r--lib/banzai.rb13
-rw-r--r--lib/banzai/cross_project_reference.rb22
-rw-r--r--lib/banzai/filter.rb10
-rw-r--r--lib/banzai/filter/abstract_reference_filter.rb (renamed from lib/gitlab/markdown/abstract_reference_filter.rb)6
-rw-r--r--lib/banzai/filter/autolink_filter.rb (renamed from lib/gitlab/markdown/filter/autolink_filter.rb)6
-rw-r--r--lib/banzai/filter/commit_range_reference_filter.rb (renamed from lib/gitlab/markdown/filter/commit_range_reference_filter.rb)6
-rw-r--r--lib/banzai/filter/commit_reference_filter.rb (renamed from lib/gitlab/markdown/filter/commit_reference_filter.rb)6
-rw-r--r--lib/banzai/filter/emoji_filter.rb (renamed from lib/gitlab/markdown/filter/emoji_filter.rb)6
-rw-r--r--lib/banzai/filter/external_issue_reference_filter.rb (renamed from lib/gitlab/markdown/filter/external_issue_reference_filter.rb)26
-rw-r--r--lib/banzai/filter/external_link_filter.rb (renamed from lib/gitlab/markdown/filter/external_link_filter.rb)6
-rw-r--r--lib/banzai/filter/issue_reference_filter.rb (renamed from lib/gitlab/markdown/filter/issue_reference_filter.rb)6
-rw-r--r--lib/banzai/filter/label_reference_filter.rb (renamed from lib/gitlab/markdown/filter/label_reference_filter.rb)6
-rw-r--r--lib/banzai/filter/markdown_filter.rb (renamed from lib/gitlab/markdown/filter/markdown_filter.rb)13
-rw-r--r--lib/banzai/filter/merge_request_reference_filter.rb (renamed from lib/gitlab/markdown/filter/merge_request_reference_filter.rb)6
-rw-r--r--lib/banzai/filter/redactor_filter.rb (renamed from lib/gitlab/markdown/filter/redactor_filter.rb)8
-rw-r--r--lib/banzai/filter/reference_filter.rb (renamed from lib/gitlab/markdown/reference_filter.rb)27
-rw-r--r--lib/banzai/filter/reference_gatherer_filter.rb (renamed from lib/gitlab/markdown/filter/reference_gatherer_filter.rb)13
-rw-r--r--lib/banzai/filter/relative_link_filter.rb (renamed from lib/gitlab/markdown/filter/relative_link_filter.rb)6
-rw-r--r--lib/banzai/filter/sanitization_filter.rb (renamed from lib/gitlab/markdown/filter/sanitization_filter.rb)6
-rw-r--r--lib/banzai/filter/snippet_reference_filter.rb (renamed from lib/gitlab/markdown/filter/snippet_reference_filter.rb)6
-rw-r--r--lib/banzai/filter/syntax_highlight_filter.rb (renamed from lib/gitlab/markdown/filter/syntax_highlight_filter.rb)6
-rw-r--r--lib/banzai/filter/table_of_contents_filter.rb (renamed from lib/gitlab/markdown/filter/table_of_contents_filter.rb)8
-rw-r--r--lib/banzai/filter/task_list_filter.rb (renamed from lib/gitlab/markdown/filter/task_list_filter.rb)6
-rw-r--r--lib/banzai/filter/upload_link_filter.rb (renamed from lib/gitlab/markdown/filter/upload_link_filter.rb)6
-rw-r--r--lib/banzai/filter/user_reference_filter.rb (renamed from lib/gitlab/markdown/filter/user_reference_filter.rb)6
-rw-r--r--lib/banzai/lazy_reference.rb27
-rw-r--r--lib/banzai/pipeline.rb10
-rw-r--r--lib/banzai/pipeline/asciidoc_pipeline.rb13
-rw-r--r--lib/banzai/pipeline/atom_pipeline.rb14
-rw-r--r--lib/banzai/pipeline/base_pipeline.rb30
-rw-r--r--lib/banzai/pipeline/combined_pipeline.rb (renamed from lib/gitlab/markdown/combined_pipeline.rb)8
-rw-r--r--lib/banzai/pipeline/description_pipeline.rb (renamed from lib/gitlab/markdown/pipeline/description_pipeline.rb)8
-rw-r--r--lib/banzai/pipeline/email_pipeline.rb (renamed from lib/gitlab/markdown/pipeline/email_pipeline.rb)8
-rw-r--r--lib/banzai/pipeline/full_pipeline.rb (renamed from lib/gitlab/markdown/pipeline/full_pipeline.rb)6
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb41
-rw-r--r--lib/banzai/pipeline/note_pipeline.rb (renamed from lib/gitlab/markdown/pipeline/note_pipeline.rb)8
-rw-r--r--lib/banzai/pipeline/plain_markdown_pipeline.rb13
-rw-r--r--lib/banzai/pipeline/post_process_pipeline.rb20
-rw-r--r--lib/banzai/pipeline/reference_extraction_pipeline.rb13
-rw-r--r--lib/banzai/pipeline/single_line_pipeline.rb9
-rw-r--r--lib/banzai/reference_extractor.rb55
-rw-r--r--lib/banzai/renderer.rb78
-rw-r--r--lib/gitlab/asciidoc.rb2
-rw-r--r--lib/gitlab/backend/shell.rb2
-rw-r--r--lib/gitlab/bitbucket_import/project_creator.rb3
-rw-r--r--lib/gitlab/diff/file.rb4
-rw-r--r--lib/gitlab/fogbugz_import/importer.rb2
-rw-r--r--lib/gitlab/fogbugz_import/project_creator.rb3
-rw-r--r--lib/gitlab/git.rb4
-rw-r--r--lib/gitlab/gitlab_import/project_creator.rb3
-rw-r--r--lib/gitlab/gitorious_import/project_creator.rb3
-rw-r--r--lib/gitlab/google_code_import/importer.rb6
-rw-r--r--lib/gitlab/google_code_import/project_creator.rb3
-rw-r--r--lib/gitlab/markdown.rb115
-rw-r--r--lib/gitlab/markdown/cross_project_reference.rb24
-rw-r--r--lib/gitlab/markdown/pipeline.rb4
-rw-r--r--lib/gitlab/markdown/pipeline/asciidoc_pipeline.rb13
-rw-r--r--lib/gitlab/markdown/pipeline/atom_pipeline.rb14
-rw-r--r--lib/gitlab/markdown/pipeline/gfm_pipeline.rb41
-rw-r--r--lib/gitlab/markdown/pipeline/plain_markdown_pipeline.rb13
-rw-r--r--lib/gitlab/markdown/pipeline/post_process_pipeline.rb20
-rw-r--r--lib/gitlab/markdown/pipeline/reference_extraction_pipeline.rb13
-rw-r--r--lib/gitlab/markdown/pipeline/single_line_pipeline.rb9
-rw-r--r--lib/gitlab/o_auth/session.rb17
-rw-r--r--lib/gitlab/reference_extractor.rb57
-rw-r--r--lib/rouge/formatters/html_gitlab.rb2
-rwxr-xr-xlib/support/init.d/gitlab3
-rwxr-xr-xlib/support/init.d/gitlab.default.example2
-rw-r--r--lib/support/nginx/gitlab146
-rw-r--r--lib/support/nginx/gitlab-ssl147
-rw-r--r--lib/tasks/gitlab/check.rake56
-rw-r--r--spec/benchmarks/lib/gitlab/markdown/reference_filter_spec.rb2
-rw-r--r--spec/factories.rb3
-rw-r--r--spec/features/commits_spec.rb29
-rw-r--r--spec/features/issues/filter_by_milestone_spec.rb4
-rw-r--r--spec/features/issues/note_polling_spec.rb16
-rw-r--r--spec/features/merge_requests/merge_when_build_succeeds_spec.rb6
-rw-r--r--spec/features/security/group_access_spec.rb4
-rw-r--r--spec/features/task_lists_spec.rb4
-rw-r--r--spec/helpers/application_helper_spec.rb7
-rw-r--r--spec/helpers/ci_status_helper_spec.rb11
-rw-r--r--spec/helpers/groups_helper.rb2
-rw-r--r--spec/helpers/merge_requests_helper_spec.rb21
-rw-r--r--spec/javascripts/fixtures/issues_show.html.haml2
-rw-r--r--spec/javascripts/fixtures/merge_requests_show.html.haml2
-rw-r--r--spec/lib/banzai/cross_project_reference_spec.rb (renamed from spec/lib/gitlab/markdown/cross_project_reference_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/autolink_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/autolink_filter_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/commit_range_reference_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/commit_range_reference_filter_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/commit_reference_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/commit_reference_filter_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/emoji_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/emoji_filter_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/external_issue_reference_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/external_issue_reference_filter_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/external_link_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/external_link_filter_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/issue_reference_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/issue_reference_filter_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/label_reference_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/label_reference_filter_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/merge_request_reference_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/merge_request_reference_filter_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/redactor_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/redactor_filter_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/reference_gatherer_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/reference_gatherer_filter_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/relative_link_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/relative_link_filter_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/sanitization_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/sanitization_filter_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/snippet_reference_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/snippet_reference_filter_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/syntax_highlight_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/syntax_highlight_filter_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/table_of_contents_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/table_of_contents_filter_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/task_list_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/task_list_filter_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/upload_link_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/upload_link_filter_spec.rb)2
-rw-r--r--spec/lib/banzai/filter/user_reference_filter_spec.rb (renamed from spec/lib/gitlab/markdown/filter/user_reference_filter_spec.rb)2
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb2
-rw-r--r--spec/lib/gitlab/reference_extractor_spec.rb10
-rw-r--r--spec/models/build_spec.rb70
-rw-r--r--spec/models/ci/commit_spec.rb4
-rw-r--r--spec/models/concerns/mentionable_spec.rb13
-rw-r--r--spec/models/jira_issue_spec.rb30
-rw-r--r--spec/models/key_spec.rb2
-rw-r--r--spec/models/merge_request_spec.rb11
-rw-r--r--spec/models/project_services/hipchat_service_spec.rb22
-rw-r--r--spec/models/project_services/jira_service_spec.rb118
-rw-r--r--spec/models/project_services/slack_service/note_message_spec.rb8
-rw-r--r--spec/models/project_spec.rb6
-rw-r--r--spec/models/user_spec.rb5
-rw-r--r--spec/requests/api/merge_requests_spec.rb2
-rw-r--r--spec/requests/api/projects_spec.rb37
-rw-r--r--spec/requests/api/services_spec.rb2
-rw-r--r--spec/services/create_commit_builds_service_spec.rb39
-rw-r--r--spec/services/git_push_service_spec.rb69
-rw-r--r--spec/services/git_tag_push_service_spec.rb16
-rw-r--r--spec/services/system_note_service_spec.rb61
-rw-r--r--spec/services/update_snippet_service_spec.rb2
-rw-r--r--spec/support/filter_spec_helper.rb36
-rw-r--r--spec/support/jira_service_helper.rb67
-rw-r--r--spec/support/repo_helpers.rb12
-rw-r--r--spec/workers/repository_fork_worker_spec.rb20
-rw-r--r--spec/workers/stuck_ci_builds_worker_spec.rb4
273 files changed, 2923 insertions, 1640 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
index b4ca11c8343..89aa0591c31 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -76,7 +76,7 @@ Style/BlockEndNewline:
Description: 'Put end statement of multiline block on its own line.'
Enabled: true
-Style/Blocks:
+Style/BlockDelimiters:
Description: >-
Avoid using {...} for multi-line blocks (multiline chaining is
always ugly).
@@ -232,6 +232,10 @@ Style/EvenOdd:
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods'
Enabled: false
+Style/ExtraSpacing:
+ Description: 'Do not use unnecessary spacing.'
+ Enabled: false
+
Style/FileName:
Description: 'Use snake_case for source file names.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-files'
@@ -431,6 +435,14 @@ Style/OpMethod:
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#other-arg'
Enabled: false
+Style/ParallelAssignment:
+ Description: >-
+ Check for simple usages of parallel assignment.
+ It will only warn when the number of variables
+ matches on both sides of the assignment.
+ StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parallel-assignment'
+ Enabled: false
+
Style/ParenthesesAroundCondition:
Description: >-
Don't use parentheses around the condition of an
@@ -669,6 +681,13 @@ Style/TrailingWhitespace:
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace'
Enabled: false
+Style/TrailingUnderscoreVariable:
+ Description: >-
+ Checks for the usage of unneeded trailing underscores at the
+ end of parallel variable assignment.
+ AllowNamedUnderscoreVariables: true
+ Enabled: false
+
Style/TrivialAccessors:
Description: 'Prefer attr_* methods to trivial readers/writers.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr_family'
@@ -690,11 +709,6 @@ Style/UnneededPercentQ:
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q'
Enabled: false
-Style/UnneededPercentX:
- Description: 'Checks for %x when `` would do.'
- StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-x'
- Enabled: false
-
Style/VariableInterpolation:
Description: >-
Don't interpolate global, instance and class variables
@@ -778,6 +792,10 @@ Metrics/MethodLength:
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods'
Enabled: false
+Metrics/ModuleLength:
+ Description: 'Avoid modules longer than 100 lines of code.'
+ Enabled: false
+
#################### Lint ################################
### Warnings
@@ -961,6 +979,12 @@ Rails/ActionFilter:
Description: 'Enforces consistent use of action filter methods.'
Enabled: true
+Rails/Date:
+ Description: >-
+ Checks the correct usage of date aware methods,
+ such as Date.today, Date.current etc.
+ Enabled: false
+
Rails/DefaultScope:
Description: 'Checks if the argument passed to default_scope is a block.'
Enabled: false
@@ -987,6 +1011,12 @@ Rails/ScopeArgs:
Description: 'Checks the arguments of ActiveRecord scopes.'
Enabled: false
+Rails/TimeZone:
+ Description: 'Checks the correct usage of time zone aware methods.'
+ StyleGuide: 'https://github.com/bbatsov/rails-style-guide#time'
+ Reference: 'http://danilenko.org/2012/7/6/rails_timezones'
+ Enabled: false
+
Rails/Validation:
Description: 'Use validates :attribute, hash of validations.'
Enabled: false
diff --git a/CHANGELOG b/CHANGELOG
index 7f9dfd98cd7..828c0a5e7fc 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,9 +1,13 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.3.0 (unreleased)
+ - Add CAS support (tduehr)
+ - Bump rack-attack to 4.3.1 for security fix (Stan Hu)
+ - API support for starred projects for authorized user (Zeger-Jan van de Weg)
+ - Add link to merge request on build detail page.
+ - Add open_issues_count to project API (Stan Hu)
- Expand character set of usernames created by Omniauth (Corey Hinshaw)
- Add button to automatically merge a merge request when the build succeeds (Zeger-Jan van de Weg)
- - Merge when build succeeds (Zeger-Jan van de Weg)
- Provide better diagnostic message upon project creation errors (Stan Hu)
- Bump devise to 3.5.3 to fix reset token expiring after account creation (Stan Hu)
- Bump gollum-lib to 4.1.0 (Stan Hu)
@@ -17,13 +21,16 @@ v 8.3.0 (unreleased)
- 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
+ - Backport JIRA features from EE to CE
- Add ignore whitespace change option to commit view
- Fire update hook from GitLab
+ - Allow account unlock via email
- Style warning about mentioning many people in a comment
- Fix: sort milestones by due date once again (Greg Smethells)
- Migrate all CI::Services and CI::WebHooks to Services and WebHooks
- Don't show project fork event as "imported"
- Add API endpoint to fetch merge request commits list
+ - Don't create CI status for refs that doesn't have .gitlab-ci.yml, even if the builds are enabled
- 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.
@@ -55,6 +62,8 @@ v 8.3.0 (unreleased)
- Do not show build status unless builds are enabled and `.gitlab-ci.yml` is present
- Persist runners registration token in database
- Fix online editor should not remove newlines at the end of the file
+ - Expose Git's version in the admin area
+
v 8.2.3
- Fix application settings cache not expiring after changes (Stan Hu)
@@ -62,8 +71,6 @@ v 8.2.3
- Update documentation for "Guest" permissions
- Properly convert Emoji-only comments into Award Emojis
- Enable devise paranoid mode to prevent user enumeration attack
-
-v 8.2.3
- Webhook payload has an added, modified and removed properties for each commit
- Fix 500 error when creating a merge request that removes a submodule
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 743af5e1251..d48d3702aed 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-2.6.8
+2.6.9
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index 2b7c5ae0184..4b9fcbec101 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-0.4.2
+0.5.1
diff --git a/Gemfile b/Gemfile
index 7298e21ce66..3f40dcdb063 100644
--- a/Gemfile
+++ b/Gemfile
@@ -23,6 +23,7 @@ gem 'devise-async', '~> 0.9.0'
gem 'doorkeeper', '~> 2.2.0'
gem 'omniauth', '~> 1.2.2'
gem 'omniauth-bitbucket', '~> 0.0.2'
+gem 'omniauth-cas3', '~> 1.1.2'
gem 'omniauth-facebook', '~> 3.0.0'
gem 'omniauth-github', '~> 1.1.1'
gem 'omniauth-gitlab', '~> 1.0.0'
@@ -175,7 +176,7 @@ gem "sanitize", '~> 2.0'
gem 'babosa', '~> 1.0.2'
# Protect against bruteforcing
-gem "rack-attack", '~> 4.3.0'
+gem "rack-attack", '~> 4.3.1'
# Ace editor
gem 'ace-rails-ap', '~> 2.0.1'
@@ -215,7 +216,7 @@ group :development do
gem "annotate", "~> 2.6.0"
gem "letter_opener", '~> 1.1.2'
gem 'quiet_assets', '~> 1.0.2'
- gem 'rerun', '~> 0.10.0'
+ gem 'rerun', '~> 0.11.0'
gem 'bullet', require: false
gem 'rblineprof', platform: :mri, require: false
gem 'web-console', '~> 2.0'
@@ -251,7 +252,7 @@ group :development, :test do
gem 'capybara', '~> 2.4.0'
gem 'capybara-screenshot', '~> 1.0.0'
- gem 'poltergeist', '~> 1.6.0'
+ gem 'poltergeist', '~> 1.8.1'
gem 'teaspoon', '~> 1.0.0'
gem 'teaspoon-jasmine', '~> 2.2.0'
@@ -261,7 +262,7 @@ group :development, :test do
gem 'spring-commands-spinach', '~> 1.0.0'
gem 'spring-commands-teaspoon', '~> 0.0.2'
- gem 'rubocop', '~> 0.28.0', require: false
+ gem 'rubocop', '~> 0.35.0', require: false
gem 'coveralls', '~> 0.8.2', require: false
gem 'simplecov', '~> 0.10.0', require: false
gem 'flog', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index ff57460f5bb..35abd152449 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -117,23 +117,6 @@ GEM
activemodel (>= 3.2.0)
activesupport (>= 3.2.0)
json (>= 1.7)
- 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)
@@ -369,7 +352,6 @@ GEM
hipchat (1.5.2)
httparty
mimemagic
- hitimes (1.2.3)
html-pipeline (1.11.0)
activesupport (>= 2)
nokogiri (~> 1.4)
@@ -410,8 +392,7 @@ GEM
addressable (~> 2.3)
letter_opener (1.1.2)
launchy (~> 2.2)
- listen (2.9.0)
- celluloid (>= 0.15.2)
+ listen (3.0.5)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
loofah (2.0.3)
@@ -424,7 +405,7 @@ GEM
method_source (0.8.2)
mime-types (1.25.1)
mimemagic (0.3.0)
- mini_portile (0.6.2)
+ mini_portile2 (2.0.0)
minitest (5.7.0)
mousetrap-rails (1.4.6)
multi_json (1.11.2)
@@ -439,8 +420,8 @@ GEM
grape
newrelic_rpm
newrelic_rpm (3.9.4.245)
- nokogiri (1.6.6.4)
- mini_portile (~> 0.6.0)
+ nokogiri (1.6.7)
+ mini_portile2 (~> 2.0.0.rc2)
nprogress-rails (0.1.6.7)
oauth (0.4.7)
oauth2 (1.0.0)
@@ -458,6 +439,10 @@ GEM
multi_json (~> 1.7)
omniauth (~> 1.1)
omniauth-oauth (~> 1.0)
+ omniauth-cas3 (1.1.3)
+ addressable (~> 2.3)
+ nokogiri (~> 1.6.6)
+ omniauth (~> 1.2)
omniauth-facebook (3.0.0)
omniauth-oauth2 (~> 1.2)
omniauth-github (1.1.2)
@@ -507,13 +492,13 @@ GEM
parser (2.2.3.0)
ast (>= 1.1, < 3.0)
pg (0.18.4)
- poltergeist (1.6.0)
+ poltergeist (1.8.1)
capybara (~> 2.1)
cliver (~> 0.3.1)
multi_json (~> 1.0)
websocket-driver (>= 0.2.0)
posix-spawn (0.3.11)
- powerpack (0.0.9)
+ powerpack (0.1.1)
pry (0.10.3)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
@@ -526,7 +511,7 @@ GEM
rack (1.6.4)
rack-accept (0.4.5)
rack (>= 0.4)
- rack-attack (4.3.0)
+ rack-attack (4.3.1)
rack
rack-cors (0.4.0)
rack-mount (0.8.3)
@@ -601,8 +586,8 @@ GEM
redis-store (1.1.7)
redis (>= 2.2)
request_store (1.2.1)
- rerun (0.10.0)
- listen (~> 2.7, >= 2.7.3)
+ rerun (0.11.0)
+ listen (~> 3.0)
responders (2.1.0)
railties (>= 4.2.0, < 5)
rest-client (1.8.0)
@@ -637,12 +622,13 @@ GEM
rspec-mocks (~> 3.3.0)
rspec-support (~> 3.3.0)
rspec-support (3.3.0)
- rubocop (0.28.0)
+ rubocop (0.35.1)
astrolabe (~> 1.3)
- parser (>= 2.2.0.pre.7, < 3.0)
- powerpack (~> 0.0.6)
+ parser (>= 2.2.3.0, < 3.0)
+ powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
- ruby-progressbar (~> 1.4)
+ ruby-progressbar (~> 1.7)
+ tins (<= 1.6.0)
ruby-fogbugz (0.2.1)
crack (~> 0.4)
ruby-progressbar (1.7.5)
@@ -758,8 +744,6 @@ GEM
thor (0.19.1)
thread_safe (0.3.5)
tilt (1.4.1)
- timers (4.1.1)
- hitimes
timfel-krb5-auth (0.8.3)
tinder (1.10.1)
eventmachine (~> 1.0)
@@ -913,6 +897,7 @@ DEPENDENCIES
octokit (~> 3.7.0)
omniauth (~> 1.2.2)
omniauth-bitbucket (~> 0.0.2)
+ omniauth-cas3 (~> 1.1.2)
omniauth-facebook (~> 3.0.0)
omniauth-github (~> 1.1.1)
omniauth-gitlab (~> 1.0.0)
@@ -925,10 +910,10 @@ DEPENDENCIES
org-ruby (~> 0.9.12)
paranoia (~> 2.0)
pg (~> 0.18.2)
- poltergeist (~> 1.6.0)
+ poltergeist (~> 1.8.1)
pry-rails
quiet_assets (~> 1.0.2)
- rack-attack (~> 4.3.0)
+ rack-attack (~> 4.3.1)
rack-cors (~> 0.4.0)
rack-oauth2 (~> 1.2.1)
rails (= 4.2.4)
@@ -940,12 +925,12 @@ DEPENDENCIES
redis-namespace
redis-rails (~> 4.0.0)
request_store (~> 1.2.0)
- rerun (~> 0.10.0)
+ rerun (~> 0.11.0)
responders (~> 2.0)
rouge (~> 1.10.1)
rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.3.0)
- rubocop (~> 0.28.0)
+ rubocop (~> 0.35.0)
ruby-fogbugz (~> 0.2.1)
sanitize (~> 2.0)
sass-rails (~> 4.0.5)
diff --git a/app/assets/javascripts/issuable_context.js.coffee b/app/assets/javascripts/issuable_context.js.coffee
index 01bd515cc02..02232698bc2 100644
--- a/app/assets/javascripts/issuable_context.js.coffee
+++ b/app/assets/javascripts/issuable_context.js.coffee
@@ -18,7 +18,7 @@ class @IssuableContext
$('.issuable-affix').affix offset:
top: ->
- @top = ($('.issuable-affix').offset().top - 60)
+ @top = ($('.issuable-affix').offset().top - 70)
bottom: ->
@bottom = $('.footer').outerHeight(true)
diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee
index 603a16da1ce..eff80bf63bb 100644
--- a/app/assets/javascripts/issue.js.coffee
+++ b/app/assets/javascripts/issue.js.coffee
@@ -10,12 +10,12 @@ class @Issue
@initTaskList()
initTaskList: ->
- $('.issue-details .js-task-list-container').taskList('enable')
- $(document).on 'tasklist:changed', '.issue-details .js-task-list-container', @updateTaskList
+ $('.detail-page-description .js-task-list-container').taskList('enable')
+ $(document).on 'tasklist:changed', '.detail-page-description .js-task-list-container', @updateTaskList
disableTaskList: ->
- $('.issue-details .js-task-list-container').taskList('disable')
- $(document).off 'tasklist:changed', '.issue-details .js-task-list-container'
+ $('.detail-page-description .js-task-list-container').taskList('disable')
+ $(document).off 'tasklist:changed', '.detail-page-description .js-task-list-container'
# TODO (rspeicher): Make the issue description inline-editable like a note so
# that we can re-use its form here
diff --git a/app/assets/javascripts/merge_request.js.coffee b/app/assets/javascripts/merge_request.js.coffee
index b21cb7904b5..9047587db81 100644
--- a/app/assets/javascripts/merge_request.js.coffee
+++ b/app/assets/javascripts/merge_request.js.coffee
@@ -40,12 +40,12 @@ class @MergeRequest
this.$('.all-commits').removeClass 'hide'
initTaskList: ->
- $('.merge-request-details .js-task-list-container').taskList('enable')
- $(document).on 'tasklist:changed', '.merge-request-details .js-task-list-container', @updateTaskList
+ $('.detail-page-description .js-task-list-container').taskList('enable')
+ $(document).on 'tasklist:changed', '.detail-page-description .js-task-list-container', @updateTaskList
disableTaskList: ->
- $('.merge-request-details .js-task-list-container').taskList('disable')
- $(document).off 'tasklist:changed', '.merge-request-details .js-task-list-container'
+ $('.detail-page-description .js-task-list-container').taskList('disable')
+ $(document).off 'tasklist:changed', '.detail-page-description .js-task-list-container'
# TODO (rspeicher): Make the merge request description inline-editable like a
# note so that we can re-use its form here
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 88da799ee2b..7562ef6d24b 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -401,6 +401,11 @@ table {
border-bottom: 1px solid $border-color;
height: 57px;
}
+
+ &.wide {
+ margin-left: -$gl-padding;
+ margin-right: -$gl-padding;
+ }
}
.center-middle-menu {
diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss
index f12d68b5a1f..fba67ba0b64 100644
--- a/app/assets/stylesheets/framework/issue_box.scss
+++ b/app/assets/stylesheets/framework/issue_box.scss
@@ -4,7 +4,7 @@
*
*/
-.issue-box {
+.status-box {
@include border-radius(2px);
display: block;
@@ -14,22 +14,22 @@
margin-right: 10px;
font-size: $gl-font-size;
- &.issue-box-closed {
+ &.status-box-closed {
background-color: $gl-danger;
color: #FFF;
}
- &.issue-box-merged {
+ &.status-box-merged {
background-color: $gl-primary;
color: #FFF;
}
- &.issue-box-open {
+ &.status-box-open {
background-color: #019875;
color: #FFF;
}
- &.issue-box-expired {
+ &.status-box-expired {
background: #cea61b;
color: #FFF;
}
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index cc48f8c8166..1c74e525a60 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -143,7 +143,11 @@ ul.controls {
> li {
float: left;
- padding-right: 10px;
+ margin-right: 10px;
+
+ &:last-child {
+ margin-right: 0;
+ }
.author_link {
display: inline-block;
diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
index 2b044786738..4a00a197d9a 100644
--- a/app/assets/stylesheets/framework/markdown_area.scss
+++ b/app/assets/stylesheets/framework/markdown_area.scss
@@ -87,7 +87,7 @@
.new_note,
.edit_note,
-.issuable-description,
+.detail-page-description,
.milestone-description,
.wiki-content,
.merge-request-form {
diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss
index 61053aff91a..57b9451b264 100644
--- a/app/assets/stylesheets/framework/panels.scss
+++ b/app/assets/stylesheets/framework/panels.scss
@@ -3,7 +3,6 @@
.panel-heading {
padding: 7px $gl-padding;
- line-height: 42px !important;
}
.panel-body {
@@ -15,3 +14,7 @@
}
}
}
+
+.container-blank .panel .panel-heading {
+ line-height: 42px !important;
+}
diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index eb53c4153d3..ff41e26ed8a 100644
--- a/app/assets/stylesheets/framework/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
@@ -10,8 +10,7 @@
margin-left: -$gl-padding;
margin-right: -$gl-padding;
color: $gl-gray;
- border-bottom: 1px solid #ECEEF1;
- border-right: 1px solid #ECEEF1;
+ border-bottom: 1px solid $border-white-light;
&:target {
background: $hover;
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 91954683c3e..2ef40a6e517 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -19,7 +19,7 @@ $border-color: #dce0e6;
$table-border-color: #eef0f2;
$background-color: #F7F8FA;
$header-height: 58px;
-$fixed-layout-width: 1200px;
+$fixed-layout-width: 1280px;
$gl-gray: #7f8fa4;
$gl-padding: 16px;
$gl-avatar-size: 46px;
diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss
new file mode 100644
index 00000000000..0f3463a9144
--- /dev/null
+++ b/app/assets/stylesheets/pages/detail_page.scss
@@ -0,0 +1,33 @@
+.detail-page-header {
+ margin: -$gl-padding;
+ padding: 7px $gl-padding;
+ margin-bottom: 0px;
+ border-bottom: 1px solid $border-color;
+ color: #5c5d5e;
+ font-size: 16px;
+ line-height: 42px;
+
+ .author {
+ color: #5c5d5e;
+ }
+
+ .identifier {
+ color: #5c5d5e;
+ }
+}
+
+.detail-page-description {
+ .title {
+ margin: 0;
+ font-size: 23px;
+ color: #313236;
+ }
+
+ .description {
+ margin-top: 6px;
+
+ p:last-child {
+ margin-bottom: 0;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 797a0af3720..9da273a0b6b 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -18,7 +18,7 @@
&.affix {
position: fixed;
- top: 60px;
+ top: 70px;
margin-right: 35px;
}
}
@@ -36,33 +36,12 @@
}
.issuable-details {
- .issue-title {
- margin: 0;
- font-size: 23px;
- color: #313236;
- }
-
- .description {
- margin-top: 6px;
-
- p:last-child {
- margin-bottom: 0;
- }
- }
-
section {
- border-right: 1px solid #ECEEF1;
+ border-right: 1px solid $border-white-light;
- > .tab-content {
+ .issuable-discussion {
margin-right: 1px;
}
-
- .issue-discussion > .gray-content-block,
- > .gray-content-block {
- margin-top: 0;
- border-top: none;
- margin-right: -15px;
- }
}
}
@@ -136,21 +115,3 @@
margin-right: 2px;
}
}
-
-.issuable-title {
- margin: -$gl-padding;
- padding: 7px $gl-padding;
- margin-bottom: 0px;
- border-bottom: 1px solid $border-color;
- color: #5c5d5e;
- font-size: 16px;
- line-height: 42px;
-
- .author {
- color: #5c5d5e;
- }
-
- .issuable-id {
- color: #5c5d5e;
- }
-}
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index a652b65502f..a02a3a72e79 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -141,11 +141,6 @@ form.edit-issue {
}
}
-.issue-closed-by-widget {
- padding: 16px 0;
- margin: 0px;
-}
-
.issue-form .select2-container {
width: 250px !important;
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 502e9552acd..82effde0bf3 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -191,7 +191,7 @@
.btn-clipboard {
@extend .pull-right;
- margin-right: 18px;
+ margin-right: 20px;
margin-top: 5px;
position: absolute;
right: 0;
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index e1a72af0013..4cf1a28c459 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -79,7 +79,6 @@
padding: $gl-padding;
margin-left: -$gl-padding;
margin-right: -$gl-padding;
- border-right: 1px solid $border-color;
border-top: 1px solid $border-color;
margin-bottom: -$gl-padding;
}
diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss
index a7d3b2197f1..4b6ef035673 100644
--- a/app/assets/stylesheets/pages/status.scss
+++ b/app/assets/stylesheets/pages/status.scss
@@ -35,3 +35,20 @@
border-color: $gl-warning;
}
}
+
+.ci-status-icon-success {
+ @extend .cgreen;
+}
+.ci-status-icon-failed {
+ @extend .cred;
+}
+.ci-status-icon-running,
+.ci-status-icon-pending {
+ // These are standard text color
+}
+.ci-status-icon-canceled,
+.ci-status-icon-disabled,
+.ci-status-icon-not-found,
+.ci-status-icon-skipped {
+ @extend .cgray;
+}
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 0d182e8eb04..01e2e7b2f98 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -10,6 +10,7 @@ class ApplicationController < ActionController::Base
before_action :authenticate_user_from_token!
before_action :authenticate_user!
+ before_action :validate_user_service_ticket!
before_action :reject_blocked!
before_action :check_password_expiration
before_action :ldap_security_check
@@ -202,6 +203,20 @@ class ApplicationController < ActionController::Base
end
end
+ def validate_user_service_ticket!
+ return unless signed_in? && session[:service_tickets]
+
+ valid = session[:service_tickets].all? do |provider, ticket|
+ Gitlab::OAuth::Session.valid?(provider, ticket)
+ end
+
+ unless valid
+ session[:service_tickets] = nil
+ sign_out current_user
+ redirect_to new_user_session_path
+ end
+ end
+
def check_password_expiration
if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && !current_user.ldap_user?
redirect_to new_profile_password_path and return
diff --git a/app/controllers/dashboard/snippets_controller.rb b/app/controllers/dashboard/snippets_controller.rb
index f4354c6d8ca..b3594d82530 100644
--- a/app/controllers/dashboard/snippets_controller.rb
+++ b/app/controllers/dashboard/snippets_controller.rb
@@ -1,6 +1,7 @@
class Dashboard::SnippetsController < Dashboard::ApplicationController
def index
- @snippets = SnippetsFinder.new.execute(current_user,
+ @snippets = SnippetsFinder.new.execute(
+ current_user,
filter: :by_user,
user: current_user,
scope: params[:scope]
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index f809fa7500a..4cad98b8e98 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -1,6 +1,6 @@
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
- protect_from_forgery except: [:kerberos, :saml]
+ protect_from_forgery except: [:kerberos, :saml, :cas3]
Gitlab.config.omniauth.providers.each do |provider|
define_method provider['name'] do
@@ -42,6 +42,14 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
render 'errors/omniauth_error', layout: "errors", status: 422
end
+ def cas3
+ ticket = params['ticket']
+ if ticket
+ handle_service_ticket oauth['provider'], ticket
+ end
+ handle_omniauth
+ end
+
private
def handle_omniauth
@@ -84,6 +92,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
redirect_to new_user_session_path
end
+ def handle_service_ticket provider, ticket
+ Gitlab::OAuth::Session.create provider, ticket
+ session[:service_tickets] ||= {}
+ session[:service_tickets][provider] = ticket
+ end
+
def oauth
@oauth ||= request.env['omniauth.auth']
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index fffd90d87eb..ab5c953189c 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -7,7 +7,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
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 :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check]
before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds]
# Allow read any merge_request
@@ -153,11 +153,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def merge_check
- if @merge_request.unchecked?
- @merge_request.check_if_can_be_merged
- end
-
- closes_issues
+ @merge_request.check_if_can_be_merged if @merge_request.unchecked?
render partial: "projects/merge_requests/widget/show.html.haml", layout: false
end
@@ -178,7 +174,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.update(merge_error: nil)
- if params[:merge_when_build_succeeds] && @merge_request.ci_commit && @merge_request.ci_commit.active?
+ if params[:merge_when_build_succeeds].present? && @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
@@ -299,6 +295,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def define_widget_vars
@ci_commit = @merge_request.ci_commit
+ closes_issues
end
def invalid_mr
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index 88b949a27ab..ee705f32e81 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -13,7 +13,8 @@ class Projects::NotesController < Projects::ApplicationController
@notes.each do |note|
notes_json[:notes] << {
id: note.id,
- html: note_to_html(note)
+ html: note_to_html(note),
+ valid: note.valid?
}
end
@@ -68,7 +69,7 @@ class Projects::NotesController < Projects::ApplicationController
data = {
author: current_user,
is_award: true,
- note: note_params[:note].gsub(":", '')
+ note: note_params[:note].delete(":")
}
note = noteable.notes.find_by(data)
diff --git a/app/controllers/projects/protected_branches_controller.rb b/app/controllers/projects/protected_branches_controller.rb
index 6b52eccebf7..e49259c34b6 100644
--- a/app/controllers/projects/protected_branches_controller.rb
+++ b/app/controllers/projects/protected_branches_controller.rb
@@ -21,7 +21,7 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
if protected_branch &&
protected_branch.update_attributes(
- developers_can_push: params[:developers_can_push]
+ developers_can_push: params[:developers_can_push]
)
respond_to do |format|
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index 6e7590260ff..8b2577aebe1 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -1,5 +1,5 @@
class Projects::ServicesController < Projects::ApplicationController
- ALLOWED_PARAMS = [:title, :token, :type, :active, :api_key, :api_version, :subdomain,
+ ALLOWED_PARAMS = [:title, :token, :type, :active, :api_key, :api_url, :api_version, :subdomain,
:room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server, :teamcity_url, :drone_url, :build_type,
@@ -10,7 +10,8 @@ class Projects::ServicesController < Projects::ApplicationController
:notify_only_broken_builds, :add_pusher,
:send_from_committer_email, :disable_diffs, :external_wiki_url,
:notify, :color,
- :server_host, :server_port, :default_irc_uri, :enable_ssl_verification]
+ :server_host, :server_port, :default_irc_uri, :enable_ssl_verification,
+ :jira_issue_transition_id]
# Parameters to ignore if no value is specified
FILTER_BLANK_PARAMS = [:password]
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 21f962df206..0b00b9a0702 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -61,7 +61,7 @@ module ApplicationHelper
options[:class] ||= ''
options[:class] << ' identicon'
bg_key = project.id % 7
- style = "background-color: ##{ allowed_colors.values[bg_key] }; color: #555"
+ style = "background-color: ##{allowed_colors.values[bg_key]}; color: #555"
content_tag(:div, class: options[:class], style: style) do
project.name[0, 1].upcase
@@ -204,12 +204,16 @@ module ApplicationHelper
# Returns an HTML-safe String
def time_ago_with_tooltip(time, placement: 'top', html_class: 'time_ago', skip_js: false)
element = content_tag :time, time.to_s,
- class: "#{html_class} js-timeago",
+ class: "#{html_class} js-timeago js-timeago-pending",
datetime: time.getutc.iso8601,
title: time.in_time_zone.stamp('Aug 21, 2011 9:23pm'),
data: { toggle: 'tooltip', placement: placement, container: 'body' }
- element += javascript_tag "$('.js-timeago').last().timeago()" unless skip_js
+ unless skip_js
+ element << javascript_tag(
+ "$('.js-timeago-pending').removeClass('js-timeago-pending').timeago()"
+ )
+ end
element
end
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index 313b6dde910..ec0e3f409c1 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -10,8 +10,8 @@ module ButtonHelper
# # => "<button class='...' data-clipboard-text='Foo'>...</button>"
#
# # Define the target element
- # clipboard_button(clipboard_target: "#foo")
- # # => "<button class='...' data-clipboard-target='#foo'>...</button>"
+ # clipboard_button(clipboard_target: "div#foo")
+ # # => "<button class='...' data-clipboard-target='div#foo'>...</button>"
#
# See http://clipboardjs.com/#usage
def clipboard_button(data = {})
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
index 8554074d619..d8bee21c82e 100644
--- a/app/helpers/ci_status_helper.rb
+++ b/app/helpers/ci_status_helper.rb
@@ -12,19 +12,6 @@ module CiStatusHelper
ci_label_for_status(ci_commit.status)
end
- def ci_status_color(ci_commit)
- case ci_commit.status
- when 'success'
- 'green'
- when 'failed'
- 'red'
- when 'running', 'pending'
- 'yellow'
- else
- 'gray'
- end
- end
-
def ci_status_with_icon(status)
content_tag :span, class: "ci-status ci-#{status}" do
ci_icon_for_status(status) + '&nbsp;'.html_safe + ci_label_for_status(status)
@@ -56,12 +43,11 @@ module CiStatusHelper
end
def render_ci_status(ci_commit)
- link_to ci_status_path(ci_commit),
- class: "ci-status-link c#{ci_status_color(ci_commit)}",
+ link_to ci_status_icon(ci_commit),
+ ci_status_path(ci_commit),
+ class: "ci-status-link ci-status-icon-#{ci_commit.status.dasherize}",
title: "Build #{ci_status_label(ci_commit)}",
- data: { toggle: 'tooltip', placement: 'left' } do
- ci_status_icon(ci_commit)
- end
+ data: { toggle: 'tooltip', placement: 'left' }
end
def no_runners_for_project?(project)
diff --git a/app/helpers/external_wiki_helper.rb b/app/helpers/external_wiki_helper.rb
index 838b85afdfe..1f3401f2906 100644
--- a/app/helpers/external_wiki_helper.rb
+++ b/app/helpers/external_wiki_helper.rb
@@ -1,7 +1,7 @@
module ExternalWikiHelper
def get_project_wiki_path(project)
external_wiki_service = project.services.
- select { |service| service.to_param == 'external_wiki' }.first
+ find { |service| service.to_param == 'external_wiki' }
if external_wiki_service.present? && external_wiki_service.active?
external_wiki_service.properties['external_wiki_url']
else
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index 5004e02ea0b..ca41657cec1 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -20,7 +20,7 @@ module GitlabMarkdownHelper
end
user = current_user if defined?(current_user)
- gfm_body = Gitlab::Markdown.render(escaped_body, project: @project, current_user: user, pipeline: :single_line)
+ gfm_body = Banzai.render(escaped_body, project: @project, current_user: user, pipeline: :single_line)
fragment = Nokogiri::HTML::DocumentFragment.parse(gfm_body)
if fragment.children.size == 1 && fragment.children[0].name == 'a'
@@ -50,7 +50,7 @@ module GitlabMarkdownHelper
context[:project] ||= @project
- html = Gitlab::Markdown.render(text, context)
+ html = Banzai.render(text, context)
context.merge!(
current_user: (current_user if defined?(current_user)),
@@ -61,11 +61,12 @@ module GitlabMarkdownHelper
ref: @ref
)
- Gitlab::Markdown.post_process(html, context)
+ Banzai.post_process(html, context)
end
def asciidoc(text)
- Gitlab::Asciidoc.render(text,
+ Gitlab::Asciidoc.render(
+ text,
project: @project,
current_user: (current_user if defined?(current_user)),
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index e66b9c628c7..d2186427dba 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -57,15 +57,15 @@ module IssuesHelper
options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id)
end
- def issue_box_class(item)
+ def status_box_class(item)
if item.respond_to?(:expired?) && item.expired?
- 'issue-box-expired'
+ 'status-box-expired'
elsif item.respond_to?(:merged?) && item.merged?
- 'issue-box-merged'
+ 'status-box-merged'
elsif item.closed?
- 'issue-box-closed'
+ 'status-box-closed'
else
- 'issue-box-open'
+ 'status-box-open'
end
end
@@ -121,6 +121,6 @@ module IssuesHelper
end
end
- # Required for Gitlab::Markdown::IssueReferenceFilter
+ # Required for Banzai::Filter::IssueReferenceFilter
module_function :url_for_issue
end
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index 795fb439f25..a2c3d4d2f32 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -107,6 +107,6 @@ module LabelsHelper
options_from_collection_for_select(grouped_labels, 'name', 'title', params[:label_name])
end
- # Required for Gitlab::Markdown::LabelReferenceFilter
+ # Required for Banzai::Filter::LabelReferenceFilter
module_function :render_colored_label, :text_color_for_bg, :escape_once
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index d061136b7b8..777817e24aa 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -330,10 +330,9 @@ module ProjectsHelper
def filename_path(project, filename)
if project && blob = project.repository.send(filename)
namespace_project_blob_path(
- project.namespace,
- project,
- tree_join(project.default_branch,
- blob.name)
+ project.namespace,
+ project,
+ tree_join(project.default_branch, blob.name)
)
end
end
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index 886a1e734b5..f448dd0ab61 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -79,7 +79,7 @@ module TreeHelper
part_path = File.join(part_path, part) unless part_path.empty?
part_path = part if part_path.empty?
- next unless parts.last(2).include?(part) if parts.count > max_links
+ next if parts.count > max_links && !parts.last(2).include?(part)
yield(part, tree_join(@ref, part_path))
end
end
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index 9beb0de68f3..3bbdd9cee76 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -17,7 +17,7 @@ class Notify < BaseMailer
subject: subject,
body: body.html_safe,
content_type: 'text/html'
- )
+ )
end
# Splits "gitlab.corp.company.com" up into "gitlab.corp.company.com",
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index faa0bdf840b..1f4e8b3ef24 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -126,12 +126,12 @@ class ApplicationSetting < ActiveRecord::Base
def restricted_signup_domains_raw=(values)
self.restricted_signup_domains = []
self.restricted_signup_domains = values.split(
- /\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace
- | # or
- \s # any whitespace character
- | # or
- [\r\n] # any number of newline characters
- /x)
+ /\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace
+ | # or
+ \s # any whitespace character
+ | # or
+ [\r\n] # any number of newline characters
+ /x)
self.restricted_signup_domains.reject! { |d| d.empty? }
end
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 6d9cdb95295..7b89fe069ea 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -135,6 +135,16 @@ module Ci
predefined_variables + yaml_variables + project_variables + trigger_variables
end
+ def merge_request
+ merge_requests = MergeRequest.includes(:merge_request_diff)
+ .where(source_branch: ref, source_project_id: commit.gl_project_id)
+ .reorder(iid: :asc)
+
+ merge_requests.find do |merge_request|
+ merge_request.commits.any? { |ci| ci.id == commit.sha }
+ end
+ end
+
def project
commit.project
end
@@ -170,7 +180,8 @@ module Ci
def extract_coverage(text, regex)
begin
- matches = text.gsub(Regexp.new(regex)).to_a.last
+ matches = text.scan(Regexp.new(regex)).last
+ matches = matches.last if matches.kind_of?(Array)
coverage = matches.gsub(/\d+(\.\d+)?/).first
if coverage.present?
diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb
index 6bf596e5d3e..d2a29236942 100644
--- a/app/models/ci/commit.rb
+++ b/app/models/ci/commit.rb
@@ -218,16 +218,6 @@ module Ci
update!(committed_at: DateTime.now)
end
- ##
- # This method checks if build status should be displayed.
- #
- # Build status should be available only if builds are enabled
- # on project level and `.gitlab-ci.yml` file is present.
- #
- def show_build_status?
- project.builds_enabled? && ci_yaml_file
- end
-
private
def save_yaml_error(error)
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index d2ea9ab7313..1fdcda97520 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -23,7 +23,7 @@ module Mentionable
included do
if self < Participable
- participant ->(current_user) { mentioned_users(current_user, load_lazy_references: false) }
+ participant ->(current_user) { mentioned_users(current_user) }
end
end
@@ -43,15 +43,15 @@ module Mentionable
self
end
- def all_references(current_user = self.author, text = nil, load_lazy_references: true)
- ext = Gitlab::ReferenceExtractor.new(self.project, current_user, load_lazy_references: load_lazy_references)
-
+ def all_references(current_user = self.author, text = nil)
+ ext = Gitlab::ReferenceExtractor.new(self.project, current_user)
+
if text
ext.analyze(text)
else
self.class.mentionable_attrs.each do |attr, options|
text = send(attr)
- options[:cache_key] = [self, attr] if options.delete(:cache)
+ options[:cache_key] = [self, attr] if options.delete(:cache) && self.persisted?
ext.analyze(text, options)
end
end
@@ -59,13 +59,13 @@ module Mentionable
ext
end
- def mentioned_users(current_user = nil, load_lazy_references: true)
- all_references(current_user, load_lazy_references: load_lazy_references).users
+ def mentioned_users(current_user = nil)
+ all_references(current_user).users
end
# Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference.
- def referenced_mentionables(current_user = self.author, text = nil, load_lazy_references: true)
- refs = all_references(current_user, text, load_lazy_references: load_lazy_references)
+ def referenced_mentionables(current_user = self.author, text = nil)
+ refs = all_references(current_user, text)
refs = (refs.issues + refs.merge_requests + refs.commits)
# We're using this method instead of Array diffing because that requires
diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb
index 85367f89f4f..fc6f83b918b 100644
--- a/app/models/concerns/participable.rb
+++ b/app/models/concerns/participable.rb
@@ -37,21 +37,22 @@ module Participable
# Be aware that this method makes a lot of sql queries.
# Save result into variable if you are going to reuse it inside same request
- def participants(current_user = self.author, load_lazy_references: true)
- participants = self.class.participant_attrs.flat_map do |attr|
- value =
- if attr.respond_to?(:call)
- instance_exec(current_user, &attr)
- else
- send(attr)
- end
+ def participants(current_user = self.author)
+ participants =
+ Gitlab::ReferenceExtractor.lazily do
+ self.class.participant_attrs.flat_map do |attr|
+ value =
+ if attr.respond_to?(:call)
+ instance_exec(current_user, &attr)
+ else
+ send(attr)
+ end
- participants_for(value, current_user)
- end.compact.uniq
-
- if load_lazy_references
- participants = Gitlab::Markdown::ReferenceFilter::LazyReference.load(participants).uniq
+ participants_for(value, current_user)
+ end.compact.uniq
+ end
+ unless Gitlab::ReferenceExtractor.lazy?
participants.select! do |user|
user.can?(:read_project, project)
end
@@ -64,12 +65,12 @@ module Participable
def participants_for(value, current_user = nil)
case value
- when User, Gitlab::Markdown::ReferenceFilter::LazyReference
+ when User, Banzai::LazyReference
[value]
when Enumerable, ActiveRecord::Relation
value.flat_map { |v| participants_for(v, current_user) }
when Participable
- value.participants(current_user, load_lazy_references: false)
+ value.participants(current_user)
end
end
end
diff --git a/app/models/concerns/token_authenticatable.rb b/app/models/concerns/token_authenticatable.rb
index 56d38fe8250..488ff8c31b7 100644
--- a/app/models/concerns/token_authenticatable.rb
+++ b/app/models/concerns/token_authenticatable.rb
@@ -13,7 +13,7 @@ module TokenAuthenticatable
@token_fields << token_field
define_singleton_method("find_by_#{token_field}") do |token|
- where(token_field => token).first if token
+ find_by(token_field => token) if token
end
define_method("ensure_#{token_field}") do
@@ -37,7 +37,7 @@ module TokenAuthenticatable
def generate_token_for(token_field)
loop do
token = Devise.friendly_token
- break token unless self.class.unscoped.where(token_field => token).first
+ break token unless self.class.unscoped.find_by(token_field => token)
end
end
end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index e04035b3af8..80ecd15077f 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -84,11 +84,11 @@ class Issue < ActiveRecord::Base
end
def referenced_merge_requests
- references = [self, *notes].flat_map do |note|
- note.all_references(load_lazy_references: false).merge_requests
- end.uniq
-
- Gitlab::Markdown::ReferenceFilter::LazyReference.load(references).uniq.sort_by(&:iid)
+ Gitlab::ReferenceExtractor.lazily do
+ [self, *notes].flat_map do |note|
+ note.all_references.merge_requests
+ end
+ end.sort_by(&:iid)
end
# Reset issue events cache
diff --git a/app/models/jira_issue.rb b/app/models/jira_issue.rb
new file mode 100644
index 00000000000..5b21aac5e43
--- /dev/null
+++ b/app/models/jira_issue.rb
@@ -0,0 +1,2 @@
+class JiraIssue < ExternalIssue
+end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index f6f77a16267..ac25d38eb63 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -194,9 +194,7 @@ class MergeRequest < ActiveRecord::Base
similar_mrs = similar_mrs.where('id not in (?)', self.id) if self.id
if similar_mrs.any?
errors.add :validate_branches,
- "Cannot Create: This merge request already exists: #{
- similar_mrs.pluck(:title)
- }"
+ "Cannot Create: This merge request already exists: #{similar_mrs.pluck(:title)}"
end
end
end
@@ -337,7 +335,7 @@ class MergeRequest < ActiveRecord::Base
issues = commits.flat_map { |c| c.closes_issues(current_user) }
issues.push(*Gitlab::ClosingIssueExtractor.new(project, current_user).
closed_by_message(description))
- issues.uniq
+ issues.uniq(&:id)
else
[]
end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 1c4e101cc10..adafabbec07 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -45,7 +45,7 @@ class Namespace < ActiveRecord::Base
class << self
def by_path(path)
- where('lower(path) = :value', value: path.downcase).first
+ find_by('lower(path) = :value', value: path.downcase)
end
# Case insensetive search for namespace by path or name
@@ -148,6 +148,6 @@ class Namespace < ActiveRecord::Base
end
def find_fork_of(project)
- projects.joins(:forked_project_link).where('forked_project_links.forked_from_project_id = ?', project.id).first
+ projects.joins(:forked_project_link).find_by('forked_project_links.forked_from_project_id = ?', project.id)
end
end
diff --git a/app/models/note.rb b/app/models/note.rb
index 04053ccc61e..8c5b5836f9a 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -373,11 +373,11 @@ class Note < ActiveRecord::Base
end
def contains_emoji_only?
- note =~ /\A#{Gitlab::Markdown::EmojiFilter.emoji_pattern}\s?\Z/
+ note =~ /\A#{Banzai::Filter::EmojiFilter.emoji_pattern}\s?\Z/
end
def award_emoji_name
- original_name = note.match(Gitlab::Markdown::EmojiFilter.emoji_pattern)[1]
+ original_name = note.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1]
AwardEmoji.normilize_emoji_name(original_name)
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index e1f7bf971e3..b28a7ca429c 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -265,7 +265,7 @@ class Project < ActiveRecord::Base
joins(:namespace).
iwhere('namespaces.path' => namespace_path)
- projects.where('projects.path' => project_path).take ||
+ projects.find_by('projects.path' => project_path) ||
projects.iwhere('projects.path' => project_path).take
end
@@ -450,7 +450,7 @@ class Project < ActiveRecord::Base
end
def external_issue_tracker
- @external_issues_tracker ||= external_issues_trackers.select(&:activated?).first
+ @external_issues_tracker ||= external_issues_trackers.find(&:activated?)
end
def can_have_issues_tracker_id?
@@ -496,7 +496,11 @@ class Project < ActiveRecord::Base
end
def ci_service
- @ci_service ||= ci_services.select(&:activated?).first
+ @ci_service ||= ci_services.find(&:activated?)
+ end
+
+ def jira_tracker?
+ issues_tracker.to_param == 'jira'
end
def avatar_type
@@ -547,7 +551,7 @@ class Project < ActiveRecord::Base
end
def project_member_by_name_or_email(name = nil, email = nil)
- user = users.where('name like ? or email like ?', name, email).first
+ user = users.find_by('name like ? or email like ?', name, email)
project_members.where(user: user) if user
end
@@ -722,7 +726,7 @@ class Project < ActiveRecord::Base
end
def project_member(user)
- project_members.where(user_id: user).first
+ project_members.find_by(user_id: user)
end
def default_branch
@@ -799,6 +803,10 @@ class Project < ActiveRecord::Base
false
end
+ def jira_tracker_active?
+ jira_tracker? && jira_service.active
+ end
+
def ci_commit(sha)
ci_commits.find_by(sha: sha)
end
@@ -850,4 +858,8 @@ class Project < ActiveRecord::Base
def build_timeout_in_minutes=(value)
self.build_timeout = value.to_i * 60
end
+
+ def open_issues_count
+ issues.opened.count
+ end
end
diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb
index 0a61ad96a0e..aa8746beb80 100644
--- a/app/models/project_services/bamboo_service.rb
+++ b/app/models/project_services/bamboo_service.rb
@@ -27,12 +27,10 @@ class BambooService < CiService
validates :build_key, presence: true, if: :activated?
validates :username,
presence: true,
- if: ->(service) { service.password? },
- if: :activated?
+ if: ->(service) { service.activated? && service.password }
validates :password,
presence: true,
- if: ->(service) { service.username? },
- if: :activated?
+ if: ->(service) { service.activated? && service.username }
attr_accessor :response
diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb
index 27fc19379f1..15c7c907f7e 100644
--- a/app/models/project_services/flowdock_service.rb
+++ b/app/models/project_services/flowdock_service.rb
@@ -58,6 +58,6 @@ class FlowdockService < Service
repo_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}",
commit_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/%s",
diff_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/compare/%s...%s",
- )
+ )
end
end
diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb
index 91ef267ad79..202fee042e3 100644
--- a/app/models/project_services/gemnasium_service.rb
+++ b/app/models/project_services/gemnasium_service.rb
@@ -57,6 +57,6 @@ class GemnasiumService < Service
token: token,
api_key: api_key,
repo: project.repository.path_to_repo
- )
+ )
end
end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index 35e30b1cb0b..e216f406e1c 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -19,9 +19,24 @@
#
class JiraService < IssueTrackerService
+ include HTTParty
include Gitlab::Application.routes.url_helpers
- prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
+ DEFAULT_API_VERSION = 2
+
+ prop_accessor :username, :password, :api_url, :jira_issue_transition_id,
+ :title, :description, :project_url, :issues_url, :new_issue_url
+
+ before_validation :set_api_url, :set_jira_issue_transition_id
+
+ before_update :reset_password
+
+ def reset_password
+ # don't reset the password if a new one is provided
+ if api_url_changed? && !password_touched?
+ self.password = nil
+ end
+ end
def help
line1 = 'Setting `project_url`, `issues_url` and `new_issue_url` will '\
@@ -54,4 +69,228 @@ class JiraService < IssueTrackerService
def to_param
'jira'
end
+
+ def fields
+ super.push(
+ { type: 'text', name: 'api_url', placeholder: 'https://jira.example.com/rest/api/2' },
+ { type: 'text', name: 'username', placeholder: '' },
+ { type: 'password', name: 'password', placeholder: '' },
+ { type: 'text', name: 'jira_issue_transition_id', placeholder: '2' }
+ )
+ end
+
+ def execute(push, issue = nil)
+ if issue.nil?
+ # No specific issue, that means
+ # we just want to test settings
+ test_settings
+ else
+ close_issue(push, issue)
+ end
+ end
+
+ def create_cross_reference_note(mentioned, noteable, author)
+ issue_name = mentioned.id
+ project = self.project
+ noteable_name = noteable.class.name.underscore.downcase
+ noteable_id = if noteable.is_a?(Commit)
+ noteable.id
+ else
+ noteable.iid
+ end
+
+ entity_url = build_entity_url(noteable_name.to_sym, noteable_id)
+
+ data = {
+ user: {
+ name: author.name,
+ url: resource_url(user_path(author)),
+ },
+ project: {
+ name: project.path_with_namespace,
+ url: resource_url(namespace_project_path(project.namespace, project))
+ },
+ entity: {
+ name: noteable_name.humanize.downcase,
+ url: entity_url
+ }
+ }
+
+ add_comment(data, issue_name)
+ end
+
+ def test_settings
+ result = JiraService.get(
+ jira_api_test_url,
+ headers: {
+ 'Content-Type' => 'application/json',
+ 'Authorization' => "Basic #{auth}"
+ }
+ )
+
+ case result.code
+ when 201, 200
+ Rails.logger.info("#{self.class.name} SUCCESS #{result.code}: Successfully connected to #{api_url}.")
+ true
+ else
+ Rails.logger.info("#{self.class.name} ERROR #{result.code}: #{result.parsed_response}")
+ false
+ end
+ rescue Errno::ECONNREFUSED => e
+ Rails.logger.info "#{self.class.name} ERROR: #{e.message}. API URL: #{api_url}."
+ false
+ end
+
+ private
+
+ def build_api_url_from_project_url
+ server = URI(project_url)
+ default_ports = [["http",80],["https",443]].include?([server.scheme,server.port])
+ server_url = "#{server.scheme}://#{server.host}"
+ server_url.concat(":#{server.port}") unless default_ports
+ "#{server_url}/rest/api/#{DEFAULT_API_VERSION}"
+ rescue
+ "" # looks like project URL was not valid
+ end
+
+ def set_api_url
+ self.api_url = build_api_url_from_project_url if self.api_url.blank?
+ end
+
+ def set_jira_issue_transition_id
+ self.jira_issue_transition_id ||= "2"
+ end
+
+ def close_issue(entity, issue)
+ commit_id = if entity.is_a?(Commit)
+ entity.id
+ elsif entity.is_a?(MergeRequest)
+ entity.last_commit.id
+ end
+ commit_url = build_entity_url(:commit, commit_id)
+
+ # Depending on the JIRA project's workflow, a comment during transition
+ # may or may not be allowed. Split the operation in to two calls so the
+ # comment always works.
+ transition_issue(issue)
+ add_issue_solved_comment(issue, commit_id, commit_url)
+ end
+
+ def transition_issue(issue)
+ message = {
+ transition: {
+ id: jira_issue_transition_id
+ }
+ }
+ send_message(close_issue_url(issue.iid), message.to_json)
+ end
+
+ def add_issue_solved_comment(issue, commit_id, commit_url)
+ comment = {
+ body: "Issue solved with [#{commit_id}|#{commit_url}]."
+ }
+
+ send_message(comment_url(issue.iid), comment.to_json)
+ end
+
+ def add_comment(data, issue_name)
+ url = comment_url(issue_name)
+ user_name = data[:user][:name]
+ user_url = data[:user][:url]
+ entity_name = data[:entity][:name]
+ entity_url = data[:entity][:url]
+ project_name = data[:project][:name]
+
+ message = {
+ body: "[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]."
+ }
+
+ unless existing_comment?(issue_name, message[:body])
+ send_message(url, message.to_json)
+ end
+ end
+
+
+ def auth
+ require 'base64'
+ Base64.urlsafe_encode64("#{self.username}:#{self.password}")
+ end
+
+ def send_message(url, message)
+ result = JiraService.post(
+ url,
+ body: message,
+ headers: {
+ 'Content-Type' => 'application/json',
+ 'Authorization' => "Basic #{auth}"
+ }
+ )
+
+ message = case result.code
+ when 201, 200, 204
+ "#{self.class.name} SUCCESS #{result.code}: Successfully posted to #{url}."
+ when 401
+ "#{self.class.name} ERROR 401: Unauthorized. Check the #{self.username} credentials and JIRA access permissions and try again."
+ else
+ "#{self.class.name} ERROR #{result.code}: #{result.parsed_response}"
+ end
+
+ Rails.logger.info(message)
+ message
+ rescue URI::InvalidURIError, Errno::ECONNREFUSED => e
+ Rails.logger.info "#{self.class.name} ERROR: #{e.message}. Hostname: #{url}."
+ end
+
+ def existing_comment?(issue_name, new_comment)
+ result = JiraService.get(
+ comment_url(issue_name),
+ headers: {
+ 'Content-Type' => 'application/json',
+ 'Authorization' => "Basic #{auth}"
+ }
+ )
+
+ case result.code
+ when 201, 200
+ existing_comments = JSON.parse(result.body)['comments']
+
+ if existing_comments.present?
+ return existing_comments.map { |comment| comment['body'].include?(new_comment) }.any?
+ end
+ end
+
+ false
+ rescue JSON::ParserError
+ false
+ end
+
+ def resource_url(resource)
+ "#{Settings.gitlab['url'].chomp("/")}#{resource}"
+ end
+
+ def build_entity_url(entity_name, entity_id)
+ resource_url(
+ polymorphic_url(
+ [
+ self.project.namespace.becomes(Namespace),
+ self.project,
+ entity_name
+ ],
+ id: entity_id,
+ routing_type: :path
+ )
+ )
+ end
+
+ def close_issue_url(issue_name)
+ "#{self.api_url}/issue/#{issue_name}/transitions"
+ end
+
+ def comment_url(issue_name)
+ "#{self.api_url}/issue/#{issue_name}/comment"
+ end
+
+ def jira_api_test_url
+ "#{self.api_url}/myself"
+ end
end
diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb
index 29d4236745a..a63700693d7 100644
--- a/app/models/project_services/teamcity_service.rb
+++ b/app/models/project_services/teamcity_service.rb
@@ -27,12 +27,10 @@ class TeamcityService < CiService
validates :build_type, presence: true, if: :activated?
validates :username,
presence: true,
- if: ->(service) { service.password? },
- if: :activated?
+ if: ->(service) { service.activated? && service.password }
validates :password,
presence: true,
- if: ->(service) { service.username? },
- if: :activated?
+ if: ->(service) { service.activated? && service.username }
attr_accessor :response
@@ -147,6 +145,6 @@ class TeamcityService < CiService
'</build>',
headers: { 'Content-type' => 'application/xml' },
basic_auth: auth
- )
+ )
end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index fdd14f4571d..df87f3b79bd 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -26,6 +26,7 @@
# bio :string(255)
# failed_attempts :integer default(0)
# locked_at :datetime
+# unlock_token :string(255)
# username :string(255)
# can_create_group :boolean default(TRUE), not null
# can_create_team :boolean default(TRUE), not null
@@ -220,9 +221,9 @@ class User < ActiveRecord::Base
def find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
- where(conditions).where(["lower(username) = :value OR lower(email) = :value", { value: login.downcase }]).first
+ where(conditions).find_by("lower(username) = :value OR lower(email) = :value", value: login.downcase)
else
- where(conditions).first
+ find_by(conditions)
end
end
@@ -285,7 +286,7 @@ class User < ActiveRecord::Base
end
def by_username_or_id(name_or_id)
- where('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i).first
+ find_by('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i)
end
def build_user(attrs = {})
diff --git a/app/services/create_commit_builds_service.rb b/app/services/create_commit_builds_service.rb
index 759c334ebe9..31b407efeb1 100644
--- a/app/services/create_commit_builds_service.rb
+++ b/app/services/create_commit_builds_service.rb
@@ -16,9 +16,23 @@ class CreateCommitBuildsService
return false
end
- tag = Gitlab::Git.tag_ref?(origin_ref)
- commit = project.ensure_ci_commit(sha)
+ commit = project.ci_commit(sha)
+ unless commit
+ commit = project.ci_commits.new(sha: sha)
+
+ # Skip creating ci_commit when no gitlab-ci.yml is found
+ unless commit.ci_yaml_file
+ return false
+ end
+
+ # Create a new ci_commit
+ commit.save!
+ end
+
+ # Skip creating builds for commits that have [ci skip]
unless commit.skip_ci?
+ # Create builds for commit
+ tag = Gitlab::Git.tag_ref?(origin_ref)
commit.update_committed!
commit.create_builds(ref, tag, user)
end
diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb
index 3d85f97b7e5..a1a20e47681 100644
--- a/app/services/issues/close_service.rb
+++ b/app/services/issues/close_service.rb
@@ -1,6 +1,11 @@
module Issues
class CloseService < Issues::BaseService
def execute(issue, commit = nil)
+ if project.jira_tracker? && project.jira_service.active
+ project.jira_service.execute(commit, issue)
+ return issue
+ end
+
if project.default_issues_tracker? && issue.close
event_service.close_issue(issue, current_user)
create_note(issue, commit)
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index b26c7513f5b..8b3d56c2b4c 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -112,7 +112,7 @@ module MergeRequests
merge_requests_for_source_branch.each do |merge_request|
SystemNoteService.change_branch_presence(
- merge_request, merge_request.project, @current_user,
+ merge_request, merge_request.project, @current_user,
:source, @branch_name, presence)
end
end
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 6975b2ee55b..98a71cbf1ad 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -241,9 +241,14 @@ class SystemNoteService
note_options.merge!(noteable: noteable)
end
- create_note(note_options)
+ if noteable.is_a?(ExternalIssue)
+ noteable.project.issues_tracker.create_cross_reference_note(noteable, mentioner, author)
+ else
+ create_note(note_options)
+ end
end
+
def self.cross_reference?(note_text)
note_text.start_with?(cross_reference_note_prefix)
end
@@ -259,7 +264,7 @@ class SystemNoteService
#
# Returns Boolean
def self.cross_reference_disallowed?(noteable, mentioner)
- return true if noteable.is_a?(ExternalIssue)
+ return true if noteable.is_a?(ExternalIssue) && !noteable.project.jira_tracker_active?
return false unless mentioner.is_a?(MergeRequest)
return false unless noteable.is_a?(Commit)
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 8657d2c71fe..531247e9148 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -80,6 +80,10 @@
%span.pull-right
= API::API::version
%p
+ Git
+ %span.pull-right
+ = Gitlab::Git.version
+ %p
Ruby
%span.pull-right
#{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml
index 44b7efe5232..4316c358dcb 100644
--- a/app/views/dashboard/milestones/show.html.haml
+++ b/app/views/dashboard/milestones/show.html.haml
@@ -1,18 +1,18 @@
- page_title @milestone.title, "Milestones"
- header_title "Milestones", dashboard_milestones_path
-.issuable-details
- .page-title
- .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" }
- - if @milestone.closed?
- Closed
- - else
- Open
+.detail-page-header
+ .status-box{ class: "status-box-#{@milestone.closed? ? 'closed' : 'open'}" }
+ - if @milestone.closed?
+ Closed
+ - else
+ Open
+ %span.identifier
Milestone #{@milestone.title}
- .gray-content-block.middle-block
- %h2.issue-title
- = markdown escape_once(@milestone.title), pipeline: :single_line
+.detail-page-description.gray-content-block.second-block
+ %h2.title
+ = markdown escape_once(@milestone.title), pipeline: :single_line
- if @milestone.complete? && @milestone.active?
.alert.alert-success.prepend-top-default
diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb
deleted file mode 100644
index 79d6c761d8f..00000000000
--- a/app/views/devise/mailer/unlock_instructions.html.erb
+++ /dev/null
@@ -1,7 +0,0 @@
-<p>Hello <%= @resource.email %>!</p>
-
-<p>Your account has been locked due to an excessive amount of unsuccessful sign in attempts.</p>
-
-<p>Click the link below to unlock your account:</p>
-
-<p><%= link_to 'Unlock your account', unlock_url(@resource, unlock_token: @token) %></p>
diff --git a/app/views/devise/mailer/unlock_instructions.html.haml b/app/views/devise/mailer/unlock_instructions.html.haml
new file mode 100644
index 00000000000..52b327e20c5
--- /dev/null
+++ b/app/views/devise/mailer/unlock_instructions.html.haml
@@ -0,0 +1,10 @@
+%p
+Hello #{@resource.name}!
+
+%p
+ Your GitLab account has been locked due to an excessive amount of unsuccessful
+ sign in attempts. Your account will automatically unlock in
+ = time_ago_in_words(Devise.unlock_in.from_now)
+ or you may click the link below to unlock now.
+
+%p= link_to 'Unlock your account', unlock_url(@resource, unlock_token: @token)
diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb
deleted file mode 100644
index f9277d1673f..00000000000
--- a/app/views/devise/unlocks/new.html.erb
+++ /dev/null
@@ -1,12 +0,0 @@
-<h2>Resend unlock instructions</h2>
-
-<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
- <%= devise_error_messages! %>
-
- <div><%= f.label :email %><br />
- <%= f.email_field :email %></div>
-
- <div><%= f.submit "Resend unlock instructions" %></div>
-<% end %>
-
-<%= render partial: "devise/shared/links" %>
diff --git a/app/views/devise/unlocks/new.html.haml b/app/views/devise/unlocks/new.html.haml
new file mode 100644
index 00000000000..49c087c0646
--- /dev/null
+++ b/app/views/devise/unlocks/new.html.haml
@@ -0,0 +1,14 @@
+.login-box
+ .login-heading
+ %h3 Resend unlock email
+ .login-body
+ = form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f|
+ .devise-errors
+ = devise_error_messages!
+ .clearfix.append-bottom-20
+ = f.email_field :email, class: 'form-control', placeholder: 'Email', autofocus: 'autofocus', autocapitalize: 'off', autocorrect: 'off'
+ .clearfix
+ = f.submit 'Resend unlock instructions', class: 'btn btn-success'
+
+.clearfix.prepend-top-20
+ = render 'devise/shared/sign_in_link'
diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml
index 350e216fcc6..d063b257b5e 100644
--- a/app/views/groups/milestones/show.html.haml
+++ b/app/views/groups/milestones/show.html.haml
@@ -1,24 +1,24 @@
- page_title @milestone.title, "Milestones"
= render "header_title"
-.issuable-details
- .page-title
- .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" }
- - if @milestone.closed?
- Closed
- - else
- Open
+.detail-page-header
+ .status-box{ class: "status-box-#{@milestone.closed? ? 'closed' : 'open'}" }
+ - if @milestone.closed?
+ Closed
+ - else
+ Open
+ %span.identifier
Milestone #{@milestone.title}
- .pull-right
- - if can?(current_user, :admin_milestones, @group)
- - if @milestone.active?
- = link_to 'Close Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close"
- - else
- = link_to 'Reopen Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen"
+ .pull-right
+ - if can?(current_user, :admin_milestones, @group)
+ - if @milestone.active?
+ = link_to 'Close Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close"
+ - else
+ = link_to 'Reopen Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen"
- .gray-content-block.middle-block
- %h2.issue-title
- = markdown escape_once(@milestone.title), pipeline: :single_line
+.detail-page-description.gray-content-block.second-block
+ %h2.title
+ = markdown escape_once(@milestone.title), pipeline: :single_line
- if @milestone.complete? && @milestone.active?
.alert.alert-success.prepend-top-default
diff --git a/app/views/notify/_note_message.html.haml b/app/views/notify/_note_message.html.haml
index 27112c6745a..00cb4aa24cc 100644
--- a/app/views/notify/_note_message.html.haml
+++ b/app/views/notify/_note_message.html.haml
@@ -1,4 +1,2 @@
%div
- "#{link_to @note.author_name, user_url(@note.author)} wrote:"
-%div
= markdown(@note.note, pipeline: :email)
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index 20a5b6a66e7..5b7ecce86ab 100644
--- a/app/views/projects/builds/show.html.haml
+++ b/app/views/projects/builds/show.html.haml
@@ -7,6 +7,10 @@
%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)
+ - merge_request = @build.merge_request
+ - if merge_request
+ via
+ = link_to "merge request ##{merge_request.iid}", merge_request_path(merge_request)
#up-build-trace
- if @commit.matrix_for_ref?(@build.ref)
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 634924db247..ddb77fd796b 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -20,8 +20,8 @@
%p
%span.light Commit
- = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace", data: { clipboard_text: @commit.id }
- = clipboard_button
+ = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace"
+ = clipboard_button(clipboard_text: @commit.id)
.commit-info-row
%span.light Authored by
%strong
@@ -40,7 +40,7 @@
- @commit.parents.each do |parent|
= link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent), class: "monospace"
-- if @ci_commit && @ci_commit.show_build_status?
+- if @ci_commit
.pull-right
= link_to ci_status_path(@ci_commit), class: "ci-status ci-#{@ci_commit.status}" do
= ci_status_icon(@ci_commit)
diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml
index 45a00e4d259..74a05df24d3 100644
--- a/app/views/projects/commit_statuses/_commit_status.html.haml
+++ b/app/views/projects/commit_statuses/_commit_status.html.haml
@@ -19,11 +19,11 @@
- if defined?(commit_sha) && commit_sha
%td
- = link_to commit_status.short_sha, namespace_project_commit_path(@project.namespace, @project, commit_status.sha), class: "monospace"
-
+ = link_to commit_status.short_sha, namespace_project_commit_path(commit_status.project.namespace, commit_status.project, commit_status.sha), class: "monospace"
+
%td
- if commit_status.ref
- = link_to commit_status.ref, namespace_project_commits_path(@project.namespace, @project, commit_status.ref)
+ = link_to commit_status.ref, namespace_project_commits_path(commit_status.project.namespace, commit_status.project, commit_status.ref)
- else
.light none
@@ -66,7 +66,7 @@
%td
.pull-right
- - if current_user && can?(current_user, :download_build_artifacts, @project) && commit_status.download_url
+ - if current_user && can?(current_user, :download_build_artifacts, commit_status.project) && commit_status.download_url
= link_to commit_status.download_url, title: 'Download artifacts' do
%i.fa.fa-download
- if current_user && can?(current_user, :manage_builds, commit_status.project)
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 1303b27c4f3..28b82dd31f3 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -17,7 +17,7 @@
%a.text-expander.js-toggle-button ...
.pull-right
- - if ci_commit && ci_commit.show_build_status?
+ - if ci_commit
= render_ci_status(ci_commit)
&nbsp;
= clipboard_button(clipboard_text: commit.id)
diff --git a/app/views/projects/issues/_closed_by_box.html.haml b/app/views/projects/issues/_closed_by_box.html.haml
index 3c491c1a8b8..de415ae51a4 100644
--- a/app/views/projects/issues/_closed_by_box.html.haml
+++ b/app/views/projects/issues/_closed_by_box.html.haml
@@ -1,3 +1,2 @@
-.issue-closed-by-widget
- = icon('check')
+.issue-closed-by-widget.gray-content-block.second-block.white
This issue will be closed automatically when merge request #{markdown(merge_requests_sentence(@closed_by_merge_requests), pipeline: :gfm)} is accepted.
diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml
index 405bae1bbb9..86d3dc546ba 100644
--- a/app/views/projects/issues/_discussion.html.haml
+++ b/app/views/projects/issues/_discussion.html.haml
@@ -5,8 +5,5 @@
- else
= link_to 'Close Issue', issue_path(@issue, issue: {state_event: :close}, status_only: true), method: :put, class: 'btn btn-grouped btn-close js-note-target-close', title: 'Close Issue'
-.gray-content-block.second-block.oneline-block
- = render 'votes/votes_block', votable: @issue
-
#notes
= render 'projects/notes/notes_with_form'
diff --git a/app/views/projects/issues/_merge_requests.html.haml b/app/views/projects/issues/_merge_requests.html.haml
index fe856ac991e..254968e4f67 100644
--- a/app/views/projects/issues/_merge_requests.html.haml
+++ b/app/views/projects/issues/_merge_requests.html.haml
@@ -15,9 +15,10 @@
%span.merge-request-info
%strong
= link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title"
- in
- - project = merge_request.target_project
- = link_to project.name_with_namespace, namespace_project_path(project.namespace, project)
+ - unless @issue.project.id == merge_request.target_project.id
+ in
+ - project = merge_request.target_project
+ = link_to project.name_with_namespace, namespace_project_path(project.namespace, project)
%span.merge-request-status.prepend-left-10
- if merge_request.merged?
MERGED
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index cc2cf8c8716..2fe6f88b2a9 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -2,60 +2,65 @@
= render "header_title"
.issue
- .issue-details.issuable-details
- .issuable-title
- .issue-box{ class: issue_box_class(@issue) }
+ .detail-page-header
+ .status-box{ class: status_box_class(@issue) }
+ - if @issue.closed?
+ Closed
+ - else
+ Open
+ %span.identifier
+ Issue ##{@issue.iid}
+ %span.creator
+ &middot;
+ opened by #{link_to_member(@project, @issue.author, size: 24)}
+ &middot;
+ = time_ago_with_tooltip(@issue.created_at, placement: 'bottom', html_class: 'issue_created_ago')
+ - if @issue.updated_at != @issue.created_at
+ %span
+ &middot;
+ = icon('edit', title: 'edited')
+ = time_ago_with_tooltip(@issue.updated_at, placement: 'bottom', html_class: 'issue_edited_ago')
+
+ .pull-right
+ - if can?(current_user, :create_issue, @project)
+ = link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'btn btn-grouped new-issue-link', title: 'New Issue', id: 'new_issue_link' do
+ = icon('plus')
+ New Issue
+ - if can?(current_user, :update_issue, @issue)
- if @issue.closed?
- Closed
+ = link_to 'Reopen', issue_path(@issue, issue: {state_event: :reopen}, status_only: true), method: :put, class: 'btn btn-grouped btn-reopen'
- else
- Open
- %span.issuable-id Issue ##{@issue.iid}
- %span.creator
- &middot;
- opened by #{link_to_member(@project, @issue.author, size: 24)}
- &middot;
- = time_ago_with_tooltip(@issue.created_at, placement: 'bottom', html_class: 'issue_created_ago')
- - if @issue.updated_at != @issue.created_at
- %span
- &middot;
- = icon('edit', title: 'edited')
- = time_ago_with_tooltip(@issue.updated_at, placement: 'bottom', html_class: 'issue_edited_ago')
+ = link_to 'Close', issue_path(@issue, issue: {state_event: :close}, status_only: true), method: :put, class: 'btn btn-grouped btn-close', title: 'Close Issue'
- .pull-right
- - if can?(current_user, :create_issue, @project)
- = link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'btn btn-grouped new-issue-link', title: 'New Issue', id: 'new_issue_link' do
- = icon('plus')
- New Issue
- - if can?(current_user, :update_issue, @issue)
- - if @issue.closed?
- = link_to 'Reopen', issue_path(@issue, issue: {state_event: :reopen}, status_only: true), method: :put, class: 'btn btn-grouped btn-reopen'
- - else
- = link_to 'Close', issue_path(@issue, issue: {state_event: :close}, status_only: true), method: :put, class: 'btn btn-grouped btn-close', title: 'Close Issue'
+ = link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'btn btn-grouped issuable-edit' do
+ = icon('pencil-square-o')
+ Edit
- = link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'btn btn-grouped issuable-edit' do
- = icon('pencil-square-o')
- Edit
+ .issue-details.issuable-details
+ .detail-page-description.gray-content-block.second-block
+ %h2.title
+ = markdown escape_once(@issue.title), pipeline: :single_line
+ %div
+ - if @issue.description.present?
+ .description{class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : ''}
+ .wiki
+ = preserve do
+ = markdown(@issue.description, cache_key: [@issue, "description"])
+ %textarea.hidden.js-task-list-field
+ = @issue.description
- .row
- %section.col-md-9
- .gray-content-block
- %h2.issue-title
- = markdown escape_once(@issue.title), pipeline: :single_line
- %div
- - if @issue.description.present?
- .description{class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : ''}
- .wiki
- = preserve do
- = markdown(@issue.description, cache_key: [@issue, "description"])
- %textarea.hidden.js-task-list-field
- = @issue.description
+ .merge-requests
+ = render 'merge_requests'
+
+ .gray-content-block.second-block.oneline-block
+ = render 'votes/votes_block', votable: @issue
- .merge-requests
- = render 'merge_requests'
+ - if @closed_by_merge_requests.present?
+ = render 'projects/issues/closed_by_box'
- - if @closed_by_merge_requests.present?
- = render 'projects/issues/closed_by_box'
- .issue-discussion
+ .row
+ %section.col-md-9
+ .issuable-discussion
= render 'projects/issues/discussion'
%aside.col-md-3
diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml
index 7a7428d35cc..399e9cc1e1b 100644
--- a/app/views/projects/merge_requests/_discussion.html.haml
+++ b/app/views/projects/merge_requests/_discussion.html.haml
@@ -5,7 +5,4 @@
- if @merge_request.closed?
= link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request"
-.gray-content-block.second-block.oneline-block
- = render 'votes/votes_block', votable: @merge_request
-
#notes= render "projects/notes/notes_with_form"
diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml
index 4172d5a4e88..a14943b15d3 100644
--- a/app/views/projects/merge_requests/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/_new_submit.html.haml
@@ -23,15 +23,15 @@
= 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: '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
+ %li.diffs-tab.active
+ = link_to url_for(params), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do
+ Changes
+ %span.badge= @diffs.size
.tab-content
#commits.commits.tab-pane
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index 04f8fd74422..e9ffbd06be2 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -5,81 +5,84 @@
- fluid_layout true
.merge-request{'data-url' => merge_request_path(@merge_request)}
- .merge-request-details.issuable-details
- = render "projects/merge_requests/show/mr_title"
- .row
- %section.col-md-9
- = render "projects/merge_requests/show/mr_box"
- .append-bottom-default.mr-source-target.prepend-top-default
- - if @merge_request.open?
- .pull-right
- - if @merge_request.source_branch_exists?
- = link_to "#modal_merge_info", class: "btn btn-sm", "data-toggle" => "modal" do
- = icon('cloud-download fw')
- Check out branch
+ = render "projects/merge_requests/show/mr_title"
- %span.dropdown
- %a.btn.btn-sm.dropdown-toggle{ data: {toggle: :dropdown} }
- = icon('download')
- Download as
- %span.caret
- %ul.dropdown-menu
- %li= link_to "Email Patches", merge_request_path(@merge_request, format: :patch)
- %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 into
- = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do
- = @merge_request.target_branch
+ .merge-request-details.issuable-details
+ = render "projects/merge_requests/show/mr_box"
+ .append-bottom-default.mr-source-target.prepend-top-default
+ - if @merge_request.open?
+ .pull-right
+ - if @merge_request.source_branch_exists?
+ = link_to "#modal_merge_info", class: "btn btn-sm", "data-toggle" => "modal" do
+ = icon('cloud-download fw')
+ Check out branch
- = render "projects/merge_requests/show/how_to_merge"
- = render "projects/merge_requests/widget/show.html.haml"
+ %span.dropdown
+ %a.btn.btn-sm.dropdown-toggle{ data: {toggle: :dropdown} }
+ = icon('download')
+ Download as
+ %span.caret
+ %ul.dropdown-menu
+ %li= link_to "Email Patches", merge_request_path(@merge_request, format: :patch)
+ %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 into
+ = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do
+ = @merge_request.target_branch
- - if @merge_request.open? && @merge_request.source_branch_exists? && @merge_request.can_be_merged? && @merge_request.can_be_merged_by?(current_user)
- .light.prepend-top-default
- You can also accept this merge request manually using the
- = succeed '.' do
- = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
+ = render "projects/merge_requests/show/how_to_merge"
+ = render "projects/merge_requests/widget/show.html.haml"
- - 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: '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: '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: '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
+ - if @merge_request.open? && @merge_request.source_branch_exists? && @merge_request.can_be_merged? && @merge_request.can_be_merged_by?(current_user)
+ .light.prepend-top-default
+ You can also accept this merge request manually using the
+ = succeed '.' do
+ = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
- .tab-content
- #notes.notes.tab-pane.voting_notes
- = render "projects/merge_requests/discussion"
- #commits.commits.tab-pane
- - # 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
+ - 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: '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: 'div#commits', action: 'commits', toggle: 'tab'} do
+ Commits
+ %span.badge= @commits.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
+ %li.diffs-tab
+ = 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
- .mr-loading-status
- = spinner
+ .tab-content
+ #notes.notes.tab-pane.voting_notes
+ .gray-content-block.second-block.oneline-block
+ = render 'votes/votes_block', votable: @merge_request
- %aside.col-md-3
- = render 'shared/issuable/sidebar', issuable: @merge_request
+ .row
+ %section.col-md-9
+ .issuable-discussion
+ = render "projects/merge_requests/discussion"
+ %aside.col-md-3
+ = render 'shared/issuable/sidebar', issuable: @merge_request
+ = render 'shared/show_aside'
- = render 'shared/show_aside'
+ #commits.commits.tab-pane
+ - # This tab is always loaded via AJAX
+ #builds.builds.tab-pane
+ - # This tab is always loaded via AJAX
+ #diffs.diffs.tab-pane
+ - # This tab is always loaded via AJAX
+ .mr-loading-status
+ = spinner
:javascript
var merge_request;
diff --git a/app/views/projects/merge_requests/show/_how_to_merge.html.haml b/app/views/projects/merge_requests/show/_how_to_merge.html.haml
index 98f0357ce4e..877cc3d744b 100644
--- a/app/views/projects/merge_requests/show/_how_to_merge.html.haml
+++ b/app/views/projects/merge_requests/show/_how_to_merge.html.haml
@@ -8,8 +8,8 @@
%p
%strong Step 1.
Fetch and check out the branch for this merge request
- = clipboard_button
- %pre.dark
+ = clipboard_button(clipboard_target: 'pre#merge-info-1')
+ %pre.dark#merge-info-1
- if @merge_request.for_fork?
:preserve
git fetch #{h @merge_request.source_project.http_url_to_repo} #{h @merge_request.source_branch}
@@ -25,8 +25,8 @@
%p
%strong Step 3.
Merge the branch and fix any conflicts that come up
- = clipboard_button
- %pre.dark
+ = clipboard_button(clipboard_target: 'pre#merge-info-3')
+ %pre.dark#merge-info-3
- if @merge_request.for_fork?
:preserve
git checkout #{h @merge_request.target_branch}
@@ -38,8 +38,8 @@
%p
%strong Step 4.
Push the result of the merge to GitLab
- = clipboard_button
- %pre.dark
+ = clipboard_button(clipboard_target: 'pre#merge-info-4')
+ %pre.dark#merge-info-4
:preserve
git push origin #{h @merge_request.target_branch}
- unless @merge_request.can_be_merged_by?(current_user)
diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml
index 9bfe202589e..0f81e5e8914 100644
--- a/app/views/projects/merge_requests/show/_mr_box.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_box.html.haml
@@ -1,5 +1,5 @@
-.gray-content-block.middle-block
- %h2.issue-title
+.detail-page-description.gray-content-block.second-block
+ %h2.title
= markdown escape_once(@merge_request.title), pipeline: :single_line
%div
diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml
index d65c3b16618..473124480ac 100644
--- a/app/views/projects/merge_requests/show/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_title.html.haml
@@ -1,7 +1,8 @@
-.issuable-title
- .issue-box{ class: issue_box_class(@merge_request) }
+.detail-page-header
+ .status-box{ class: status_box_class(@merge_request) }
= @merge_request.state_human_name
- %span.issuable-id Merge Request ##{@merge_request.iid}
+ %span.identifier
+ Merge Request ##{@merge_request.iid}
%span.creator
&middot;
opened by #{link_to_member(@project, @merge_request.author, size: 24)}
@@ -16,9 +17,9 @@
.issue-btn-group.pull-right
- if can?(current_user, :update_merge_request, @merge_request)
- if @merge_request.open?
- = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: "btn btn-grouped btn-close", title: "Close merge request"
- = link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "btn btn-grouped issuable-edit", id: "edit_merge_request" do
+ = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: 'btn btn-grouped btn-close', title: 'Close merge request'
+ = link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-grouped issuable-edit', id: 'edit_merge_request' do
%i.fa.fa-pencil-square-o
Edit
- if @merge_request.closed?
- = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link", title: "Close merge request"
+ = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: 'btn btn-grouped btn-reopen reopen-mr-link', title: 'Reopen merge request'
diff --git a/app/views/projects/merge_requests/widget/_merged.html.haml b/app/views/projects/merge_requests/widget/_merged.html.haml
index 6f52c963a53..d1d602eecdc 100644
--- a/app/views/projects/merge_requests/widget/_merged.html.haml
+++ b/app/views/projects/merge_requests/widget/_merged.html.haml
@@ -8,19 +8,15 @@
#{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
%div
- if !@merge_request.source_branch_exists? || (params[:delete_source] == 'true')
- = succeed '.' do
- The changes were merged into
- = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do
- = @merge_request.target_branch
+ The changes were merged into
+ #{link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch"}.
The source branch has been removed.
- elsif @merge_request.can_remove_source_branch?(current_user)
.remove_source_branch_widget
%p
- = succeed '.' do
- The changes were merged into
- = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do
- = @merge_request.target_branch
+ The changes were merged into
+ #{link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch"}.
You can remove the source branch now.
= link_to namespace_project_branch_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request.source_branch), remote: true, method: :delete, class: "btn btn-primary btn-sm remove_source_branch" do
%i.fa.fa-times
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 c6bc4ca5beb..d9a1730a8bc 100644
--- a/app/views/projects/merge_requests/widget/open/_accept.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml
@@ -7,13 +7,13 @@
.accept-action
- if @ci_commit && @ci_commit.active?
%span.btn-group
- = link_to "#", class: "btn btn-create merge_when_build_succeeds" do
+ = button_tag class: "btn btn-create js-merge-button merge_when_build_succeeds" do
Merge When Build Succeeds
- %a.btn.btn-success.dropdown-toggle{ 'data-toggle' => 'dropdown' }
+ = button_tag class: "btn btn-success dropdown-toggle", 'data-toggle' => 'dropdown' do
%span.caret
%span.sr-only
Select Merge Moment
- %ul.dropdown-menu.dropdown-menu-right{ role: 'menu' }
+ %ul.js-merge-dropdown.dropdown-menu.dropdown-menu-right{ role: 'menu' }
%li
= link_to "#", class: "merge_when_build_succeeds" do
= icon('check fw')
@@ -23,7 +23,7 @@
= icon('warning fw')
Merge Immediately
- else
- = f.button class: "btn btn-create btn-grouped accept_merge_request #{status_class}" do
+ = f.button class: "btn btn-create btn-grouped js-merge-button accept_merge_request #{status_class}" do
Accept Merge Request
- if @merge_request.can_remove_source_branch?(current_user)
.accept-control.checkbox
@@ -42,21 +42,19 @@
= hidden_field_tag :merge_when_build_succeeds, "", autocomplete: "off"
:javascript
- $('.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();
+ $('.accept_merge_request').on('click', function() {
+ $('.js-merge-button').html("<i class='fa fa-spinner fa-spin'></i> Merge in progress");
});
- $('a.merge_when_build_succeeds').on('click', function(e) {
- e.preventDefault();
+ $('.merge_when_build_succeeds').on('click', function() {
$("#merge_when_build_succeeds").val("1");
+ });
+
+ $('.js-merge-dropdown a').on('click', function(e) {
+ e.preventDefault();
$(this).closest("form").submit();
});
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index 7ecee440337..7e73ae274e9 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -1,44 +1,44 @@
- page_title @milestone.title, "Milestones"
= render "header_title"
-.issuable-details
- .page-title
- .issue-box{ class: issue_box_class(@milestone) }
- - if @milestone.closed?
- Closed
- - elsif @milestone.expired?
- Expired
- - else
- Open
+.detail-page-header
+ .status-box{ class: status_box_class(@milestone) }
+ - if @milestone.closed?
+ Closed
+ - elsif @milestone.expired?
+ Expired
+ - else
+ Open
+ %span.identifier
Milestone ##{@milestone.iid}
- - if @milestone.expires_at
- %span.creator
- &middot;
- = @milestone.expires_at
- .pull-right
- - if can?(current_user, :admin_milestone, @project)
- = link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped" do
- %i.fa.fa-pencil-square-o
- Edit
+ - if @milestone.expires_at
+ %span.creator
+ &middot;
+ = @milestone.expires_at
+ .pull-right
+ - if can?(current_user, :admin_milestone, @project)
+ - if @milestone.active?
+ = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-grouped"
+ - else
+ = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped"
- - if @milestone.active?
- = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-grouped"
- - else
- = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped"
+ = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-remove" do
+ %i.fa.fa-trash-o
+ Delete
- = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-remove" do
- %i.fa.fa-trash-o
- Delete
+ = link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped" do
+ %i.fa.fa-pencil-square-o
+ Edit
- .gray-content-block.middle-block
- %h2.issue-title
- = markdown escape_once(@milestone.title), pipeline: :single_line
- %div
- - if @milestone.description.present?
- .description
- .wiki
- = preserve do
- = markdown @milestone.description
+.detail-page-description.gray-content-block.second-block
+ %h2.title
+ = markdown escape_once(@milestone.title), pipeline: :single_line
+ %div
+ - if @milestone.description.present?
+ .description
+ .wiki
+ = preserve do
+ = markdown @milestone.description
- if @milestone.issues.any? && @milestone.can_be_closed?
.alert.alert-success.prepend-top-default
diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml
index 99c1b0fa43e..eb378b42603 100644
--- a/app/views/projects/notes/_notes_with_form.html.haml
+++ b/app/views/projects/notes/_notes_with_form.html.haml
@@ -7,4 +7,4 @@
= render "projects/notes/form", view: diff_view
:javascript
- new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{diff_view}")
+ var notes = new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{diff_view}")
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index cefe33e581f..89b072cea92 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -20,16 +20,24 @@
%li
= link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'Create file', id: 'new-file-link' do
= icon('pencil fw')
- Create file
+ New file
%li
= link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do
= icon('file fw')
Upload file
- %li.divider
%li
= link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
= icon('folder fw')
New directory
+ %li.divider
+ %li
+ = link_to new_namespace_project_branch_path(@project.namespace, @project) do
+ = icon('code-fork fw')
+ New branch
+ %li
+ = link_to new_namespace_project_tag_path(@project.namespace, @project) do
+ = icon('tags fw')
+ New tag
- 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'}}
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index 91ccd1ef660..90dc0062481 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -19,7 +19,7 @@
- else
Start the title with <code>[WIP]</code> or <code>WIP:</code> to prevent a
<strong>Work In Progress</strong> merge request from being merged before it's ready.
-.form-group.issuable-description
+.form-group.detail-page-description
= f.label :description, 'Description', class: 'control-label'
.col-sm-10
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 0019f739b89..79c5cc7f40a 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -1,13 +1,5 @@
.issuable-sidebar.issuable-affix
= form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f|
- .block
- .title
- Cross-project reference
- .cross-project-reference
- %span#cross-project-reference
- = cross_project_reference(@project, issuable)
- = clipboard_button(clipboard_target: 'span#cross-project-reference')
-
.block.assignee
.title
%label
@@ -62,6 +54,14 @@
= f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
{ selected: issuable.label_ids }, multiple: true, class: 'select2 js-select2', data: { placeholder: "Select labels" }
+ .block
+ .title
+ Cross-project reference
+ .cross-project-reference
+ %span#cross-project-reference
+ = cross_project_reference(@project, issuable)
+ = clipboard_button(clipboard_target: 'span#cross-project-reference')
+
= render "shared/issuable/participants", participants: issuable.participants(current_user)
- if current_user
diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml
index 669e6119fb6..aa5acee9c14 100644
--- a/app/views/shared/snippets/_header.html.haml
+++ b/app/views/shared/snippets/_header.html.haml
@@ -1,25 +1,25 @@
-.issuable-details
- .page-title
- .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)
+.detail-page-header
+ .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)
+ %span.identifier
Snippet ##{@snippet.id}
- %span.creator
- &middot; created by #{link_to_member(@project, @snippet.author, size: 24)}
- &middot;
- = time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago')
- - if @snippet.updated_at != @snippet.created_at
- %span
- &middot;
- = icon('edit', title: 'edited')
- = time_ago_with_tooltip(@snippet.updated_at, placement: 'bottom', html_class: 'snippet_edited_ago')
+ %span.creator
+ &middot; created by #{link_to_member(@project, @snippet.author, size: 24)}
+ &middot;
+ = time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago')
+ - if @snippet.updated_at != @snippet.created_at
+ %span
+ &middot;
+ = icon('edit', title: 'edited')
+ = time_ago_with_tooltip(@snippet.updated_at, placement: 'bottom', html_class: 'snippet_edited_ago')
- .pull-right
- - if @snippet.project_id?
- = render "projects/snippets/actions"
- - else
- = render "snippets/actions"
+ .pull-right
+ - if @snippet.project_id?
+ = render "projects/snippets/actions"
+ - else
+ = render "snippets/actions"
- .gray-content-block.middle-block
- %h2.issue-title
- = markdown escape_once(@snippet.title), pipeline: :single_line
+.detail-page-description.gray-content-block.second-block
+ %h2.title
+ = markdown escape_once(@snippet.title), pipeline: :single_line
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index a0a6e2d9810..b7a7eb4e6f7 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -73,7 +73,7 @@
.user-calendar-activities
-%ul.center-top-menu.no-top.no-bottom.bottom-border
+%ul.center-top-menu.no-top.no-bottom.bottom-border.wide
%li.active
= link_to "#activity", 'data-toggle' => 'tab' do
Activity
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index db378118f85..fcf034d7911 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -287,6 +287,15 @@ production: &base
# arguments, followed by optional 'args' which can be either a hash or an array.
# Documentation for this is available at http://doc.gitlab.com/ce/integration/omniauth.html
providers:
+ # See omniauth-cas3 for more configuration details
+ # - { name: 'cas3',
+ # label: 'cas3',
+ # args: {
+ # url: 'https://sso.example.com',
+ # disable_ssl_verification: false,
+ # login_url: '/cas/login',
+ # service_validate_url: '/cas/p3/serviceValidate',
+ # logout_url: '/cas/logout'} }
# - { name: 'github',
# app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET',
@@ -324,6 +333,10 @@ production: &base
# application_name: 'YOUR_APP_NAME',
# application_password: 'YOUR_APP_PASSWORD' } }
+ # SSO maximum session duration in seconds. Defaults to CAS default of 8 hours.
+ # cas3:
+ # session_duration: 28800
+
# Shared file storage settings
shared:
# path: /mnt/gitlab # Default: shared
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 63d8ae17436..1043fc01ab1 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -126,6 +126,10 @@ Settings.omniauth['block_auto_created_users'] = true if Settings.omniauth['block
Settings.omniauth['auto_link_ldap_user'] = false if Settings.omniauth['auto_link_ldap_user'].nil?
Settings.omniauth['providers'] ||= []
+Settings.omniauth['cas3'] ||= Settingslogic.new({})
+Settings.omniauth.cas3['session_duration'] ||= 8.hours
+Settings.omniauth['session_tickets'] ||= Settingslogic.new({})
+Settings.omniauth.session_tickets['cas3'] = 'ticket'
Settings['shared'] ||= Settingslogic.new({})
Settings.shared['path'] = File.expand_path(Settings.shared['path'] || "shared", Rails.root)
@@ -164,7 +168,7 @@ Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].
Settings.gitlab['twitter_sharing_enabled'] ||= true if Settings.gitlab['twitter_sharing_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
-Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)' if Settings.gitlab['issue_closing_pattern'].nil?
+Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z]*-\d*))+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab['webhook_timeout'] ||= 10
Settings.gitlab['max_attachment_size'] ||= 10
diff --git a/config/initializers/carrierwave.rb b/config/initializers/carrierwave.rb
index bfb8656df55..df28d30d750 100644
--- a/config/initializers/carrierwave.rb
+++ b/config/initializers/carrierwave.rb
@@ -31,11 +31,11 @@ if File.exists?(aws_file)
if Rails.env.test?
Fog.mock!
connection = ::Fog::Storage.new(
- aws_access_key_id: AWS_CONFIG['access_key_id'],
- aws_secret_access_key: AWS_CONFIG['secret_access_key'],
- provider: 'AWS',
- region: AWS_CONFIG['region']
- )
+ aws_access_key_id: AWS_CONFIG['access_key_id'],
+ aws_secret_access_key: AWS_CONFIG['secret_access_key'],
+ provider: 'AWS',
+ region: AWS_CONFIG['region']
+ )
connection.directories.create(key: AWS_CONFIG['bucket'])
end
end
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index 5fb43a86e13..d82cfb3ec0c 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -121,14 +121,14 @@ Devise.setup do |config|
config.lock_strategy = :failed_attempts
# Defines which key will be used when locking and unlocking an account
- # config.unlock_keys = [ :email ]
+ config.unlock_keys = [ :email ]
# Defines which strategy will be used to unlock an account.
# :email = Sends an unlock link to the user email
# :time = Re-enables login after a certain amount of time (see :unlock_in below)
# :both = Enables both strategies
# :none = No unlock strategy. You should handle unlocking by yourself.
- config.unlock_strategy = :time
+ config.unlock_strategy = :both
# Number of authentication tries before locking an account if lock_strategy
# is failed attempts.
@@ -241,6 +241,16 @@ Devise.setup do |config|
# An Array from the configuration will be expanded.
provider_arguments.concat provider['args']
when Hash
+ # Add procs for handling SLO
+ if provider['name'] == 'cas3'
+ provider['args'][:on_single_sign_out] = lambda do |request|
+ ticket = request.params[:session_index]
+ raise "Service Ticket not found." unless Gitlab::OAuth::Session.valid?(:cas3, ticket)
+ Gitlab::OAuth::Session.destroy(:cas3, ticket)
+ true
+ end
+ end
+
# A Hash from the configuration will be passed as is.
provider_arguments << provider['args'].symbolize_keys
end
diff --git a/config/routes.rb b/config/routes.rb
index 9900329a315..b9242327de1 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -441,7 +441,7 @@ Rails.application.routes.draw do
scope do
post(
- '/create_dir/*id',
+ '/create_dir/*id',
to: 'tree#create_dir',
constraints: { id: /.+/ },
as: 'create_dir'
diff --git a/db/migrate/20151012173029_set_jira_service_api_url.rb b/db/migrate/20151012173029_set_jira_service_api_url.rb
new file mode 100644
index 00000000000..2af99e0db0b
--- /dev/null
+++ b/db/migrate/20151012173029_set_jira_service_api_url.rb
@@ -0,0 +1,50 @@
+class SetJiraServiceApiUrl < ActiveRecord::Migration
+ # This migration can be performed online without errors, but some Jira API calls may be missed
+ # when doing so because api_url is not yet available.
+
+ def build_api_url_from_project_url(project_url, api_version)
+ # this is the exact logic previously used to build the Jira API URL from project_url
+ server = URI(project_url)
+ default_ports = [80, 443].include?(server.port)
+ server_url = "#{server.scheme}://#{server.host}"
+ server_url.concat(":#{server.port}") unless default_ports
+ "#{server_url}/rest/api/#{api_version}"
+ end
+
+ def get_api_version_from_api_url(api_url)
+ match = /\/rest\/api\/(?<api_version>\w+)$/.match(api_url)
+ match && match['api_version']
+ end
+
+ def change
+ reversible do |dir|
+ select_all("SELECT id, properties FROM services WHERE services.type IN ('JiraService')").each do |jira_service|
+ id = jira_service["id"]
+ properties = JSON.parse(jira_service["properties"])
+ properties_was = properties.clone
+
+ dir.up do
+ # remove api_version and set api_url
+ if properties['api_version'].present? && properties['project_url'].present?
+ begin
+ properties['api_url'] ||= build_api_url_from_project_url(properties['project_url'], properties['api_version'])
+ rescue
+ # looks like project_url was not a valid URL. Do nothing.
+ end
+ end
+ properties.delete('api_version') if properties.include?('api_version')
+ end
+
+ dir.down do
+ # remove api_url and set api_version (default to '2')
+ properties['api_version'] ||= get_api_version_from_api_url(properties['api_url']) || '2'
+ properties.delete('api_url') if properties.include?('api_url')
+ end
+
+ if properties != properties_was
+ execute("UPDATE services SET properties = '#{quote_string(properties.to_json)}' WHERE id = #{id}")
+ end
+ end
+ end
+ end
+end
diff --git a/db/migrate/20151210030143_add_unlock_token_to_user.rb b/db/migrate/20151210030143_add_unlock_token_to_user.rb
new file mode 100644
index 00000000000..0ea66ba65df
--- /dev/null
+++ b/db/migrate/20151210030143_add_unlock_token_to_user.rb
@@ -0,0 +1,5 @@
+class AddUnlockTokenToUser < ActiveRecord::Migration
+ def change
+ add_column :users, :unlock_token, :string
+ end
+end
diff --git a/db/migrate/20151210125930_migrate_ci_to_project.rb b/db/migrate/20151210125930_migrate_ci_to_project.rb
index 7dfe05174ee..75278997862 100644
--- a/db/migrate/20151210125930_migrate_ci_to_project.rb
+++ b/db/migrate/20151210125930_migrate_ci_to_project.rb
@@ -26,7 +26,8 @@ class MigrateCiToProject < ActiveRecord::Migration
def migrate_project_column(column, new_column = nil)
new_column ||= column
- subquery = "SELECT ci_projects.#{column} FROM ci_projects WHERE projects.id = ci_projects.gitlab_id"
+ subquery = "SELECT ci_projects.#{column} FROM ci_projects WHERE projects.id = ci_projects.gitlab_id " \
+ 'ORDER BY ci_projects.updated_at DESC LIMIT 1'
execute("UPDATE projects SET #{new_column}=(#{subquery}) WHERE (#{subquery}) IS NOT NULL")
end
diff --git a/db/schema.rb b/db/schema.rb
index 0167e30ff8b..60b42f7a473 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -837,6 +837,7 @@ ActiveRecord::Schema.define(version: 20151210125932) do
t.integer "consumed_timestep"
t.integer "layout", default: 0
t.boolean "hide_project_limit", default: false
+ t.string "unlock_token"
end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
diff --git a/doc/README.md b/doc/README.md
index a3098094210..8bac00f2f23 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -7,6 +7,7 @@
- [GitLab Basics](gitlab-basics/README.md) Find step by step how to start working on your commandline and on GitLab.
- [Importing to GitLab](workflow/importing/README.md).
- [Markdown](markdown/markdown.md) GitLab's advanced formatting system.
+- [Migrating from SVN](migration/README.md) Convert a SVN repository to Git and GitLab
- [Permissions](permissions/permissions.md) Learn what each role in a project (guest/reporter/developer/master/owner) can do.
- [Profile Settings](profile/README.md)
- [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat.
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 1a524400627..658e65c6f01 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -58,6 +58,7 @@ Parameters:
"path": "diaspora-client",
"path_with_namespace": "diaspora/diaspora-client",
"issues_enabled": true,
+ "open_issues_count": 1,
"merge_requests_enabled": true,
"builds_enabled": true,
"wiki_enabled": true,
@@ -100,6 +101,7 @@ Parameters:
"path": "puppet",
"path_with_namespace": "brightbox/puppet",
"issues_enabled": true,
+ "open_issues_count": 1,
"merge_requests_enabled": true,
"builds_enabled": true,
"wiki_enabled": true,
@@ -137,6 +139,21 @@ Parameters:
- `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
+### List starred projects
+
+Get a list of projects which are starred by the authenticated user.
+
+```
+GET /projects/starred
+```
+
+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
+
### List ALL projects
Get a list of all GitLab projects (admin only).
@@ -189,6 +206,7 @@ Parameters:
"path": "diaspora-project-site",
"path_with_namespace": "diaspora/diaspora-project-site",
"issues_enabled": true,
+ "open_issues_count": 1,
"merge_requests_enabled": true,
"builds_enabled": true,
"wiki_enabled": true,
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index 8d4bd44053e..31458d61674 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -1,11 +1,11 @@
# Using Docker Images
-GitLab CI in conjuction with [GitLab Runner](../runners/README.md) can use
+GitLab CI in conjunction with [GitLab Runner](../runners/README.md) can use
[Docker Engine](https://www.docker.com/) to test and build any application.
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
+instance. [Docker Hub][hub] has a rich database of pre-built images that can be
used to test and build your applications.
Docker, when used with GitLab CI, runs each build in a separate and isolated
@@ -136,6 +136,24 @@ Look for the `[runners.docker]` section:
The image and services defined this way will be added to all builds run by
that runner.
+## Define an image from a private Docker registry
+
+Starting with GitLab Runner 0.6.0, you are able to define images located to
+private registries that could also require authentication.
+
+All you have to do is be explicit on the image definition in `.gitlab-ci.yml`.
+
+```yaml
+image: my.registry.tld:5000/namepace/image:tag
+```
+
+In the example above, GitLab Runner will look at `my.registry.tld:5000` for the
+image `namespace/image:tag`.
+
+If the repository is private you need to authenticate your GitLab Runner in the
+registry. Learn how to do that on
+[GitLab Runner's documentation][runner-priv-reg].
+
## Accessing the services
Let's say that you need a Wordpress instance to test some API integration with
@@ -258,3 +276,4 @@ creation.
[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/
+[runner-priv-reg]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/configuration/advanced-configuration.md#using-a-private-docker-registry
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 022afb70042..b99ea25a3fe 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -27,7 +27,6 @@ The API_TOKEN will take the Secure Variable value: `SECURE`.
| **CI_BUILD_TAG** | 0.5 | The commit tag name. Present only when building tags. |
| **CI_BUILD_NAME** | 0.5 | The name of the build as defined in `.gitlab-ci.yml` |
| **CI_BUILD_STAGE** | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` |
-| **CI_BUILD_BEFORE_SHA** | all | The first commit that were included in push request |
| **CI_BUILD_REF_NAME** | all | The branch or tag name for which project is built |
| **CI_BUILD_ID** | all | The unique id of the current build that GitLab CI uses internally |
| **CI_BUILD_REPO** | all | The URL to clone the Git repository |
@@ -40,7 +39,6 @@ The API_TOKEN will take the Secure Variable value: `SECURE`.
Example values:
```bash
-export CI_BUILD_BEFORE_SHA="9df57456fa9de2a6d335ca5edf9750ed812b9df0"
export CI_BUILD_ID="50"
export CI_BUILD_REF="1ecfd275763eff1d6b4844ea3168962458c9f27a"
export CI_BUILD_REF_NAME="master"
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 7e2edb945da..fd0d49de4e4 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -1,9 +1,12 @@
# Configuration of your builds with .gitlab-ci.yml
-From version 7.12, GitLab CI uses a [YAML](https://en.wikipedia.org/wiki/YAML) file (**.gitlab-ci.yml**) for the project configuration.
-It is placed in the root of your repository and contains definitions of how your project should be built.
-The YAML file defines a set of jobs with constraints stating when they should be run.
-The jobs are defined as top-level elements with a name and always have to contain the `script` clause:
+From version 7.12, GitLab CI uses a [YAML](https://en.wikipedia.org/wiki/YAML)
+file (`.gitlab-ci.yml`) for the project configuration. It is placed in the root
+of your repository and contains definitions of how your project should be built.
+
+The YAML file defines a set of jobs with constraints stating when they should
+be run. The jobs are defined as top-level elements with a name and always have
+to contain the `script` clause:
```yaml
job1:
@@ -13,15 +16,21 @@ job2:
script: "execute-script-for-job2"
```
-The above example is the simplest possible CI configuration with two separate jobs,
-where each of the jobs executes a different command.
-Of course a command can execute code directly (`./configure;make;make install`) or run a script (`test.sh`) in the repository.
+The above example is the simplest possible CI configuration with two separate
+jobs, where each of the jobs executes a different command.
+
+Of course a command can execute code directly (`./configure;make;make install`)
+or run a script (`test.sh`) in the repository.
-Jobs are used to create builds, which are then picked up by [runners](../runners/README.md) and executed within the environment of the runner.
-What is important, is that each job is run independently from each other.
+Jobs are used to create builds, which are then picked up by
+[runners](../runners/README.md) and executed within the environment of the
+runner. What is important, is that each job is run independently from each
+other.
## .gitlab-ci.yml
-The YAML syntax allows for using more complex job specifications than in the above example:
+
+The YAML syntax allows for using more complex job specifications than in the
+above example:
```yaml
image: ruby:2.1
@@ -46,26 +55,31 @@ job1:
- docker
```
-There are a few `keywords` that can't be used as job names:
+There are a few reserved `keywords` that **cannot** be used as job names:
-| keyword | required | description |
+| Keyword | Required | Description |
|---------------|----------|-------------|
-| image | optional | Use docker image, covered in [Use Docker](../docker/README.md) |
-| services | optional | Use docker services, covered in [Use Docker](../docker/README.md) |
-| stages | optional | Define build stages |
-| types | optional | Alias for `stages` |
-| before_script | optional | Define commands prepended for each job's script |
-| variables | optional | Define build variables |
-| cache | optional | Define list of files that should be cached between subsequent runs |
+| image | no | Use docker image, covered in [Use Docker](../docker/README.md) |
+| services | no | Use docker services, covered in [Use Docker](../docker/README.md) |
+| stages | no | Define build stages |
+| types | no | Alias for `stages` |
+| before_script | no | Define commands that run before each job's script |
+| variables | no | Define build variables |
+| cache | no | Define list of files that should be cached between subsequent runs |
### image and services
-This allows to specify a custom Docker image and a list of services that can be used for time of the build.
-The configuration of this feature is covered in separate document: [Use Docker](../docker/README.md).
+
+This allows to specify a custom Docker image and a list of services that can be
+used for time of the build. The configuration of this feature is covered in
+separate document: [Use Docker](../docker/README.md).
### before_script
-`before_script` is used to define the command that should be run before all builds, including deploy builds. This can be an array or a multiline string.
+
+`before_script` is used to define the command that should be run before all
+builds, including deploy builds. This can be an array or a multi-line string.
### stages
+
`stages` is used to define build stages that can be used by jobs.
The specification of `stages` allows for having flexible multi stage pipelines.
@@ -75,7 +89,8 @@ The ordering of elements in `stages` defines the ordering of builds' execution:
1. Builds of next stage are run after success.
Let's consider the following example, which defines 3 stages:
-```
+
+```yaml
stages:
- build
- test
@@ -86,21 +101,26 @@ stages:
1. If all jobs of `build` succeeds, the `test` jobs are executed in parallel.
1. If all jobs of `test` succeeds, the `deploy` jobs are executed in parallel.
1. If all jobs of `deploy` succeeds, the commit is marked as `success`.
-1. If any of the previous jobs fails, the commit is marked as `failed` and no jobs of further stage are executed.
+1. If any of the previous jobs fails, the commit is marked as `failed` and no
+ jobs of further stage are executed.
There are also two edge cases worth mentioning:
-1. If no `stages` is defined in `.gitlab-ci.yml`, then by default the `build`, `test` and `deploy` are allowed to be used as job's stage by default.
+1. If no `stages` is defined in `.gitlab-ci.yml`, then by default the `build`,
+ `test` and `deploy` are allowed to be used as job's stage by default.
2. If a job doesn't specify `stage`, the job is assigned the `test` stage.
### types
+
Alias for [stages](#stages).
### variables
-**This feature requires `gitlab-runner` with version equal or greater than 0.5.0.**
-GitLab CI allows you to add to `.gitlab-ci.yml` variables that are set in build environment.
-The variables are stored in repository and are meant to store non-sensitive project configuration, ie. RAILS_ENV or DATABASE_URL.
+_**Note:** Introduced in GitLab Runner v0.5.0._
+
+GitLab CI allows you to add to `.gitlab-ci.yml` variables that are set in build
+environment. The variables are stored in the git repository and are meant to
+store non-sensitive project configuration, for example:
```yaml
variables:
@@ -109,18 +129,23 @@ variables:
These variables can be later used in all executed commands and scripts.
-The YAML-defined variables are also set to all created service containers, thus allowing to fine tune them.
+The YAML-defined variables are also set to all created service containers,
+thus allowing to fine tune them.
### cache
-`cache` is used to specify list of files and directories which should be cached between builds.
-Caches are stored according to the branch/ref and the job name. Caches are not
-currently shared between different job names or between branches/refs. This means
-caching will benefit you if you push subsequent commits to an existing feature branch.
-**The global setting allows to specify default cached files for all jobs.**
+`cache` is used to specify a list of files and directories which should be
+cached between builds. Caches are stored according to the branch/ref and the
+job name. They are not currently shared between different job names or between
+branches/refs, which means that caching will benefit you if you push subsequent
+commits to an existing feature branch.
+
+If `cache` is defined outside the scope of the jobs, it means it is set
+globally and all jobs will use its definition.
To cache all git untracked files and files in `binaries`:
-```
+
+```yaml
cache:
untracked: true
paths:
@@ -128,9 +153,10 @@ cache:
```
## Jobs
-`.gitlab-ci.yml` allows you to specify an unlimited number of jobs.
-Each job has to have a unique `job_name`, which is not one of the keywords mentioned above.
-A job is defined by a list of parameters that define the build behaviour.
+
+`.gitlab-ci.yml` allows you to specify an unlimited number of jobs. Each job
+must have a unique name, which is not one of the Keywords mentioned above.
+A job is defined by a list of parameters that define the build behavior.
```yaml
job_name:
@@ -148,21 +174,22 @@ job_name:
allow_failure: true
```
-| keyword | required | description |
+| Keyword | Required | Description |
|---------------|----------|-------------|
-| script | required | Defines a shell script which is executed by runner |
-| stage | optional (default: test) | Defines a build stage |
-| type | optional | Alias for `stage` |
-| only | optional | Defines a list of git refs for which build is created |
-| except | optional | Defines a list of git refs for which build is not created |
-| tags | optional | Defines a list of tags which are used to select runner |
-| allow_failure | optional | Allow build to fail. Failed build doesn't contribute to commit status |
-| when | optional | Define when to run build. Can be `on_success`, `on_failure` or `always` |
-| artifacts | optional | Define list build artifacts |
-| cache | optional | Define list of files that should be cached between subsequent runs |
+| script | yes | Defines a shell script which is executed by runner |
+| stage | no (default: `test`) | Defines a build stage |
+| type | no | Alias for `stage` |
+| only | no | Defines a list of git refs for which build is created |
+| except | no | Defines a list of git refs for which build is not created |
+| tags | no | Defines a list of tags which are used to select runner |
+| allow_failure | no | Allow build to fail. Failed build doesn't contribute to commit status |
+| when | no | Define when to run build. Can be `on_success`, `on_failure` or `always` |
+| artifacts | no | Define list build artifacts |
+| cache | no | Define list of files that should be cached between subsequent runs |
### script
-`script` is a shell script which is executed by runner. The shell script is prepended with `before_script`.
+
+`script` is a shell script which is executed by the runner. For example:
```yaml
job:
@@ -170,6 +197,7 @@ job:
```
This parameter can also contain several commands using an array:
+
```yaml
job:
script:
@@ -178,31 +206,45 @@ job:
```
### stage
-`stage` allows to group build into different stages. Builds of the same `stage` are executed in `parallel`.
-For more info about the use of `stage` please check the [stages](#stages).
+
+`stage` allows to group build into different stages. Builds of the same `stage`
+are executed in `parallel`. For more info about the use of `stage` please check
+[stages](#stages).
### only and except
-This are two parameters that allow for setting a refs policy to limit when jobs are built:
-1. `only` defines the names of branches and tags for which job will be built.
-2. `except` defines the names of branches and tags for which the job wil **not** be built.
-There are a few rules that apply to usage of refs policy:
+`only` and `except` are two parameters that set a refs policy to limit when
+jobs are built:
-1. `only` and `except` are inclusive. If both `only` and `except` are defined in job specification the ref is filtered by `only` and `except`.
-1. `only` and `except` allow for using the regexp expressions.
-1. `only` and `except` allow for using special keywords: `branches` and `tags`.
-These names can be used for example to exclude all tags and all branches.
+1. `only` defines the names of branches and tags for which the job will be
+ built.
+2. `except` defines the names of branches and tags for which the job will
+ **not** be built.
+
+There are a few rules that apply to the usage of refs policy:
+
+* `only` and `except` are inclusive. If both `only` and `except` are defined
+ in a job specification, the ref is filtered by `only` and `except`.
+* `only` and `except` allow the use of regular expressions.
+* `only` and `except` allow the use of special keywords: `branches` and `tags`.
+* `only` and `except` allow to specify a repository path to filter jobs for
+ forks.
+
+In the example below, `job` will run only for refs that start with `issue-`,
+whereas all branches will be skipped.
```yaml
job:
+ # use regexp
only:
- - /^issue-.*$/ # use regexp
+ - /^issue-.*$/
+ # use special keyword
except:
- - branches # use special keyword
+ - branches
```
-1. `only` and `except` allow for specify repository path to filter jobs for forks.
-The repository path can be used to have jobs executed only for parent repository.
+The repository path can be used to have jobs executed only for the parent
+repository and not forks:
```yaml
job:
@@ -211,33 +253,47 @@ job:
except:
- master@gitlab-org/gitlab-ce
```
-The above will run `job` for all branches on `gitlab-org/gitlab-ce`, except master .
+
+The above example will run `job` for all branches on `gitlab-org/gitlab-ce`,
+except master.
### tags
-`tags` is used to select specific runners from the list of all runners that are allowed to run this project.
-During registration of a runner, you can specify the runner's tags, ie.: `ruby`, `postgres`, `development`.
-`tags` allow you to run builds with runners that have the specified tags assigned:
+`tags` is used to select specific runners from the list of all runners that are
+allowed to run this project.
-```
+During the registration of a runner, you can specify the runner's tags, for
+example `ruby`, `postgres`, `development`.
+
+`tags` allow you to run builds with runners that have the specified tags
+assigned to them:
+
+```yaml
job:
tags:
- ruby
- postgres
```
-The above specification will make sure that `job` is built by a runner that have `ruby` AND `postgres` tags defined.
+The specification above, will make sure that `job` is built by a runner that
+has both `ruby` AND `postgres` tags defined.
### when
-`when` is used to implement jobs that are run in case of failure or despite the failure.
+
+`when` is used to implement jobs that are run in case of failure or despite the
+failure.
`when` can be set to one of the following values:
-1. `on_success` - execute build only when all builds from prior stages succeeded. This is the default.
-1. `on_failure` - execute build only when at least one build from prior stages failed.
+1. `on_success` - execute build only when all builds from prior stages
+ succeeded. This is the default.
+1. `on_failure` - execute build only when at least one build from prior stages
+ failed.
1. `always` - execute build despite the status of builds from prior stages.
-```
+For example:
+
+```yaml
stages:
- build
- cleanup_build
@@ -245,28 +301,28 @@ stages:
- deploy
- cleanup
-build:
+build_job:
stage: build
script:
- make build
-cleanup_build:
+cleanup_build_job:
stage: cleanup_build
script:
- cleanup build when failed
when: on_failure
-test:
+test_job:
stage: test
script:
- make test
-deploy:
+deploy_job:
stage: deploy
script:
- make deploy
-cleanup:
+cleanup_job:
stage: cleanup
script:
- cleanup after builds
@@ -274,84 +330,108 @@ cleanup:
```
The above script will:
-1. Execute `cleanup_build` only when the `build` failed,
-2. Always execute `cleanup` as the last step in pipeline.
+
+1. Execute `cleanup_build_job` only when `build_job` fails
+2. Always execute `cleanup_job` as the last step in pipeline.
### artifacts
-`artifacts` is used to specify list of files and directories which should be attached to build after success.
-1. Send all files in `binaries` and `.config`:
+_**Note:** Introduced in GitLab Runner v0.7.0. Also, the Windows shell executor
+ does not currently support artifact uploads._
- artifacts:
- paths:
- - binaries/
- - .config
+`artifacts` is used to specify list of files and directories which should be
+attached to build after success. Below are some examples.
-2. Send all git untracked files:
+Send all files in `binaries` and `.config`:
- artifacts:
- untracked: true
+```yaml
+artifacts:
+ paths:
+ - binaries/
+ - .config
+```
-3. Send all git untracked files and files in `binaries`:
+Send all git untracked files:
- artifacts:
- untracked: true
- paths:
- - binaries/
+```yaml
+artifacts:
+ untracked: true
+```
+
+Send all git untracked files and files in `binaries`:
-The artifacts will be send after the build success to GitLab and will be accessible in GitLab interface to download.
+```yaml
+artifacts:
+ untracked: true
+ paths:
+ - binaries/
+```
-This feature requires GitLab Runner v0.7.0 or higher.
+The artifacts will be send after a successful build success to GitLab, and will
+be accessible in the GitLab UI to download.
### cache
-`cache` is used to specify list of files and directories which should be cached between builds.
-1. Cache all files in `binaries` and `.config`:
+_**Note:** Introduced in GitLab Runner v0.7.0._
- rspec:
- script: test
- cache:
- paths:
- - binaries/
- - .config
+`cache` is used to specify list of files and directories which should be cached
+between builds. Below are some examples:
-2. Cache all git untracked files:
+Cache all files in `binaries` and `.config`:
- rspec:
- script: test
- cache:
- untracked: true
-
-3. Cache all git untracked files and files in `binaries`:
+```yaml
+rspec:
+ script: test
+ cache:
+ paths:
+ - binaries/
+ - .config
+```
- rspec:
- script: test
- cache:
- untracked: true
- paths:
- - binaries/
+Cache all git untracked files:
-4. Locally defined cache overwrites globally defined options. This will cache only `binaries/`:
+```yaml
+rspec:
+ script: test
+ cache:
+ untracked: true
+```
+
+Cache all git untracked files and files in `binaries`:
+
+```yaml
+rspec:
+ script: test
+ cache:
+ untracked: true
+ paths:
+ - binaries/
+```
- cache:
- paths:
- - my/files
-
- rspec:
- script: test
- cache:
- paths:
- - binaries/
+Locally defined cache overwrites globally defined options. This will cache only
+`binaries/`:
-The cache is provided on best effort basis, so don't expect that cache will be present.
-For implementation details please check GitLab Runner.
+```yaml
+cache:
+ paths:
+ - my/files
-This feature requires GitLab Runner v0.7.0 or higher.
+rspec:
+ script: test
+ cache:
+ paths:
+ - binaries/
+```
+The cache is provided on best effort basis, so don't expect that cache will be
+always present. For implementation details please check GitLab Runner.
## Validate the .gitlab-ci.yml
+
Each instance of GitLab CI has an embedded debug tool called Lint.
-You can find the link to the Lint in the project's settings page or use short url `/lint`.
+You can find the link under `/ci/lint` of your gitlab instance.
## Skipping builds
-There is one more way to skip all builds, if your commit message contains tag [ci skip]. In this case, commit will be created but builds will be skipped
+
+If your commit message contains `[ci skip]`, the commit will be created but the
+builds will be skipped.
diff --git a/doc/install/installation.md b/doc/install/installation.md
index f8116a8a31c..81edd8da2b8 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -348,7 +348,7 @@ GitLab Shell is an SSH access and repository management software developed speci
cd /home/git
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
cd gitlab-workhorse
- sudo -u git -H git checkout 0.4.2
+ sudo -u git -H git checkout 0.5.1
sudo -u git -H make
### Initialize Database and Activate Advanced Features
diff --git a/doc/integration/README.md b/doc/integration/README.md
index eff39a626ae..6263353851f 100644
--- a/doc/integration/README.md
+++ b/doc/integration/README.md
@@ -4,10 +4,12 @@ GitLab integrates with multiple third-party services to allow external issue tra
See the documentation below for details on how to configure these services.
+- [Jira](jira.md) Integrate with the JIRA issue tracker
- [External issue tracker](external-issue-tracker.md) Redmine, JIRA, etc.
- [LDAP](ldap.md) Set up sign in via LDAP
- [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, GitLab, and Google via OAuth.
- [SAML](saml.md) Configure GitLab as a SAML 2.0 Service Provider
+- [CAS](cas.md) Configure GitLab to sign in using CAS
- [Slack](slack.md) Integrate with the Slack chat service
- [OAuth2 provider](oauth_provider.md) OAuth2 application creation
- [Gmail actions buttons](gmail_action_buttons_for_gitlab.md) Adds GitLab actions to messages
diff --git a/doc/integration/cas.md b/doc/integration/cas.md
new file mode 100644
index 00000000000..e6b2071f193
--- /dev/null
+++ b/doc/integration/cas.md
@@ -0,0 +1,62 @@
+# CAS OmniAuth Provider
+
+To enable the CAS OmniAuth provider you must register your application with your CAS instance. This requires the service URL GitLab will supply to CAS. It should be something like: `https://gitlab.example.com:443/users/auth/cas3/callback?url`. By default handling for SLO is enabled, you only need to configure CAS for backchannel logout.
+
+1. On your GitLab server, open the configuration file.
+
+ For omnibus package:
+
+ ```sh
+ sudo editor /etc/gitlab/gitlab.rb
+ ```
+
+ For installations from source:
+
+ ```sh
+ cd /home/git/gitlab
+
+ sudo -u git -H editor config/gitlab.yml
+ ```
+
+1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings.
+
+1. Add the provider configuration:
+
+ For omnibus package:
+
+ ```ruby
+ gitlab_rails['omniauth_providers'] = [
+ {
+ name: "cas3",
+ label: "cas",
+ args: {
+ url: 'CAS_SERVER',
+ login_url: '/CAS_PATH/login',
+ service_validate_url: '/CAS_PATH/p3/serviceValidate',
+ logout_url: '/CAS_PATH/logout'} }
+ }
+ }
+ ]
+ ```
+
+ For installations from source:
+
+ ```
+ - { name: 'cas3',
+ label: 'cas',
+ args: {
+ url: 'CAS_SERVER',
+ login_url: '/CAS_PATH/login',
+ service_validate_url: '/CAS_PATH/p3/serviceValidate',
+ logout_url: '/CAS_PATH/logout'} }
+ ```
+
+1. Change 'CAS_PATH' to the root of your CAS instance (ie. `cas`).
+
+1. If your CAS instance does not use default TGC lifetimes, update the `cas3.session_duration` to at least the current TGC maximum lifetime. To explicitly disable SLO, regardless of CAS settings, set this to 0.
+
+1. Save the configuration file.
+
+1. Restart GitLab for the changes to take effect.
+
+On the sign in page there should now be a CAS tab in the sign in form.
diff --git a/doc/integration/jira.md b/doc/integration/jira.md
new file mode 100644
index 00000000000..624601d0fac
--- /dev/null
+++ b/doc/integration/jira.md
@@ -0,0 +1,113 @@
+# GitLab Jira integration
+
+GitLab can be configured to interact with Jira.
+Configuration happens via username and password.
+Connecting to a Jira server via CAS is not possible.
+
+Each project can be configured to connect to a different Jira instance, configuration is explained [here](#configuration).
+If you have one Jira instance you can pre-fill the settings page with a default template. To configure the template [see external issue tracker document](external-issue-tracker.md#service-template)).
+
+Once the project is connected to Jira, you can reference and close the issues in Jira directly from GitLab.
+
+
+## Table of Contents
+
+* [Referencing Jira Issues from GitLab](#referencing-jira-issues)
+* [Closing Jira Issues from GitLab](#closing-jira-issues)
+* [Configuration](#configuration)
+
+### Referencing Jira Issues
+
+When GitLab project has Jira issue tracker configured and enabled, mentioning Jira issue in GitLab will automatically add a comment in Jira issue with the link back to GitLab. This means that in comments in merge requests and commits referencing an issue, eg. `PROJECT-7`, will add a comment in Jira issue in the format:
+
+
+```
+ USER mentioned this issue in LINK_TO_THE_MENTION
+```
+
+* `USER` A user that mentioned the issue. This is the link to the user profile in GitLab.
+* `LINK_TO_THE_MENTION` Link to the origin of mention with a name of the entity where Jira issue was mentioned.
+Can be commit or merge request.
+
+
+![example of mentioning or closing the Jira issue](jira_issue_reference.png)
+
+
+### Closing Jira Issues
+
+Jira issues can be closed directly from GitLab by using trigger words, eg. `Resolves PROJECT-1`, `Closes PROJECT-1` or `Fixes PROJECT-1`, in commits and merge requests.
+When a commit which contains the trigger word in the commit message is pushed, GitLab will add a comment in the mentioned Jira issue.
+
+For example, for project named PROJECT in Jira, we implemented a new feature and created a merge request in GitLab.
+
+This feature was requested in Jira issue PROJECT-7. Merge request in GitLab contains the improvement and in merge request description we say that this merge request `Closes PROJECT-7` issue.
+
+Once this merge request is merged, Jira issue will be automatically closed with a link to the commit that resolved the issue.
+
+![A Git commit that causes the Jira issue to be closed](merge_request_close_jira.png)
+
+
+![The GitLab integration user leaves a comment on Jira](jira_service_close_issue.png)
+
+
+## Configuration
+
+### Configuring JIRA
+
+We need to create a user in JIRA which will have access to all projects that need to integrate with GitLab.
+Login to your JIRA instance as admin and under Administration go to User Management and create a new user.
+As an example, we'll create a user named `gitlab` and add it to `jira-developers` group.
+
+**It is important that the user `gitlab` has write-access to projects in JIRA**
+
+### Configuring GitLab
+
+### GitLab 7.8 EE and up with JIRA v6.x
+
+To enable JIRA integration in a project, navigate to the project Settings page and go to Services. Here you will find JIRA.
+
+Fill in the required details on the page:
+
+![Jira service page](jira_service_page.png)
+
+* `description` A name for the issue tracker (to differentiate between instances, for instance).
+* `project url` The URL to the JIRA project which is being linked to this GitLab project.
+* `issues url` The URL to the JIRA project issues overview for the project that is linked to this GitLab project.
+* `new issue url` This is the URL to create a new issue in JIRA for the project linked to this GitLab project.
+* `api url` The base URL of the JIRA API. It may be omitted, in which case GitLab will automatically use API version `2` based on the `project url`, i.e. `https://jira.example.com/rest/api/2`.
+* `username` The username of the user created in [configuring JIRA step](#configuring-jira).
+* `password` The password of the user created in [configuring JIRA step](#configuring-jira).
+* `Jira issue transition` This is the id of a transition that moves issues to a closed state. You can find this number under [JIRA workflow administration, see screenshot](jira_workflow_screenshot.png). By default, this id is `2`. (In the example image, this is `2` as well)
+
+After saving the configuration, your GitLab project will be able to interact with the linked JIRA project.
+
+
+### GitLab 6.x-7.7 with JIRA v6.x
+
+**Note: GitLab 7.8 and up contain various integration improvements. We strongly recommend upgrading.**
+
+
+In `gitlab.yml` enable [JIRA issue tracker section by uncommenting the lines](https://gitlab.com/subscribers/gitlab-ee/blob/6-8-stable-ee/config/gitlab.yml.example#L111-115).
+This will make sure that all issues within GitLab are pointing to the JIRA issue tracker.
+
+We can also enable JIRA service that will allow us to interact with JIRA issues.
+
+For example, we can close issues in JIRA by a commit in GitLab.
+
+Go to project settings page and fill in the project name for the JIRA project:
+
+![Set the JIRA project name in GitLab to 'NEW'](jira_project_name.png)
+
+Next, go to the services page and find JIRA.
+
+![Jira services page](jira_service.png)
+
+1. Tick the active check box to enable the service.
+1. Supply the url to JIRA server, for example http://jira.sample
+1. Supply the username of a user we created under `Configuring JIRA` section, for example `gitlab`
+1. Supply the password of the user
+1. Optional: supply the JIRA api version, default is version
+1. Optional: supply the JIRA issue transition ID (issue transition to closed). This is dependant on JIRA settings, default is 2
+1. Save
+
+Now we should be able to interact with JIRA issues.
diff --git a/doc/integration/jira_issue_reference.png b/doc/integration/jira_issue_reference.png
new file mode 100644
index 00000000000..15739a22dc7
--- /dev/null
+++ b/doc/integration/jira_issue_reference.png
Binary files differ
diff --git a/doc/integration/jira_project_name.png b/doc/integration/jira_project_name.png
new file mode 100644
index 00000000000..5986fdb63fb
--- /dev/null
+++ b/doc/integration/jira_project_name.png
Binary files differ
diff --git a/doc/integration/jira_service.png b/doc/integration/jira_service.png
new file mode 100644
index 00000000000..1f6628c4371
--- /dev/null
+++ b/doc/integration/jira_service.png
Binary files differ
diff --git a/doc/integration/jira_service_close_issue.png b/doc/integration/jira_service_close_issue.png
new file mode 100644
index 00000000000..67dfc6144c4
--- /dev/null
+++ b/doc/integration/jira_service_close_issue.png
Binary files differ
diff --git a/doc/integration/jira_service_page.png b/doc/integration/jira_service_page.png
new file mode 100644
index 00000000000..69ec44e826f
--- /dev/null
+++ b/doc/integration/jira_service_page.png
Binary files differ
diff --git a/doc/integration/jira_workflow_screenshot.png b/doc/integration/jira_workflow_screenshot.png
new file mode 100644
index 00000000000..8635a32eb68
--- /dev/null
+++ b/doc/integration/jira_workflow_screenshot.png
Binary files differ
diff --git a/doc/raketasks/README.md b/doc/raketasks/README.md
index a8dc5c24df2..cc8a22cd003 100644
--- a/doc/raketasks/README.md
+++ b/doc/raketasks/README.md
@@ -1,10 +1,11 @@
# Rake tasks
- [Backup restore](backup_restore.md)
+- [Check](check.md)
- [Cleanup](cleanup.md)
- [Features](features.md)
- [Maintenance](maintenance.md) and self-checks
- [User management](user_management.md)
- [Web hooks](web_hooks.md)
- [Import](import.md) of git repositories in bulk
-- [Rebuild authorized_keys file](http://doc.gitlab.com/ce/raketasks/maintenance.html#rebuild-authorized_keys-file) task for administrators \ No newline at end of file
+- [Rebuild authorized_keys file](http://doc.gitlab.com/ce/raketasks/maintenance.html#rebuild-authorized_keys-file) task for administrators
diff --git a/doc/raketasks/check.md b/doc/raketasks/check.md
new file mode 100644
index 00000000000..3ff3fee6a40
--- /dev/null
+++ b/doc/raketasks/check.md
@@ -0,0 +1,63 @@
+# Check Rake Tasks
+
+## Repository Integrity
+
+Even though Git is very resilient and tries to prevent data integrity issues,
+there are times when things go wrong. The following Rake tasks intend to
+help GitLab administrators diagnose problem repositories so they can be fixed.
+
+There are 3 things that are checked to determine integrity.
+
+1. Git repository file system check ([git fsck](https://git-scm.com/docs/git-fsck)).
+ This step verifies the connectivity and validity of objects in the repository.
+1. Check for `config.lock` in the repository directory.
+1. Check for any branch/references lock files in `refs/heads`.
+
+It's important to note that the existence of `config.lock` or reference locks
+alone do not necessarily indicate a problem. Lock files are routinely created
+and removed as Git and GitLab perform operations on the repository. They serve
+to prevent data integrity issues. However, if a Git operation is interrupted these
+locks may not be cleaned up properly.
+
+The following symptoms may indicate a problem with repository integrity. If users
+experience these symptoms you may use the rake tasks described below to determine
+exactly which repositories are causing the trouble.
+
+- Receiving an error when trying to push code - `remote: error: cannot lock ref`
+- A 500 error when viewing the GitLab dashboard or when accessing a specific project.
+
+### Check all GitLab repositories
+
+This task loops through all repositories on the GitLab server and runs the
+3 integrity checks described previously.
+
+```
+# omnibus-gitlab
+sudo gitlab-rake gitlab:repo:check
+
+# installation from source
+bundle exec rake gitlab:repo:check RAILS_ENV=production
+```
+
+### Check repositories for a specific user
+
+This task checks all repositories that a specific user has access to. This is important
+because sometimes you know which user is experiencing trouble but you don't know
+which project might be the cause.
+
+If the rake task is executed without brackets at the end, you will be prompted
+to enter a username.
+
+```bash
+# omnibus-gitlab
+sudo gitlab-rake gitlab:user:check_repos
+sudo gitlab-rake gitlab:user:check_repos[<username>]
+
+# installation from source
+bundle exec rake gitlab:user:check_repos RAILS_ENV=production
+bundle exec rake gitlab:user:check_repos[<username>] RAILS_ENV=production
+```
+
+Example output:
+
+![gitlab:user:check_repos output](check_repos_output.png)
diff --git a/doc/raketasks/check_repos_output.png b/doc/raketasks/check_repos_output.png
new file mode 100644
index 00000000000..916b1685101
--- /dev/null
+++ b/doc/raketasks/check_repos_output.png
Binary files differ
diff --git a/doc/update/8.2-to-8.3.md b/doc/update/8.2-to-8.3.md
index e69c4f7ed3c..c4661dc16af 100644
--- a/doc/update/8.2-to-8.3.md
+++ b/doc/update/8.2-to-8.3.md
@@ -67,7 +67,7 @@ sudo -u git -H git checkout 8-3-stable-ee
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch --all
-sudo -u git -H git checkout v2.6.8
+sudo -u git -H git checkout v2.6.9
```
### 5. Update gitlab-workhorse
@@ -78,7 +78,7 @@ which should already be on your system from GitLab 8.1.
```bash
cd /home/git/gitlab-workhorse
sudo -u git -H git fetch --all
-sudo -u git -H git checkout 0.4.2
+sudo -u git -H git checkout 0.5.1
sudo -u git -H make
```
@@ -115,6 +115,12 @@ git diff origin/8-2-stable:config/gitlab.yml.example origin/8-3-stable:config/gi
#### Nginx configuration
+GitLab 8.3 introduces major changes in the NGINX configuration.
+Because all HTTP requests pass through gitlab-workhorse now a lot of
+directives need to be removed from NGINX. During future upgrades there
+should be much less changes in the NGINX configuration because of
+this.
+
View changes between the previous recommended Nginx configuration and the
current one:
@@ -134,6 +140,18 @@ via [/etc/default/gitlab].
[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-3-stable/lib/support/init.d/gitlab.default.example#L34
+#### Init script
+
+We updated the init script for GitLab in order to pass new
+configuration options to gitlab-workhorse. We let gitlab-workhorse
+connect to the Rails application via a Unix domain socket and we tell
+it where the 'public' directory of GitLab is.
+
+```
+cd /home/git/gitlab
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+```
+
### 8. Use Redis v2.8.0+
Previous versions of GitLab allowed Redis versions >= 2.0 to be used, but
diff --git a/doc/workflow/README.md b/doc/workflow/README.md
index d2642495c9a..3651b55f438 100644
--- a/doc/workflow/README.md
+++ b/doc/workflow/README.md
@@ -19,3 +19,4 @@
- ["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)
+- [Importing from SVN, GitHub, BitBucket, etc](importing/README.md)
diff --git a/doc/workflow/importing/README.md b/doc/workflow/importing/README.md
index 7ccf06fbd60..18e5d950866 100644
--- a/doc/workflow/importing/README.md
+++ b/doc/workflow/importing/README.md
@@ -1,13 +1,17 @@
# Migrating projects to a GitLab instance
1. [Bitbucket](import_projects_from_bitbucket.md)
-2. [GitHub](import_projects_from_github.md)
-3. [GitLab.com](import_projects_from_gitlab_com.md)
-4. [FogBugz](import_projects_from_fogbugz.md)
-4. [SVN](migrating_from_svn.md)
+1. [GitHub](import_projects_from_github.md)
+1. [GitLab.com](import_projects_from_gitlab_com.md)
+1. [FogBugz](import_projects_from_fogbugz.md)
+1. [SVN](migrating_from_svn.md)
-### Note
-* If you'd like to migrate from a self-hosted GitLab instance to GitLab.com, you can copy your repos by changing the remote and pushing to the new server; but issues and merge requests can't be imported.
+In addition to the specific migration documentation above, you can import any
+Git repository via HTTP from the New Project page. Be aware that if the
+repository is too large the import can timeout.
+
+### Migrating from self-hosted GitLab to GitLab.com
+
+You can copy your repos by changing the remote and pushing to the new server;
+but issues and merge requests can't be imported.
-* You can import any Git repository via HTTP from the New Project page.
-If the repository is too large, it can timeout.
diff --git a/doc/workflow/importing/migrating_from_svn.md b/doc/workflow/importing/migrating_from_svn.md
index 1938ccd0c26..b355a91b5a6 100644
--- a/doc/workflow/importing/migrating_from_svn.md
+++ b/doc/workflow/importing/migrating_from_svn.md
@@ -1,17 +1,78 @@
# Migrating from SVN to GitLab
-SVN stands for Subversion and is a version control system (VCS).
-Git is a distributed version control system.
+Subversion (SVN) is a central version control system (VCS) while
+Git is a distributed version control system. There are some major differences
+between the two, for more information consult your favorite search engine.
-There are some major differences between the two, for more information consult your favorite search engine.
+If you are currently using an SVN repository, you can migrate the repository
+to Git and GitLab. We recommend a hard cut over - run the migration command once
+and then have all developers start using the new GitLab repository immediately.
+Otherwise, it's hard to keep changing in sync in both directions. The conversion
+process should be run on a local workstation.
-Git has tools for migrating SVN repositories to git, namely `git svn`. You can read more about this at
-[git documentation pages](https://git-scm.com/book/en/Git-and-Other-Systems-Git-and-Subversion).
+Install `svn2git`. On all systems you can install as a Ruby gem if you already
+have Ruby and Git installed.
-Apart from the [official git documentation](https://git-scm.com/book/en/Git-and-Other-Systems-Migrating-to-Git) there is also
-user created step by step guide for migrating from SVN to GitLab.
+```bash
+sudo gem install svn2git
+```
-[Benjamin New](https://github.com/leftclickben) wrote [a guide that shows how to do a migration](https://gist.github.com/leftclickben/322b7a3042cbe97ed2af). Mirrors can be found [here](https://gitlab.com/snippets/2168) and [here](https://gist.github.com/maxlazio/f1b593b0d00aa966e9ca).
+On Debian-based Linux distributions you can install the native packages:
+
+```bash
+sudo apt-get install git-core git-svn ruby
+```
+
+Optionally, prepare an authors file so `svn2git` can map SVN authors to Git authors.
+If you choose not to create the authors file then commits will not be attributed
+to the correct GitLab user. Some users may not consider this a big issue while
+others will want to ensure they complete this step. If you choose to map authors
+you will be required to map every author that is present on changes in the SVN
+repository. If you don't, the conversion will fail and you will have to update
+the author file accordingly. The following command will search through the
+repository and output a list of authors.
+
+```bash
+svn log --quiet | grep -E "r[0-9]+ \| .+ \|" | cut -d'|' -f2 | sed 's/ //g' | sort | uniq
+```
+
+Use the output from the last command to construct the authors file.
+Create a file called `authors.txt` and add one mapping per line.
+
+```
+janedoe = Jane Doe <janedoe@example.com>
+johndoe = John Doe <johndoe@example.com>
+```
+
+If your SVN repository is in the standard format (trunk, branches, tags,
+not nested) the conversion is simple. For a non-standard repository see
+[svn2git documentation](https://github.com/nirvdrum/svn2git). The following
+command will checkout the repository and do the conversion in the current
+working directory. Be sure to create a new directory for each repository before
+running the `svn2git` command. The conversion process will take some time.
+
+```bash
+svn2git https://svn.example.com/path/to/repo --authors /path/to/authors.txt
+```
+
+If your SVN repository requires a username and password add the
+`--username <username>` and `--password <password` flags to the above command.
+`svn2git` also supports excluding certain file paths, branches, tags, etc. See
+[svn2git documentation](https://github.com/nirvdrum/svn2git) or run
+`svn2git --help` for full documentation on all of the available options.
+
+Create a new GitLab project, where you will eventually push your converted code.
+Copy the SSH or HTTP(S) repository URL from the project page. Add the GitLab
+repository as a Git remote and push all the changes. This will push all commits,
+branches and tags.
+
+```bash
+git remote add origin git@gitlab.com:<group>/<project>.git
+git push --all origin
+```
## Contribute to this guide
-We welcome all contributions that would expand this guide with instructions on how to migrate from SVN and other version control systems.
+We welcome all contributions that would expand this guide with instructions on
+how to migrate from SVN and other version control systems.
+
+
diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature
index f08b30e0b88..ab234bc7507 100644
--- a/features/project/issues/issues.feature
+++ b/features/project/issues/issues.feature
@@ -197,3 +197,9 @@ 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"
+
+ @javascript
+ Scenario: Another user adds a comment to issue I'm currently viewing
+ Given I visit issue page "Release 0.4"
+ And another user adds a comment with text "Yay!" to issue "Release 0.4"
+ Then I should see a new comment with text "Yay!"
diff --git a/features/project/service.feature b/features/project/service.feature
index ff3e7a0b38e..3a7b8308524 100644
--- a/features/project/service.feature
+++ b/features/project/service.feature
@@ -55,6 +55,12 @@ Feature: Project Services
And I fill email on push settings
Then I should see email on push service settings saved
+ Scenario: Activate JIRA service
+ When I visit project "Shop" services page
+ And I click jira service link
+ And I fill jira settings
+ Then I should see jira service settings saved
+
Scenario: Activate Irker (IRC Gateway) service
When I visit project "Shop" services page
And I click Irker service link
diff --git a/features/steps/explore/groups.rb b/features/steps/explore/groups.rb
index 87cd33c37eb..87f32e70d59 100644
--- a/features/steps/explore/groups.rb
+++ b/features/steps/explore/groups.rb
@@ -75,18 +75,18 @@ class Spinach::Features::ExploreGroups < Spinach::FeatureSteps
name: projectname,
path: "#{groupname}-#{projectname}",
visibility_level: visibility_level
- )
+ )
create(:issue,
title: "#{projectname} feature",
project: project
- )
+ )
create(:merge_request,
title: "#{projectname} feature implemented",
source_project: project,
target_project: project
- )
+ )
create(:closed_issue_event,
project: project
- )
+ )
end
end
diff --git a/features/steps/explore/projects.rb b/features/steps/explore/projects.rb
index f819dec2192..742ba5d71f6 100644
--- a/features/steps/explore/projects.rb
+++ b/features/steps/explore/projects.rb
@@ -61,11 +61,11 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps
create(:issue,
title: "Bug",
project: public_project
- )
+ )
create(:issue,
title: "New feature",
project: public_project
- )
+ )
visit namespace_project_issues_path(public_project.namespace, public_project)
end
@@ -80,11 +80,11 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps
create(:issue,
title: "Internal Bug",
project: internal_project
- )
+ )
create(:issue,
title: "New internal feature",
project: internal_project
- )
+ )
visit namespace_project_issues_path(internal_project.namespace, internal_project)
end
@@ -104,7 +104,7 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps
title: "Bug fix for public project",
source_project: public_project,
target_project: public_project,
- )
+ )
end
step 'I should see list of merge requests for "Community" project' do
@@ -121,7 +121,7 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps
title: "Feature implemented",
source_project: internal_project,
target_project: internal_project
- )
+ )
end
step 'I should see list of merge requests for "Internal" project' do
diff --git a/features/steps/groups.rb b/features/steps/groups.rb
index f5e3fee61c0..4c5122d1b7d 100644
--- a/features/steps/groups.rb
+++ b/features/steps/groups.rb
@@ -85,7 +85,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
step 'I should see new group "Owned" avatar' do
expect(owned_group.avatar).to be_instance_of AvatarUploader
- expect(owned_group.avatar.url).to eq "/uploads/group/avatar/#{ Group.find_by(name:"Owned").id }/banana_sample.gif"
+ expect(owned_group.avatar.url).to eq "/uploads/group/avatar/#{Group.find_by(name:"Owned").id}/banana_sample.gif"
end
step 'I should see the "Remove avatar" button' do
diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb
index 40b2aa7c357..0305f7e6da0 100644
--- a/features/steps/profile/profile.rb
+++ b/features/steps/profile/profile.rb
@@ -34,7 +34,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
step 'I should see new avatar' do
expect(@user.avatar).to be_instance_of AvatarUploader
- expect(@user.avatar.url).to eq "/uploads/user/avatar/#{ @user.id }/banana_sample.gif"
+ expect(@user.avatar.url).to eq "/uploads/user/avatar/#{@user.id}/banana_sample.gif"
end
step 'I should see the "Remove avatar" button' do
diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb
index d3675060994..cbdce78dc0c 100644
--- a/features/steps/project/forked_merge_requests.rb
+++ b/features/steps/project/forked_merge_requests.rb
@@ -41,7 +41,8 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
click_button "Compare branches and continue"
- expect(page).to have_content "New Merge Request"
+ expect(page).to have_css("h3.page-title", text: "New Merge Request")
+
fill_in "merge_request_title", with: "Merge Request On Forked Project"
end
diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb
index a13044c3ae1..4a7ff21d385 100644
--- a/features/steps/project/issues/issues.rb
+++ b/features/steps/project/issues/issues.rb
@@ -284,6 +284,16 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
end
end
+ step 'another user adds a comment with text "Yay!" to issue "Release 0.4"' do
+ issue = Issue.find_by!(title: 'Release 0.4')
+ create(:note_on_issue, noteable: issue, note: 'Yay!')
+ end
+
+ step 'I should see a new comment with text "Yay!"' do
+ page.within '#notes' do
+ expect(page).to have_content('Yay!')
+ end
+ end
def filter_issue(text)
fill_in 'issue_search', with: text
end
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index 0d340d97ff9..be993d11093 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -273,7 +273,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end
step 'I should see merged request' do
- page.within '.issue-box' do
+ page.within '.status-box' do
expect(page).to have_content "Merged"
end
end
@@ -283,7 +283,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end
step 'I should see reopened merge request "Bug NS-04"' do
- page.within '.issue-box' do
+ page.within '.status-box' do
expect(page).to have_content "Open"
end
end
diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb
index 9ca7c8ebbc7..37bf52b4a95 100644
--- a/features/steps/project/project.rb
+++ b/features/steps/project/project.rb
@@ -37,7 +37,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps
step 'I should see new project avatar' do
expect(@project.avatar).to be_instance_of AvatarUploader
url = @project.avatar.url
- expect(url).to eq "/uploads/project/avatar/#{ @project.id }/banana_sample.gif"
+ expect(url).to eq "/uploads/project/avatar/#{@project.id}/banana_sample.gif"
end
step 'I should see the "Remove avatar" button' do
diff --git a/features/steps/project/services.rb b/features/steps/project/services.rb
index ed3957ca873..536199ddb4f 100644
--- a/features/steps/project/services.rb
+++ b/features/steps/project/services.rb
@@ -173,6 +173,24 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
expect(find_field('Sound').find('option[selected]').value).to eq 'bike'
end
+ step 'I click jira service link' do
+ click_link 'JIRA'
+ end
+
+ step 'I fill jira settings' do
+ fill_in 'Project url', with: 'http://jira.example'
+ fill_in 'Username', with: 'gitlab'
+ fill_in 'Password', with: 'gitlab'
+ fill_in 'Api url', with: 'http://jira.example/rest/api/2'
+ click_button 'Save'
+ end
+
+ step 'I should see jira service settings saved' do
+ expect(find_field('Project url').value).to eq 'http://jira.example'
+ expect(find_field('Username').value).to eq 'gitlab'
+ expect(find_field('Api url').value).to eq 'http://jira.example/rest/api/2'
+ end
+
step 'I click Atlassian Bamboo CI service link' do
click_link 'Atlassian Bamboo CI'
end
diff --git a/features/steps/project/snippets.rb b/features/steps/project/snippets.rb
index a3aef9bf8c3..504654f90dd 100644
--- a/features/steps/project/snippets.rb
+++ b/features/steps/project/snippets.rb
@@ -42,7 +42,7 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps
end
step 'I click link "Edit"' do
- page.within ".page-title" do
+ page.within ".detail-page-header" do
click_link "Edit"
end
end
diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb
index b88709620ab..0c6df18ce2e 100644
--- a/features/steps/project/source/browse_files.rb
+++ b/features/steps/project/source/browse_files.rb
@@ -238,13 +238,13 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I am redirected to the new file' do
- expect(current_path).to eq(namespace_project_blob_path(
- @project.namespace, @project, 'master/' + new_file_name))
+ expect(current_path).to eq(
+ namespace_project_blob_path(@project.namespace, @project, 'master/' + new_file_name))
end
step 'I am redirected to the new file with directory' do
- expect(current_path).to eq(namespace_project_blob_path(
- @project.namespace, @project, 'master/' + new_file_name_with_directory))
+ expect(current_path).to eq(
+ namespace_project_blob_path(@project.namespace, @project, 'master/' + new_file_name_with_directory))
end
step 'I am redirected to the new merge request page' do
@@ -252,8 +252,8 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I am redirected to the root directory' do
- expect(current_path).to eq(namespace_project_tree_path(
- @project.namespace, @project, 'master/'))
+ expect(current_path).to eq(
+ namespace_project_tree_path(@project.namespace, @project, 'master/'))
end
step "I don't see the permalink link" do
diff --git a/features/steps/shared/diff_note.rb b/features/steps/shared/diff_note.rb
index dd466cde28d..c6a0ae2ba38 100644
--- a/features/steps/shared/diff_note.rb
+++ b/features/steps/shared/diff_note.rb
@@ -166,7 +166,7 @@ module SharedDiffNote
end
step 'I should see add a diff comment button' do
- expect(page).to have_css('.js-add-diff-note-button', visible: true)
+ expect(page).to have_css('.js-add-diff-note-button')
end
step 'I should see an empty diff comment form' do
diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb
index c74a5fd3bc7..b33bd332655 100644
--- a/features/steps/shared/paths.rb
+++ b/features/steps/shared/paths.rb
@@ -212,8 +212,8 @@ module SharedPaths
end
step 'I visit a binary file in the repo' do
- visit namespace_project_blob_path(@project.namespace, @project, File.join(
- root_ref, 'files/images/logo-black.png'))
+ visit namespace_project_blob_path(@project.namespace, @project,
+ File.join(root_ref, 'files/images/logo-black.png'))
end
step "I visit my project's commits page" do
@@ -316,8 +316,8 @@ module SharedPaths
end
step 'I am on the ".gitignore" edit file page' do
- expect(current_path).to eq(namespace_project_edit_blob_path(
- @project.namespace, @project, File.join(root_ref, '.gitignore')))
+ expect(current_path).to eq(
+ namespace_project_edit_blob_path(@project.namespace, @project, File.join(root_ref, '.gitignore')))
end
step 'I visit project source page for "6d39438"' do
diff --git a/features/steps/snippets/snippets.rb b/features/steps/snippets/snippets.rb
index 80d1ddeef05..023032e679f 100644
--- a/features/steps/snippets/snippets.rb
+++ b/features/steps/snippets/snippets.rb
@@ -13,7 +13,7 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps
end
step 'I click link "Edit"' do
- page.within ".page-title" do
+ page.within ".detail-page-header" do
click_link "Edit"
end
end
diff --git a/features/support/capybara.rb b/features/support/capybara.rb
index 31dbf0feb2f..4156c7ec484 100644
--- a/features/support/capybara.rb
+++ b/features/support/capybara.rb
@@ -2,7 +2,7 @@ require 'spinach/capybara'
require 'capybara/poltergeist'
# Give CI some extra time
-timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 90 : 10
+timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 90 : 15
Capybara.javascript_driver = :poltergeist
Capybara.register_driver :poltergeist do |app|
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index b1cd80bdf65..f8511ac5f5c 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -67,9 +67,10 @@ module API
expose :shared_runners_enabled
expose :creator_id
expose :namespace
- expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda{ | project, options | project.forked? }
+ expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda{ |project, options| project.forked? }
expose :avatar_url
expose :star_count, :forks_count
+ expose :open_issues_count, if: lambda { |project, options| project.issues_enabled? && project.default_issues_tracker? }
end
class ProjectMember < UserBasic
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index bdf4b77596e..5e75cd35c56 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -39,6 +39,17 @@ module API
present @projects, with: Entities::Project
end
+ # Gets starred project for the authenticated user
+ #
+ # Example Request:
+ # GET /projects/starred
+ get '/starred' do
+ @projects = current_user.starred_projects
+ @projects = filter_projects(@projects)
+ @projects = paginate @projects
+ present @projects, with: Entities::Project
+ end
+
# Get all projects for admin user
#
# Example Request:
diff --git a/lib/banzai.rb b/lib/banzai.rb
new file mode 100644
index 00000000000..093382261ae
--- /dev/null
+++ b/lib/banzai.rb
@@ -0,0 +1,13 @@
+module Banzai
+ def self.render(text, context = {})
+ Renderer.render(text, context)
+ end
+
+ def self.render_result(text, context = {})
+ Renderer.render_result(text, context)
+ end
+
+ def self.post_process(html, context)
+ Renderer.post_process(html, context)
+ end
+end
diff --git a/lib/banzai/cross_project_reference.rb b/lib/banzai/cross_project_reference.rb
new file mode 100644
index 00000000000..ba2866e1efa
--- /dev/null
+++ b/lib/banzai/cross_project_reference.rb
@@ -0,0 +1,22 @@
+require 'banzai'
+
+module Banzai
+ # Common methods for ReferenceFilters that support an optional cross-project
+ # reference.
+ module CrossProjectReference
+ # Given a cross-project reference string, get the Project record
+ #
+ # Defaults to value of `context[:project]` if:
+ # * No reference is given OR
+ # * Reference given doesn't exist
+ #
+ # ref - String reference.
+ #
+ # Returns a Project, or nil if the reference can't be found
+ def project_from_ref(ref)
+ return context[:project] unless ref
+
+ Project.find_with_namespace(ref)
+ end
+ end
+end
diff --git a/lib/banzai/filter.rb b/lib/banzai/filter.rb
new file mode 100644
index 00000000000..fd4fe024252
--- /dev/null
+++ b/lib/banzai/filter.rb
@@ -0,0 +1,10 @@
+require 'active_support/core_ext/string/output_safety'
+require 'banzai'
+
+module Banzai
+ module Filter
+ def self.[](name)
+ const_get("#{name.to_s.camelize}Filter")
+ end
+ end
+end
diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
index 9488e980c08..bdaa4721b4b 100644
--- a/lib/gitlab/markdown/abstract_reference_filter.rb
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -1,7 +1,7 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# Issues, Merge Requests, Snippets, Commits and Commit Ranges share
# similar functionality in reference filtering.
class AbstractReferenceFilter < ReferenceFilter
diff --git a/lib/gitlab/markdown/filter/autolink_filter.rb b/lib/banzai/filter/autolink_filter.rb
index c37c3bc55bf..da4ee80c1b5 100644
--- a/lib/gitlab/markdown/filter/autolink_filter.rb
+++ b/lib/banzai/filter/autolink_filter.rb
@@ -1,9 +1,9 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
require 'uri'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML Filter for auto-linking URLs in HTML.
#
# Based on HTML::Pipeline::AutolinkFilter
diff --git a/lib/gitlab/markdown/filter/commit_range_reference_filter.rb b/lib/banzai/filter/commit_range_reference_filter.rb
index 36b3258ef76..e67cd45ab9b 100644
--- a/lib/gitlab/markdown/filter/commit_range_reference_filter.rb
+++ b/lib/banzai/filter/commit_range_reference_filter.rb
@@ -1,7 +1,7 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that replaces commit range references with links.
#
# This filter supports cross-project references.
diff --git a/lib/gitlab/markdown/filter/commit_reference_filter.rb b/lib/banzai/filter/commit_reference_filter.rb
index e3066a89b04..9e57608b483 100644
--- a/lib/gitlab/markdown/filter/commit_reference_filter.rb
+++ b/lib/banzai/filter/commit_reference_filter.rb
@@ -1,7 +1,7 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that replaces commit references with links.
#
# This filter supports cross-project references.
diff --git a/lib/gitlab/markdown/filter/emoji_filter.rb b/lib/banzai/filter/emoji_filter.rb
index da10e4d3760..86838e1483c 100644
--- a/lib/gitlab/markdown/filter/emoji_filter.rb
+++ b/lib/banzai/filter/emoji_filter.rb
@@ -1,10 +1,10 @@
require 'action_controller'
-require 'gitlab/markdown'
+require 'banzai'
require 'gitlab_emoji'
require 'html/pipeline/filter'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that replaces :emoji: with images.
#
# Based on HTML::Pipeline::EmojiFilter
diff --git a/lib/gitlab/markdown/filter/external_issue_reference_filter.rb b/lib/banzai/filter/external_issue_reference_filter.rb
index 14bdf5521fc..f5942740cd6 100644
--- a/lib/gitlab/markdown/filter/external_issue_reference_filter.rb
+++ b/lib/banzai/filter/external_issue_reference_filter.rb
@@ -1,7 +1,7 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that replaces external issue tracker references with links.
# References are ignored if the project doesn't use an external issue
# tracker.
@@ -23,6 +23,18 @@ module Gitlab
end
end
+ def self.referenced_by(node)
+ project = Project.find(node.attr("data-project")) rescue nil
+ return unless project
+
+ id = node.attr("data-external-issue")
+ external_issue = ExternalIssue.new(id, project)
+
+ return unless external_issue
+
+ { external_issue: external_issue }
+ end
+
def call
# Early return if the project isn't using an external tracker
return doc if project.nil? || project.default_issues_tracker?
@@ -46,12 +58,14 @@ module Gitlab
def issue_link_filter(text, link_text: nil)
project = context[:project]
- self.class.references_in(text) do |match, issue|
- url = url_for_issue(issue, project, only_path: context[:only_path])
+ self.class.references_in(text) do |match, id|
+ ExternalIssue.new(id, project)
+
+ url = url_for_issue(id, project, only_path: context[:only_path])
title = escape_once("Issue in #{project.external_issue_tracker.title}")
klass = reference_class(:issue)
- data = data_attribute(project: project.id)
+ data = data_attribute(project: project.id, external_issue: id)
text = link_text || match
diff --git a/lib/gitlab/markdown/filter/external_link_filter.rb b/lib/banzai/filter/external_link_filter.rb
index e09dfcb83c8..ac87b9820af 100644
--- a/lib/gitlab/markdown/filter/external_link_filter.rb
+++ b/lib/banzai/filter/external_link_filter.rb
@@ -1,8 +1,8 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML Filter to add a `rel="nofollow"` attribute to external links
#
class ExternalLinkFilter < HTML::Pipeline::Filter
diff --git a/lib/gitlab/markdown/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb
index 1ed69e2f431..51180cb901a 100644
--- a/lib/gitlab/markdown/filter/issue_reference_filter.rb
+++ b/lib/banzai/filter/issue_reference_filter.rb
@@ -1,7 +1,7 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that replaces issue references with links. References to
# issues that do not exist are ignored.
#
diff --git a/lib/gitlab/markdown/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb
index a2026eecaeb..07bac2dd7fd 100644
--- a/lib/gitlab/markdown/filter/label_reference_filter.rb
+++ b/lib/banzai/filter/label_reference_filter.rb
@@ -1,7 +1,7 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that replaces label references with links.
class LabelReferenceFilter < ReferenceFilter
# Public: Find label references in text
diff --git a/lib/gitlab/markdown/filter/markdown_filter.rb b/lib/banzai/filter/markdown_filter.rb
index 921e2a0794e..d09cf41df39 100644
--- a/lib/gitlab/markdown/filter/markdown_filter.rb
+++ b/lib/banzai/filter/markdown_filter.rb
@@ -1,9 +1,12 @@
-module Gitlab
- module Markdown
+require 'banzai'
+require 'html/pipeline/filter'
+
+module Banzai
+ module Filter
class MarkdownFilter < HTML::Pipeline::TextFilter
def initialize(text, context = nil, result = nil)
super text, context, result
- @text = @text.gsub "\r", ''
+ @text = @text.delete "\r"
end
def call
@@ -11,8 +14,8 @@ module Gitlab
html.rstrip!
html
end
-
- private
+
+ private
def self.redcarpet_options
# https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
diff --git a/lib/gitlab/markdown/filter/merge_request_reference_filter.rb b/lib/banzai/filter/merge_request_reference_filter.rb
index 2eb77c46da7..755b946a34b 100644
--- a/lib/gitlab/markdown/filter/merge_request_reference_filter.rb
+++ b/lib/banzai/filter/merge_request_reference_filter.rb
@@ -1,7 +1,7 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that replaces merge request references with links. References
# to merge requests that do not exist are ignored.
#
diff --git a/lib/gitlab/markdown/filter/redactor_filter.rb b/lib/banzai/filter/redactor_filter.rb
index 33ef7ce18b5..89e7a79789a 100644
--- a/lib/gitlab/markdown/filter/redactor_filter.rb
+++ b/lib/banzai/filter/redactor_filter.rb
@@ -1,8 +1,8 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that removes references to records that the current user does
# not have permission to view.
#
@@ -27,7 +27,7 @@ module Gitlab
def user_can_reference?(node)
if node.has_attribute?('data-reference-filter')
reference_type = node.attr('data-reference-filter')
- reference_filter = Gitlab::Markdown.const_get(reference_type)
+ reference_filter = Banzai::Filter.const_get(reference_type)
reference_filter.user_can_reference?(current_user, node, context)
else
diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/banzai/filter/reference_filter.rb
index 3b83b8bd8f8..33457a3f361 100644
--- a/lib/gitlab/markdown/reference_filter.rb
+++ b/lib/banzai/filter/reference_filter.rb
@@ -1,9 +1,9 @@
require 'active_support/core_ext/string/output_safety'
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# Base class for GitLab Flavored Markdown reference filters.
#
# References within <pre>, <code>, <a>, and <style> elements are ignored.
@@ -12,27 +12,6 @@ module Gitlab
# :project (required) - Current project, ignored if reference is cross-project.
# :only_path - Generate path-only links.
class ReferenceFilter < HTML::Pipeline::Filter
- LazyReference = Struct.new(:klass, :ids) do
- def self.load(refs)
- lazy_references, values = refs.partition { |ref| ref.is_a?(self) }
-
- lazy_values = lazy_references.group_by(&:klass).flat_map do |klass, refs|
- ids = refs.flat_map(&:ids)
- klass.where(id: ids)
- end
-
- values + lazy_values
- end
-
- def load
- self.klass.where(id: self.ids)
- end
- end
-
- def self.[](name)
- Markdown.const_get("#{name.to_s.camelize}ReferenceFilter")
- end
-
def self.user_can_reference?(user, node, context)
if node.has_attribute?('data-project')
project_id = node.attr('data-project').to_i
diff --git a/lib/gitlab/markdown/filter/reference_gatherer_filter.rb b/lib/banzai/filter/reference_gatherer_filter.rb
index 62f241b4967..855f238ac1e 100644
--- a/lib/gitlab/markdown/filter/reference_gatherer_filter.rb
+++ b/lib/banzai/filter/reference_gatherer_filter.rb
@@ -1,8 +1,8 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that gathers all referenced records that the current user has
# permission to view.
#
@@ -20,7 +20,7 @@ module Gitlab
gather_references(node)
end
- load_lazy_references unless context[:load_lazy_references] == false
+ load_lazy_references unless ReferenceExtractor.lazy?
doc
end
@@ -31,7 +31,7 @@ module Gitlab
return unless node.has_attribute?('data-reference-filter')
reference_type = node.attr('data-reference-filter')
- reference_filter = Gitlab::Markdown.const_get(reference_type)
+ reference_filter = Banzai::Filter.const_get(reference_type)
return if context[:reference_filter] && reference_filter != context[:reference_filter]
@@ -47,11 +47,10 @@ module Gitlab
end
end
- # Will load all references of one type using one query.
def load_lazy_references
refs = result[:references]
refs.each do |type, values|
- refs[type] = ReferenceFilter::LazyReference.load(values)
+ refs[type] = ReferenceExtractor.lazily(values)
end
end
diff --git a/lib/gitlab/markdown/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb
index 81f60120fcd..5a081125f21 100644
--- a/lib/gitlab/markdown/filter/relative_link_filter.rb
+++ b/lib/banzai/filter/relative_link_filter.rb
@@ -1,9 +1,9 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
require 'uri'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that "fixes" relative links to files in a repository.
#
# Context options:
diff --git a/lib/gitlab/markdown/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb
index cf153f30622..d03e3ae4b3c 100644
--- a/lib/gitlab/markdown/filter/sanitization_filter.rb
+++ b/lib/banzai/filter/sanitization_filter.rb
@@ -1,9 +1,9 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
require 'html/pipeline/sanitization_filter'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# Sanitize HTML
#
# Extends HTML::Pipeline::SanitizationFilter with a custom whitelist.
diff --git a/lib/gitlab/markdown/filter/snippet_reference_filter.rb b/lib/banzai/filter/snippet_reference_filter.rb
index f7bd07c2a34..1ad5df96f85 100644
--- a/lib/gitlab/markdown/filter/snippet_reference_filter.rb
+++ b/lib/banzai/filter/snippet_reference_filter.rb
@@ -1,7 +1,7 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that replaces snippet references with links. References to
# snippets that do not exist are ignored.
#
diff --git a/lib/gitlab/markdown/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb
index 8597e02f0de..c889cc1e97c 100644
--- a/lib/gitlab/markdown/filter/syntax_highlight_filter.rb
+++ b/lib/banzai/filter/syntax_highlight_filter.rb
@@ -1,9 +1,9 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
require 'rouge/plugins/redcarpet'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML Filter to highlight fenced code blocks
#
class SyntaxHighlightFilter < HTML::Pipeline::Filter
diff --git a/lib/gitlab/markdown/filter/table_of_contents_filter.rb b/lib/banzai/filter/table_of_contents_filter.rb
index bbb3bf7fc8b..9b3e67206d5 100644
--- a/lib/gitlab/markdown/filter/table_of_contents_filter.rb
+++ b/lib/banzai/filter/table_of_contents_filter.rb
@@ -1,8 +1,8 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that adds an anchor child element to all Headers in a
# document, so that they can be linked to.
#
@@ -31,7 +31,7 @@ module Gitlab
id = text.downcase
id.gsub!(PUNCTUATION_REGEXP, '') # remove punctuation
- id.gsub!(' ', '-') # replace spaces with dash
+ id.tr!(' ', '-') # replace spaces with dash
id.squeeze!('-') # replace multiple dashes with one
uniq = (headers[id] > 0) ? "-#{headers[id]}" : ''
diff --git a/lib/gitlab/markdown/filter/task_list_filter.rb b/lib/banzai/filter/task_list_filter.rb
index 2f133ae8500..bdf7c2ebdfc 100644
--- a/lib/gitlab/markdown/filter/task_list_filter.rb
+++ b/lib/banzai/filter/task_list_filter.rb
@@ -1,8 +1,8 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'task_list/filter'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# Work around a bug in the default TaskList::Filter that adds a `task-list`
# class to every list element, regardless of whether or not it contains a
# task list.
diff --git a/lib/gitlab/markdown/filter/upload_link_filter.rb b/lib/banzai/filter/upload_link_filter.rb
index fbada73ab86..1a1d0aad8ca 100644
--- a/lib/gitlab/markdown/filter/upload_link_filter.rb
+++ b/lib/banzai/filter/upload_link_filter.rb
@@ -1,9 +1,9 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
require 'uri'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that "fixes" relative upload links to files.
# Context options:
# :project (required) - Current project
diff --git a/lib/gitlab/markdown/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb
index 0a20d9c0347..67c24faf991 100644
--- a/lib/gitlab/markdown/filter/user_reference_filter.rb
+++ b/lib/banzai/filter/user_reference_filter.rb
@@ -1,7 +1,7 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that replaces user or group references with links.
#
# A special `@all` reference is also supported.
diff --git a/lib/banzai/lazy_reference.rb b/lib/banzai/lazy_reference.rb
new file mode 100644
index 00000000000..073ec5d9801
--- /dev/null
+++ b/lib/banzai/lazy_reference.rb
@@ -0,0 +1,27 @@
+require 'banzai'
+
+module Banzai
+ class LazyReference
+ def self.load(refs)
+ lazy_references, values = refs.partition { |ref| ref.is_a?(self) }
+
+ lazy_values = lazy_references.group_by(&:klass).flat_map do |klass, refs|
+ ids = refs.flat_map(&:ids)
+ klass.where(id: ids)
+ end
+
+ values + lazy_values
+ end
+
+ attr_reader :klass, :ids
+
+ def initialize(klass, ids)
+ @klass = klass
+ @ids = Array.wrap(ids).map(&:to_i)
+ end
+
+ def load
+ self.klass.where(id: self.ids)
+ end
+ end
+end
diff --git a/lib/banzai/pipeline.rb b/lib/banzai/pipeline.rb
new file mode 100644
index 00000000000..4e017809d9d
--- /dev/null
+++ b/lib/banzai/pipeline.rb
@@ -0,0 +1,10 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ def self.[](name)
+ name ||= :full
+ const_get("#{name.to_s.camelize}Pipeline")
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/asciidoc_pipeline.rb b/lib/banzai/pipeline/asciidoc_pipeline.rb
new file mode 100644
index 00000000000..5e76a817be5
--- /dev/null
+++ b/lib/banzai/pipeline/asciidoc_pipeline.rb
@@ -0,0 +1,13 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ class AsciidocPipeline < BasePipeline
+ def self.filters
+ [
+ Filter::RelativeLinkFilter
+ ]
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/atom_pipeline.rb b/lib/banzai/pipeline/atom_pipeline.rb
new file mode 100644
index 00000000000..957f352aec5
--- /dev/null
+++ b/lib/banzai/pipeline/atom_pipeline.rb
@@ -0,0 +1,14 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ class AtomPipeline < FullPipeline
+ def self.transform_context(context)
+ super(context).merge(
+ only_path: false,
+ xhtml: true
+ )
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/base_pipeline.rb b/lib/banzai/pipeline/base_pipeline.rb
new file mode 100644
index 00000000000..cd30009e5c0
--- /dev/null
+++ b/lib/banzai/pipeline/base_pipeline.rb
@@ -0,0 +1,30 @@
+require 'banzai'
+require 'html/pipeline'
+
+module Banzai
+ module Pipeline
+ class BasePipeline
+ def self.filters
+ []
+ end
+
+ def self.transform_context(context)
+ context
+ end
+
+ def self.html_pipeline
+ @html_pipeline ||= HTML::Pipeline.new(filters)
+ end
+
+ class << self
+ %i(call to_document to_html).each do |meth|
+ define_method(meth) do |text, context|
+ context = transform_context(context)
+
+ html_pipeline.send(meth, text, context)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/markdown/combined_pipeline.rb b/lib/banzai/pipeline/combined_pipeline.rb
index 6b08a5e9f72..f3bf1809d18 100644
--- a/lib/gitlab/markdown/combined_pipeline.rb
+++ b/lib/banzai/pipeline/combined_pipeline.rb
@@ -1,10 +1,10 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Pipeline
module CombinedPipeline
def self.new(*pipelines)
- Class.new(Pipeline) do
+ Class.new(BasePipeline) do
const_set :PIPELINES, pipelines
def self.pipelines
diff --git a/lib/gitlab/markdown/pipeline/description_pipeline.rb b/lib/banzai/pipeline/description_pipeline.rb
index 76f6948af8f..94c2cb165a5 100644
--- a/lib/gitlab/markdown/pipeline/description_pipeline.rb
+++ b/lib/banzai/pipeline/description_pipeline.rb
@@ -1,10 +1,10 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Pipeline
class DescriptionPipeline < FullPipeline
def self.transform_context(context)
- super(context).merge(
+ super(context).merge(
# SanitizationFilter
inline_sanitization: true
)
diff --git a/lib/gitlab/markdown/pipeline/email_pipeline.rb b/lib/banzai/pipeline/email_pipeline.rb
index b88cb790270..14356145a35 100644
--- a/lib/gitlab/markdown/pipeline/email_pipeline.rb
+++ b/lib/banzai/pipeline/email_pipeline.rb
@@ -1,10 +1,10 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Pipeline
class EmailPipeline < FullPipeline
def self.transform_context(context)
- super(context).merge(
+ super(context).merge(
only_path: false
)
end
diff --git a/lib/gitlab/markdown/pipeline/full_pipeline.rb b/lib/banzai/pipeline/full_pipeline.rb
index b3b7a3c27c0..72395a5d50e 100644
--- a/lib/gitlab/markdown/pipeline/full_pipeline.rb
+++ b/lib/banzai/pipeline/full_pipeline.rb
@@ -1,7 +1,7 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Pipeline
class FullPipeline < CombinedPipeline.new(PlainMarkdownPipeline, GfmPipeline)
end
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
new file mode 100644
index 00000000000..38750b55ec7
--- /dev/null
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -0,0 +1,41 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ class GfmPipeline < BasePipeline
+ def self.filters
+ @filters ||= [
+ Filter::SyntaxHighlightFilter,
+ Filter::SanitizationFilter,
+
+ Filter::UploadLinkFilter,
+ Filter::EmojiFilter,
+ Filter::TableOfContentsFilter,
+ Filter::AutolinkFilter,
+ Filter::ExternalLinkFilter,
+
+ Filter::UserReferenceFilter,
+ Filter::IssueReferenceFilter,
+ Filter::ExternalIssueReferenceFilter,
+ Filter::MergeRequestReferenceFilter,
+ Filter::SnippetReferenceFilter,
+ Filter::CommitRangeReferenceFilter,
+ Filter::CommitReferenceFilter,
+ Filter::LabelReferenceFilter,
+
+ Filter::TaskListFilter
+ ]
+ end
+
+ def self.transform_context(context)
+ context.merge(
+ only_path: true,
+
+ # EmojiFilter
+ asset_host: Gitlab::Application.config.asset_host,
+ asset_root: Gitlab.config.gitlab.base_url
+ )
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/markdown/pipeline/note_pipeline.rb b/lib/banzai/pipeline/note_pipeline.rb
index a8bf5f42d8e..89335143852 100644
--- a/lib/gitlab/markdown/pipeline/note_pipeline.rb
+++ b/lib/banzai/pipeline/note_pipeline.rb
@@ -1,10 +1,10 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Pipeline
class NotePipeline < FullPipeline
def self.transform_context(context)
- super(context).merge(
+ super(context).merge(
# TableOfContentsFilter
no_header_anchors: true
)
diff --git a/lib/banzai/pipeline/plain_markdown_pipeline.rb b/lib/banzai/pipeline/plain_markdown_pipeline.rb
new file mode 100644
index 00000000000..998fd75daa2
--- /dev/null
+++ b/lib/banzai/pipeline/plain_markdown_pipeline.rb
@@ -0,0 +1,13 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ class PlainMarkdownPipeline < BasePipeline
+ def self.filters
+ [
+ Filter::MarkdownFilter
+ ]
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/post_process_pipeline.rb b/lib/banzai/pipeline/post_process_pipeline.rb
new file mode 100644
index 00000000000..148f24b6ce1
--- /dev/null
+++ b/lib/banzai/pipeline/post_process_pipeline.rb
@@ -0,0 +1,20 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ class PostProcessPipeline < BasePipeline
+ def self.filters
+ [
+ Filter::RelativeLinkFilter,
+ Filter::RedactorFilter
+ ]
+ end
+
+ def self.transform_context(context)
+ context.merge(
+ post_process: true
+ )
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/reference_extraction_pipeline.rb b/lib/banzai/pipeline/reference_extraction_pipeline.rb
new file mode 100644
index 00000000000..4f9bc9fcccc
--- /dev/null
+++ b/lib/banzai/pipeline/reference_extraction_pipeline.rb
@@ -0,0 +1,13 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ class ReferenceExtractionPipeline < BasePipeline
+ def self.filters
+ [
+ Filter::ReferenceGathererFilter
+ ]
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/single_line_pipeline.rb b/lib/banzai/pipeline/single_line_pipeline.rb
new file mode 100644
index 00000000000..6725c9039a9
--- /dev/null
+++ b/lib/banzai/pipeline/single_line_pipeline.rb
@@ -0,0 +1,9 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ class SingleLinePipeline < GfmPipeline
+
+ end
+ end
+end
diff --git a/lib/banzai/reference_extractor.rb b/lib/banzai/reference_extractor.rb
new file mode 100644
index 00000000000..2c197d31898
--- /dev/null
+++ b/lib/banzai/reference_extractor.rb
@@ -0,0 +1,55 @@
+require 'banzai'
+
+module Banzai
+ # Extract possible GFM references from an arbitrary String for further processing.
+ class ReferenceExtractor
+ class << self
+ LAZY_KEY = :banzai_reference_extractor_lazy
+
+ def lazy?
+ Thread.current[LAZY_KEY]
+ end
+
+ def lazily(values = nil, &block)
+ return (values || block.call).uniq if lazy?
+
+ begin
+ Thread.current[LAZY_KEY] = true
+
+ values ||= block.call
+
+ Banzai::LazyReference.load(values.uniq).uniq
+ ensure
+ Thread.current[LAZY_KEY] = false
+ end
+ end
+ end
+
+ def initialize
+ @texts = []
+ end
+
+ def analyze(text, context = {})
+ @texts << Renderer.render(text, context)
+ end
+
+ def references(type, context = {})
+ filter = Banzai::Filter["#{type}_reference"]
+
+ context.merge!(
+ pipeline: :reference_extraction,
+
+ # ReferenceGathererFilter
+ reference_filter: filter
+ )
+
+ self.class.lazily do
+ @texts.flat_map do |html|
+ text_context = context.dup
+ result = Renderer.render_result(html, text_context)
+ result[:references][type]
+ end.uniq
+ end
+ end
+ end
+end
diff --git a/lib/banzai/renderer.rb b/lib/banzai/renderer.rb
new file mode 100644
index 00000000000..115ae914524
--- /dev/null
+++ b/lib/banzai/renderer.rb
@@ -0,0 +1,78 @@
+module Banzai
+ module Renderer
+ CACHE_ENABLED = false
+
+ # Convert a Markdown String into an HTML-safe String of HTML
+ #
+ # Note that while the returned HTML will have been sanitized of dangerous
+ # HTML, it may post a risk of information leakage if it's not also passed
+ # through `post_process`.
+ #
+ # Also note that the returned String is always HTML, not XHTML. Views
+ # requiring XHTML, such as Atom feeds, need to call `post_process` on the
+ # result, providing the appropriate `pipeline` option.
+ #
+ # markdown - Markdown String
+ # context - Hash of context options passed to our HTML Pipeline
+ #
+ # Returns an HTML-safe String
+ def self.render(text, context = {})
+ cache_key = context.delete(:cache_key)
+ cache_key = full_cache_key(cache_key, context[:pipeline])
+
+ if cache_key && CACHE_ENABLED
+ Rails.cache.fetch(cache_key) do
+ cacheless_render(text, context)
+ end
+ else
+ cacheless_render(text, context)
+ end
+ end
+
+ def self.render_result(text, context = {})
+ Pipeline[context[:pipeline]].call(text, context)
+ end
+
+ # Perform post-processing on an HTML String
+ #
+ # This method is used to perform state-dependent changes to a String of
+ # HTML, such as removing references that the current user doesn't have
+ # permission to make (`RedactorFilter`).
+ #
+ # html - String to process
+ # context - Hash of options to customize output
+ # :pipeline - Symbol pipeline type
+ # :project - Project
+ # :user - User object
+ #
+ # Returns an HTML-safe String
+ def self.post_process(html, context)
+ context = Pipeline[context[:pipeline]].transform_context(context)
+
+ pipeline = Pipeline[:post_process]
+ if context[:xhtml]
+ pipeline.to_document(html, context).to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML)
+ else
+ pipeline.to_html(html, context)
+ end.html_safe
+ end
+
+ private
+
+ def self.cacheless_render(text, context = {})
+ result = render_result(text, context)
+
+ output = result[:output]
+ if output.respond_to?(:to_html)
+ output.to_html
+ else
+ output.to_s
+ end
+ end
+
+ def self.full_cache_key(cache_key, pipeline_name)
+ return unless cache_key
+ ["banzai", *cache_key, pipeline_name || :full]
+ end
+ end
+end
diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb
index 330d3342dd1..b203b9d70e4 100644
--- a/lib/gitlab/asciidoc.rb
+++ b/lib/gitlab/asciidoc.rb
@@ -32,7 +32,7 @@ module Gitlab
html = ::Asciidoctor.convert(input, asciidoc_opts)
if context[:project]
- html = Gitlab::Markdown.render(html, context.merge(pipeline: :asciidoc))
+ html = Banzai.render(html, context.merge(pipeline: :asciidoc))
end
html.html_safe
diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb
index 87ac30b5ffe..459e3d6bcdb 100644
--- a/lib/gitlab/backend/shell.rb
+++ b/lib/gitlab/backend/shell.rb
@@ -2,7 +2,7 @@ module Gitlab
class Shell
class Error < StandardError; end
- class KeyAdder < Struct.new(:io)
+ KeyAdder = Struct.new(:io) do
def add_key(id, key)
key.gsub!(/[[:space:]]+/, ' ').strip!
io.puts("#{id}\t#{key}")
diff --git a/lib/gitlab/bitbucket_import/project_creator.rb b/lib/gitlab/bitbucket_import/project_creator.rb
index 35e34d033e0..03aac1a025a 100644
--- a/lib/gitlab/bitbucket_import/project_creator.rb
+++ b/lib/gitlab/bitbucket_import/project_creator.rb
@@ -11,7 +11,8 @@ module Gitlab
end
def execute
- project = ::Projects::CreateService.new(current_user,
+ project = ::Projects::CreateService.new(
+ current_user,
name: repo["name"],
path: repo["slug"],
description: repo["description"],
diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb
index 142058aa69d..79061cd0141 100644
--- a/lib/gitlab/diff/file.rb
+++ b/lib/gitlab/diff/file.rb
@@ -46,11 +46,11 @@ module Gitlab
end
def added_lines
- diff_lines.select(&:added?).size
+ diff_lines.count(&:added?)
end
def removed_lines
- diff_lines.select(&:removed?).size
+ diff_lines.count(&:removed?)
end
end
end
diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb
index 496256700b8..403ebeec474 100644
--- a/lib/gitlab/fogbugz_import/importer.rb
+++ b/lib/gitlab/fogbugz_import/importer.rb
@@ -199,7 +199,7 @@ module Gitlab
s = s.gsub(/^#/, "\\#")
s = s.gsub(/^-/, "\\-")
s = s.gsub("`", "\\~")
- s = s.gsub("\r", "")
+ s = s.delete("\r")
s = s.gsub("\n", " \n")
s
end
diff --git a/lib/gitlab/fogbugz_import/project_creator.rb b/lib/gitlab/fogbugz_import/project_creator.rb
index 8b1b6f48ed5..e0163499e30 100644
--- a/lib/gitlab/fogbugz_import/project_creator.rb
+++ b/lib/gitlab/fogbugz_import/project_creator.rb
@@ -12,7 +12,8 @@ module Gitlab
end
def execute
- project = ::Projects::CreateService.new(current_user,
+ project = ::Projects::CreateService.new(
+ current_user,
name: repo.safe_name,
path: repo.path,
namespace: namespace,
diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb
index 0c350d7c675..f065cc5e9e9 100644
--- a/lib/gitlab/git.rb
+++ b/lib/gitlab/git.rb
@@ -20,6 +20,10 @@ module Gitlab
def blank_ref?(ref)
ref == BLANK_SHA
end
+
+ def version
+ Gitlab::VersionInfo.parse(Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} --version)).first)
+ end
end
end
end
diff --git a/lib/gitlab/gitlab_import/project_creator.rb b/lib/gitlab/gitlab_import/project_creator.rb
index d9452de6a50..7baaadb813c 100644
--- a/lib/gitlab/gitlab_import/project_creator.rb
+++ b/lib/gitlab/gitlab_import/project_creator.rb
@@ -11,7 +11,8 @@ module Gitlab
end
def execute
- project = ::Projects::CreateService.new(current_user,
+ project = ::Projects::CreateService.new(
+ current_user,
name: repo["name"],
path: repo["path"],
description: repo["description"],
diff --git a/lib/gitlab/gitorious_import/project_creator.rb b/lib/gitlab/gitorious_import/project_creator.rb
index cc9a91c91f4..8e22aa9286d 100644
--- a/lib/gitlab/gitorious_import/project_creator.rb
+++ b/lib/gitlab/gitorious_import/project_creator.rb
@@ -10,7 +10,8 @@ module Gitlab
end
def execute
- ::Projects::CreateService.new(current_user,
+ ::Projects::CreateService.new(
+ current_user,
name: repo.name,
path: repo.path,
description: repo.description,
diff --git a/lib/gitlab/google_code_import/importer.rb b/lib/gitlab/google_code_import/importer.rb
index 87fee28dc01..62da327931f 100644
--- a/lib/gitlab/google_code_import/importer.rb
+++ b/lib/gitlab/google_code_import/importer.rb
@@ -171,8 +171,6 @@ module Gitlab
when /\AMilestone:/
"#fee3ff"
- when *@closed_statuses.map { |s| nice_status_name(s) }
- "#cfcfcf"
when "Status: New"
"#428bca"
when "Status: Accepted"
@@ -199,6 +197,8 @@ module Gitlab
"#8e44ad"
when "Type: Other"
"#7f8c8d"
+ when *@closed_statuses.map { |s| nice_status_name(s) }
+ "#cfcfcf"
else
"#e2e2e2"
end
@@ -227,7 +227,7 @@ module Gitlab
s = s.gsub("`", "\\`")
# Carriage returns make me sad
- s = s.gsub("\r", "")
+ s = s.delete("\r")
# Markdown ignores single newlines, but we need them as <br />.
s = s.gsub("\n", " \n")
diff --git a/lib/gitlab/google_code_import/project_creator.rb b/lib/gitlab/google_code_import/project_creator.rb
index 1cb7d16aeb3..87821c23460 100644
--- a/lib/gitlab/google_code_import/project_creator.rb
+++ b/lib/gitlab/google_code_import/project_creator.rb
@@ -11,7 +11,8 @@ module Gitlab
end
def execute
- project = ::Projects::CreateService.new(current_user,
+ project = ::Projects::CreateService.new(
+ current_user,
name: repo.name,
path: repo.name,
description: repo.summary,
diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
deleted file mode 100644
index f4e2cefca51..00000000000
--- a/lib/gitlab/markdown.rb
+++ /dev/null
@@ -1,115 +0,0 @@
-require 'html/pipeline'
-
-module Gitlab
- # Custom parser for GitLab-flavored Markdown
- #
- # See the files in `lib/gitlab/markdown/` for specific processing information.
- module Markdown
- # Convert a Markdown String into an HTML-safe String of HTML
- #
- # Note that while the returned HTML will have been sanitized of dangerous
- # HTML, it may post a risk of information leakage if it's not also passed
- # through `post_process`.
- #
- # Also note that the returned String is always HTML, not XHTML. Views
- # requiring XHTML, such as Atom feeds, need to call `post_process` on the
- # result, providing the appropriate `pipeline` option.
- #
- # markdown - Markdown String
- # context - Hash of context options passed to our HTML Pipeline
- #
- # Returns an HTML-safe String
- def self.render(text, context = {})
- cache_key = context.delete(:cache_key)
- cache_key = full_cache_key(cache_key, context[:pipeline])
-
- if cache_key
- Rails.cache.fetch(cache_key) do
- cacheless_render(text, context)
- end
- else
- cacheless_render(text, context)
- end
- end
-
- def self.render_result(text, context = {})
- Pipeline[context[:pipeline]].call(text, context)
- end
-
- # Perform post-processing on an HTML String
- #
- # This method is used to perform state-dependent changes to a String of
- # HTML, such as removing references that the current user doesn't have
- # permission to make (`RedactorFilter`).
- #
- # html - String to process
- # context - Hash of options to customize output
- # :pipeline - Symbol pipeline type
- # :project - Project
- # :user - User object
- #
- # Returns an HTML-safe String
- def self.post_process(html, context)
- context = Pipeline[context[:pipeline]].transform_context(context)
-
- pipeline = Pipeline[:post_process]
- if context[:xhtml]
- pipeline.to_document(html, context).to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML)
- else
- pipeline.to_html(html, context)
- end.html_safe
- end
-
- private
-
- def self.cacheless_render(text, context = {})
- result = render_result(text, context)
-
- output = result[:output]
- if output.respond_to?(:to_html)
- output.to_html
- else
- output.to_s
- end
- end
-
- def self.full_cache_key(cache_key, pipeline_name)
- return unless cache_key
- ["markdown", *cache_key, pipeline_name || :full]
- end
-
- # Provide autoload paths for filters to prevent a circular dependency error
- autoload :AutolinkFilter, 'gitlab/markdown/filter/autolink_filter'
- autoload :CommitRangeReferenceFilter, 'gitlab/markdown/filter/commit_range_reference_filter'
- autoload :CommitReferenceFilter, 'gitlab/markdown/filter/commit_reference_filter'
- autoload :EmojiFilter, 'gitlab/markdown/filter/emoji_filter'
- autoload :ExternalIssueReferenceFilter, 'gitlab/markdown/filter/external_issue_reference_filter'
- autoload :ExternalLinkFilter, 'gitlab/markdown/filter/external_link_filter'
- autoload :IssueReferenceFilter, 'gitlab/markdown/filter/issue_reference_filter'
- autoload :LabelReferenceFilter, 'gitlab/markdown/filter/label_reference_filter'
- autoload :MarkdownFilter, 'gitlab/markdown/filter/markdown_filter'
- autoload :MergeRequestReferenceFilter, 'gitlab/markdown/filter/merge_request_reference_filter'
- autoload :RedactorFilter, 'gitlab/markdown/filter/redactor_filter'
- autoload :ReferenceGathererFilter, 'gitlab/markdown/filter/reference_gatherer_filter'
- autoload :RelativeLinkFilter, 'gitlab/markdown/filter/relative_link_filter'
- autoload :SanitizationFilter, 'gitlab/markdown/filter/sanitization_filter'
- autoload :SnippetReferenceFilter, 'gitlab/markdown/filter/snippet_reference_filter'
- autoload :SyntaxHighlightFilter, 'gitlab/markdown/filter/syntax_highlight_filter'
- autoload :TableOfContentsFilter, 'gitlab/markdown/filter/table_of_contents_filter'
- autoload :TaskListFilter, 'gitlab/markdown/filter/task_list_filter'
- autoload :UserReferenceFilter, 'gitlab/markdown/filter/user_reference_filter'
- autoload :UploadLinkFilter, 'gitlab/markdown/filter/upload_link_filter'
-
- autoload :AsciidocPipeline, 'gitlab/markdown/pipeline/asciidoc_pipeline'
- autoload :AtomPipeline, 'gitlab/markdown/pipeline/atom_pipeline'
- autoload :DescriptionPipeline, 'gitlab/markdown/pipeline/description_pipeline'
- autoload :EmailPipeline, 'gitlab/markdown/pipeline/email_pipeline'
- autoload :FullPipeline, 'gitlab/markdown/pipeline/full_pipeline'
- autoload :GfmPipeline, 'gitlab/markdown/pipeline/gfm_pipeline'
- autoload :NotePipeline, 'gitlab/markdown/pipeline/note_pipeline'
- autoload :PlainMarkdownPipeline, 'gitlab/markdown/pipeline/plain_markdown_pipeline'
- autoload :PostProcessPipeline, 'gitlab/markdown/pipeline/post_process_pipeline'
- autoload :ReferenceExtractionPipeline, 'gitlab/markdown/pipeline/reference_extraction_pipeline'
- autoload :SingleLinePipeline, 'gitlab/markdown/pipeline/single_line_pipeline'
- end
-end
diff --git a/lib/gitlab/markdown/cross_project_reference.rb b/lib/gitlab/markdown/cross_project_reference.rb
deleted file mode 100644
index 6ab04a584b0..00000000000
--- a/lib/gitlab/markdown/cross_project_reference.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require 'gitlab/markdown'
-
-module Gitlab
- module Markdown
- # Common methods for ReferenceFilters that support an optional cross-project
- # reference.
- module CrossProjectReference
- # Given a cross-project reference string, get the Project record
- #
- # Defaults to value of `context[:project]` if:
- # * No reference is given OR
- # * Reference given doesn't exist
- #
- # ref - String reference.
- #
- # Returns a Project, or nil if the reference can't be found
- def project_from_ref(ref)
- return context[:project] unless ref
-
- Project.find_with_namespace(ref)
- end
- end
- end
-end
diff --git a/lib/gitlab/markdown/pipeline.rb b/lib/gitlab/markdown/pipeline.rb
index d683756f95a..8f3f43c0e91 100644
--- a/lib/gitlab/markdown/pipeline.rb
+++ b/lib/gitlab/markdown/pipeline.rb
@@ -1,11 +1,11 @@
-require 'gitlab/markdown'
+require 'banzai'
module Gitlab
module Markdown
class Pipeline
def self.[](name)
name ||= :full
- Markdown.const_get("#{name.to_s.camelize}Pipeline")
+ const_get("#{name.to_s.camelize}Pipeline")
end
def self.filters
diff --git a/lib/gitlab/markdown/pipeline/asciidoc_pipeline.rb b/lib/gitlab/markdown/pipeline/asciidoc_pipeline.rb
deleted file mode 100644
index 6829b4acb95..00000000000
--- a/lib/gitlab/markdown/pipeline/asciidoc_pipeline.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require 'gitlab/markdown'
-
-module Gitlab
- module Markdown
- class AsciidocPipeline < Pipeline
- def self.filters
- [
- Gitlab::Markdown::RelativeLinkFilter
- ]
- end
- end
- end
-end
diff --git a/lib/gitlab/markdown/pipeline/atom_pipeline.rb b/lib/gitlab/markdown/pipeline/atom_pipeline.rb
deleted file mode 100644
index e151f8f5e5a..00000000000
--- a/lib/gitlab/markdown/pipeline/atom_pipeline.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-require 'gitlab/markdown'
-
-module Gitlab
- module Markdown
- class AtomPipeline < FullPipeline
- def self.transform_context(context)
- super(context).merge(
- only_path: false,
- xhtml: true
- )
- end
- end
- end
-end
diff --git a/lib/gitlab/markdown/pipeline/gfm_pipeline.rb b/lib/gitlab/markdown/pipeline/gfm_pipeline.rb
deleted file mode 100644
index ca90bd75d77..00000000000
--- a/lib/gitlab/markdown/pipeline/gfm_pipeline.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-require 'gitlab/markdown'
-
-module Gitlab
- module Markdown
- class GfmPipeline < Pipeline
- def self.filters
- @filters ||= [
- Gitlab::Markdown::SyntaxHighlightFilter,
- Gitlab::Markdown::SanitizationFilter,
-
- Gitlab::Markdown::UploadLinkFilter,
- Gitlab::Markdown::EmojiFilter,
- Gitlab::Markdown::TableOfContentsFilter,
- Gitlab::Markdown::AutolinkFilter,
- Gitlab::Markdown::ExternalLinkFilter,
-
- Gitlab::Markdown::UserReferenceFilter,
- Gitlab::Markdown::IssueReferenceFilter,
- Gitlab::Markdown::ExternalIssueReferenceFilter,
- Gitlab::Markdown::MergeRequestReferenceFilter,
- Gitlab::Markdown::SnippetReferenceFilter,
- Gitlab::Markdown::CommitRangeReferenceFilter,
- Gitlab::Markdown::CommitReferenceFilter,
- Gitlab::Markdown::LabelReferenceFilter,
-
- Gitlab::Markdown::TaskListFilter
- ]
- end
-
- def self.transform_context(context)
- context.merge(
- only_path: true,
-
- # EmojiFilter
- asset_host: Gitlab::Application.config.asset_host,
- asset_root: Gitlab.config.gitlab.base_url
- )
- end
- end
- end
-end
diff --git a/lib/gitlab/markdown/pipeline/plain_markdown_pipeline.rb b/lib/gitlab/markdown/pipeline/plain_markdown_pipeline.rb
deleted file mode 100644
index 0abb93f8a03..00000000000
--- a/lib/gitlab/markdown/pipeline/plain_markdown_pipeline.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require 'gitlab/markdown'
-
-module Gitlab
- module Markdown
- class PlainMarkdownPipeline < Pipeline
- def self.filters
- [
- Gitlab::Markdown::MarkdownFilter
- ]
- end
- end
- end
-end
diff --git a/lib/gitlab/markdown/pipeline/post_process_pipeline.rb b/lib/gitlab/markdown/pipeline/post_process_pipeline.rb
deleted file mode 100644
index 60cc32f490e..00000000000
--- a/lib/gitlab/markdown/pipeline/post_process_pipeline.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require 'gitlab/markdown'
-
-module Gitlab
- module Markdown
- class PostProcessPipeline < Pipeline
- def self.filters
- [
- Gitlab::Markdown::RelativeLinkFilter,
- Gitlab::Markdown::RedactorFilter
- ]
- end
-
- def self.transform_context(context)
- context.merge(
- post_process: true
- )
- end
- end
- end
-end
diff --git a/lib/gitlab/markdown/pipeline/reference_extraction_pipeline.rb b/lib/gitlab/markdown/pipeline/reference_extraction_pipeline.rb
deleted file mode 100644
index a89ab462bac..00000000000
--- a/lib/gitlab/markdown/pipeline/reference_extraction_pipeline.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require 'gitlab/markdown'
-
-module Gitlab
- module Markdown
- class ReferenceExtractionPipeline < Pipeline
- def self.filters
- [
- Gitlab::Markdown::ReferenceGathererFilter
- ]
- end
- end
- end
-end
diff --git a/lib/gitlab/markdown/pipeline/single_line_pipeline.rb b/lib/gitlab/markdown/pipeline/single_line_pipeline.rb
deleted file mode 100644
index 2f24927b879..00000000000
--- a/lib/gitlab/markdown/pipeline/single_line_pipeline.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'gitlab/markdown'
-
-module Gitlab
- module Markdown
- class SingleLinePipeline < GfmPipeline
-
- end
- end
-end
diff --git a/lib/gitlab/o_auth/session.rb b/lib/gitlab/o_auth/session.rb
new file mode 100644
index 00000000000..f33bfd0bd0e
--- /dev/null
+++ b/lib/gitlab/o_auth/session.rb
@@ -0,0 +1,17 @@
+module Gitlab
+ module OAuth
+ module Session
+ def self.create(provider, ticket)
+ Rails.cache.write("gitlab:#{provider}:#{ticket}", ticket, expires_in: Gitlab.config.omniauth.cas3.session_duration)
+ end
+
+ def self.destroy(provider, ticket)
+ Rails.cache.delete("gitlab:#{provider}:#{ticket}")
+ end
+
+ def self.valid?(provider, ticket)
+ Rails.cache.read("gitlab:#{provider}:#{ticket}").present?
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index e83167fa7d7..0a70d21b1ce 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -1,62 +1,37 @@
-require 'gitlab/markdown'
+require 'banzai'
module Gitlab
# Extract possible GFM references from an arbitrary String for further processing.
- class ReferenceExtractor
- attr_accessor :project, :current_user, :load_lazy_references
+ class ReferenceExtractor < Banzai::ReferenceExtractor
+ attr_accessor :project, :current_user
- def initialize(project, current_user = nil, load_lazy_references: true)
+ def initialize(project, current_user = nil)
@project = project
@current_user = current_user
- @load_lazy_references = load_lazy_references
- @texts = []
@references = {}
+
+ super()
end
- def analyze(text, options = {})
- @texts << Gitlab::Markdown.render(text, options.merge(project: project))
+ def analyze(text, context = {})
+ super(text, context.merge(project: project))
end
- %i(user label issue merge_request snippet commit commit_range).each do |type|
+ %i(user label merge_request snippet commit commit_range).each do |type|
define_method("#{type}s") do
- @references[type] ||= pipeline_result(type)
+ @references[type] ||= references(type, project: project, current_user: current_user)
end
end
- private
-
- # Instantiate and call HTML::Pipeline with a single reference filter type,
- # returning the result
- #
- # filter_type - Symbol reference type (e.g., :commit, :issue, etc.)
- #
- # Returns the results Array for the requested filter type
- def pipeline_result(filter_type)
- filter = Gitlab::Markdown::ReferenceFilter[filter_type]
-
- context = {
- pipeline: :reference_extraction,
-
- project: project,
- current_user: current_user,
+ def issues
+ options = { project: project, current_user: current_user }
- # ReferenceGathererFilter
- load_lazy_references: false,
- reference_filter: filter
- }
-
- values = @texts.flat_map do |html|
- text_context = context.dup
- result = Gitlab::Markdown.render_result(html, text_context)
- result[:references][filter_type]
- end.uniq
-
- if @load_lazy_references
- values = Gitlab::Markdown::ReferenceFilter::LazyReference.load(values).uniq
+ if project && project.jira_tracker?
+ @references[:external_issue] ||= references(:external_issue, options)
+ else
+ @references[:issue] ||= references(:issue, options)
end
-
- values
end
end
end
diff --git a/lib/rouge/formatters/html_gitlab.rb b/lib/rouge/formatters/html_gitlab.rb
index 6762ca47c32..8c309efc7b8 100644
--- a/lib/rouge/formatters/html_gitlab.rb
+++ b/lib/rouge/formatters/html_gitlab.rb
@@ -39,7 +39,7 @@ module Rouge
lineanchorsid: 'L',
anchorlinenos: false,
inline_theme: nil
- )
+ )
@nowrap = nowrap
@cssclass = cssclass
@linenos = linenos
diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab
index 43fda6fa92e..aa2da92c138 100755
--- a/lib/support/init.d/gitlab
+++ b/lib/support/init.d/gitlab
@@ -33,12 +33,13 @@ app_user="git"
app_root="/home/$app_user/gitlab"
pid_path="$app_root/tmp/pids"
socket_path="$app_root/tmp/sockets"
+rails_socket="$socket_path/gitlab.socket"
web_server_pid_path="$pid_path/unicorn.pid"
sidekiq_pid_path="$pid_path/sidekiq.pid"
mail_room_enabled=false
mail_room_pid_path="$pid_path/mail_room.pid"
gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid"
-gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080"
+gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080 -authSocket $rails_socket -documentRoot $app_root/public"
gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log"
shell_path="/bin/bash"
diff --git a/lib/support/init.d/gitlab.default.example b/lib/support/init.d/gitlab.default.example
index 79ae8e0ae55..7fc495db545 100755
--- a/lib/support/init.d/gitlab.default.example
+++ b/lib/support/init.d/gitlab.default.example
@@ -36,7 +36,7 @@ gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid"
# '-listenNetwork tcp -listenAddr localhost:8181'.
# The -authBackend setting tells gitlab-workhorse where it can reach
# Unicorn.
-gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080"
+gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080 -authSocket $socket_path/gitlab.socket -documentRoot $app_root/public"
gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log"
# mail_room_enabled specifies whether mail_room, which is used to process incoming email, is enabled.
diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab
index 2a79fbdcf93..fc5475c4eef 100644
--- a/lib/support/nginx/gitlab
+++ b/lib/support/nginx/gitlab
@@ -10,34 +10,12 @@
## If you change this file in a Merge Request, please also create
## a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
##
-##################################
-## CHUNKED TRANSFER ##
-##################################
-##
-## It is a known issue that Git-over-HTTP requires chunked transfer encoding [0]
-## which is not supported by Nginx < 1.3.9 [1]. As a result, pushing a large object
-## with Git (i.e. a single large file) can lead to a 411 error. In theory you can get
-## around this by tweaking this configuration file and either:
-## - installing an old version of Nginx with the chunkin module [2] compiled in, or
-## - using a newer version of Nginx.
-##
-## At the time of writing we do not know if either of these theoretical solutions works.
-## As a workaround users can use Git over SSH to push large files.
-##
-## [0] https://git.kernel.org/cgit/git/git.git/tree/Documentation/technical/http-protocol.txt#n99
-## [1] https://github.com/agentzh/chunkin-nginx-module#status
-## [2] https://github.com/agentzh/chunkin-nginx-module
-##
###################################
## configuration ##
###################################
##
## See installation.md#using-https for additional HTTPS configuration details.
-upstream gitlab {
- server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0;
-}
-
upstream gitlab-workhorse {
server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0;
}
@@ -54,10 +32,6 @@ server {
server_tokens off; ## Don't show the nginx version number, a security best practice
root /home/git/gitlab/public;
- ## Increase this if you want to upload large attachments
- ## Or if you want to accept large git objects over http
- client_max_body_size 20m;
-
## See app/controllers/application_controller.rb for headers set
## Individual nginx logs for this GitLab vhost
@@ -65,103 +39,8 @@ server {
error_log /var/log/nginx/gitlab_error.log;
location / {
- ## Serve static files from defined root folder.
- ## @gitlab is a named location for the upstream fallback, see below.
- try_files $uri /index.html $uri.html @gitlab;
- }
-
- ## We route uploads through GitLab to prevent XSS and enforce access control.
- location /uploads/ {
- ## If you use HTTPS make sure you disable gzip compression
- ## to be safe against BREACH attack.
- # gzip off;
-
- ## https://github.com/gitlabhq/gitlabhq/issues/694
- ## Some requests take more than 30 seconds.
- proxy_read_timeout 300;
- proxy_connect_timeout 300;
- proxy_redirect off;
-
- proxy_set_header Host $http_host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Frame-Options SAMEORIGIN;
-
- proxy_pass http://gitlab;
- }
-
- ## If a file, which is not found in the root folder is requested,
- ## then the proxy passes the request to the upsteam (gitlab unicorn).
- location @gitlab {
- ## If you use HTTPS make sure you disable gzip compression
- ## to be safe against BREACH attack.
- # gzip off;
-
- ## https://github.com/gitlabhq/gitlabhq/issues/694
- ## Some requests take more than 30 seconds.
- proxy_read_timeout 300;
- proxy_connect_timeout 300;
- proxy_redirect off;
-
- proxy_set_header Host $http_host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Frame-Options SAMEORIGIN;
-
- proxy_pass http://gitlab;
- }
-
- location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects {
- client_max_body_size 0;
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ {
- client_max_body_size 0;
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- location ~ ^/[\w\.-]+/[\w\.-]+/repository/archive {
- client_max_body_size 0;
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- location ~ ^/api/v3/projects/.*/repository/archive {
- client_max_body_size 0;
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- # Build artifacts should be submitted to this location
- location ~ ^/[\w\.-]+/[\w\.-]+/builds/download {
client_max_body_size 0;
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- # Build artifacts should be submitted to this location
- location ~ /ci/api/v1/builds/[0-9]+/artifacts {
- client_max_body_size 0;
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- location @gitlab-workhorse {
- client_max_body_size 0;
- ## If you use HTTPS make sure you disable gzip compression
- ## to be safe against BREACH attack.
- # gzip off;
+ gzip off;
## https://github.com/gitlabhq/gitlabhq/issues/694
## Some requests take more than 30 seconds.
@@ -169,14 +48,7 @@ server {
proxy_connect_timeout 300;
proxy_redirect off;
- # Do not buffer Git HTTP responses
- proxy_buffering off;
-
- # The following settings only work with NGINX 1.7.11 or newer
- #
- # # Pass chunked request bodies to gitlab-workhorse as-is
- # proxy_request_buffering off;
- # proxy_http_version 1.1;
+ proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
@@ -185,18 +57,4 @@ server {
proxy_pass http://gitlab-workhorse;
}
-
- ## Enable gzip compression as per rails guide:
- ## http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression
- ## WARNING: If you are using relative urls remove the block below
- ## See config/application.rb under "Relative url support" for the list of
- ## other files that need to be changed for relative url support
- location ~ ^/(assets)/ {
- root /home/git/gitlab/public;
- gzip_static on; # to serve pre-gzipped version
- expires max;
- add_header Cache-Control public;
- }
-
- error_page 502 /502.html;
}
diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl
index 79fe1474821..1e5f85413ec 100644
--- a/lib/support/nginx/gitlab-ssl
+++ b/lib/support/nginx/gitlab-ssl
@@ -14,34 +14,12 @@
## If you change this file in a Merge Request, please also create
## a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
##
-##################################
-## CHUNKED TRANSFER ##
-##################################
-##
-## It is a known issue that Git-over-HTTP requires chunked transfer encoding [0]
-## which is not supported by Nginx < 1.3.9 [1]. As a result, pushing a large object
-## with Git (i.e. a single large file) can lead to a 411 error. In theory you can get
-## around this by tweaking this configuration file and either:
-## - installing an old version of Nginx with the chunkin module [2] compiled in, or
-## - using a newer version of Nginx.
-##
-## At the time of writing we do not know if either of these theoretical solutions works.
-## As a workaround users can use Git over SSH to push large files.
-##
-## [0] https://git.kernel.org/cgit/git/git.git/tree/Documentation/technical/http-protocol.txt#n99
-## [1] https://github.com/agentzh/chunkin-nginx-module#status
-## [2] https://github.com/agentzh/chunkin-nginx-module
-##
###################################
## configuration ##
###################################
##
## See installation.md#using-https for additional HTTPS configuration details.
-upstream gitlab {
- server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0;
-}
-
upstream gitlab-workhorse {
server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0;
}
@@ -61,7 +39,6 @@ server {
error_log /var/log/nginx/gitlab_error.log;
}
-
## HTTPS host
server {
listen 0.0.0.0:443 ssl;
@@ -70,10 +47,6 @@ server {
server_tokens off; ## Don't show the nginx version number, a security best practice
root /home/git/gitlab/public;
- ## Increase this if you want to upload large attachments
- ## Or if you want to accept large git objects over http
- client_max_body_size 20m;
-
## Strong SSL Security
## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/
ssl on;
@@ -110,104 +83,7 @@ server {
error_log /var/log/nginx/gitlab_error.log;
location / {
- ## Serve static files from defined root folder.
- ## @gitlab is a named location for the upstream fallback, see below.
- try_files $uri /index.html $uri.html @gitlab;
- }
-
- ## We route uploads through GitLab to prevent XSS and enforce access control.
- location /uploads/ {
- ## If you use HTTPS make sure you disable gzip compression
- ## to be safe against BREACH attack.
- gzip off;
-
- ## https://github.com/gitlabhq/gitlabhq/issues/694
- ## Some requests take more than 30 seconds.
- proxy_read_timeout 300;
- proxy_connect_timeout 300;
- proxy_redirect off;
-
- proxy_set_header Host $http_host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-Ssl on;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Frame-Options SAMEORIGIN;
-
- proxy_pass http://gitlab;
- }
-
- ## If a file, which is not found in the root folder is requested,
- ## then the proxy passes the request to the upsteam (gitlab unicorn).
- location @gitlab {
- ## If you use HTTPS make sure you disable gzip compression
- ## to be safe against BREACH attack.
- gzip off;
-
- ## https://github.com/gitlabhq/gitlabhq/issues/694
- ## Some requests take more than 30 seconds.
- proxy_read_timeout 300;
- proxy_connect_timeout 300;
- proxy_redirect off;
-
- proxy_set_header Host $http_host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-Ssl on;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Frame-Options SAMEORIGIN;
-
- proxy_pass http://gitlab;
- }
-
- location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects {
- client_max_body_size 0;
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ {
- client_max_body_size 0;
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- location ~ ^/[\w\.-]+/[\w\.-]+/repository/archive {
client_max_body_size 0;
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- location ~ ^/api/v3/projects/.*/repository/archive {
- client_max_body_size 0;
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- # Build artifacts should be submitted to this location
- location ~ ^/[\w\.-]+/[\w\.-]+/builds/download {
- client_max_body_size 0;
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- # Build artifacts should be submitted to this location
- location ~ /ci/api/v1/builds/[0-9]+/artifacts {
- client_max_body_size 0;
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- location @gitlab-workhorse {
- client_max_body_size 0;
- ## If you use HTTPS make sure you disable gzip compression
- ## to be safe against BREACH attack.
gzip off;
## https://github.com/gitlabhq/gitlabhq/issues/694
@@ -216,14 +92,7 @@ server {
proxy_connect_timeout 300;
proxy_redirect off;
- # Do not buffer Git HTTP responses
- proxy_buffering off;
-
- # The following settings only work with NGINX 1.7.11 or newer
- #
- # # Pass chunked request bodies to gitlab-workhorse as-is
- # proxy_request_buffering off;
- # proxy_http_version 1.1;
+ proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
@@ -232,18 +101,4 @@ server {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://gitlab-workhorse;
}
-
- ## Enable gzip compression as per rails guide:
- ## http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression
- ## WARNING: If you are using relative urls remove the block below
- ## See config/application.rb under "Relative url support" for the list of
- ## other files that need to be changed for relative url support
- location ~ ^/(assets)/ {
- root /home/git/gitlab/public;
- gzip_static on; # to serve pre-gzipped version
- expires max;
- add_header Cache-Control public;
- }
-
- error_page 502 /502.html;
}
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index b5af3d88b4c..0469c5a61c3 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -822,10 +822,27 @@ namespace :gitlab do
namespace_dirs.each do |namespace_dir|
repo_dirs = Dir.glob(File.join(namespace_dir, '*'))
- repo_dirs.each do |dir|
- puts "\nChecking repo at #{dir}"
- system(*%W(#{Gitlab.config.git.bin_path} fsck), chdir: dir)
- end
+ repo_dirs.each { |repo_dir| check_repo_integrity(repo_dir) }
+ end
+ end
+ end
+
+ namespace :user do
+ desc "GitLab | Check the integrity of a specific user's repositories"
+ task :check_repos, [:username] => :environment do |t, args|
+ username = args[:username] || prompt("Check repository integrity for which username? ".blue)
+ user = User.find_by(username: username)
+ if user
+ repo_dirs = user.authorized_projects.map do |p|
+ File.join(
+ Gitlab.config.gitlab_shell.repos_path,
+ "#{p.path_with_namespace}.git"
+ )
+ end
+
+ repo_dirs.each { |repo_dir| check_repo_integrity(repo_dir) }
+ else
+ puts "\nUser '#{username}' not found".red
end
end
end
@@ -952,4 +969,35 @@ namespace :gitlab do
false
end
end
+
+ def check_repo_integrity(repo_dir)
+ puts "\nChecking repo at #{repo_dir.yellow}"
+
+ git_fsck(repo_dir)
+ check_config_lock(repo_dir)
+ check_ref_locks(repo_dir)
+ end
+
+ def git_fsck(repo_dir)
+ puts "Running `git fsck`".yellow
+ system(*%W(#{Gitlab.config.git.bin_path} fsck), chdir: repo_dir)
+ end
+
+ def check_config_lock(repo_dir)
+ config_exists = File.exist?(File.join(repo_dir,'config.lock'))
+ config_output = config_exists ? 'yes'.red : 'no'.green
+ puts "'config.lock' file exists?".yellow + " ... #{config_output}"
+ end
+
+ def check_ref_locks(repo_dir)
+ lock_files = Dir.glob(File.join(repo_dir,'refs/heads/*.lock'))
+ if lock_files.present?
+ puts "Ref lock files exist:".red
+ lock_files.each do |lock_file|
+ puts " #{lock_file}"
+ end
+ else
+ puts "No ref lock files exist".green
+ end
+ end
end
diff --git a/spec/benchmarks/lib/gitlab/markdown/reference_filter_spec.rb b/spec/benchmarks/lib/gitlab/markdown/reference_filter_spec.rb
index 34cd9f7e4eb..3855763b200 100644
--- a/spec/benchmarks/lib/gitlab/markdown/reference_filter_spec.rb
+++ b/spec/benchmarks/lib/gitlab/markdown/reference_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Markdown::ReferenceFilter, benchmark: true do
+describe Banzai::Filter::ReferenceFilter, benchmark: true do
let(:input) do
html = <<-EOF
<p>Hello @alice and @bob, how are you doing today?</p>
diff --git a/spec/factories.rb b/spec/factories.rb
index 4bf93adabe2..d6b4efa9a03 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -43,7 +43,8 @@ FactoryGirl.define do
end
after(:create) do |user, evaluator|
- user.identities << create(:identity,
+ user.identities << create(
+ :identity,
provider: evaluator.provider,
extern_uid: evaluator.extern_uid
)
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index ecc85376ffc..fe7f07f5b75 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -19,30 +19,13 @@ describe 'Commits' do
let!(:build) { FactoryGirl.create :ci_build, commit: commit }
describe 'Project commits' do
- context 'builds enabled' do
- context '.gitlab-ci.yml found' do
- before do
- visit namespace_project_commits_path(project.namespace, project, :master)
- end
-
- it 'should show build status' do
- page.within("//li[@id='commit-#{commit.short_sha}']") do
- expect(page).to have_css(".ci-status-link")
- end
- end
- end
+ before do
+ visit namespace_project_commits_path(project.namespace, project, :master)
+ end
- context 'no .gitlab-ci.yml found' do
- before do
- stub_ci_commit_yaml_file(nil)
- visit namespace_project_commits_path(project.namespace, project, :master)
- end
-
- it 'should not show build status' do
- page.within("//li[@id='commit-#{commit.short_sha}']") do
- expect(page).to have_no_css(".ci-status-link")
- end
- end
+ it 'should show build status' do
+ page.within("//li[@id='commit-#{commit.short_sha}']") do
+ expect(page).to have_css(".ci-status-link")
end
end
end
diff --git a/spec/features/issues/filter_by_milestone_spec.rb b/spec/features/issues/filter_by_milestone_spec.rb
index f600f8684ac..38c8d343ce3 100644
--- a/spec/features/issues/filter_by_milestone_spec.rb
+++ b/spec/features/issues/filter_by_milestone_spec.rb
@@ -13,7 +13,7 @@ feature 'Issue filtering by Milestone', feature: true do
visit_issues(project)
filter_by_milestone(Milestone::None.title)
- expect(page).to have_css('.issue-title', count: 1)
+ expect(page).to have_css('.title', count: 1)
end
scenario 'filters by a specific Milestone', js: true do
@@ -23,7 +23,7 @@ feature 'Issue filtering by Milestone', feature: true do
visit_issues(project)
filter_by_milestone(milestone.title)
- expect(page).to have_css('.issue-title', count: 1)
+ expect(page).to have_css('.title', count: 1)
end
def visit_issues(project)
diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb
new file mode 100644
index 00000000000..e4efdbe2421
--- /dev/null
+++ b/spec/features/issues/note_polling_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+feature 'Issue notes polling' do
+ let!(:project) { create(:project, :public) }
+ let!(:issue) { create(:issue, project: project) }
+
+ background do
+ visit namespace_project_issue_path(project.namespace, project, issue)
+ end
+
+ scenario 'Another user adds a comment to an issue', js: true do
+ note = create(:note_on_issue, noteable: issue, note: 'Looks good!')
+ page.execute_script('notes.refresh();')
+ expect(page).to have_selector("#note_#{note.id}", text: 'Looks good!')
+ 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
index 28a46a0725d..7aa7eb965e9 100644
--- a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb
+++ b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb
@@ -21,12 +21,12 @@ feature 'Merge When Build Succeeds', feature: true, js: true do
end
it 'displays the Merge When Build Succeeds button' do
- expect(page).to have_link "Merge When Build Succeeds"
+ expect(page).to have_button "Merge When Build Succeeds"
end
context "Merge When Build succeeds enabled" do
before do
- click_link "Merge When Build Succeeds"
+ click_button "Merge When Build Succeeds"
end
it 'activates Merge When Build Succeeds feature' do
@@ -58,7 +58,7 @@ feature 'Merge When Build Succeeds', feature: true, js: true do
it 'cancels the automatic merge' do
click_link "Cancel Automatic Merge"
- expect(page).to have_link "Merge When Build Succeeds"
+ expect(page).to have_button "Merge When Build Succeeds"
visit_merge_request(merge_request) # Needed to refresh the page
expect(page).to have_content "Canceled the automatic merge"
diff --git a/spec/features/security/group_access_spec.rb b/spec/features/security/group_access_spec.rb
index 4b78e3a61f0..65f8073c693 100644
--- a/spec/features/security/group_access_spec.rb
+++ b/spec/features/security/group_access_spec.rb
@@ -16,11 +16,11 @@ describe 'Group access', feature: true do
end
end
- def group_member(access_level, group = group)
+ def group_member(access_level, grp = group())
level = Object.const_get("Gitlab::Access::#{access_level.upcase}")
create(:user).tap do |user|
- group.add_user(user, level)
+ grp.add_user(user, level)
end
end
diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index fca3c77fc64..b7368cca29d 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -47,7 +47,7 @@ feature 'Task Lists', feature: true do
it 'contains the required selectors' do
visit_issue(project, issue)
- container = '.issue-details .description.js-task-list-container'
+ container = '.detail-page-description .description.js-task-list-container'
expect(page).to have_selector(container)
expect(page).to have_selector("#{container} .wiki .task-list .task-list-item .task-list-item-checkbox")
@@ -123,7 +123,7 @@ feature 'Task Lists', feature: true do
it 'contains the required selectors' do
visit_merge_request(project, merge)
- container = '.merge-request-details .description.js-task-list-container'
+ container = '.detail-page-description .description.js-task-list-container'
expect(page).to have_selector(container)
expect(page).to have_selector("#{container} .wiki .task-list .task-list-item .task-list-item-checkbox")
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 5568f06639c..68527c3a4f8 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -263,11 +263,12 @@ describe ApplicationHelper do
end
it 'includes a default js-timeago class' do
- expect(element.attr('class')).to eq 'time_ago js-timeago'
+ expect(element.attr('class')).to eq 'time_ago js-timeago js-timeago-pending'
end
it 'accepts a custom html_class' do
- expect(element(html_class: 'custom_class').attr('class')).to eq 'custom_class js-timeago'
+ expect(element(html_class: 'custom_class').attr('class')).
+ to eq 'custom_class js-timeago js-timeago-pending'
end
it 'accepts a custom tooltip placement' do
@@ -278,7 +279,7 @@ describe ApplicationHelper do
el = element.next_element
expect(el.name).to eq 'script'
- expect(el.text).to include "$('.js-timeago').last().timeago()"
+ expect(el.text).to include "$('.js-timeago-pending').removeClass('js-timeago-pending').timeago()"
end
it 'allows the script tag to be excluded' do
diff --git a/spec/helpers/ci_status_helper_spec.rb b/spec/helpers/ci_status_helper_spec.rb
index 7fc53eb1472..4f8d9c67262 100644
--- a/spec/helpers/ci_status_helper_spec.rb
+++ b/spec/helpers/ci_status_helper_spec.rb
@@ -6,13 +6,8 @@ describe CiStatusHelper do
let(:success_commit) { double("Ci::Commit", status: 'success') }
let(:failed_commit) { double("Ci::Commit", status: 'failed') }
- describe 'ci_status_color' do
- it { expect(ci_status_icon(success_commit)).to include('fa-check') }
- it { expect(ci_status_icon(failed_commit)).to include('fa-close') }
- end
-
- describe 'ci_status_color' do
- it { expect(ci_status_color(success_commit)).to eq('green') }
- it { expect(ci_status_color(failed_commit)).to eq('red') }
+ describe 'ci_status_icon' do
+ it { expect(helper.ci_status_icon(success_commit)).to include('fa-check') }
+ it { expect(helper.ci_status_icon(failed_commit)).to include('fa-close') }
end
end
diff --git a/spec/helpers/groups_helper.rb b/spec/helpers/groups_helper.rb
index 5d174460681..4ea90a80a92 100644
--- a/spec/helpers/groups_helper.rb
+++ b/spec/helpers/groups_helper.rb
@@ -9,7 +9,7 @@ describe GroupsHelper do
group.avatar = File.open(avatar_file_path)
group.save!
expect(group_icon(group.path).to_s).
- to match("/uploads/group/avatar/#{ group.id }/banana_sample.gif")
+ to match("/uploads/group/avatar/#{group.id}/banana_sample.gif")
end
it 'should give default avatar_icon when no avatar is present' do
diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb
index 0ef1efb8bce..28fb3216f60 100644
--- a/spec/helpers/merge_requests_helper_spec.rb
+++ b/spec/helpers/merge_requests_helper_spec.rb
@@ -1,24 +1,37 @@
require 'spec_helper'
describe MergeRequestsHelper do
- describe "#issues_sentence" do
+ describe '#issues_sentence' do
subject { issues_sentence(issues) }
let(:issues) do
[build(:issue, iid: 1), build(:issue, iid: 2), build(:issue, iid: 3)]
end
it { is_expected.to eq('#1, #2, and #3') }
+
+ context 'for JIRA issues' do
+ let(:project) { create(:project) }
+ let(:issues) do
+ [
+ JiraIssue.new('JIRA-123', project),
+ JiraIssue.new('JIRA-456', project),
+ JiraIssue.new('FOOBAR-7890', project)
+ ]
+ end
+
+ it { is_expected.to eq('FOOBAR-7890, JIRA-123, and JIRA-456') }
+ end
end
- describe "#format_mr_branch_names" do
- describe "within the same project" do
+ describe '#format_mr_branch_names' do
+ describe 'within the same project' do
let(:merge_request) { create(:merge_request) }
subject { format_mr_branch_names(merge_request) }
it { is_expected.to eq([merge_request.source_branch, merge_request.target_branch]) }
end
- describe "within different projects" do
+ describe 'within different projects' do
let(:project) { create(:project) }
let(:fork_project) { create(:project, forked_from_project: project) }
let(:merge_request) { create(:merge_request, source_project: fork_project, target_project: project) }
diff --git a/spec/javascripts/fixtures/issues_show.html.haml b/spec/javascripts/fixtures/issues_show.html.haml
index 7e8b2a64351..8447dfdda32 100644
--- a/spec/javascripts/fixtures/issues_show.html.haml
+++ b/spec/javascripts/fixtures/issues_show.html.haml
@@ -1,6 +1,6 @@
%a.btn-close
-.issue-details
+.detail-page-description
.description.js-task-list-container
.wiki
%ul.task-list
diff --git a/spec/javascripts/fixtures/merge_requests_show.html.haml b/spec/javascripts/fixtures/merge_requests_show.html.haml
index f0c622935f8..8447dfdda32 100644
--- a/spec/javascripts/fixtures/merge_requests_show.html.haml
+++ b/spec/javascripts/fixtures/merge_requests_show.html.haml
@@ -1,6 +1,6 @@
%a.btn-close
-.merge-request-details
+.detail-page-description
.description.js-task-list-container
.wiki
%ul.task-list
diff --git a/spec/lib/gitlab/markdown/cross_project_reference_spec.rb b/spec/lib/banzai/cross_project_reference_spec.rb
index f594fe4ccf6..81b9a513ce3 100644
--- a/spec/lib/gitlab/markdown/cross_project_reference_spec.rb
+++ b/spec/lib/banzai/cross_project_reference_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Markdown::CrossProjectReference, lib: true do
+describe Banzai::CrossProjectReference, lib: true do
include described_class
describe '#project_from_ref' do
diff --git a/spec/lib/gitlab/markdown/filter/autolink_filter_spec.rb b/spec/lib/banzai/filter/autolink_filter_spec.rb
index a0844aee559..84c2ddf444e 100644
--- a/spec/lib/gitlab/markdown/filter/autolink_filter_spec.rb
+++ b/spec/lib/banzai/filter/autolink_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Markdown::AutolinkFilter, lib: true do
+describe Banzai::Filter::AutolinkFilter, lib: true do
include FilterSpecHelper
let(:link) { 'http://about.gitlab.com/' }
diff --git a/spec/lib/gitlab/markdown/filter/commit_range_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
index 570c9767628..c2a8ad36c30 100644
--- a/spec/lib/gitlab/markdown/filter/commit_range_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Markdown::CommitRangeReferenceFilter, lib: true do
+describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
include FilterSpecHelper
let(:project) { create(:project, :public) }
diff --git a/spec/lib/gitlab/markdown/filter/commit_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
index 76e7957bbb9..473534ba68a 100644
--- a/spec/lib/gitlab/markdown/filter/commit_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Markdown::CommitReferenceFilter, lib: true do
+describe Banzai::Filter::CommitReferenceFilter, lib: true do
include FilterSpecHelper
let(:project) { create(:project, :public) }
diff --git a/spec/lib/gitlab/markdown/filter/emoji_filter_spec.rb b/spec/lib/banzai/filter/emoji_filter_spec.rb
index ea9b81862cf..cf314058158 100644
--- a/spec/lib/gitlab/markdown/filter/emoji_filter_spec.rb
+++ b/spec/lib/banzai/filter/emoji_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Markdown::EmojiFilter, lib: true do
+describe Banzai::Filter::EmojiFilter, lib: true do
include FilterSpecHelper
before do
diff --git a/spec/lib/gitlab/markdown/filter/external_issue_reference_filter_spec.rb b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
index d8420102648..953466679e4 100644
--- a/spec/lib/gitlab/markdown/filter/external_issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Markdown::ExternalIssueReferenceFilter, lib: true do
+describe Banzai::Filter::ExternalIssueReferenceFilter, lib: true do
include FilterSpecHelper
def helper
diff --git a/spec/lib/gitlab/markdown/filter/external_link_filter_spec.rb b/spec/lib/banzai/filter/external_link_filter_spec.rb
index e559f5741cc..e3a8e15330e 100644
--- a/spec/lib/gitlab/markdown/filter/external_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/external_link_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Markdown::ExternalLinkFilter, lib: true do
+describe Banzai::Filter::ExternalLinkFilter, lib: true do
include FilterSpecHelper
it 'ignores elements without an href attribute' do
diff --git a/spec/lib/gitlab/markdown/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
index 1aa5d44568e..5a0d3d577a8 100644
--- a/spec/lib/gitlab/markdown/filter/issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Markdown::IssueReferenceFilter, lib: true do
+describe Banzai::Filter::IssueReferenceFilter, lib: true do
include FilterSpecHelper
def helper
diff --git a/spec/lib/gitlab/markdown/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index 4fcbb329fe4..b46ccc47605 100644
--- a/spec/lib/gitlab/markdown/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
require 'html/pipeline'
-describe Gitlab::Markdown::LabelReferenceFilter, lib: true do
+describe Banzai::Filter::LabelReferenceFilter, lib: true do
include FilterSpecHelper
let(:project) { create(:empty_project, :public) }
diff --git a/spec/lib/gitlab/markdown/filter/merge_request_reference_filter_spec.rb b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
index 589550e15c4..352710df307 100644
--- a/spec/lib/gitlab/markdown/filter/merge_request_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Markdown::MergeRequestReferenceFilter, lib: true do
+describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
include FilterSpecHelper
let(:project) { create(:project, :public) }
diff --git a/spec/lib/gitlab/markdown/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb
index 9e6ee9f0d61..e9bb388e361 100644
--- a/spec/lib/gitlab/markdown/filter/redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/redactor_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Markdown::RedactorFilter, lib: true do
+describe Banzai::Filter::RedactorFilter, lib: true do
include ActionView::Helpers::UrlHelper
include FilterSpecHelper
diff --git a/spec/lib/gitlab/markdown/filter/reference_gatherer_filter_spec.rb b/spec/lib/banzai/filter/reference_gatherer_filter_spec.rb
index abfb5ad5e49..c8b1dfdf944 100644
--- a/spec/lib/gitlab/markdown/filter/reference_gatherer_filter_spec.rb
+++ b/spec/lib/banzai/filter/reference_gatherer_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Markdown::ReferenceGathererFilter, lib: true do
+describe Banzai::Filter::ReferenceGathererFilter, lib: true do
include ActionView::Helpers::UrlHelper
include FilterSpecHelper
diff --git a/spec/lib/gitlab/markdown/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb
index e0f53e2a533..0b3e5ecbc9f 100644
--- a/spec/lib/gitlab/markdown/filter/relative_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Gitlab::Markdown::RelativeLinkFilter, lib: true do
+describe Banzai::Filter::RelativeLinkFilter, lib: true do
def filter(doc, contexts = {})
contexts.reverse_merge!({
commit: project.commit,
diff --git a/spec/lib/gitlab/markdown/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb
index a5e5ee0e08a..760d60a4190 100644
--- a/spec/lib/gitlab/markdown/filter/sanitization_filter_spec.rb
+++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Markdown::SanitizationFilter, lib: true do
+describe Banzai::Filter::SanitizationFilter, lib: true do
include FilterSpecHelper
describe 'default whitelist' do
diff --git a/spec/lib/gitlab/markdown/filter/snippet_reference_filter_spec.rb b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
index 51526b58597..26466fbb180 100644
--- a/spec/lib/gitlab/markdown/filter/snippet_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Markdown::SnippetReferenceFilter, lib: true do
+describe Banzai::Filter::SnippetReferenceFilter, lib: true do
include FilterSpecHelper
let(:project) { create(:empty_project, :public) }
diff --git a/spec/lib/gitlab/markdown/filter/syntax_highlight_filter_spec.rb b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
index 8b76048f3e3..407617f3307 100644
--- a/spec/lib/gitlab/markdown/filter/syntax_highlight_filter_spec.rb
+++ b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Markdown::SyntaxHighlightFilter, lib: true do
+describe Banzai::Filter::SyntaxHighlightFilter, lib: true do
include FilterSpecHelper
it 'highlights valid code blocks' do
diff --git a/spec/lib/gitlab/markdown/filter/table_of_contents_filter_spec.rb b/spec/lib/banzai/filter/table_of_contents_filter_spec.rb
index c8c79c41847..6a5d003e87f 100644
--- a/spec/lib/gitlab/markdown/filter/table_of_contents_filter_spec.rb
+++ b/spec/lib/banzai/filter/table_of_contents_filter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Gitlab::Markdown::TableOfContentsFilter, lib: true do
+describe Banzai::Filter::TableOfContentsFilter, lib: true do
include FilterSpecHelper
def header(level, text)
diff --git a/spec/lib/gitlab/markdown/filter/task_list_filter_spec.rb b/spec/lib/banzai/filter/task_list_filter_spec.rb
index 1b1714ef882..f2e3a44478d 100644
--- a/spec/lib/gitlab/markdown/filter/task_list_filter_spec.rb
+++ b/spec/lib/banzai/filter/task_list_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Markdown::TaskListFilter, lib: true do
+describe Banzai::Filter::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/filter/upload_link_filter_spec.rb b/spec/lib/banzai/filter/upload_link_filter_spec.rb
index 38a007b5bee..3b073a90a95 100644
--- a/spec/lib/gitlab/markdown/filter/upload_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/upload_link_filter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Gitlab::Markdown::UploadLinkFilter, lib: true do
+describe Banzai::Filter::UploadLinkFilter, lib: true do
def filter(doc, contexts = {})
contexts.reverse_merge!({
project: project
diff --git a/spec/lib/gitlab/markdown/filter/user_reference_filter_spec.rb b/spec/lib/banzai/filter/user_reference_filter_spec.rb
index 277037cf68a..3534bf97784 100644
--- a/spec/lib/gitlab/markdown/filter/user_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/user_reference_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Markdown::UserReferenceFilter, lib: true do
+describe Banzai::Filter::UserReferenceFilter, lib: true do
include FilterSpecHelper
let(:project) { create(:empty_project, :public) }
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index 3a860899e18..6beb21c6d2b 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -50,7 +50,7 @@ module Gitlab
filtered_html = '<b>ASCII</b>'
allow(Asciidoctor).to receive(:convert).and_return(html)
- expect(Gitlab::Markdown).to receive(:render)
+ expect(Banzai).to receive(:render)
.with(html, context.merge(pipeline: :asciidoc))
.and_return(filtered_html)
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index 66dc5d4911d..7d963795e17 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -97,6 +97,16 @@ describe Gitlab::ReferenceExtractor, lib: true do
expect(extracted.first.commit_to).to eq commit
end
+ context 'with an external issue tracker' do
+ let(:project) { create(:jira_project) }
+ subject { described_class.new(project, project.creator) }
+
+ it 'returns JIRA issues for a JIRA-integrated project' do
+ subject.analyze('JIRA-123 and FOOBAR-4567')
+ expect(subject.issues).to eq [JiraIssue.new('JIRA-123', project), JiraIssue.new('FOOBAR-4567', project)]
+ end
+ end
+
context 'with a project with an underscore' do
let(:other_project) { create(:project, path: 'test_project') }
let(:issue) { create(:issue, project: other_project) }
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index 96b6f1dbca6..1c22e3cb7c4 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -189,6 +189,12 @@ describe Ci::Build, models: true do
it { is_expected.to eq(98.29) }
end
+
+ context 'using a regex capture' do
+ subject { build.extract_coverage('TOTAL 9926 3489 65%', 'TOTAL\s+\d+\s+\d+\s+(\d{1,3}\%)') }
+
+ it { is_expected.to eq(65) }
+ end
end
describe :variables do
@@ -390,4 +396,68 @@ describe Ci::Build, models: true do
it { is_expected.to include('gitlab-ci-token') }
it { is_expected.to include(project.web_url[7..-1]) }
end
+
+ def create_mr(build, commit, factory: :merge_request, created_at: Time.now)
+ FactoryGirl.create(factory,
+ source_project_id: commit.gl_project_id,
+ target_project_id: commit.gl_project_id,
+ source_branch: build.ref,
+ created_at: created_at)
+ end
+
+ describe :merge_request do
+ context 'when a MR has a reference to the commit' do
+ before do
+ @merge_request = create_mr(build, commit, factory: :merge_request)
+
+ commits = [double(id: commit.sha)]
+ allow(@merge_request).to receive(:commits).and_return(commits)
+ allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request])
+ end
+
+ it 'returns the single associated MR' do
+ expect(build.merge_request.id).to eq(@merge_request.id)
+ end
+ end
+
+ context 'when there is not a MR referencing the commit' do
+ it 'returns nil' do
+ expect(build.merge_request).to be_nil
+ end
+ end
+
+ context 'when more than one MR have a reference to the commit' do
+ before do
+ @merge_request = create_mr(build, commit, factory: :merge_request)
+ @merge_request.close!
+ @merge_request2 = create_mr(build, commit, factory: :merge_request)
+
+ commits = [double(id: commit.sha)]
+ allow(@merge_request).to receive(:commits).and_return(commits)
+ allow(@merge_request2).to receive(:commits).and_return(commits)
+ allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request, @merge_request2])
+ end
+
+ it 'returns the first MR' do
+ expect(build.merge_request.id).to eq(@merge_request.id)
+ end
+ end
+
+ context 'when a Build is created after the MR' do
+ before do
+ @merge_request = create_mr(build, commit, factory: :merge_request_with_diffs)
+ commit2 = FactoryGirl.create :ci_commit, project: project
+ @build2 = FactoryGirl.create :ci_build, commit: commit2
+
+ commits = [double(id: commit.sha), double(id: commit2.sha)]
+ allow(@merge_request).to receive(:commits).and_return(commits)
+ allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request])
+ end
+
+ it 'returns the current MR' do
+ expect(@build2.merge_request.id).to eq(@merge_request.id)
+ end
+ end
+
+ end
end
diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb
index ac61c8fb525..b193e16e7f8 100644
--- a/spec/models/ci/commit_spec.rb
+++ b/spec/models/ci/commit_spec.rb
@@ -37,14 +37,14 @@ describe Ci::Commit, models: true do
it 'returns ordered list of commits' do
commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: project
- commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: project
+ commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hours.ago, project: project
expect(project.ci_commits.ordered).to eq([commit2, commit1])
end
it 'returns commits ordered by committed_at and id, with nulls last' do
commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: project
commit2 = FactoryGirl.create :ci_commit, committed_at: nil, project: project
- commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: project
+ commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hours.ago, project: project
commit4 = FactoryGirl.create :ci_commit, committed_at: nil, project: project
expect(project.ci_commits.ordered).to eq([commit2, commit4, commit3, commit1])
end
diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb
index 6179882e935..6653621a83e 100644
--- a/spec/models/concerns/mentionable_spec.rb
+++ b/spec/models/concerns/mentionable_spec.rb
@@ -1,5 +1,18 @@
require 'spec_helper'
+describe Mentionable do
+ include Mentionable
+
+ describe :references do
+ let(:project) { create(:project) }
+
+ it 'excludes JIRA references' do
+ allow(project).to receive_messages(jira_tracker?: true)
+ expect(referenced_mentionables(project, 'JIRA-123')).to be_empty
+ end
+ end
+end
+
describe Issue, "Mentionable" do
describe '#mentioned_users' do
let!(:user) { create(:user, username: 'stranger') }
diff --git a/spec/models/jira_issue_spec.rb b/spec/models/jira_issue_spec.rb
new file mode 100644
index 00000000000..1634265b439
--- /dev/null
+++ b/spec/models/jira_issue_spec.rb
@@ -0,0 +1,30 @@
+require 'spec_helper'
+
+describe JiraIssue do
+ let(:project) { create(:project) }
+ subject { JiraIssue.new('JIRA-123', project) }
+
+ describe 'id' do
+ subject { super().id }
+ it { is_expected.to eq('JIRA-123') }
+ end
+
+ describe 'iid' do
+ subject { super().iid }
+ it { is_expected.to eq('JIRA-123') }
+ end
+
+ describe 'to_s' do
+ subject { super().to_s }
+ it { is_expected.to eq('JIRA-123') }
+ end
+
+ describe :== do
+ specify { expect(subject).to eq(JiraIssue.new('JIRA-123', project)) }
+ specify { expect(subject).not_to eq(JiraIssue.new('JIRA-124', project)) }
+
+ it 'only compares with JiraIssues' do
+ expect(subject).not_to eq('JIRA-123')
+ end
+ end
+end
diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb
index d7fe01976d8..c962b83644a 100644
--- a/spec/models/key_spec.rb
+++ b/spec/models/key_spec.rb
@@ -81,7 +81,7 @@ describe Key, models: true do
it 'rejects the multiple line key' do
key = build(:key)
- key.key.gsub!(' ', "\n")
+ key.key.tr!(' ', "\n")
expect(key).not_to be_valid
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 1aeba9b2b3b..e0653a8327d 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -164,6 +164,17 @@ describe MergeRequest, models: true do
expect(subject.closes_issues).to include(issue2)
end
+
+ context 'for a project with JIRA integration' do
+ let(:issue0) { JiraIssue.new('JIRA-123', subject.project) }
+ let(:issue1) { JiraIssue.new('FOOBAR-4567', subject.project) }
+
+ it 'returns sorted JiraIssues' do
+ allow(subject.project).to receive_messages(default_branch: subject.target_branch)
+
+ expect(subject.closes_issues).to eq([issue0, issue1])
+ end
+ end
end
describe "#work_in_progress?" do
diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb
index a5662b08bda..91dd92b7c67 100644
--- a/spec/models/project_services/hipchat_service_spec.rb
+++ b/spec/models/project_services/hipchat_service_spec.rb
@@ -57,23 +57,21 @@ describe HipchatService, models: true do
it 'should use v1 if version is provided' do
allow(hipchat).to receive(:api_version).and_return('v1')
- expect(HipChat::Client).to receive(:new).
- with(token,
- api_version: 'v1',
- server_url: server_url).
- and_return(
- double(:hipchat_service).as_null_object)
+ expect(HipChat::Client).to receive(:new).with(
+ token,
+ api_version: 'v1',
+ server_url: server_url
+ ).and_return(double(:hipchat_service).as_null_object)
hipchat.execute(push_sample_data)
end
it 'should use v2 as the version when nothing is provided' do
allow(hipchat).to receive(:api_version).and_return('')
- expect(HipChat::Client).to receive(:new).
- with(token,
- api_version: 'v2',
- server_url: server_url).
- and_return(
- double(:hipchat_service).as_null_object)
+ expect(HipChat::Client).to receive(:new).with(
+ token,
+ api_version: 'v2',
+ server_url: server_url
+ ).and_return(double(:hipchat_service).as_null_object)
hipchat.execute(push_sample_data)
end
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index 7d91ebe9ce6..2f8193170ae 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -26,6 +26,113 @@ describe JiraService, models: true do
it { is_expected.to have_one :service_hook }
end
+ describe "Execute" do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:merge_request) { create(:merge_request) }
+
+ before do
+ @jira_service = JiraService.new
+ allow(@jira_service).to receive_messages(
+ project_id: project.id,
+ project: project,
+ service_hook: true,
+ project_url: 'http://jira.example.com',
+ username: 'gitlab_jira_username',
+ password: 'gitlab_jira_password'
+ )
+ @jira_service.save # will build API URL, as api_url was not specified above
+ @sample_data = Gitlab::PushDataBuilder.build_sample(project, user)
+ # https://github.com/bblimke/webmock#request-with-basic-authentication
+ @api_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/issue/JIRA-123/transitions'
+ @comment_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/issue/JIRA-123/comment'
+
+ WebMock.stub_request(:post, @api_url)
+ WebMock.stub_request(:post, @comment_url)
+ end
+
+ it "should call JIRA API" do
+ @jira_service.execute(merge_request, JiraIssue.new("JIRA-123", project))
+ expect(WebMock).to have_requested(:post, @comment_url).with(
+ body: /Issue solved with/
+ ).once
+ end
+
+ it "calls the api with jira_issue_transition_id" do
+ @jira_service.jira_issue_transition_id = 'this-is-a-custom-id'
+ @jira_service.execute(merge_request, JiraIssue.new("JIRA-123", project))
+ expect(WebMock).to have_requested(:post, @api_url).with(
+ body: /this-is-a-custom-id/
+ ).once
+ end
+ end
+
+ describe "Stored password invalidation" do
+ let(:project) { create(:project) }
+
+ context "when a password was previously set" do
+ before do
+ @jira_service = JiraService.create(
+ project: create(:project),
+ properties: {
+ api_url: 'http://jira.example.com/rest/api/2',
+ username: 'mic',
+ password: "password"
+ }
+ )
+ end
+
+ it "reset password if url changed" do
+ @jira_service.api_url = 'http://jira_edited.example.com/rest/api/2'
+ @jira_service.save
+ expect(@jira_service.password).to be_nil
+ end
+
+ it "does not reset password if username changed" do
+ @jira_service.username = "some_name"
+ @jira_service.save
+ expect(@jira_service.password).to eq("password")
+ end
+
+ it "does not reset password if new url is set together with password, even if it's the same password" do
+ @jira_service.api_url = 'http://jira_edited.example.com/rest/api/2'
+ @jira_service.password = 'password'
+ @jira_service.save
+ expect(@jira_service.password).to eq("password")
+ expect(@jira_service.api_url).to eq("http://jira_edited.example.com/rest/api/2")
+ end
+
+ it "should reset password if url changed, even if setter called multiple times" do
+ @jira_service.api_url = 'http://jira1.example.com/rest/api/2'
+ @jira_service.api_url = 'http://jira1.example.com/rest/api/2'
+ @jira_service.save
+ expect(@jira_service.password).to be_nil
+ end
+ end
+
+ context "when no password was previously set" do
+ before do
+ @jira_service = JiraService.create(
+ project: create(:project),
+ properties: {
+ api_url: 'http://jira.example.com/rest/api/2',
+ username: 'mic'
+ }
+ )
+ end
+
+ it "saves password if new url is set together with password" do
+ @jira_service.api_url = 'http://jira_edited.example.com/rest/api/2'
+ @jira_service.password = 'password'
+ @jira_service.save
+ expect(@jira_service.password).to eq("password")
+ expect(@jira_service.api_url).to eq("http://jira_edited.example.com/rest/api/2")
+ end
+
+ end
+ end
+
+
describe "Validations" do
context "active" do
before do
@@ -78,11 +185,12 @@ describe JiraService, models: true do
context 'when gitlab.yml was initialized' do
before do
- settings = { "jira" => {
- "title" => "Jira",
- "project_url" => "http://jira.sample/projects/project_a",
- "issues_url" => "http://jira.sample/issues/:id",
- "new_issue_url" => "http://jira.sample/projects/project_a/issues/new"
+ settings = {
+ "jira" => {
+ "title" => "Jira",
+ "project_url" => "http://jira.sample/projects/project_a",
+ "issues_url" => "http://jira.sample/issues/:id",
+ "new_issue_url" => "http://jira.sample/projects/project_a/issues/new"
}
}
allow(Gitlab.config).to receive(:issues_tracker).and_return(settings)
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 ebf8837570e..06006b9a4f5 100644
--- a/spec/models/project_services/slack_service/note_message_spec.rb
+++ b/spec/models/project_services/slack_service/note_message_spec.rb
@@ -89,10 +89,10 @@ describe SlackService::NoteMessage, models: true do
it 'returns a message regarding notes on an issue' do
message = SlackService::NoteMessage.new(@args)
expect(message.pretext).to eq(
- "Test User commented on " \
- "<url|issue #20> in <somewhere.com|project_name>: " \
- "*issue title*")
- expected_attachments = [
+ "Test User commented on " \
+ "<url|issue #20> in <somewhere.com|project_name>: " \
+ "*issue title*")
+ expected_attachments = [
{
text: "comment on an issue",
color: color,
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 87582e07494..c4d3813e9c9 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -172,13 +172,17 @@ describe Project, models: true do
describe '#get_issue' do
let(:project) { create(:empty_project) }
- let(:issue) { create(:issue, project: project) }
+ let!(:issue) { create(:issue, project: project) }
context 'with default issues tracker' do
it 'returns an issue' do
expect(project.get_issue(issue.iid)).to eq issue
end
+ it 'returns count of open issues' do
+ expect(project.open_issues_count).to eq(1)
+ end
+
it 'returns nil when no issue found' do
expect(project.get_issue(999)).to be_nil
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index daa9d1087bf..2f184bbaf92 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -26,6 +26,7 @@
# bio :string(255)
# failed_attempts :integer default(0)
# locked_at :datetime
+# unlock_token :string(255)
# username :string(255)
# can_create_group :boolean default(TRUE), not null
# can_create_team :boolean default(TRUE), not null
@@ -462,8 +463,8 @@ describe User, models: true do
expect(User.search(user1.username.downcase).to_a).to eq([user1])
expect(User.search(user2.username.upcase).to_a).to eq([user2])
expect(User.search(user2.username.downcase).to_a).to eq([user2])
- expect(User.search(user1.username.downcase).to_a.count).to eq(2)
- expect(User.search(user2.username.downcase).to_a.count).to eq(1)
+ expect(User.search(user1.username.downcase).to_a.size).to eq(2)
+ expect(User.search(user2.username.downcase).to_a.size).to eq(1)
end
end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index a91fa735321..e194eb93cf4 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -6,7 +6,7 @@ describe API::API, api: true do
let(:user) { create(:user) }
let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) }
let!(:merge_request) { create(:merge_request, :simple, author: user, assignee: user, source_project: project, target_project: project, title: "Test", created_at: base_time) }
- let!(:merge_request_closed) { create(:merge_request, state: "closed", author: user, assignee: user, source_project: project, target_project: project, title: "Closed test", created_at: base_time + 1.seconds) }
+ let!(:merge_request_closed) { create(:merge_request, state: "closed", author: user, assignee: user, source_project: project, target_project: project, title: "Closed test", created_at: base_time + 1.second) }
let!(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignee: user, source_project: project, target_project: project, title: "Merged test", created_at: base_time + 2.seconds) }
let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
let!(:note2) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") }
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index e784b7d1f2d..01d2ec79482 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -65,6 +65,22 @@ describe API::API, api: true do
expect(json_response.first.keys).to include('tag_list')
end
+ it 'should include open_issues_count' do
+ get api('/projects', user)
+ expect(response.status).to eq 200
+ expect(json_response).to be_an Array
+ expect(json_response.first.keys).to include('open_issues_count')
+ end
+
+ it 'should not include open_issues_count' do
+ project.update_attributes( { issues_enabled: false } )
+
+ get api('/projects', user)
+ expect(response.status).to eq 200
+ expect(json_response).to be_an Array
+ expect(json_response.first.keys).not_to include('open_issues_count')
+ end
+
context 'and using search' do
it 'should return searched project' do
get api('/projects', user), { search: project.name }
@@ -123,6 +139,25 @@ describe API::API, api: true do
end
end
+ describe 'GET /projects/starred' do
+ before do
+ admin.starred_projects << project
+ admin.save!
+ end
+
+ it 'should return the starred projects' do
+ get api('/projects/all', admin)
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+
+ expect(json_response).to satisfy do |response|
+ response.one? do |entry|
+ entry['name'] == project.name
+ end
+ end
+ end
+ end
+
describe 'POST /projects' do
context 'maximum number of projects reached' do
it 'should not create new project and respond with 403' do
@@ -455,7 +490,7 @@ describe API::API, api: true do
end
end
- describe 'PUT /projects/:id/snippets/:shippet_id' do
+ describe 'PUT /projects/:id/snippets/:snippet_id' do
it 'should update an existing project snippet' do
put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
code: 'updated code'
diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb
index b180d2fec77..fed9ae1949b 100644
--- a/spec/requests/api/services_spec.rb
+++ b/spec/requests/api/services_spec.rb
@@ -29,7 +29,7 @@ describe API::API, api: true do
if required_attributes.empty?
expected_code = 200
else
- attrs.delete(required_attributes.shuffle.first)
+ attrs.delete(required_attributes.sample)
expected_code = 400
end
diff --git a/spec/services/create_commit_builds_service_spec.rb b/spec/services/create_commit_builds_service_spec.rb
index 798c480b81a..ea5dcfa068a 100644
--- a/spec/services/create_commit_builds_service_spec.rb
+++ b/spec/services/create_commit_builds_service_spec.rb
@@ -17,7 +17,7 @@ describe CreateCommitBuildsService, services: true do
before: '00000000',
after: '31das312',
commits: [{ message: "Message" }]
- )
+ )
end
it { expect(commit).to be_kind_of(Ci::Commit) }
@@ -34,7 +34,7 @@ describe CreateCommitBuildsService, services: true do
before: '00000000',
after: '31das312',
commits: [{ message: "Message" }]
- )
+ )
expect(result).to be_persisted
end
@@ -47,26 +47,24 @@ describe CreateCommitBuildsService, services: true do
before: '00000000',
after: '31das312',
commits: [{ message: "Message" }]
- )
+ )
expect(result).to be_persisted
end
end
- it 'skips commits without .gitlab-ci.yml' do
+ it 'skips creating ci_commit for refs without .gitlab-ci.yml' do
stub_ci_commit_yaml_file(nil)
result = service.execute(project, user,
ref: 'refs/heads/0_1',
before: '00000000',
after: '31das312',
commits: [{ message: 'Message' }]
- )
- expect(result).to be_persisted
- expect(result.builds.any?).to be_falsey
- expect(result.status).to eq('skipped')
- expect(result.yaml_errors).to be_nil
+ )
+ expect(result).to be_falsey
+ expect(Ci::Commit.count).to eq(0)
end
- it 'skips commits if yaml is invalid' do
+ it 'fails commits if yaml is invalid' do
message = 'message'
allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message }
stub_ci_commit_yaml_file('invalid: file: file')
@@ -76,7 +74,8 @@ describe CreateCommitBuildsService, services: true do
before: '00000000',
after: '31das312',
commits: commits
- )
+ )
+ expect(commit).to be_persisted
expect(commit.builds.any?).to be false
expect(commit.status).to eq('failed')
expect(commit.yaml_errors).to_not be_nil
@@ -96,7 +95,8 @@ describe CreateCommitBuildsService, services: true do
before: '00000000',
after: '31das312',
commits: commits
- )
+ )
+ expect(commit).to be_persisted
expect(commit.builds.any?).to be false
expect(commit.status).to eq("skipped")
end
@@ -110,8 +110,9 @@ describe CreateCommitBuildsService, services: true do
before: '00000000',
after: '31das312',
commits: commits
- )
+ )
+ expect(commit).to be_persisted
expect(commit.builds.first.name).to eq("staging")
end
@@ -123,7 +124,8 @@ describe CreateCommitBuildsService, services: true do
before: '00000000',
after: '31das312',
commits: commits
- )
+ )
+ expect(commit).to be_persisted
expect(commit.builds.any?).to be false
expect(commit.status).to eq("skipped")
expect(commit.yaml_errors).to be_nil
@@ -139,7 +141,8 @@ describe CreateCommitBuildsService, services: true do
before: '00000000',
after: '31das312',
commits: commits
- )
+ )
+ expect(commit).to be_persisted
expect(commit.builds.count(:all)).to eq(2)
commit = service.execute(project, user,
@@ -147,7 +150,8 @@ describe CreateCommitBuildsService, services: true do
before: '00000000',
after: '31das312',
commits: commits
- )
+ )
+ expect(commit).to be_persisted
expect(commit.builds.count(:all)).to eq(2)
end
@@ -161,8 +165,9 @@ describe CreateCommitBuildsService, services: true do
before: '00000000',
after: '31das312',
commits: commits
- )
+ )
+ expect(commit).to be_persisted
expect(commit.status).to eq("failed")
expect(commit.builds.any?).to be false
end
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index a04c242cf0e..c1080ef190a 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -265,6 +265,75 @@ describe GitPushService, services: true do
expect(Issue.find(issue.id)).to be_opened
end
end
+
+ # EE-only tests
+ context "for jira issue tracker" do
+ include JiraServiceHelper
+
+ let(:jira_tracker) { project.create_jira_service if project.jira_service.nil? }
+
+ before do
+ jira_service_settings
+
+ WebMock.stub_request(:post, jira_api_transition_url)
+ WebMock.stub_request(:post, jira_api_comment_url)
+ WebMock.stub_request(:get, jira_api_comment_url).to_return(body: jira_issue_comments)
+ WebMock.stub_request(:get, jira_api_test_url)
+
+ allow(closing_commit).to receive_messages({
+ issue_closing_regex: Regexp.new(Gitlab.config.gitlab.issue_closing_pattern),
+ safe_message: message,
+ author_name: commit_author.name,
+ author_email: commit_author.email
+ })
+
+ allow(project.repository).to receive_messages(commits_between: [closing_commit])
+ end
+
+ after do
+ jira_tracker.destroy!
+ end
+
+ context "mentioning an issue" do
+ let(:message) { "this is some work.\n\nrelated to JIRA-1" }
+
+ it "should initiate one api call to jira server to mention the issue" do
+ service.execute(project, user, @oldrev, @newrev, @ref)
+
+ expect(WebMock).to have_requested(:post, jira_api_comment_url).with(
+ body: /mentioned this issue in/
+ ).once
+ end
+ end
+
+ context "closing an issue" do
+ let(:message) { "this is some work.\n\ncloses JIRA-1" }
+
+ it "should initiate one api call to jira server to close the issue" do
+ transition_body = {
+ transition: {
+ id: '2'
+ }
+ }.to_json
+
+ service.execute(project, user, @oldrev, @newrev, @ref)
+ expect(WebMock).to have_requested(:post, jira_api_transition_url).with(
+ body: transition_body
+ ).once
+ end
+
+ it "should initiate one api call to jira server to comment on the issue" do
+ comment_body = {
+ body: "Issue solved with [#{closing_commit.id}|http://localhost/#{project.path_with_namespace}/commit/#{closing_commit.id}]."
+ }.to_json
+
+ service.execute(project, user, @oldrev, @newrev, @ref)
+ expect(WebMock).to have_requested(:post, jira_api_comment_url).with(
+ body: comment_body
+ ).once
+ end
+ end
+ end
end
describe "empty project" do
diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb
index e2d15f1a83d..b982274c529 100644
--- a/spec/services/git_tag_push_service_spec.rb
+++ b/spec/services/git_tag_push_service_spec.rb
@@ -58,14 +58,14 @@ describe GitTagPushService, services: true do
it { is_expected.to include(timestamp: @commit.date.xmlschema) }
it do
is_expected.to include(
- url: [
- Gitlab.config.gitlab.url,
- project.namespace.to_param,
- project.to_param,
- 'commit',
- @commit.id
- ].join('/')
- )
+ url: [
+ Gitlab.config.gitlab.url,
+ project.namespace.to_param,
+ project.to_param,
+ 'commit',
+ @commit.id
+ ].join('/')
+ )
end
context "with a author" do
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 0a4f9b230e8..c9f828ae2f7 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -425,4 +425,65 @@ describe SystemNoteService, services: true do
end
end
end
+
+ include JiraServiceHelper
+
+ describe 'JIRA integration' do
+ let(:project) { create(:project) }
+ let(:author) { create(:user) }
+ let(:issue) { create(:issue, project: project) }
+ let(:mergereq) { create(:merge_request, :simple, target_project: project, source_project: project) }
+ let(:jira_issue) { JiraIssue.new("JIRA-1", project)}
+ let(:jira_tracker) { project.create_jira_service if project.jira_service.nil? }
+ let(:commit) { project.commit }
+
+ context 'in JIRA issue tracker' do
+ before do
+ jira_service_settings
+ WebMock.stub_request(:post, jira_api_comment_url)
+ end
+
+ after do
+ jira_tracker.destroy!
+ end
+
+ describe "new reference" do
+ before do
+ WebMock.stub_request(:get, jira_api_comment_url).to_return(body: jira_issue_comments)
+ end
+
+ subject { described_class.cross_reference(jira_issue, commit, author) }
+
+ it { is_expected.to eq(jira_status_message) }
+ end
+
+ describe "existing reference" do
+ before do
+ message = "[#{author.name}|http://localhost/u/#{author.username}] mentioned this issue in [a commit of #{project.path_with_namespace}|http://localhost/#{project.path_with_namespace}/commit/#{commit.id}]."
+ WebMock.stub_request(:get, jira_api_comment_url).to_return(body: "{\"comments\":[{\"body\":\"#{message}\"}]}")
+ end
+
+ subject { described_class.cross_reference(jira_issue, commit, author) }
+ it { is_expected.not_to eq(jira_status_message) }
+ end
+ end
+
+ context 'issue from an issue' do
+ context 'in JIRA issue tracker' do
+ before do
+ jira_service_settings
+ WebMock.stub_request(:post, jira_api_comment_url)
+ WebMock.stub_request(:get, jira_api_comment_url).to_return(body: jira_issue_comments)
+ end
+
+ after do
+ jira_tracker.destroy!
+ end
+
+ subject { described_class.cross_reference(jira_issue, issue, author) }
+
+ it { is_expected.to eq(jira_status_message) }
+ end
+ end
+ end
end
diff --git a/spec/services/update_snippet_service_spec.rb b/spec/services/update_snippet_service_spec.rb
index 124bb76e678..48d114896d0 100644
--- a/spec/services/update_snippet_service_spec.rb
+++ b/spec/services/update_snippet_service_spec.rb
@@ -42,7 +42,7 @@ describe UpdateSnippetService, services: true do
CreateSnippetService.new(project, user, opts).execute
end
- def update_snippet(project = nil, user, snippet, opts)
+ def update_snippet(project, user, snippet, opts)
UpdateSnippetService.new(project, user, snippet, opts).execute
end
end
diff --git a/spec/support/filter_spec_helper.rb b/spec/support/filter_spec_helper.rb
index 91e3bee13c1..d6e03cbef3d 100644
--- a/spec/support/filter_spec_helper.rb
+++ b/spec/support/filter_spec_helper.rb
@@ -1,4 +1,4 @@
-# Helper methods for Gitlab::Markdown filter specs
+# Helper methods for Banzai filter specs
#
# Must be included into specs manually
module FilterSpecHelper
@@ -10,49 +10,49 @@ module FilterSpecHelper
# if none is provided.
#
# html - HTML String to pass to the filter's `call` method.
- # contexts - Hash context for the filter. (default: {project: project})
+ # context - Hash context for the filter. (default: {project: project})
#
# Returns a Nokogiri::XML::DocumentFragment
- def filter(html, contexts = {})
+ def filter(html, context = {})
if defined?(project)
- contexts.reverse_merge!(project: project)
+ context.reverse_merge!(project: project)
end
- described_class.call(html, contexts)
+ described_class.call(html, context)
end
# Run text through HTML::Pipeline with the current filter and return the
# result Hash
#
# body - String text to run through the pipeline
- # contexts - Hash context for the filter. (default: {project: project})
+ # context - Hash context for the filter. (default: {project: project})
#
# Returns the Hash
- def pipeline_result(body, contexts = {})
- contexts.reverse_merge!(project: project) if defined?(project)
+ def pipeline_result(body, context = {})
+ context.reverse_merge!(project: project) if defined?(project)
- pipeline = HTML::Pipeline.new([described_class], contexts)
+ pipeline = HTML::Pipeline.new([described_class], context)
pipeline.call(body)
end
- def reference_pipeline(contexts = {})
- contexts.reverse_merge!(project: project) if defined?(project)
+ def reference_pipeline(context = {})
+ context.reverse_merge!(project: project) if defined?(project)
filters = [
- Gitlab::Markdown::AutolinkFilter,
+ Banzai::Filter::AutolinkFilter,
described_class,
- Gitlab::Markdown::ReferenceGathererFilter
+ Banzai::Filter::ReferenceGathererFilter
]
- HTML::Pipeline.new(filters, contexts)
+ HTML::Pipeline.new(filters, context)
end
- def reference_pipeline_result(body, contexts = {})
- reference_pipeline(contexts).call(body)
+ def reference_pipeline_result(body, context = {})
+ reference_pipeline(context).call(body)
end
- def reference_filter(html, contexts = {})
- reference_pipeline(contexts).to_document(html)
+ def reference_filter(html, context = {})
+ reference_pipeline(context).to_document(html)
end
# Modify a String reference to make it invalid
diff --git a/spec/support/jira_service_helper.rb b/spec/support/jira_service_helper.rb
new file mode 100644
index 00000000000..a3f496359b1
--- /dev/null
+++ b/spec/support/jira_service_helper.rb
@@ -0,0 +1,67 @@
+module JiraServiceHelper
+
+ def jira_service_settings
+ properties = {
+ "title"=>"JIRA tracker",
+ "project_url"=>"http://jira.example/issues/?jql=project=A",
+ "issues_url"=>"http://jira.example/browse/JIRA-1",
+ "new_issue_url"=>"http://jira.example/secure/CreateIssue.jspa",
+ "api_url"=>"http://jira.example/rest/api/2"
+ }
+
+ jira_tracker.update_attributes(properties: properties, active: true)
+ end
+
+ def jira_status_message
+ "JiraService SUCCESS 200: Successfully posted to #{jira_api_comment_url}."
+ end
+
+ def jira_issue_comments
+ "{\"startAt\":0,\"maxResults\":11,\"total\":11,
+ \"comments\":[{\"self\":\"http://0.0.0.0:4567/rest/api/2/issue/10002/comment/10609\",
+ \"id\":\"10609\",\"author\":{\"self\":\"http://0.0.0.0:4567/rest/api/2/user?username=gitlab\",
+ \"name\":\"gitlab\",\"emailAddress\":\"gitlab@example.com\",
+ \"avatarUrls\":{\"16x16\":\"http://0.0.0.0:4567/secure/useravatar?size=xsmall&avatarId=10122\",
+ \"24x24\":\"http://0.0.0.0:4567/secure/useravatar?size=small&avatarId=10122\",
+ \"32x32\":\"http://0.0.0.0:4567/secure/useravatar?size=medium&avatarId=10122\",
+ \"48x48\":\"http://0.0.0.0:4567/secure/useravatar?avatarId=10122\"},
+ \"displayName\":\"GitLab\",\"active\":true},
+ \"body\":\"[Administrator|http://localhost:3000/u/root] mentioned JIRA-1 in Merge request of [gitlab-org/gitlab-test|http://localhost:3000/gitlab-org/gitlab-test/merge_requests/2].\",
+ \"updateAuthor\":{\"self\":\"http://0.0.0.0:4567/rest/api/2/user?username=gitlab\",\"name\":\"gitlab\",\"emailAddress\":\"gitlab@example.com\",
+ \"avatarUrls\":{\"16x16\":\"http://0.0.0.0:4567/secure/useravatar?size=xsmall&avatarId=10122\",
+ \"24x24\":\"http://0.0.0.0:4567/secure/useravatar?size=small&avatarId=10122\",
+ \"32x32\":\"http://0.0.0.0:4567/secure/useravatar?size=medium&avatarId=10122\",
+ \"48x48\":\"http://0.0.0.0:4567/secure/useravatar?avatarId=10122\"},\"displayName\":\"GitLab\",\"active\":true},
+ \"created\":\"2015-02-12T22:47:07.826+0100\",
+ \"updated\":\"2015-02-12T22:47:07.826+0100\"},
+ {\"self\":\"http://0.0.0.0:4567/rest/api/2/issue/10002/comment/10700\",
+ \"id\":\"10700\",\"author\":{\"self\":\"http://0.0.0.0:4567/rest/api/2/user?username=gitlab\",
+ \"name\":\"gitlab\",\"emailAddress\":\"gitlab@example.com\",
+ \"avatarUrls\":{\"16x16\":\"http://0.0.0.0:4567/secure/useravatar?size=xsmall&avatarId=10122\",
+ \"24x24\":\"http://0.0.0.0:4567/secure/useravatar?size=small&avatarId=10122\",
+ \"32x32\":\"http://0.0.0.0:4567/secure/useravatar?size=medium&avatarId=10122\",
+ \"48x48\":\"http://0.0.0.0:4567/secure/useravatar?avatarId=10122\"},\"displayName\":\"GitLab\",\"active\":true},
+ \"body\":\"[Administrator|http://localhost:3000/u/root] mentioned this issue in [a commit of h5bp/html5-boilerplate|http://localhost:3000/h5bp/html5-boilerplate/commit/2439f77897122fbeee3bfd9bb692d3608848433e].\",
+ \"updateAuthor\":{\"self\":\"http://0.0.0.0:4567/rest/api/2/user?username=gitlab\",\"name\":\"gitlab\",\"emailAddress\":\"gitlab@example.com\",
+ \"avatarUrls\":{\"16x16\":\"http://0.0.0.0:4567/secure/useravatar?size=xsmall&avatarId=10122\",
+ \"24x24\":\"http://0.0.0.0:4567/secure/useravatar?size=small&avatarId=10122\",
+ \"32x32\":\"http://0.0.0.0:4567/secure/useravatar?size=medium&avatarId=10122\",
+ \"48x48\":\"http://0.0.0.0:4567/secure/useravatar?avatarId=10122\"},\"displayName\":\"GitLab\",\"active\":true},
+ \"created\":\"2015-04-01T03:45:55.667+0200\",
+ \"updated\":\"2015-04-01T03:45:55.667+0200\"
+ }
+ ]}"
+ end
+
+ def jira_api_comment_url
+ 'http://jira.example/rest/api/2/issue/JIRA-1/comment'
+ end
+
+ def jira_api_transition_url
+ 'http://jira.example/rest/api/2/issue/JIRA-1/transitions'
+ end
+
+ def jira_api_test_url
+ 'http://jira.example/rest/api/2/myself'
+ end
+end
diff --git a/spec/support/repo_helpers.rb b/spec/support/repo_helpers.rb
index aadf791bf3f..aa8258d6dad 100644
--- a/spec/support/repo_helpers.rb
+++ b/spec/support/repo_helpers.rb
@@ -45,12 +45,12 @@ eos
def another_sample_commit
OpenStruct.new(
- id: "e56497bb5f03a90a51293fc6d516788730953899",
- parent_id: '4cd80ccab63c82b4bad16faa5193fbd2aa06df40',
- author_full_name: "Sytse Sijbrandij",
- author_email: "sytse@gitlab.com",
- files_changed_count: 1,
- message: <<eos
+ id: "e56497bb5f03a90a51293fc6d516788730953899",
+ parent_id: '4cd80ccab63c82b4bad16faa5193fbd2aa06df40',
+ author_full_name: "Sytse Sijbrandij",
+ author_email: "sytse@gitlab.com",
+ files_changed_count: 1,
+ message: <<eos
Add directory structure for tree_helper spec
This directory structure is needed for a testing the method flatten_tree(tree) in the TreeHelper module
diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb
index 245f066df1f..dae31992620 100644
--- a/spec/workers/repository_fork_worker_spec.rb
+++ b/spec/workers/repository_fork_worker_spec.rb
@@ -9,20 +9,22 @@ describe RepositoryForkWorker do
describe "#perform" do
it "creates a new repository from a fork" do
expect_any_instance_of(Gitlab::Shell).to receive(:fork_repository).with(
- project.path_with_namespace,
- fork_project.namespace.path).
- and_return(true)
+ project.path_with_namespace,
+ fork_project.namespace.path
+ ).and_return(true)
- subject.perform(project.id,
- project.path_with_namespace,
- fork_project.namespace.path)
+ subject.perform(
+ project.id,
+ project.path_with_namespace,
+ fork_project.namespace.path)
end
it "handles bad fork" do
expect_any_instance_of(Gitlab::Shell).to receive(:fork_repository).and_return(false)
- subject.perform(project.id,
- project.path_with_namespace,
- fork_project.namespace.path)
+ subject.perform(
+ project.id,
+ project.path_with_namespace,
+ fork_project.namespace.path)
end
end
end
diff --git a/spec/workers/stuck_ci_builds_worker_spec.rb b/spec/workers/stuck_ci_builds_worker_spec.rb
index f9d87d97014..665ec20f224 100644
--- a/spec/workers/stuck_ci_builds_worker_spec.rb
+++ b/spec/workers/stuck_ci_builds_worker_spec.rb
@@ -15,7 +15,7 @@ describe StuckCiBuildsWorker do
end
it 'gets dropped if it was updated over 2 days ago' do
- build.update!(updated_at: 2.day.ago)
+ build.update!(updated_at: 2.days.ago)
StuckCiBuildsWorker.new.perform
is_expected.to eq('failed')
end
@@ -35,7 +35,7 @@ describe StuckCiBuildsWorker do
end
it "is still #{status}" do
- build.update!(updated_at: 2.day.ago)
+ build.update!(updated_at: 2.days.ago)
StuckCiBuildsWorker.new.perform
is_expected.to eq(status)
end