summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.flayignore1
-rw-r--r--.gitignore1
-rw-r--r--.gitlab-ci.yml9
-rw-r--r--.haml-lint.yml103
-rw-r--r--.rubocop_todo.yml125
-rw-r--r--CHANGELOG1738
-rw-r--r--CONTRIBUTING.md18
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile7
-rw-r--r--Gemfile.lock27
-rw-r--r--app/assets/javascripts/LabelManager.js5
-rw-r--r--app/assets/javascripts/api.js6
-rw-r--r--app/assets/javascripts/application.js21
-rw-r--r--app/assets/javascripts/autosave.js6
-rw-r--r--app/assets/javascripts/awards_handler.js5
-rw-r--r--app/assets/javascripts/behaviors/autosize.js2
-rw-r--r--app/assets/javascripts/behaviors/details_behavior.js6
-rw-r--r--app/assets/javascripts/behaviors/quick_submit.js20
-rw-r--r--app/assets/javascripts/behaviors/requires_input.js19
-rw-r--r--app/assets/javascripts/behaviors/toggler_behavior.js7
-rw-r--r--app/assets/javascripts/blob/blob_file_dropzone.js3
-rw-r--r--app/assets/javascripts/blob/template_selector.js3
-rw-r--r--app/assets/javascripts/blob_edit/edit_blob.js2
-rw-r--r--app/assets/javascripts/breakpoints.js2
-rw-r--r--app/assets/javascripts/build.js8
-rw-r--r--app/assets/javascripts/commit/image-file.js2
-rw-r--r--app/assets/javascripts/commits.js1
-rw-r--r--app/assets/javascripts/copy_to_clipboard.js7
-rw-r--r--app/assets/javascripts/diff.js3
-rw-r--r--app/assets/javascripts/dispatcher.js4
-rw-r--r--app/assets/javascripts/due_date_select.js3
-rw-r--r--app/assets/javascripts/extensions/jquery.js2
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js.es624
-rw-r--r--app/assets/javascripts/gl_dropdown.js64
-rw-r--r--app/assets/javascripts/gl_form.js6
-rw-r--r--app/assets/javascripts/graphs/graphs_bundle.js8
-rw-r--r--app/assets/javascripts/graphs/stat_graph_contributors_graph.js1
-rw-r--r--app/assets/javascripts/groups_select.js1
-rw-r--r--app/assets/javascripts/issuable.js.es62
-rw-r--r--app/assets/javascripts/issue.js9
-rw-r--r--app/assets/javascripts/issues-bulk-assignment.js6
-rw-r--r--app/assets/javascripts/labels.js3
-rw-r--r--app/assets/javascripts/labels_select.js9
-rw-r--r--app/assets/javascripts/lib/chart.js1
-rw-r--r--app/assets/javascripts/lib/cropper.js1
-rw-r--r--app/assets/javascripts/lib/d3.js1
-rw-r--r--app/assets/javascripts/lib/raphael.js5
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js1
-rw-r--r--app/assets/javascripts/lib/utils/emoji_aliases.js.coffee.erb2
-rw-r--r--app/assets/javascripts/lib/utils/emoji_aliases.js.erb6
-rw-r--r--app/assets/javascripts/lib/utils/notify.js5
-rw-r--r--app/assets/javascripts/lib/utils/text_utility.js5
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js6
-rw-r--r--app/assets/javascripts/line_highlighter.js69
-rw-r--r--app/assets/javascripts/merge_request.js15
-rw-r--r--app/assets/javascripts/merge_request_tabs.js84
-rw-r--r--app/assets/javascripts/merge_request_widget.js8
-rw-r--r--app/assets/javascripts/milestone.js1
-rw-r--r--app/assets/javascripts/milestone_select.js1
-rw-r--r--app/assets/javascripts/network/branch-graph.js13
-rw-r--r--app/assets/javascripts/network/network_bundle.js7
-rw-r--r--app/assets/javascripts/notes.js74
-rw-r--r--app/assets/javascripts/preview_markdown.js10
-rw-r--r--app/assets/javascripts/profile/gl_crop.js12
-rw-r--r--app/assets/javascripts/profile/profile.js4
-rw-r--r--app/assets/javascripts/profile/profile_bundle.js1
-rw-r--r--app/assets/javascripts/project.js6
-rw-r--r--app/assets/javascripts/project_find_file.js9
-rw-r--r--app/assets/javascripts/project_show.js2
-rw-r--r--app/assets/javascripts/projects_list.js1
-rw-r--r--app/assets/javascripts/protected_branch_dropdown.js.es61
-rw-r--r--app/assets/javascripts/search_autocomplete.js22
-rw-r--r--app/assets/javascripts/shortcuts.js1
-rw-r--r--app/assets/javascripts/shortcuts_find_file.js2
-rw-r--r--app/assets/javascripts/shortcuts_issuable.js6
-rw-r--r--app/assets/javascripts/syntax_highlight.js11
-rw-r--r--app/assets/javascripts/todos.js6
-rw-r--r--app/assets/javascripts/tree.js3
-rw-r--r--app/assets/javascripts/u2f/authenticate.js18
-rw-r--r--app/assets/javascripts/u2f/register.js7
-rw-r--r--app/assets/javascripts/user_tabs.js69
-rw-r--r--app/assets/javascripts/users/calendar.js7
-rw-r--r--app/assets/javascripts/users/users_bundle.js1
-rw-r--r--app/assets/javascripts/users_select.js8
-rw-r--r--app/assets/javascripts/zen_mode.js37
-rw-r--r--app/assets/stylesheets/framework/mobile.scss4
-rw-r--r--app/assets/stylesheets/pages/issuable.scss2
-rw-r--r--app/assets/stylesheets/pages/issues.scss9
-rw-r--r--app/assets/stylesheets/pages/projects.scss4
-rw-r--r--app/controllers/concerns/authenticates_with_two_factor.rb1
-rw-r--r--app/controllers/concerns/creates_commit.rb3
-rw-r--r--app/controllers/projects/blob_controller.rb10
-rw-r--r--app/controllers/projects/git_http_client_controller.rb4
-rw-r--r--app/controllers/projects/git_http_controller.rb3
-rw-r--r--app/controllers/projects/lfs_storage_controller.rb11
-rw-r--r--app/controllers/projects/merge_requests_controller.rb6
-rw-r--r--app/helpers/search_helper.rb31
-rw-r--r--app/helpers/workhorse_helper.rb4
-rw-r--r--app/models/application_setting.rb2
-rw-r--r--app/models/blob.rb12
-rw-r--r--app/models/ci/build.rb11
-rw-r--r--app/models/commit_status.rb12
-rw-r--r--app/models/concerns/has_status.rb9
-rw-r--r--app/models/member.rb33
-rw-r--r--app/models/merge_request.rb10
-rw-r--r--app/models/project.rb18
-rw-r--r--app/models/repository.rb35
-rw-r--r--app/models/service.rb1
-rw-r--r--app/services/ci/process_pipeline_service.rb6
-rw-r--r--app/services/projects/destroy_service.rb2
-rw-r--r--app/services/projects/housekeeping_service.rb12
-rw-r--r--app/views/admin/builds/_build.html.haml77
-rw-r--r--app/views/admin/builds/index.html.haml43
-rw-r--r--app/views/devise/sessions/two_factor.html.haml3
-rw-r--r--app/views/profiles/keys/index.html.haml1
-rw-r--r--app/views/projects/branches/_branch.html.haml5
-rw-r--r--app/views/projects/builds/_table.html.haml24
-rw-r--r--app/views/projects/builds/index.html.haml47
-rw-r--r--app/views/projects/ci/builds/_build.html.haml38
-rw-r--r--app/views/projects/issues/_merge_requests.html.haml2
-rw-r--r--app/views/projects/issues/_related_branches.html.haml2
-rw-r--r--app/views/projects/merge_requests/_discussion.html.haml2
-rw-r--r--app/views/projects/merge_requests/_show.html.haml31
-rw-r--r--app/views/projects/notes/_note.html.haml3
-rw-r--r--app/views/search/results/_blob.html.haml2
-rw-r--r--app/views/search/results/_wiki_blob.html.haml2
-rw-r--r--app/views/shared/builds/_tabs.html.haml24
-rw-r--r--app/views/u2f/_authenticate.html.haml2
-rw-r--r--changelogs/archive.md1810
-rw-r--r--changelogs/unreleased/.gitkeep0
-rw-r--r--config/initializers/gitlab_workhorse_secret.rb8
-rw-r--r--config/initializers/metrics.rb3
-rw-r--r--config/initializers/mime_types.rb8
-rw-r--r--db/migrate/20160725104020_merge_request_diff_remove_uniq.rb20
-rw-r--r--db/migrate/20160913162434_remove_projects_pushes_since_gc.rb19
-rw-r--r--db/schema.rb3
-rw-r--r--doc/administration/container_registry.md4
-rw-r--r--doc/api/README.md1
-rw-r--r--doc/api/ci/builds.md9
-rw-r--r--doc/api/notification_settings.md169
-rw-r--r--doc/ci/examples/README.md14
-rw-r--r--doc/ci/quick_start/README.md3
-rw-r--r--doc/ci/variables/README.md6
-rw-r--r--doc/development/instrumentation.md15
-rw-r--r--doc/install/installation.md2
-rw-r--r--doc/update/8.11-to-8.12.md4
-rw-r--r--doc/user/permissions.md2
-rw-r--r--doc/user/project/merge_requests.md3
-rw-r--r--doc/workflow/importing/import_projects_from_github.md1
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/commit_statuses.rb54
-rw-r--r--lib/api/entities.rb17
-rw-r--r--lib/api/helpers.rb4
-rw-r--r--lib/api/issues.rb12
-rw-r--r--lib/api/notification_settings.rb97
-rw-r--r--lib/api/projects.rb15
-rw-r--r--lib/banzai/filter/wiki_link_filter/rewriter.rb1
-rw-r--r--lib/ci/api/builds.rb6
-rw-r--r--lib/ci/api/helpers.rb8
-rw-r--r--lib/gitlab/backend/shell.rb2
-rw-r--r--lib/gitlab/checks/change_access.rb1
-rw-r--r--lib/gitlab/git/hook.rb12
-rw-r--r--lib/gitlab/github_import/base_formatter.rb5
-rw-r--r--lib/gitlab/github_import/comment_formatter.rb8
-rw-r--r--lib/gitlab/github_import/importer.rb16
-rw-r--r--lib/gitlab/github_import/issue_formatter.rb8
-rw-r--r--lib/gitlab/github_import/label_formatter.rb6
-rw-r--r--lib/gitlab/github_import/pull_request_formatter.rb8
-rw-r--r--lib/gitlab/github_import/release_formatter.rb23
-rw-r--r--lib/gitlab/ldap/adapter.rb58
-rw-r--r--lib/gitlab/popen.rb12
-rw-r--r--lib/gitlab/workhorse.rb52
-rw-r--r--lib/tasks/haml-lint.rake5
-rw-r--r--spec/controllers/projects_controller_spec.rb19
-rw-r--r--spec/controllers/sessions_controller_spec.rb23
-rw-r--r--spec/factories/ci/runners.rb4
-rw-r--r--spec/factories/issues.rb4
-rw-r--r--spec/features/profiles/keys_spec.rb18
-rw-r--r--spec/features/projects/branches_spec.rb48
-rw-r--r--spec/features/projects_spec.rb8
-rw-r--r--spec/features/u2f_spec.rb14
-rw-r--r--spec/finders/pipelines_finder_spec.rb5
-rw-r--r--spec/helpers/search_helper_spec.rb32
-rw-r--r--spec/javascripts/awards_handler_spec.js7
-rw-r--r--spec/javascripts/behaviors/quick_submit_spec.js3
-rw-r--r--spec/javascripts/fixtures/u2f/authenticate.html.haml2
-rw-r--r--spec/javascripts/graphs/stat_graph_contributors_graph_spec.js8
-rw-r--r--spec/javascripts/issue_spec.js2
-rw-r--r--spec/javascripts/new_branch_spec.js2
-rw-r--r--spec/javascripts/project_title_spec.js12
-rw-r--r--spec/javascripts/right_sidebar_spec.js4
-rw-r--r--spec/javascripts/search_autocomplete_spec.js14
-rw-r--r--spec/javascripts/shortcuts_issuable_spec.js1
-rw-r--r--spec/javascripts/spec_helper.js38
-rw-r--r--spec/javascripts/u2f/authenticate_spec.js8
-rw-r--r--spec/javascripts/u2f/register_spec.js8
-rw-r--r--spec/javascripts/zen_mode_spec.js4
-rw-r--r--spec/lib/banzai/pipeline/wiki_pipeline_spec.rb7
-rw-r--r--spec/lib/gitlab/github_import/comment_formatter_spec.rb6
-rw-r--r--spec/lib/gitlab/github_import/importer_spec.rb44
-rw-r--r--spec/lib/gitlab/github_import/issue_formatter_spec.rb6
-rw-r--r--spec/lib/gitlab/github_import/label_formatter_spec.rb28
-rw-r--r--spec/lib/gitlab/github_import/pull_request_formatter_spec.rb6
-rw-r--r--spec/lib/gitlab/github_import/release_formatter_spec.rb54
-rw-r--r--spec/lib/gitlab/ldap/adapter_spec.rb71
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb91
-rw-r--r--spec/models/blob_spec.rb20
-rw-r--r--spec/models/build_spec.rb28
-rw-r--r--spec/models/ci/pipeline_spec.rb28
-rw-r--r--spec/models/commit_status_spec.rb14
-rw-r--r--spec/models/member_spec.rb42
-rw-r--r--spec/models/merge_request_spec.rb77
-rw-r--r--spec/models/project_spec.rb86
-rw-r--r--spec/models/repository_spec.rb26
-rw-r--r--spec/requests/api/commit_statuses_spec.rb39
-rw-r--r--spec/requests/api/issues_spec.rb129
-rw-r--r--spec/requests/api/notification_settings_spec.rb89
-rw-r--r--spec/requests/ci/api/builds_spec.rb71
-rw-r--r--spec/requests/git_http_spec.rb18
-rw-r--r--spec/requests/lfs_http_spec.rb19
-rw-r--r--spec/services/projects/housekeeping_service_spec.rb29
-rw-r--r--spec/support/ldap_helpers.rb47
-rw-r--r--spec/support/workhorse_helpers.rb5
-rw-r--r--vendor/assets/javascripts/task_list.js161
-rw-r--r--vendor/gitignore/Global/NetBeans.gitignore1
-rw-r--r--vendor/gitignore/Global/Tags.gitignore1
-rw-r--r--vendor/gitignore/Global/macOS.gitignore (renamed from vendor/gitignore/Global/OSX.gitignore)3
-rw-r--r--vendor/gitignore/Haskell.gitignore1
-rw-r--r--vendor/gitignore/Joomla.gitignore16
-rw-r--r--vendor/gitignore/Node.gitignore3
-rw-r--r--vendor/gitignore/Objective-C.gitignore2
-rw-r--r--vendor/gitignore/Python.gitignore1
-rw-r--r--vendor/gitignore/Rails.gitignore6
-rw-r--r--vendor/gitignore/VisualStudio.gitignore7
-rw-r--r--vendor/gitlab-ci-yml/Docker.gitlab-ci.yml7
-rw-r--r--vendor/gitlab-ci-yml/Ruby.gitlab-ci.yml9
-rw-r--r--vendor/gitlab-ci-yml/Swift.gitlab-ci.yml30
238 files changed, 5099 insertions, 2467 deletions
diff --git a/.flayignore b/.flayignore
index 9c9875d4f9e..f120de527bd 100644
--- a/.flayignore
+++ b/.flayignore
@@ -1 +1,2 @@
*.erb
+lib/gitlab/sanitizers/svg/whitelist.rb
diff --git a/.gitignore b/.gitignore
index 1bf9a47aef6..9166512606d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,3 +48,4 @@
/vendor/bundle/*
/builds/*
/shared/*
+/.gitlab_workhorse_secret
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f51d506f64a..b167fc74996 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -206,10 +206,15 @@ spinach 9 10 ruby21: *spinach-knapsack-ruby21
- bundle exec $CI_BUILD_NAME
rubocop: *exec
+rake haml_lint: *exec
rake scss_lint: *exec
rake brakeman: *exec
-rake flog: *exec
-rake flay: *exec
+rake flog:
+ <<: *exec
+ allow_failure: yes
+rake flay:
+ <<: *exec
+ allow_failure: yes
license_finder: *exec
rake downtime_check: *exec
diff --git a/.haml-lint.yml b/.haml-lint.yml
new file mode 100644
index 00000000000..da9a43d9c6d
--- /dev/null
+++ b/.haml-lint.yml
@@ -0,0 +1,103 @@
+# Whether to ignore frontmatter at the beginning of HAML documents for
+# frameworks such as Jekyll/Middleman
+skip_frontmatter: false
+exclude:
+ - 'vendor/**/*'
+ - 'spec/**/*'
+
+linters:
+ AltText:
+ enabled: false
+
+ ClassAttributeWithStaticValue:
+ enabled: false
+
+ ClassesBeforeIds:
+ enabled: false
+
+ ConsecutiveComments:
+ enabled: false
+
+ ConsecutiveSilentScripts:
+ enabled: false
+ max_consecutive: 2
+
+ EmptyObjectReference:
+ enabled: true
+
+ EmptyScript:
+ enabled: true
+
+ FinalNewline:
+ enabled: false
+ present: true
+
+ HtmlAttributes:
+ enabled: false
+
+ ImplicitDiv:
+ enabled: false
+
+ LeadingCommentSpace:
+ enabled: false
+
+ LineLength:
+ enabled: false
+ max: 80
+
+ MultilinePipe:
+ enabled: false
+
+ MultilineScript:
+ enabled: true
+
+ ObjectReferenceAttributes:
+ enabled: true
+
+ RuboCop:
+ enabled: false
+ # These cops are incredibly noisy when it comes to HAML templates, so we
+ # ignore them.
+ ignored_cops:
+ - Lint/BlockAlignment
+ - Lint/EndAlignment
+ - Lint/Void
+ - Metrics/LineLength
+ - Style/AlignParameters
+ - Style/BlockNesting
+ - Style/ElseAlignment
+ - Style/FileName
+ - Style/FinalNewline
+ - Style/FrozenStringLiteralComment
+ - Style/IfUnlessModifier
+ - Style/IndentationWidth
+ - Style/Next
+ - Style/TrailingBlankLines
+ - Style/TrailingWhitespace
+ - Style/WhileUntilModifier
+
+ RubyComments:
+ enabled: false
+
+ SpaceBeforeScript:
+ enabled: false
+
+ SpaceInsideHashAttributes:
+ enabled: false
+ style: space
+
+ Indentation:
+ enabled: true
+ character: space # or tab
+
+ TagName:
+ enabled: true
+
+ TrailingWhitespace:
+ enabled: false
+
+ UnnecessaryInterpolation:
+ enabled: false
+
+ UnnecessaryStringOutput:
+ enabled: false
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 20daf1619a7..87520c67dd5 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -1,21 +1,21 @@
# This configuration was generated by
# `rubocop --auto-gen-config --exclude-limit 0`
-# on 2016-07-13 12:36:08 -0600 using RuboCop version 0.41.2.
+# on 2016-09-14 15:44:53 -0400 using RuboCop version 0.42.0.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
-# Offense count: 154
+# Offense count: 158
Lint/AmbiguousRegexpLiteral:
Enabled: false
-# Offense count: 43
+# Offense count: 41
# Configuration parameters: AllowSafeAssignment.
Lint/AssignmentInCondition:
Enabled: false
-# Offense count: 14
+# Offense count: 16
Lint/HandleExceptions:
Enabled: false
@@ -23,28 +23,28 @@ Lint/HandleExceptions:
Lint/Loop:
Enabled: false
-# Offense count: 15
+# Offense count: 16
Lint/ShadowingOuterLocalVariable:
Enabled: false
-# Offense count: 3
+# Offense count: 6
# Cop supports --auto-correct.
Lint/StringConversionInInterpolation:
Enabled: false
-# Offense count: 44
+# Offense count: 49
# Cop supports --auto-correct.
# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
Lint/UnusedBlockArgument:
Enabled: false
-# Offense count: 129
+# Offense count: 144
# Cop supports --auto-correct.
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods.
Lint/UnusedMethodArgument:
Enabled: false
-# Offense count: 12
+# Offense count: 9
# Cop supports --auto-correct.
Performance/PushSplat:
Enabled: false
@@ -59,51 +59,51 @@ Performance/RedundantBlockCall:
Performance/RedundantMatch:
Enabled: false
-# Offense count: 24
+# Offense count: 27
# Cop supports --auto-correct.
# Configuration parameters: MaxKeyValuePairs.
Performance/RedundantMerge:
Enabled: false
-# Offense count: 60
+# Offense count: 61
Rails/OutputSafety:
Enabled: false
-# Offense count: 128
+# Offense count: 129
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: strict, flexible
Rails/TimeZone:
Enabled: false
-# Offense count: 12
+# Offense count: 15
# Cop supports --auto-correct.
# Configuration parameters: Include.
# Include: app/models/**/*.rb
Rails/Validation:
Enabled: false
-# Offense count: 217
+# Offense count: 273
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
# SupportedStyles: with_first_parameter, with_fixed_indentation
Style/AlignParameters:
Enabled: false
-# Offense count: 32
+# Offense count: 30
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: always, conditionals
Style/AndOr:
Enabled: false
-# Offense count: 47
+# Offense count: 50
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: percent_q, bare_percent
Style/BarePercentLiterals:
Enabled: false
-# Offense count: 258
+# Offense count: 289
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: braces, no_braces, context_dependent
@@ -126,14 +126,14 @@ Style/ColonMethodCall:
Style/CommentAnnotation:
Enabled: false
-# Offense count: 34
+# Offense count: 33
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles, SingleLineConditionsOnly.
# SupportedStyles: assign_to_condition, assign_inside_condition
Style/ConditionalAssignment:
Enabled: false
-# Offense count: 789
+# Offense count: 881
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: leading, trailing
@@ -144,11 +144,12 @@ Style/DotPosition:
Style/DoubleNegation:
Enabled: false
-# Offense count: 3
+# Offense count: 4
+# Cop supports --auto-correct.
Style/EachWithObject:
Enabled: false
-# Offense count: 30
+# Offense count: 25
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: empty, nil, both
@@ -160,7 +161,7 @@ Style/EmptyElse:
Style/EmptyLiteral:
Enabled: false
-# Offense count: 123
+# Offense count: 135
# Cop supports --auto-correct.
# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment.
Style/ExtraSpacing:
@@ -172,16 +173,16 @@ Style/ExtraSpacing:
Style/FormatString:
Enabled: false
-# Offense count: 48
+# Offense count: 51
# Configuration parameters: MinBodyLength.
Style/GuardClause:
Enabled: false
-# Offense count: 11
+# Offense count: 9
Style/IfInsideElse:
Enabled: false
-# Offense count: 177
+# Offense count: 174
# Cop supports --auto-correct.
# Configuration parameters: MaxLineLength.
Style/IfUnlessModifier:
@@ -194,7 +195,7 @@ Style/IfUnlessModifier:
Style/IndentArray:
Enabled: false
-# Offense count: 89
+# Offense count: 97
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
# SupportedStyles: special_inside_parentheses, consistent, align_braces
@@ -208,7 +209,7 @@ Style/IndentHash:
Style/Lambda:
Enabled: false
-# Offense count: 6
+# Offense count: 5
# Cop supports --auto-correct.
Style/LineEndConcatenation:
Enabled: false
@@ -218,17 +219,21 @@ Style/LineEndConcatenation:
Style/MethodCallParentheses:
Enabled: false
-# Offense count: 62
+# Offense count: 8
+Style/MethodMissing:
+ Enabled: false
+
+# Offense count: 85
# Cop supports --auto-correct.
Style/MutableConstant:
Enabled: false
-# Offense count: 10
+# Offense count: 8
# Cop supports --auto-correct.
Style/NestedParenthesizedCalls:
Enabled: false
-# Offense count: 12
+# Offense count: 13
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles.
# SupportedStyles: skip_modifier_ifs, always
@@ -242,12 +247,19 @@ Style/Next:
Style/NumericLiteralPrefix:
Enabled: false
+# Offense count: 64
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: predicate, comparison
+Style/NumericPredicate:
+ Enabled: false
+
# Offense count: 29
# Cop supports --auto-correct.
Style/ParallelAssignment:
Enabled: false
-# Offense count: 208
+# Offense count: 264
# Cop supports --auto-correct.
# Configuration parameters: PreferredDelimiters.
Style/PercentLiteralDelimiters:
@@ -265,7 +277,7 @@ Style/PercentQLiterals:
Style/PerlBackrefs:
Enabled: false
-# Offense count: 32
+# Offense count: 35
# Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist.
# NamePrefix: is_, has_, have_
# NamePrefixBlacklist: is_, has_, have_
@@ -273,7 +285,7 @@ Style/PerlBackrefs:
Style/PredicateName:
Enabled: false
-# Offense count: 28
+# Offense count: 27
# Cop supports --auto-correct.
Style/PreferredHashMethods:
Enabled: false
@@ -283,14 +295,14 @@ Style/PreferredHashMethods:
Style/Proc:
Enabled: false
-# Offense count: 20
+# Offense count: 22
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: compact, exploded
Style/RaiseArgs:
Enabled: false
-# Offense count: 3
+# Offense count: 4
# Cop supports --auto-correct.
Style/RedundantBegin:
Enabled: false
@@ -300,29 +312,29 @@ Style/RedundantBegin:
Style/RedundantException:
Enabled: false
-# Offense count: 23
+# Offense count: 24
# Cop supports --auto-correct.
Style/RedundantFreeze:
Enabled: false
-# Offense count: 377
+# Offense count: 408
# Cop supports --auto-correct.
Style/RedundantSelf:
Enabled: false
-# Offense count: 94
+# Offense count: 93
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes.
# SupportedStyles: slashes, percent_r, mixed
Style/RegexpLiteral:
Enabled: false
-# Offense count: 17
+# Offense count: 18
# Cop supports --auto-correct.
Style/RescueModifier:
Enabled: false
-# Offense count: 2
+# Offense count: 5
# Cop supports --auto-correct.
Style/SelfAssignment:
Enabled: false
@@ -339,42 +351,42 @@ Style/SingleLineBlockParams:
Style/SingleLineMethods:
Enabled: false
-# Offense count: 119
+# Offense count: 124
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: space, no_space
Style/SpaceBeforeBlockBraces:
Enabled: false
-# Offense count: 11
+# Offense count: 10
# Cop supports --auto-correct.
# Configuration parameters: AllowForAlignment.
Style/SpaceBeforeFirstArg:
Enabled: false
-# Offense count: 130
+# Offense count: 141
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
# SupportedStyles: space, no_space
Style/SpaceInsideBlockBraces:
Enabled: false
-# Offense count: 98
+# Offense count: 96
# Cop supports --auto-correct.
Style/SpaceInsideBrackets:
Enabled: false
-# Offense count: 60
+# Offense count: 62
# Cop supports --auto-correct.
Style/SpaceInsideParens:
Enabled: false
-# Offense count: 5
+# Offense count: 7
# Cop supports --auto-correct.
Style/SpaceInsidePercentLiteralDelimiters:
Enabled: false
-# Offense count: 36
+# Offense count: 40
# Cop supports --auto-correct.
# Configuration parameters: SupportedStyles.
# SupportedStyles: use_perl_names, use_english_names
@@ -388,21 +400,28 @@ Style/SpecialGlobalVars:
Style/StringLiteralsInInterpolation:
Enabled: false
-# Offense count: 24
+# Offense count: 32
# Cop supports --auto-correct.
# Configuration parameters: IgnoredMethods.
# IgnoredMethods: respond_to, define_method
Style/SymbolProc:
Enabled: false
-# Offense count: 23
+# Offense count: 5
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, AllowSafeAssignment.
+# SupportedStyles: require_parentheses, require_no_parentheses
+Style/TernaryParentheses:
+ Enabled: false
+
+# Offense count: 24
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
# SupportedStyles: comma, consistent_comma, no_comma
Style/TrailingCommaInArguments:
Enabled: false
-# Offense count: 113
+# Offense count: 102
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
# SupportedStyles: comma, consistent_comma, no_comma
@@ -415,7 +434,7 @@ Style/TrailingCommaInLiteral:
Style/TrailingUnderscoreVariable:
Enabled: false
-# Offense count: 90
+# Offense count: 76
# Cop supports --auto-correct.
Style/TrailingWhitespace:
Enabled: false
@@ -427,12 +446,12 @@ Style/TrailingWhitespace:
Style/TrivialAccessors:
Enabled: false
-# Offense count: 3
+# Offense count: 2
# Cop supports --auto-correct.
Style/UnlessElse:
Enabled: false
-# Offense count: 13
+# Offense count: 14
# Cop supports --auto-correct.
Style/UnneededInterpolation:
Enabled: false
diff --git a/CHANGELOG b/CHANGELOG
index d45b1694ff9..0056c6cc649 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,29 +2,39 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.12.0 (unreleased)
- Update the rouge gem to 2.0.6, which adds highlighting support for JSX, Prometheus, and others. !6251
+ - Only check :can_resolve permission if the note is resolvable
- Add ability to fork to a specific namespace using API. (ritave)
- Cleanup misalignments in Issue list view !6206
- Prune events older than 12 months. (ritave)
- Prepend blank line to `Closes` message on merge request linked to issue (lukehowell)
- Filter tags by name !6121
+ - Update gitlab shell secret file also when it is empty. !3774 (glensc)
- Give project selection dropdowns responsive width, make non-wrapping.
- Make push events have equal vertical spacing.
- Add two-factor recovery endpoint to internal API !5510
+ - Pass the "Remember me" value to the U2F authentication form
- Remove vendor prefixes for linear-gradient CSS (ClemMakesApps)
+ - Move pushes_since_gc from the database to Redis
- Add font color contrast to external label in admin area (ClemMakesApps)
- Change logo animation to CSS (ClemMakesApps)
- Instructions for enabling Git packfile bitmaps !6104
+ - Use Search::GlobalService.new in the `GET /projects/search/:query` endpoint
- Fix pagination on user snippets page
+ - Fix sorting of issues in API
+ - Ensure specs on sorting of issues in API are deterministic on MySQL
- Escape search term before passing it to Regexp.new !6241 (winniehell)
- Fix pinned sidebar behavior in smaller viewports !6169
+ - Fix file permissions change when updating a file on the Gitlab UI !5979
- Change merge_error column from string to text type
- Reduce contributions calendar data payload (ClemMakesApps)
- Add `web_url` field to issue, merge request, and snippet API objects (Ben Boeckel)
+ - Enable pipeline events by default !6278
- Move parsing of sidekiq ps into helper !6245 (pascalbetz)
- Expose `sha` and `merge_commit_sha` in merge request API (Ben Boeckel)
- Set path for all JavaScript cookies to honor GitLab's subdirectory setting !5627 (Mike Greiling)
- Fix blame table layout width
- Fix bug where pagination is still displayed despite all todos marked as done (ClemMakesApps)
+ - Request only the LDAP attributes we need !6187
- Center build stage columns in pipeline overview (ClemMakesApps)
- Rename behaviour to behavior in bug issue template for consistency (ClemMakesApps)
- Fix bug stopping issue description being scrollable after selecting issue template
@@ -35,6 +45,7 @@ v 8.12.0 (unreleased)
- Add textarea autoresize after comment (ClemMakesApps)
- Refresh todos count cache when an Issue/MR is deleted
- Fix branches page dropdown sort alignment (ClemMakesApps)
+ - Hides merge request button on branches page is user doesn't have permissions
- Add white background for no readme container (ClemMakesApps)
- API: Expose issue confidentiality flag. (Robert Schilling)
- Fix markdown anchor icon interaction (ClemMakesApps)
@@ -71,6 +82,7 @@ v 8.12.0 (unreleased)
- Remove inconsistent font weight for sidebar's labels (ClemMakesApps)
- Align add button on repository view (ClemMakesApps)
- Fix contributions calendar month label truncation (ClemMakesApps)
+ - Import release note descriptions from GitHub (EspadaV8)
- Added tests for diff notes
- Add pipeline events to Slack integration !5525
- Add a button to download latest successful artifacts for branches and tags !5142
@@ -82,12 +94,14 @@ v 8.12.0 (unreleased)
- Fix repo title alignment (ClemMakesApps)
- Change update interval of contacted_at
- Fix branch title trailing space on hover (ClemMakesApps)
+ - Don't include 'Created By' tag line when importing from GitHub if there is a linked GitLab account (EspadaV8)
- Award emoji tooltips containing more than 10 usernames are now truncated !4780 (jlogandavison)
- Fix duplicate "me" in award emoji tooltip !5218 (jlogandavison)
- Order award emoji tooltips in order they were added (EspadaV8)
- Fix spacing and vertical alignment on build status icon on commits page (ClemMakesApps)
- Update merge_requests.md with a simpler way to check out a merge request. !5944
- Fix button missing type (ClemMakesApps)
+ - Gitlab::Checks is now instrumented
- Move to project dropdown with infinite scroll for better performance
- Fix leaking of submit buttons outside the width of a main container !18731 (originally by @pavelloz)
- Load branches asynchronously in Cherry Pick and Revert dialogs.
@@ -103,8 +117,11 @@ v 8.12.0 (unreleased)
- Use the default branch for displaying the project icon instead of master !5792 (Hannes Rosenögger)
- Adds response mime type to transaction metric action when it's not HTML
- Fix hover leading space bug in pipeline graph !5980
+ - Avoid conflict with admin labels when importing GitHub labels
- User can edit closed MR with deleted fork (Katarzyna Kobierska Ula Budziszewska) !5496
- Fix repository page ui issues
+ - Avoid protected branches checks when verifying access without branch name
+ - Add information about user and manual build start to runner as variables !6201 (Sergey Gnuskov)
- Fixed invisible scroll controls on build page on iPhone
- Fix error on raw build trace download for old builds stored in database !4822
- Refactor the triggers page and documentation !6217
@@ -112,8 +129,24 @@ v 8.12.0 (unreleased)
- Use default clone protocol on "check out, review, and merge locally" help page URL
- API for Ci Lint !5953 (Katarzyna Kobierska Urszula Budziszewska)
- Allow bulk update merge requests from merge requests index page
-
-v 8.11.6 (unreleased)
+ - Add notification_settings API calls !5632 (mahcsig)
+ - Remove duplication between project builds and admin builds view !5680 (Katarzyna Kobierska Ula Budziszewska)
+ - Fix URLs with anchors in wiki !6300 (houqp)
+ - Deleting source project with existing fork link will close all related merge requests !6177 (Katarzyna Kobierska Ula Budziszeska)
+ - Return 204 instead of 404 for /ci/api/v1/builds/register.json if no builds are scheduled for a runner !6225
+ - Fix Gitlab::Popen.popen thread-safety issue
+ - Add specs to removing project (Katarzyna Kobierska Ula Budziszewska)
+ - Clean environment variables when running git hooks
+
+v 8.11.6
+ - Fix unnecessary horizontal scroll area in pipeline visualizations. !6005
+ - Make merge conflict file size limit 200 KB, to match the docs. !6052
+ - Fix an error where we were unable to create a CommitStatus for running state. !6107
+ - Optimize discussion notes resolving and unresolving. !6141
+ - Fix GitLab import button. !6167
+ - Restore SSH Key title auto-population behavior. !6186
+ - Fix DB schema to match latest migration. !6256
+ - Exclude some pending or inactivated rows in Member scopes.
v 8.11.5
- Optimize branch lookups and force a repository reload for Repository#find_branch. !6087
@@ -318,6 +351,13 @@ v 8.11.0
- Update gitlab_git gem to 10.4.7
- Simplify SQL queries of marking a todo as done
+v 8.10.9
+ - Exclude some pending or inactivated rows in Member scopes
+
+v 8.10.8
+ - Fix information disclosure in issue boards.
+ - Fix privilege escalation in project import.
+
v 8.10.7
- Upgrade Hamlit to 2.6.1. !5873
- Upgrade Doorkeeper to 4.2.0. !5881
@@ -543,6 +583,9 @@ v 8.10.0
- Fix migration corrupting import data for old version upgrades
- Show tooltip on GitLab export link in new project page
+v 8.9.9
+ - Exclude some pending or inactivated rows in Member scopes
+
v 8.9.8
- Upgrade Doorkeeper to 4.2.0. !5881
@@ -2029,1692 +2072,5 @@ v 8.0.0
- Redirect from incorrectly cased group or project path to correct one (Francesco Levorato)
- Removed API calls from CE to CI
-v 7.14.3
- - No changes
-
-v 7.14.2
- - Upgrade gitlab_git to 7.2.15 to fix `git blame` errors with ISO-encoded files (Stan Hu)
- - Allow configuration of LDAP attributes GitLab will use for the new user account.
-
-v 7.14.1
- - Improve abuse reports management from admin area
- - Fix "Reload with full diff" URL button in compare branch view (Stan Hu)
- - Disabled DNS lookups for SSH in docker image (Rowan Wookey)
- - Only include base URL in OmniAuth full_host parameter (Stan Hu)
- - Fix Error 500 in API when accessing a group that has an avatar (Stan Hu)
- - Ability to enable SSL verification for Webhooks
-
-v 7.14.0
- - Fix bug where non-project members of the target project could set labels on new merge requests.
- - Update default robots.txt rules to disallow crawling of irrelevant pages (Ben Bodenmiller)
- - Fix redirection after sign in when using auto_sign_in_with_provider
- - Upgrade gitlab_git to 7.2.14 to ignore CRLFs in .gitmodules (Stan Hu)
- - Clear cache to prevent listing deleted branches after MR removes source branch (Stan Hu)
- - Provide more feedback what went wrong if HipChat service failed test (Stan Hu)
- - Fix bug where backslashes in inline diffs could be dropped (Stan Hu)
- - Disable turbolinks when linking to Bitbucket import status (Stan Hu)
- - Fix broken code import and display error messages if something went wrong with creating project (Stan Hu)
- - Fix corrupted binary files when using API files endpoint (Stan Hu)
- - Bump Haml to 4.0.7 to speed up textarea rendering (Stan Hu)
- - Show incompatible projects in Bitbucket import status (Stan Hu)
- - Fix coloring of diffs on MR Discussion-tab (Gert Goet)
- - Fix "Network" and "Graphs" pages for branches with encoded slashes (Stan Hu)
- - Fix errors deleting and creating branches with encoded slashes (Stan Hu)
- - Always add current user to autocomplete controller to support filter by "Me" (Stan Hu)
- - Fix multi-line syntax highlighting (Stan Hu)
- - Fix network graph when branch name has single quotes (Stan Hu)
- - Add "Confirm user" button in user admin page (Stan Hu)
- - Upgrade gitlab_git to version 7.2.6 to fix Error 500 when creating network graphs (Stan Hu)
- - Add support for Unicode filenames in relative links (Hiroyuki Sato)
- - Fix URL used for refreshing notes if relative_url is present (Bartłomiej Święcki)
- - Fix commit data retrieval when branch name has single quotes (Stan Hu)
- - Check that project was actually created rather than just validated in import:repos task (Stan Hu)
- - Fix full screen mode for snippet comments (Daniel Gerhardt)
- - Fix 404 error in files view after deleting the last file in a repository (Stan Hu)
- - Fix the "Reload with full diff" URL button (Stan Hu)
- - Fix label read access for unauthenticated users (Daniel Gerhardt)
- - Fix access to disabled features for unauthenticated users (Daniel Gerhardt)
- - Fix OAuth provider bug where GitLab would not go return to the redirect_uri after sign-in (Stan Hu)
- - Fix file upload dialog for comment editing (Daniel Gerhardt)
- - Set OmniAuth full_host parameter to ensure redirect URIs are correct (Stan Hu)
- - Return comments in created order in merge request API (Stan Hu)
- - Disable internal issue tracker controller if external tracker is used (Stan Hu)
- - Expire Rails cache entries after two weeks to prevent endless Redis growth
- - Add support for destroying project milestones (Stan Hu)
- - Allow custom backup archive permissions
- - Add project star and fork count, group avatar URL and user/group web URL attributes to API
- - Show who last edited a comment if it wasn't the original author
- - Send notification to all participants when MR is merged.
- - Add ability to manage user email addresses via the API.
- - Show buttons to add license, changelog and contribution guide if they're missing.
- - Tweak project page buttons.
- - Disabled autocapitalize and autocorrect on login field (Daryl Chan)
- - Mention group and project name in creation, update and deletion notices (Achilleas Pipinellis)
- - Update gravatar link on profile page to link to configured gravatar host (Ben Bodenmiller)
- - Remove redis-store TTL monkey patch
- - Add support for CI skipped status
- - Fetch code from forks to refs/merge-requests/:id/head when merge request created
- - Remove comments and email addresses when publicly exposing ssh keys (Zeger-Jan van de Weg)
- - Add "Check out branch" button to the MR page.
- - Improve MR merge widget text and UI consistency.
- - Improve text in MR "How To Merge" modal.
- - Cache all events
- - Order commits by date when comparing branches
- - Fix bug causing error when the target branch of a symbolic ref was deleted
- - Include branch/tag name in archive file and directory name
- - Add dropzone upload progress
- - Add a label for merged branches on branches page (Florent Baldino)
- - Detect .mkd and .mkdn files as markdown (Ben Boeckel)
- - Fix: User search feature in admin area does not respect filters
- - Set max-width for README, issue and merge request description for easier read on big screens
- - Update Flowdock integration to support new Flowdock API (Boyan Tabakov)
- - Remove author from files view (Sven Strickroth)
- - Fix infinite loop when SAML was incorrectly configured.
-
-v 7.13.5
- - Satellites reverted
-
-v 7.13.4
- - Allow users to send abuse reports
-
-v 7.13.3
- - Fix bug causing Bitbucket importer to crash when OAuth application had been removed.
- - Allow users to send abuse reports
- - Remove satellites
- - Link username to profile on Group Members page (Tom Webster)
-
-v 7.13.2
- - Fix randomly failed spec
- - Create project services on Project creation
- - Add admin_merge_request ability to Developer level and up
- - Fix Error 500 when browsing projects with no HEAD (Stan Hu)
- - Fix labels / assignee / milestone for the merge requests when issues are disabled
- - Show the first tab automatically on MergeRequests#new
- - Add rake task 'gitlab:update_commit_count' (Daniel Gerhardt)
- - Fix Gmail Actions
-
-v 7.13.1
- - Fix: Label modifications are not reflected in existing notes and in the issue list
- - Fix: Label not shown in the Issue list, although it's set through web interface
- - Fix: Group/project references are linked incorrectly
- - Improve documentation
- - Fix of migration: Check if session_expire_delay column exists before adding the column
- - Fix: ActionView::Template::Error
- - Fix: "Create Merge Request" isn't always shown in event for newly pushed branch
- - Fix bug causing "Remove source-branch" option not to work for merge requests from the same project.
- - Render Note field hints consistently for "new" and "edit" forms
-
-v 7.13.0
- - Remove repository graph log to fix slow cache updates after push event (Stan Hu)
- - Only enable HSTS header for HTTPS and port 443 (Stan Hu)
- - Fix user autocomplete for unauthenticated users accessing public projects (Stan Hu)
- - Fix redirection to home page URL for unauthorized users (Daniel Gerhardt)
- - Add branch switching support for graphs (Daniel Gerhardt)
- - Fix external issue tracker hook/test for HTTPS URLs (Daniel Gerhardt)
- - Remove link leading to a 404 error in Deploy Keys page (Stan Hu)
- - Add support for unlocking users in admin settings (Stan Hu)
- - Add Irker service configuration options (Stan Hu)
- - Fix order of issues imported from GitHub (Hiroyuki Sato)
- - Bump rugments to 1.0.0beta8 to fix C prototype function highlighting (Jonathon Reinhart)
- - Fix Merge Request webhook to properly fire "merge" action when accepted from the web UI
- - Add `two_factor_enabled` field to admin user API (Stan Hu)
- - Fix invalid timestamps in RSS feeds (Rowan Wookey)
- - Fix downloading of patches on public merge requests when user logged out (Stan Hu)
- - Fix Error 500 when relative submodule resolves to a namespace that has a different name from its path (Stan Hu)
- - Extract the longest-matching ref from a commit path when multiple matches occur (Stan Hu)
- - Update maintenance documentation to explain no need to recompile asssets for omnibus installations (Stan Hu)
- - Support commenting on diffs in side-by-side mode (Stan Hu)
- - Fix JavaScript error when clicking on the comment button on a diff line that has a comment already (Stan Hu)
- - Return 40x error codes if branch could not be deleted in UI (Stan Hu)
- - Remove project visibility icons from dashboard projects list
- - Rename "Design" profile settings page to "Preferences".
- - Allow users to customize their default Dashboard page.
- - Update ssl_ciphers in Nginx example to remove DHE settings. This will deny forward secrecy for Android 2.3.7, Java 6 and OpenSSL 0.9.8
- - Admin can edit and remove user identities
- - Convert CRLF newlines to LF when committing using the web editor.
- - API request /projects/:project_id/merge_requests?state=closed will return only closed merge requests without merged one. If you need ones that were merged - use state=merged.
- - Allow Administrators to filter the user list by those with or without Two-factor Authentication enabled.
- - Show a user's Two-factor Authentication status in the administration area.
- - Explicit error when commit not found in the CI
- - Improve performance for issue and merge request pages
- - Users with guest access level can not set assignee, labels or milestones for issue and merge request
- - Reporter role can manage issue tracker now: edit any issue, set assignee or milestone and manage labels
- - Better performance for pages with events list, issues list and commits list
- - Faster automerge check and merge itself when source and target branches are in same repository
- - Correctly show anonymous authorized applications under Profile > Applications.
- - Query Optimization in MySQL.
- - Allow users to be blocked and unblocked via the API
- - Use native Postgres database cleaning during backup restore
- - Redesign project page. Show README as default instead of activity. Move project activity to separate page
- - Make left menu more hierarchical and less contextual by adding back item at top
- - A fork can’t have a visibility level that is greater than the original project.
- - Faster code search in repository and wiki. Fixes search page timeout for big repositories
- - Allow administrators to disable 2FA for a specific user
- - Add error message for SSH key linebreaks
- - Store commits count in database (will populate with valid values only after first push)
- - Rebuild cache after push to repository in background job
- - Fix transferring of project to another group using the API.
-
-v 7.12.2
- - Correctly show anonymous authorized applications under Profile > Applications.
- - Faster automerge check and merge itself when source and target branches are in same repository
- - Audit log for user authentication
- - Allow custom label to be set for authentication providers.
-
-v 7.12.1
- - Fix error when deleting a user who has projects (Stan Hu)
- - Fix post-receive errors on a push when an external issue tracker is configured (Stan Hu)
- - Add SAML to list of social_provider (Matt Firtion)
- - Fix merge requests API scope to keep compatibility in 7.12.x patch release (Dmitriy Zaporozhets)
- - Fix closed merge request scope at milestone page (Dmitriy Zaporozhets)
- - Revert merge request states renaming
- - Fix hooks for web based events with external issue references (Daniel Gerhardt)
- - Improve performance for issue and merge request pages
- - Compress database dumps to reduce backup size
-
-v 7.12.0
- - Fix Error 500 when one user attempts to access a personal, internal snippet (Stan Hu)
- - Disable changing of target branch in new merge request page when a branch has already been specified (Stan Hu)
- - Fix post-receive errors on a push when an external issue tracker is configured (Stan Hu)
- - Update oauth button logos for Twitter and Google to recommended assets
- - Update browser gem to version 0.8.0 for IE11 support (Stan Hu)
- - Fix timeout when rendering file with thousands of lines.
- - Add "Remember me" checkbox to LDAP signin form.
- - Add session expiration delay configuration through UI application settings
- - Don't notify users mentioned in code blocks or blockquotes.
- - Omit link to generate labels if user does not have access to create them (Stan Hu)
- - Show warning when a comment will add 10 or more people to the discussion.
- - Disable changing of the source branch in merge request update API (Stan Hu)
- - Shorten merge request WIP text.
- - Add option to disallow users from registering any application to use GitLab as an OAuth provider
- - Support editing target branch of merge request (Stan Hu)
- - Refactor permission checks with issues and merge requests project settings (Stan Hu)
- - Fix Markdown preview not working in Edit Milestone page (Stan Hu)
- - Fix Zen Mode not closing with ESC key (Stan Hu)
- - Allow HipChat API version to be blank and default to v2 (Stan Hu)
- - Add file attachment support in Milestone description (Stan Hu)
- - Fix milestone "Browse Issues" button.
- - Set milestone on new issue when creating issue from index with milestone filter active.
- - Make namespace API available to all users (Stan Hu)
- - Add webhook support for note events (Stan Hu)
- - Disable "New Issue" and "New Merge Request" buttons when features are disabled in project settings (Stan Hu)
- - Remove Rack Attack monkey patches and bump to version 4.3.0 (Stan Hu)
- - Fix clone URL losing selection after a single click in Safari and Chrome (Stan Hu)
- - Fix git blame syntax highlighting when different commits break up lines (Stan Hu)
- - Add "Resend confirmation e-mail" link in profile settings (Stan Hu)
- - Allow to configure location of the `.gitlab_shell_secret` file. (Jakub Jirutka)
- - Disabled expansion of top/bottom blobs for new file diffs
- - Update Asciidoctor gem to version 1.5.2. (Jakub Jirutka)
- - Fix resolving of relative links to repository files in AsciiDoc documents. (Jakub Jirutka)
- - Use the user list from the target project in a merge request (Stan Hu)
- - Default extention for wiki pages is now .md instead of .markdown (Jeroen van Baarsen)
- - Add validation to wiki page creation (only [a-zA-Z0-9/_-] are allowed) (Jeroen van Baarsen)
- - Fix new/empty milestones showing 100% completion value (Jonah Bishop)
- - Add a note when an Issue or Merge Request's title changes
- - Consistently refer to MRs as either Merged or Closed.
- - Add Merged tab to MR lists.
- - Prefix EmailsOnPush email subject with `[Git]`.
- - Group project contributions by both name and email.
- - Clarify navigation labels for Project Settings and Group Settings.
- - Move user avatar and logout button to sidebar
- - You can not remove user if he/she is an only owner of group
- - User should be able to leave group. If not - show him proper message
- - User has ability to leave project
- - Add SAML support as an omniauth provider
- - Allow to configure a URL to show after sign out
- - Add an option to automatically sign-in with an Omniauth provider
- - GitLab CI service sends .gitlab-ci.yml in each push call
- - When remove project - move repository and schedule it removal
- - Improve group removing logic
- - Trigger create-hooks on backup restore task
- - Add option to automatically link omniauth and LDAP identities
- - Allow special character in users bio. I.e.: I <3 GitLab
-
-v 7.11.4
- - Fix missing bullets when creating lists
- - Set rel="nofollow" on external links
-
-v 7.11.3
- - no changes
- - Fix upgrader script (Martins Polakovs)
-
-v 7.11.2
- - no changes
-
-v 7.11.1
- - no changes
-
-v 7.11.0
- - Fall back to Plaintext when Syntaxhighlighting doesn't work. Fixes some buggy lexers (Hannes Rosenögger)
- - Get editing comments to work in Chrome 43 again.
- - Fix broken view when viewing history of a file that includes a path that used to be another file (Stan Hu)
- - Don't show duplicate deploy keys
- - Fix commit time being displayed in the wrong timezone in some cases (Hannes Rosenögger)
- - Make the first branch pushed to an empty repository the default HEAD (Stan Hu)
- - Fix broken view when using a tag to display a tree that contains git submodules (Stan Hu)
- - Make Reply-To config apply to change e-mail confirmation and other Devise notifications (Stan Hu)
- - Add application setting to restrict user signups to e-mail domains (Stan Hu)
- - Don't allow a merge request to be merged when its title starts with "WIP".
- - Add a page title to every page.
- - Allow primary email to be set to an email that you've already added.
- - Fix clone URL field and X11 Primary selection (Dmitry Medvinsky)
- - Ignore invalid lines in .gitmodules
- - Fix "Cannot move project" error message from popping up after a successful transfer (Stan Hu)
- - Redirect to sign in page after signing out.
- - Fix "Hello @username." references not working by no longer allowing usernames to end in period.
- - Fix "Revspec not found" errors when viewing diffs in a forked project with submodules (Stan Hu)
- - Improve project page UI
- - Fix broken file browsing with relative submodule in personal projects (Stan Hu)
- - Add "Reply quoting selected text" shortcut key (`r`)
- - Fix bug causing `@whatever` inside an issue's first code block to be picked up as a user mention.
- - Fix bug causing `@whatever` inside an inline code snippet (backtick-style) to be picked up as a user mention.
- - When use change branches link at MR form - save source branch selection instead of target one
- - Improve handling of large diffs
- - Added GitLab Event header for project hooks
- - Add Two-factor authentication (2FA) for GitLab logins
- - Show Atom feed buttons everywhere where applicable.
- - Add project activity atom feed.
- - Don't crash when an MR from a fork has a cross-reference comment from the target project on one of its commits.
- - Explain how to get a new password reset token in welcome emails
- - Include commit comments in MR from a forked project.
- - Group milestones by title in the dashboard and all other issue views.
- - Query issues, merge requests and milestones with their IID through API (Julien Bianchi)
- - Add default project and snippet visibility settings to the admin web UI.
- - Show incompatible projects in Google Code import status (Stan Hu)
- - Fix bug where commit data would not appear in some subdirectories (Stan Hu)
- - Task lists are now usable in comments, and will show up in Markdown previews.
- - Fix bug where avatar filenames were not actually deleted from the database during removal (Stan Hu)
- - Fix bug where Slack service channel was not saved in admin template settings. (Stan Hu)
- - Protect OmniAuth request phase against CSRF.
- - Don't send notifications to mentioned users that don't have access to the project in question.
- - Add search issues/MR by number
- - Change plots to bar graphs in commit statistics screen
- - Move snippets UI to fluid layout
- - Improve UI for sidebar. Increase separation between navigation and content
- - Improve new project command options (Ben Bodenmiller)
- - Add common method to force UTF-8 and use it to properly handle non-ascii OAuth user properties (Onur Küçük)
- - Prevent sending empty messages to HipChat (Chulki Lee)
- - Improve UI for mobile phones on dashboard and project pages
- - Add room notification and message color option for HipChat
- - Allow to use non-ASCII letters and dashes in project and namespace name. (Jakub Jirutka)
- - Add footnotes support to Markdown (Guillaume Delbergue)
- - Add current_sign_in_at to UserFull REST api.
- - Make Sidekiq MemoryKiller shutdown signal configurable
- - Add "Create Merge Request" buttons to commits and branches pages and push event.
- - Show user roles by comments.
- - Fix automatic blocking of auto-created users from Active Directory.
- - Call merge request webhook for each new commits (Arthur Gautier)
- - Use SIGKILL by default in Sidekiq::MemoryKiller
- - Fix mentioning of private groups.
- - Add style for <kbd> element in markdown
- - Spin spinner icon next to "Checking for CI status..." on MR page.
- - Fix reference links in dashboard activity and ATOM feeds.
- - Ensure that the first added admin performs repository imports
-
-v 7.10.4
- - Fix migrations broken in 7.10.2
- - Make tags for GitLab installations running on MySQL case sensitive
- - Get Gitorious importer to work again.
- - Fix adding new group members from admin area
- - Fix DB error when trying to tag a repository (Stan Hu)
- - Fix Error 500 when searching Wiki pages (Stan Hu)
- - Unescape branch names in compare commit (Stan Hu)
- - Order commit comments chronologically in API.
-
-v 7.10.2
- - Fix CI links on MR page
-
-v 7.10.0
- - Ignore submodules that are defined in .gitmodules but are checked in as directories.
- - Allow projects to be imported from Google Code.
- - Remove access control for uploaded images to fix broken images in emails (Hannes Rosenögger)
- - Allow users to be invited by email to join a group or project.
- - Don't crash when project repository doesn't exist.
- - Add config var to block auto-created LDAP users.
- - Don't use HTML ellipsis in EmailsOnPush subject truncated commit message.
- - Set EmailsOnPush reply-to address to committer email when enabled.
- - Fix broken file browsing with a submodule that contains a relative link (Stan Hu)
- - Fix persistent XSS vulnerability around profile website URLs.
- - Fix project import URL regex to prevent arbitary local repos from being imported.
- - Fix directory traversal vulnerability around uploads routes.
- - Fix directory traversal vulnerability around help pages.
- - Don't leak existence of project via search autocomplete.
- - Don't leak existence of group or project via search.
- - Fix bug where Wiki pages that included a '/' were no longer accessible (Stan Hu)
- - Fix bug where error messages from Dropzone would not be displayed on the issues page (Stan Hu)
- - Add a rake task to check repository integrity with `git fsck`
- - Add ability to configure Reply-To address in gitlab.yml (Stan Hu)
- - Move current user to the top of the list in assignee/author filters (Stan Hu)
- - Fix broken side-by-side diff view on merge request page (Stan Hu)
- - Set Application controller default URL options to ensure all url_for calls are consistent (Stan Hu)
- - Allow HTML tags in Markdown input
- - Fix code unfold not working on Compare commits page (Stan Hu)
- - Fix generating SSH key fingerprints with OpenSSH 6.8. (Sašo Stanovnik)
- - Fix "Import projects from" button to show the correct instructions (Stan Hu)
- - Fix dots in Wiki slugs causing errors (Stan Hu)
- - Make maximum attachment size configurable via Application Settings (Stan Hu)
- - Update poltergeist to version 1.6.0 to support PhantomJS 2.0 (Zeger-Jan van de Weg)
- - Fix cross references when usernames, milestones, or project names contain underscores (Stan Hu)
- - Disable reference creation for comments surrounded by code/preformatted blocks (Stan Hu)
- - Reduce Rack Attack false positives causing 403 errors during HTTP authentication (Stan Hu)
- - enable line wrapping per default and remove the checkbox to toggle it (Hannes Rosenögger)
- - Fix a link in the patch update guide
- - Add a service to support external wikis (Hannes Rosenögger)
- - Omit the "email patches" link and fix plain diff view for merge commits
- - List new commits for newly pushed branch in activity view.
- - Add sidetiq gem dependency to match EE
- - Add changelog, license and contribution guide links to project tab bar.
- - Improve diff UI
- - Fix alignment of navbar toggle button (Cody Mize)
- - Fix checkbox rendering for nested task lists
- - Identical look of selectboxes in UI
- - Upgrade the gitlab_git gem to version 7.1.3
- - Move "Import existing repository by URL" option to button.
- - Improve error message when save profile has error.
- - Passing the name of pushed ref to CI service (requires GitLab CI 7.9+)
- - Add location field to user profile
- - Fix print view for markdown files and wiki pages
- - Fix errors when deleting old backups
- - Improve GitLab performance when working with git repositories
- - Add tag message and last commit to tag hook (Kamil Trzciński)
- - Restrict permissions on backup files
- - Improve oauth accounts UI in profile page
- - Add ability to unlink connected accounts
- - Replace commits calendar with faster contribution calendar that includes issues and merge requests
- - Add inifinite scroll to user page activity
- - Don't include system notes in issue/MR comment count.
- - Don't mark merge request as updated when merge status relative to target branch changes.
- - Link note avatar to user.
- - Make Git-over-SSH errors more descriptive.
- - Fix EmailsOnPush.
- - Refactor issue filtering
- - AJAX selectbox for issue assignee and author filters
- - Fix issue with missing options in issue filtering dropdown if selected one
- - Prevent holding Control-Enter or Command-Enter from posting comment multiple times.
- - Prevent note form from being cleared when submitting failed.
- - Improve file icons rendering on tree (Sullivan Sénéchal)
- - API: Add pagination to project events
- - Get issue links in notification mail to work again.
- - Don't show commit comment button when user is not signed in.
- - Fix admin user projects lists.
- - Don't leak private group existence by redirecting from namespace controller to group controller.
- - Ability to skip some items from backup (database, respositories or uploads)
- - Archive repositories in background worker.
- - Import GitHub, Bitbucket or GitLab.com projects owned by authenticated user into current namespace.
- - Project labels are now available over the API under the "tag_list" field (Cristian Medina)
- - Fixed link paths for HTTP and SSH on the admin project view (Jeremy Maziarz)
- - Fix and improve help rendering (Sullivan Sénéchal)
- - Fix final line in EmailsOnPush email diff being rendered as error.
- - Prevent duplicate Buildkite service creation.
- - Fix git over ssh errors 'fatal: protocol error: bad line length character'
- - Automatically setup GitLab CI project for forks if origin project has GitLab CI enabled
- - Bust group page project list cache when namespace name or path changes.
- - Explicitly set image alt-attribute to prevent graphical glitches if gravatars could not be loaded
- - Allow user to choose a public email to show on public profile
- - Remove truncation from issue titles on milestone page (Jason Blanchard)
- - Fix stuck Merge Request merging events from old installations (Ben Bodenmiller)
- - Fix merge request comments on files with multiple commits
- - Fix Resource Owner Password Authentication Flow
- - Add icons to Add dropdown items.
- - Allow admin to create public deploy keys that are accessible to any project.
- - Warn when gitlab-shell version doesn't match requirement.
- - Skip email confirmation when set by admin or via LDAP.
- - Only allow users to reference groups, projects, issues, MRs, commits they have access to.
-
-v 7.9.4
- - Security: Fix project import URL regex to prevent arbitary local repos from being imported
- - Fixed issue where only 25 commits would load in file listings
- - Fix LDAP identities after config update
-
-v 7.9.3
- - Contains no changes
-
-v 7.9.2
- - Contains no changes
-
-v 7.9.1
- - Include missing events and fix save functionality in admin service template settings form (Stan Hu)
- - Fix "Import projects from" button to show the correct instructions (Stan Hu)
- - Fix OAuth2 issue importing a new project from GitHub and GitLab (Stan Hu)
- - Fix for LDAP with commas in DN
- - Fix missing events and in admin Slack service template settings form (Stan Hu)
- - Don't show commit comment button when user is not signed in.
- - Downgrade gemnasium-gitlab-service gem
-
-v 7.9.0
- - Add HipChat integration documentation (Stan Hu)
- - Update documentation for object_kind field in Webhook push and tag push Webhooks (Stan Hu)
- - Fix broken email images (Hannes Rosenögger)
- - Automatically config git if user forgot, where possible (Zeger-Jan van de Weg)
- - Fix mass SQL statements on initial push (Hannes Rosenögger)
- - Add tag push notifications and normalize HipChat and Slack messages to be consistent (Stan Hu)
- - Add comment notification events to HipChat and Slack services (Stan Hu)
- - Add issue and merge request events to HipChat and Slack services (Stan Hu)
- - Fix merge request URL passed to Webhooks. (Stan Hu)
- - Fix bug that caused a server error when editing a comment to "+1" or "-1" (Stan Hu)
- - Fix code preview theme setting for comments, issues, merge requests, and snippets (Stan Hu)
- - Move labels/milestones tabs to sidebar
- - Upgrade Rails gem to version 4.1.9.
- - Improve error messages for file edit failures
- - Improve UI for commits, issues and merge request lists
- - Fix commit comments on first line of diff not rendering in Merge Request Discussion view.
- - Allow admins to override restricted project visibility settings.
- - Move restricted visibility settings from gitlab.yml into the web UI.
- - Improve trigger merge request hook when source project branch has been updated (Kirill Zaitsev)
- - Save web edit in new branch
- - Fix ordering of imported but unchanged projects (Marco Wessel)
- - Mobile UI improvements: make aside content expandable
- - Expose avatar_url in projects API
- - Fix checkbox alignment on the application settings page.
- - Generalize image upload in drag and drop in markdown to all files (Hannes Rosenögger)
- - Fix mass-unassignment of issues (Robert Speicher)
- - Fix hidden diff comments in merge request discussion view
- - Allow user confirmation to be skipped for new users via API
- - Add a service to send updates to an Irker gateway (Romain Coltel)
- - Add brakeman (security scanner for Ruby on Rails)
- - Slack username and channel options
- - Add grouped milestones from all projects to dashboard.
- - Webhook sends pusher email as well as commiter
- - Add Bitbucket omniauth provider.
- - Add Bitbucket importer.
- - Support referencing issues to a project whose name starts with a digit
- - Condense commits already in target branch when updating merge request source branch.
- - Send notifications and leave system comments when bulk updating issues.
- - Automatically link commit ranges to compare page: sha1...sha4 or sha1..sha4 (includes sha1 in comparison)
- - Move groups page from profile to dashboard
- - Starred projects page at dashboard
- - Blocking user does not remove him/her from project/groups but show blocked label
- - Change subject of EmailsOnPush emails to include namespace, project and branch.
- - Change subject of EmailsOnPush emails to include first commit message when multiple were pushed.
- - Remove confusing footer from EmailsOnPush mail body.
- - Add list of changed files to EmailsOnPush emails.
- - Add option to send EmailsOnPush emails from committer email if domain matches.
- - Add option to disable code diffs in EmailOnPush emails.
- - Wrap commit message in EmailsOnPush email.
- - Send EmailsOnPush emails when deleting commits using force push.
- - Fix EmailsOnPush email comparison link to include first commit.
- - Fix highliht of selected lines in file
- - Reject access to group/project avatar if the user doesn't have access.
- - Add database migration to clean group duplicates with same path and name (Make sure you have a backup before update)
- - Add GitLab active users count to rake gitlab:check
- - Starred projects page at dashboard
- - Make email display name configurable
- - Improve json validation in hook data
- - Use Emoji One
- - Updated emoji help documentation to properly reference EmojiOne.
- - Fix missing GitHub organisation repositories on import page.
- - Added blue theme
- - Remove annoying notice messages when create/update merge request
- - Allow smb:// links in Markdown text.
- - Filter merge request by title or description at Merge Requests page
- - Block user if he/she was blocked in Active Directory
- - Fix import pages not working after first load.
- - Use custom LDAP label in LDAP signin form.
- - Execute hooks and services when branch or tag is created or deleted through web interface.
- - Block and unblock user if he/she was blocked/unblocked in Active Directory
- - Raise recommended number of unicorn workers from 2 to 3
- - Use same layout and interactivity for project members as group members.
- - Prevent gitlab-shell character encoding issues by receiving its changes as raw data.
- - Ability to unsubscribe/subscribe to issue or merge request
- - Delete deploy key when last connection to a project is destroyed.
- - Fix invalid Atom feeds when using emoji, horizontal rules, or images (Christian Walther)
- - Backup of repositories with tar instead of git bundle (only now are git-annex files included in the backup)
- - Add canceled status for CI
- - Send EmailsOnPush email when branch or tag is created or deleted.
- - Faster merge request processing for large repository
- - Prevent doubling AJAX request with each commit visit via Turbolink
- - Prevent unnecessary doubling of js events on import pages and user calendar
-
-v 7.8.4
- - Fix issue_tracker_id substitution in custom issue trackers
- - Fix path and name duplication in namespaces
-
-v 7.8.3
- - Bump version of gitlab_git fixing annotated tags without message
-
-v 7.8.2
- - Fix service migration issue when upgrading from versions prior to 7.3
- - Fix setting of the default use project limit via admin UI
- - Fix showing of already imported projects for GitLab and Gitorious importers
- - Fix response of push to repository to return "Not found" if user doesn't have access
- - Fix check if user is allowed to view the file attachment
- - Fix import check for case sensetive namespaces
- - Increase timeout for Git-over-HTTP requests to 1 hour since large pulls/pushes can take a long time.
- - Properly handle autosave local storage exceptions.
- - Escape wildcards when searching LDAP by username.
-
-v 7.8.1
- - Fix run of custom post receive hooks
- - Fix migration that caused issues when upgrading to version 7.8 from versions prior to 7.3
- - Fix the warning for LDAP users about need to set password
- - Fix avatars which were not shown for non logged in users
- - Fix urls for the issues when relative url was enabled
-
-v 7.8.0
- - Fix access control and protection against XSS for note attachments and other uploads.
- - Replace highlight.js with rouge-fork rugments (Stefan Tatschner)
- - Make project search case insensitive (Hannes Rosenögger)
- - Include issue/mr participants in list of recipients for reassign/close/reopen emails
- - Expose description in groups API
- - Better UI for project services page
- - Cleaner UI for web editor
- - Add diff syntax highlighting in email-on-push service notifications (Hannes Rosenögger)
- - Add API endpoint to fetch all changes on a MergeRequest (Jeroen van Baarsen)
- - View note image attachments in new tab when clicked instead of downloading them
- - Improve sorting logic in UI and API. Explicitly define what sorting method is used by default
- - Fix overflow at sidebar when have several items
- - Add notes for label changes in issue and merge requests
- - Show tags in commit view (Hannes Rosenögger)
- - Only count a user's vote once on a merge request or issue (Michael Clarke)
- - Increase font size when browse source files and diffs
- - Service Templates now let you set default values for all services
- - Create new file in empty repository using GitLab UI
- - Ability to clone project using oauth2 token
- - Upgrade Sidekiq gem to version 3.3.0
- - Stop git zombie creation during force push check
- - Show success/error messages for test setting button in services
- - Added Rubocop for code style checks
- - Fix commits pagination
- - Async load a branch information at the commit page
- - Disable blacklist validation for project names
- - Allow configuring protection of the default branch upon first push (Marco Wessel)
- - Add gitlab.com importer
- - Add an ability to login with gitlab.com
- - Add a commit calendar to the user profile (Hannes Rosenögger)
- - Submit comment on command-enter
- - Notify all members of a group when that group is mentioned in a comment, for example: `@gitlab-org` or `@sales`.
- - Extend issue clossing pattern to include "Resolve", "Resolves", "Resolved", "Resolving" and "Close" (Julien Bianchi and Hannes Rosenögger)
- - Fix long broadcast message cut-off on left sidebar (Visay Keo)
- - Add Project Avatars (Steven Thonus and Hannes Rosenögger)
- - Password reset token validity increased from 2 hours to 2 days since it is also send on account creation.
- - Edit group members via API
- - Enable raw image paste from clipboard, currently Chrome only (Marco Cyriacks)
- - Add action property to merge request hook (Julien Bianchi)
- - Remove duplicates from group milestone participants list.
- - Add a new API function that retrieves all issues assigned to a single milestone (Justin Whear and Hannes Rosenögger)
- - API: Access groups with their path (Julien Bianchi)
- - Added link to milestone and keeping resource context on smaller viewports for issues and merge requests (Jason Blanchard)
- - Allow notification email to be set separately from primary email.
- - API: Add support for editing an existing project (Mika Mäenpää and Hannes Rosenögger)
- - Don't have Markdown preview fail for long comments/wiki pages.
- - When test webhook - show error message instead of 500 error page if connection to hook url was reset
- - Added support for firing system hooks on group create/destroy and adding/removing users to group (Boyan Tabakov)
- - Added persistent collapse button for left side nav bar (Jason Blanchard)
- - Prevent losing unsaved comments by automatically restoring them when comment page is loaded again.
- - Don't allow page to be scaled on mobile.
- - Clean the username acquired from OAuth/LDAP so it doesn't fail username validation and block signing up.
- - Show assignees in merge request index page (Kelvin Mutuma)
- - Link head panel titles to relevant root page.
- - Allow users that signed up via OAuth to set their password in order to use Git over HTTP(S).
- - Show users button to share their newly created public or internal projects on twitter
- - Add quick help links to the GitLab pricing and feature comparison pages.
- - Fix duplicate authorized applications in user profile and incorrect application client count in admin area.
- - Make sure Markdown previews always use the same styling as the eventual destination.
- - Remove deprecated Group#owner_id from API
- - Show projects user contributed to on user page. Show stars near project on user page.
- - Improve database performance for GitLab
- - Add Asana service (Jeremy Benoist)
- - Improve project webhooks with extra data
-
-v 7.7.2
- - Update GitLab Shell to version 2.4.2 that fixes a bug when developers can push to protected branch
- - Fix issue when LDAP user can't login with existing GitLab account
-
-v 7.7.1
- - Improve mention autocomplete performance
- - Show setup instructions for GitHub import if disabled
- - Allow use http for OAuth applications
-
-v 7.7.0
- - Import from GitHub.com feature
- - Add Jetbrains Teamcity CI service (Jason Lippert)
- - Mention notification level
- - Markdown preview in wiki (Yuriy Glukhov)
- - Raise group avatar filesize limit to 200kb
- - OAuth applications feature
- - Show user SSH keys in admin area
- - Developer can push to protected branches option
- - Set project path instead of project name in create form
- - Block Git HTTP access after 10 failed authentication attempts
- - Updates to the messages returned by API (sponsored by O'Reilly Media)
- - New UI layout with side navigation
- - Add alert message in case of outdated browser (IE < 10)
- - Added API support for sorting projects
- - Update gitlab_git to version 7.0.0.rc14
- - Add API project search filter option for authorized projects
- - Fix File blame not respecting branch selection
- - Change some of application settings on fly in admin area UI
- - Redesign signin/signup pages
- - Close standard input in Gitlab::Popen.popen
- - Trigger GitLab CI when push tags
- - When accept merge request - do merge using sidaekiq job
- - Enable web signups by default
- - Fixes for diff comments: drag-n-drop images, selecting images
- - Fixes for edit comments: drag-n-drop images, preview mode, selecting images, save & update
- - Remove password strength indicator
-
-v 7.6.0
- - Fork repository to groups
- - New rugged version
- - Add CRON=1 backup setting for quiet backups
- - Fix failing wiki restore
- - Add optional Sidekiq MemoryKiller middleware (enabled via SIDEKIQ_MAX_RSS env variable)
- - Monokai highlighting style now more faithful to original design (Mark Riedesel)
- - Create project with repository in synchrony
- - Added ability to create empty repo or import existing one if project does not have repository
- - Reactivate highlight.js language autodetection
- - Mobile UI improvements
- - Change maximum avatar file size from 100KB to 200KB
- - Strict validation for snippet file names
- - Enable Markdown preview for issues, merge requests, milestones, and notes (Vinnie Okada)
- - In the docker directory is a container template based on the Omnibus packages.
- - Update Sidekiq to version 2.17.8
- - Add author filter to project issues and merge requests pages
- - Atom feed for user activity
- - Support multiple omniauth providers for the same user
- - Rendering cross reference in issue title and tooltip for merge request
- - Show username in comments
- - Possibility to create Milestones or Labels when Issues are disabled
- - Fix bug with showing gpg signature in tag
-
-v 7.5.3
- - Bump gitlab_git to 7.0.0.rc12 (includes Rugged 0.21.2)
-
-v 7.5.2
- - Don't log Sidekiq arguments by default
- - Fix restore of wiki repositories from backups
-
-v 7.5.1
- - Add missing timestamps to 'members' table
-
-v 7.5.0
- - API: Add support for Hipchat (Kevin Houdebert)
- - Add time zone configuration in gitlab.yml (Sullivan Senechal)
- - Fix LDAP authentication for Git HTTP access
- - Run 'GC.start' after every EmailsOnPushWorker job
- - Fix LDAP config lookup for provider 'ldap'
- - Drop all sequences during Postgres database restore
- - Project title links to project homepage (Ben Bodenmiller)
- - Add Atlassian Bamboo CI service (Drew Blessing)
- - Mentioned @user will receive email even if he is not participating in issue or commit
- - Session API: Use case-insensitive authentication like in UI (Andrey Krivko)
- - Tie up loose ends with annotated tags: API & UI (Sean Edge)
- - Return valid json for deleting branch via API (sponsored by O'Reilly Media)
- - Expose username in project events API (sponsored by O'Reilly Media)
- - Adds comments to commits in the API
- - Performance improvements
- - Fix post-receive issue for projects with deleted forks
- - New gitlab-shell version with custom hooks support
- - Improve code
- - GitLab CI 5.2+ support (does not support older versions)
- - Fixed bug when you can not push commits starting with 000000 to protected branches
- - Added a password strength indicator
- - Change project name and path in one form
- - Display renamed files in diff views (Vinnie Okada)
- - Fix raw view for public snippets
- - Use secret token with GitLab internal API.
- - Add missing timestamps to 'members' table
-
-v 7.4.5
- - Bump gitlab_git to 7.0.0.rc12 (includes Rugged 0.21.2)
-
-v 7.4.4
- - No changes
-
-v 7.4.3
- - Fix raw snippets view
- - Fix security issue for member api
- - Fix buildbox integration
-
-v 7.4.2
- - Fix internal snippet exposing for unauthenticated users
-
-v 7.4.1
- - Fix LDAP authentication for Git HTTP access
- - Fix LDAP config lookup for provider 'ldap'
- - Fix public snippets
- - Fix 500 error on projects with nested submodules
-
-v 7.4.0
- - Refactored membership logic
- - Improve error reporting on users API (Julien Bianchi)
- - Refactor test coverage tools usage. Use SIMPLECOV=true to generate it locally
- - Default branch is protected by default
- - Increase unicorn timeout to 60 seconds
- - Sort search autocomplete projects by stars count so most popular go first
- - Add README to tab on project show page
- - Do not delete tmp/repositories itself during clean-up, only its contents
- - Support for backup uploads to remote storage
- - Prevent notes polling when there are not notes
- - Internal ForkService: Prepare support for fork to a given namespace
- - API: Add support for forking a project via the API (Bernhard Kaindl)
- - API: filter project issues by milestone (Julien Bianchi)
- - Fail harder in the backup script
- - Changes to Slack service structure, only webhook url needed
- - Zen mode for wiki and milestones (Robert Schilling)
- - Move Emoji parsing to html-pipeline-gitlab (Robert Schilling)
- - Font Awesome 4.2 integration (Sullivan Senechal)
- - Add Pushover service integration (Sullivan Senechal)
- - Add select field type for services options (Sullivan Senechal)
- - Add cross-project references to the Markdown parser (Vinnie Okada)
- - Add task lists to issue and merge request descriptions (Vinnie Okada)
- - Snippets can be public, internal or private
- - Improve danger zone: ask project path to confirm data-loss action
- - Raise exception on forgery
- - Show build coverage in Merge Requests (requires GitLab CI v5.1)
- - New milestone and label links on issue edit form
- - Improved repository graphs
- - Improve event note display in dashboard and project activity views (Vinnie Okada)
- - Add users sorting to admin area
- - UI improvements
- - Fix ambiguous sha problem with mentioned commit
- - Fixed bug with apostrophe when at mentioning users
- - Add active directory ldap option
- - Developers can push to wiki repo. Protected branches does not affect wiki repo any more
- - Faster rev list
- - Fix branch removal
-
-v 7.3.2
- - Fix creating new file via web editor
- - Use gitlab-shell v2.0.1
-
-v 7.3.1
- - Fix ref parsing in Gitlab::GitAccess
- - Fix error 500 when viewing diff on a file with changed permissions
- - Fix adding comments to MR when source branch is master
- - Fix error 500 when searching description contains relative link
-
-v 7.3.0
- - Always set the 'origin' remote in satellite actions
- - Write authorized_keys in tmp/ during tests
- - Use sockets to connect to Redis
- - Add dormant New Relic gem (can be enabled via environment variables)
- - Expire Rack sessions after 1 week
- - Cleaner signin/signup pages
- - Improved comments UI
- - Better search with filtering, pagination etc
- - Added a checkbox to toggle line wrapping in diff (Yuriy Glukhov)
- - Prevent project stars duplication when fork project
- - Use the default Unicorn socket backlog value of 1024
- - Support Unix domain sockets for Redis
- - Store session Redis keys in 'session:gitlab:' namespace
- - Deprecate LDAP account takeover based on partial LDAP email / GitLab username match
- - Use /bin/sh instead of Bash in bin/web, bin/background_jobs (Pavel Novitskiy)
- - Keyboard shortcuts for productivity (Robert Schilling)
- - API: filter issues by state (Julien Bianchi)
- - API: filter issues by labels (Julien Bianchi)
- - Add system hook for ssh key changes
- - Add blob permalink link (Ciro Santilli)
- - Create annotated tags through UI and API (Sean Edge)
- - Snippets search (Charles Bushong)
- - Comment new push to existing MR
- - Add 'ci' to the blacklist of forbidden names
- - Improve text filtering on issues page
- - Comment & Close button
- - Process git push --all much faster
- - Don't allow edit of system notes
- - Project wiki search (Ralf Seidler)
- - Enabled Shibboleth authentication support (Matus Banas)
- - Zen mode (fullscreen) for issues/MR/notes (Robert Schilling)
- - Add ability to configure webhook timeout via gitlab.yml (Wes Gurney)
- - Sort project merge requests in asc or desc order for updated_at or created_at field (sponsored by O'Reilly Media)
- - Add Redis socket support to 'rake gitlab:shell:install'
-
-v 7.2.1
- - Delete orphaned labels during label migration (James Brooks)
- - Security: prevent XSS with stricter MIME types for raw repo files
-
-v 7.2.0
- - Explore page
- - Add project stars (Ciro Santilli)
- - Log Sidekiq arguments
- - Better labels: colors, ability to rename and remove
- - Improve the way merge request collects diffs
- - Improve compare page for large diffs
- - Expose the full commit message via API
- - Fix 500 error on repository rename
- - Fix bug when MR download patch return invalid diff
- - Test gitlab-shell integration
- - Repository import timeout increased from 2 to 4 minutes allowing larger repos to be imported
- - API for labels (Robert Schilling)
- - API: ability to set an import url when creating project for specific user
-
-v 7.1.1
- - Fix cpu usage issue in Firefox
- - Fix redirect loop when changing password by new user
- - Fix 500 error on new merge request page
-
-v 7.1.0
- - Remove observers
- - Improve MR discussions
- - Filter by description on Issues#index page
- - Fix bug with namespace select when create new project page
- - Show README link after description for non-master members
- - Add @all mention for comments
- - Dont show reply button if user is not signed in
- - Expose more information for issues with webhook
- - Add a mention of the merge request into the default merge request commit message
- - Improve code highlight, introduce support for more languages like Go, Clojure, Erlang etc
- - Fix concurrency issue in repository download
- - Dont allow repository name start with ?
- - Improve email threading (Pierre de La Morinerie)
- - Cleaner help page
- - Group milestones
- - Improved email notifications
- - Contributors API (sponsored by Mobbr)
- - Fix LDAP TLS authentication (Boris HUISGEN)
- - Show VERSION information on project sidebar
- - Improve branch removal logic when accept MR
- - Fix bug where comment form is spawned inside the Reply button
- - Remove Dir.chdir from Satellite#lock for thread-safety
- - Increased default git max_size value from 5MB to 20MB in gitlab.yml. Please update your configs!
- - Show error message in case of timeout in satellite when create MR
- - Show first 100 files for huge diff instead of hiding all
- - Change default admin email from admin@local.host to admin@example.com
-
-v 7.0.0
- - The CPU no longer overheats when you hold down the spacebar
- - Improve edit file UI
- - Add ability to upload group avatar when create
- - Protected branch cannot be removed
- - Developers can remove normal branches with UI
- - Remove branch via API (sponsored by O'Reilly Media)
- - Move protected branches page to Project settings area
- - Redirect to Files view when create new branch via UI
- - Drag and drop upload of image in every markdown-area (Earle Randolph Bunao and Neil Francis Calabroso)
- - Refactor the markdown relative links processing
- - Make it easier to implement other CI services for GitLab
- - Group masters can create projects in group
- - Deprecate ruby 1.9.3 support
- - Only masters can rewrite/remove git tags
- - Add X-Frame-Options SAMEORIGIN to Nginx config so Sidekiq admin is visible
- - UI improvements
- - Case-insensetive search for issues
- - Update to rails 4.1
- - Improve performance of application for projects and groups with a lot of members
- - Formally support Ruby 2.1
- - Include Nginx gitlab-ssl config
- - Add manual language detection for highlight.js
- - Added example.com/:username routing
- - Show notice if your profile is public
- - UI improvements for mobile devices
- - Improve diff rendering performance
- - Drag-n-drop for issues and merge requests between states at milestone page
- - Fix '0 commits' message for huge repositories on project home page
- - Prevent 500 error page when visit commit page from large repo
- - Add notice about huge push over http to unicorn config
- - File action in satellites uses default 30 seconds timeout instead of old 10 seconds one
- - Overall performance improvements
- - Skip init script check on omnibus-gitlab
- - Be more selective when killing stray Sidekiqs
- - Check LDAP user filter during sign-in
- - Remove wall feature (no data loss - you can take it from database)
- - Dont expose user emails via API unless you are admin
- - Detect issues closed by Merge Request description
- - Better email subject lines from email on push service (Alex Elman)
- - Enable identicon for gravatar be default
-
-v 6.9.2
- - Revert the commit that broke the LDAP user filter
-
-v 6.9.1
- - Fix scroll to highlighted line
- - Fix the pagination on load for commits page
-
-v 6.9.0
- - Store Rails cache data in the Redis `cache:gitlab` namespace
- - Adjust MySQL limits for existing installations
- - Add db index on project_id+iid column. This prevents duplicate on iid (During migration duplicates will be removed)
- - Markdown preview or diff during editing via web editor (Evgeniy Sokovikov)
- - Give the Rails cache its own Redis namespace
- - Add ability to set different ssh host, if different from http/https
- - Fix syntax highlighting for code comments blocks
- - Improve comments loading logic
- - Stop refreshing comments when the tab is hidden
- - Improve issue and merge request mobile UI (Drew Blessing)
- - Document how to convert a backup to PostgreSQL
- - Fix locale bug in backup manager
- - Fix can not automerge when MR description is too long
- - Fix wiki backup skip bug
- - Two Step MR creation process
- - Remove unwanted files from satellite working directory with git clean -fdx
- - Accept merge request via API (sponsored by O'Reilly Media)
- - Add more access checks during API calls
- - Block SSH access for 'disabled' Active Directory users
- - Labels for merge requests (Drew Blessing)
- - Threaded emails by setting a Message-ID (Philip Blatter)
-
-v 6.8.0
- - Ability to at mention users that are participating in issue and merge req. discussion
- - Enabled GZip Compression for assets in example Nginx, make sure that Nginx is compiled with --with-http_gzip_static_module flag (this is default in Ubuntu)
- - Make user search case-insensitive (Christopher Arnold)
- - Remove omniauth-ldap nickname bug workaround
- - Drop all tables before restoring a Postgres backup
- - Make the repository downloads path configurable
- - Create branches via API (sponsored by O'Reilly Media)
- - Changed permission of gitlab-satellites directory not to be world accessible
- - Protected branch does not allow force push
- - Fix popen bug in `rake gitlab:satellites:create`
- - Disable connection reaping for MySQL
- - Allow oauth signup without email for twitter and github
- - Fix faulty namespace names that caused 500 on user creation
- - Option to disable standard login
- - Clean old created archives from repository downloads directory
- - Fix download link for huge MR diffs
- - Expose event and mergerequest timestamps in API
- - Fix emails on push service when only one commit is pushed
-
-v 6.7.3
- - Fix the merge notification email not being sent (Pierre de La Morinerie)
- - Drop all tables before restoring a Postgres backup
- - Remove yanked modernizr gem
-
-v 6.7.2
- - Fix upgrader script
-
-v 6.7.1
- - Fix GitLab CI integration
-
-v 6.7.0
- - Increased the example Nginx client_max_body_size from 5MB to 20MB, consider updating it manually on existing installations
- - Add support for Gemnasium as a Project Service (Olivier Gonzalez)
- - Add edit file button to MergeRequest diff
- - Public groups (Jason Hollingsworth)
- - Cleaner headers in Notification Emails (Pierre de La Morinerie)
- - Blob and tree gfm links to anchors work
- - Piwik Integration (Sebastian Winkler)
- - Show contribution guide link for new issue form (Jeroen van Baarsen)
- - Fix CI status for merge requests from fork
- - Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard)
- - New page load indicator that includes a spinner that scrolls with the page
- - Converted all the help sections into markdown
- - LDAP user filters
- - Streamline the content of notification emails (Pierre de La Morinerie)
- - Fixes a bug with group member administration (Matt DeTullio)
- - Sort tag names using VersionSorter (Robert Speicher)
- - Add GFM autocompletion for MergeRequests (Robert Speicher)
- - Add webhook when a new tag is pushed (Jeroen van Baarsen)
- - Add button for toggling inline comments in diff view
- - Add retry feature for repository import
- - Reuse the GitLab LDAP connection within each request
- - Changed markdown new line behaviour to conform to markdown standards
- - Fix global search
- - Faster authorized_keys rebuilding in `rake gitlab:shell:setup` (requires gitlab-shell 1.8.5)
- - Create and Update MR calls now support the description parameter (Greg Messner)
- - Markdown relative links in the wiki link to wiki pages, markdown relative links in repositories link to files in the repository
- - Added Slack service integration (Federico Ravasio)
- - Better API responses for access_levels (sponsored by O'Reilly Media)
- - Requires at least 2 unicorn workers
- - Requires gitlab-shell v1.9+
- - Replaced gemoji(due to closed licencing problem) with Phantom Open Emoji library(combined SIL Open Font License, MIT License and the CC 3.0 License)
- - Fix `/:username.keys` response content type (Dmitry Medvinsky)
-
-v 6.6.5
- - Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard)
- - Hide mr close button for comment form if merge request was closed or inline comment
- - Adds ability to reopen closed merge request
-
-v 6.6.4
- - Add missing html escape for highlighted code blocks in comments, issues
-
-v 6.6.3
- - Fix 500 error when edit yourself from admin area
- - Hide private groups for public profiles
-
-v 6.6.2
- - Fix 500 error on branch/tag create or remove via UI
-
-v 6.6.1
- - Fix 500 error on files tab if submodules presents
-
-v 6.6.0
- - Retrieving user ssh keys publically(github style): http://__HOST__/__USERNAME__.keys
- - Permissions: Developer now can manage issue tracker (modify any issue)
- - Improve Code Compare page performance
- - Group avatar
- - Pygments.rb replaced with highlight.js
- - Improve Merge request diff store logic
- - Improve render performnace for MR show page
- - Fixed Assembla hardcoded project name
- - Jira integration documentation
- - Refactored app/services
- - Remove snippet expiration
- - Mobile UI improvements (Drew Blessing)
- - Fix block/remove UI for admin::users#show page
- - Show users' group membership on users' activity page (Robert Djurasaj)
- - User pages are visible without login if user is authorized to a public project
- - Markdown rendered headers have id derived from their name and link to their id
- - Improve application to work faster with large groups (100+ members)
- - Multiple emails per user
- - Show last commit for file when view file source
- - Restyle Issue#show page and MR#show page
- - Ability to filter by multiple labels for Issues page
- - Rails version to 4.0.3
- - Fixed attachment identifier displaying underneath note text (Jason Blanchard)
-
-v 6.5.1
- - Fix branch selectbox when create merge request from fork
-
-v 6.5.0
- - Dropdown menus on issue#show page for assignee and milestone (Jason Blanchard)
- - Add color custimization and previewing to broadcast messages
- - Fixed notes anchors
- - Load new comments in issues dynamically
- - Added sort options to Public page
- - New filters (assigned/authored/all) for Dashboard#issues/merge_requests (sponsored by Say Media)
- - Add project visibility icons to dashboard
- - Enable secure cookies if https used
- - Protect users/confirmation with rack_attack
- - Default HTTP headers to protect against MIME-sniffing, force https if enabled
- - Bootstrap 3 with responsive UI
- - New repository download formats: tar.bz2, zip, tar (Jason Hollingsworth)
- - Restyled accept widgets for MR
- - SCSS refactored
- - Use jquery timeago plugin
- - Fix 500 error for rdoc files
- - Ability to customize merge commit message (sponsored by Say Media)
- - Search autocomplete via ajax
- - Add website url to user profile
- - Files API supports base64 encoded content (sponsored by O'Reilly Media)
- - Added support for Go's repository retrieval (Bruno Albuquerque)
-
-v 6.4.3
- - Don't use unicorn worker killer if PhusionPassenger is defined
-
-v 6.4.2
- - Fixed wrong behaviour of script/upgrade.rb
-
-v 6.4.1
- - Fixed bug with repository rename
- - Fixed bug with project transfer
-
-v 6.4.0
- - Added sorting to project issues page (Jason Blanchard)
- - Assembla integration (Carlos Paramio)
- - Fixed another 500 error with submodules
- - UI: More compact issues page
- - Minimal password length increased to 8 symbols
- - Side-by-side diff view (Steven Thonus)
- - Internal projects (Jason Hollingsworth)
- - Allow removal of avatar (Drew Blessing)
- - Project webhooks now support issues and merge request events
- - Visiting project page while not logged in will redirect to sign-in instead of 404 (Jason Hollingsworth)
- - Expire event cache on avatar creation/removal (Drew Blessing)
- - Archiving old projects (Steven Thonus)
- - Rails 4
- - Add time ago tooltips to show actual date/time
- - UI: Fixed UI for admin system hooks
- - Ruby script for easier GitLab upgrade
- - Do not remove Merge requests if fork project was removed
- - Improve sign-in/signup UX
- - Add resend confirmation link to sign-in page
- - Set noreply@HOSTNAME for reply_to field in all emails
- - Show GitLab API version on Admin#dashboard
- - API Cross-origin resource sharing
- - Show READMe link at project home page
- - Show repo size for projects in Admin area
-
-v 6.3.0
- - API for adding gitlab-ci service
- - Init script now waits for pids to appear after (re)starting before reporting status (Rovanion Luckey)
- - Restyle project home page
- - Grammar fixes
- - Show branches list (which branches contains commit) on commit page (Andrew Kumanyaev)
- - Security improvements
- - Added support for GitLab CI 4.0
- - Fixed issue with 500 error when group did not exist
- - Ability to leave project
- - You can create file in repo using UI
- - You can remove file from repo using UI
- - API: dropped default_branch attribute from project during creation
- - Project default_branch is not stored in db any more. It takes from repo now.
- - Admin broadcast messages
- - UI improvements
- - Dont show last push widget if user removed this branch
- - Fix 500 error for repos with newline in file name
- - Extended html titles
- - API: create/update/delete repo files
- - Admin can transfer project to any namespace
- - API: projects/all for admin users
- - Fix recent branches order
-
-v 6.2.4
- - Security: Cast API private_token to string (CVE-2013-4580)
- - Security: Require gitlab-shell 1.7.8 (CVE-2013-4581, CVE-2013-4582, CVE-2013-4583)
- - Fix for Git SSH access for LDAP users
-
-v 6.2.3
- - Security: More protection against CVE-2013-4489
- - Security: Require gitlab-shell 1.7.4 (CVE-2013-4490, CVE-2013-4546)
- - Fix sidekiq rake tasks
-
-v 6.2.2
- - Security: Update gitlab_git (CVE-2013-4489)
-
-v 6.2.1
- - Security: Fix issue with generated passwords for new users
-
-v 6.2.0
- - Public project pages are now visible to everyone (files, issues, wik, etc.)
- THIS MEANS YOUR ISSUES AND WIKI FOR PUBLIC PROJECTS ARE PUBLICLY VISIBLE AFTER THE UPGRADE
- - Add group access to permissions page
- - Require current password to change one
- - Group owner or admin can remove other group owners
- - Remove group transfer since we have multiple owners
- - Respect authorization in Repository API
- - Improve UI for Project#files page
- - Add more security specs
- - Added search for projects by name to api (Izaak Alpert)
- - Make default user theme configurable (Izaak Alpert)
- - Update logic for validates_merge_request for tree of MR (Andrew Kumanyaev)
- - Rake tasks for webhooks management (Jonhnny Weslley)
- - Extended User API to expose admin and can_create_group for user creation/updating (Boyan Tabakov)
- - API: Remove group
- - API: Remove project
- - Avatar upload on profile page with a maximum of 100KB (Steven Thonus)
- - Store the sessions in Redis instead of the cookie store
- - Fixed relative links in markdown
- - User must confirm their email if signup enabled
- - User must confirm changed email
-
-v 6.1.0
- - Project specific IDs for issues, mr, milestones
- Above items will get a new id and for example all bookmarked issue urls will change.
- Old issue urls are redirected to the new one if the issue id is too high for an internal id.
- - Description field added to Merge Request
- - API: Sudo api calls (Izaak Alpert)
- - API: Group membership api (Izaak Alpert)
- - Improved commit diff
- - Improved large commit handling (Boyan Tabakov)
- - Rewrite: Init script now less prone to errors and keeps better track of the service (Rovanion Luckey)
- - Link issues, merge requests, and commits when they reference each other with GFM (Ash Wilson)
- - Close issues automatically when pushing commits with a special message
- - Improve user removal from admin area
- - Invalidate events cache when project was moved
- - Remove deprecated classes and rake tasks
- - Add event filter for group and project show pages
- - Add links to create branch/tag from project home page
- - Add public-project? checkbox to new-project view
- - Improved compare page. Added link to proceed into Merge Request
- - Send an email to a user when they are added to group
- - New landing page when you have 0 projects
-
-v 6.0.0
- - Feature: Replace teams with group membership
- We introduce group membership in 6.0 as a replacement for teams.
- The old combination of groups and teams was confusing for a lot of people.
- And when the members of a team where changed this wasn't reflected in the project permissions.
- In GitLab 6.0 you will be able to add members to a group with a permission level for each member.
- These group members will have access to the projects in that group.
- Any changes to group members will immediately be reflected in the project permissions.
- You can even have multiple owners for a group, greatly simplifying administration.
- - Feature: Ability to have multiple owners for group
- - Feature: Merge Requests between fork and project (Izaak Alpert)
- - Feature: Generate fingerprint for ssh keys
- - Feature: Ability to create and remove branches with UI
- - Feature: Ability to create and remove git tags with UI
- - Feature: Groups page in profile. You can leave group there
- - API: Allow login with LDAP credentials
- - Redesign: project settings navigation
- - Redesign: snippets area
- - Redesign: ssh keys page
- - Redesign: buttons, blocks and other ui elements
- - Add comment title to rss feed
- - You can use arrows to navigate at tree view
- - Add project filter on dashboard
- - Cache project graph
- - Drop support of root namespaces
- - Default theme is classic now
- - Cache result of methods like authorize_projects, project.team.members etc
- - Remove $.ready events
- - Fix onclick events being double binded
- - Add notification level to group membership
- - Move all project controllers/views under Projects:: module
- - Move all profile controllers/views under Profiles:: module
- - Apply user project limit only for personal projects
- - Unicorn is default web server again
- - Store satellites lock files inside satellites dir
- - Disabled threadsafety mode in rails
- - Fixed bug with loosing MR comments
- - Improved MR comments logic
- - Render readme file for projects in public area
-
-v 5.4.2
- - Security: Cast API private_token to string (CVE-2013-4580)
- - Security: Require gitlab-shell 1.7.8 (CVE-2013-4581, CVE-2013-4582, CVE-2013-4583)
-
-v 5.4.1
- - Security: Fixes for CVE-2013-4489
- - Security: Require gitlab-shell 1.7.4 (CVE-2013-4490, CVE-2013-4546)
-
-v 5.4.0
- - Ability to edit own comments
- - Documentation improvements
- - Improve dashboard projects page
- - Fixed nav for empty repos
- - GitLab Markdown help page
- - Misspelling fixes
- - Added support of unicorn and fog gems
- - Added client list to API doc
- - Fix PostgreSQL database restoration problem
- - Increase snippet content column size
- - allow project import via git:// url
- - Show participants on issues, including mentions
- - Notify mentioned users with email
-
-v 5.3.0
- - Refactored services
- - Campfire service added
- - HipChat service added
- - Fixed bug with LDAP + git over http
- - Fixed bug with google analytics code being ignored
- - Improve sign-in page if ldap enabled
- - Respect newlines in wall messages
- - Generate the Rails secret token on first run
- - Rename repo feature
- - Init.d: remove gitlab.socket on service start
- - Api: added teams api
- - Api: Prevent blob content being escaped
- - Api: Smart deploy key add behaviour
- - Api: projects/owned.json return user owned project
- - Fix bug with team assignation on project from #4109
- - Advanced snippets: public/private, project/personal (Andrew Kulakov)
- - Repository Graphs (Karlo Nicholas T. Soriano)
- - Fix dashboard lost if comment on commit
- - Update gitlab-grack. Fixes issue with --depth option
- - Fix project events duplicate on project page
- - Fix postgres error when displaying network graph.
- - Fix dashboard event filter when navigate via turbolinks
- - init.d: Ensure socket is removed before starting service
- - Admin area: Style teams:index, group:show pages
- - Own page for failed forking
- - Scrum view for milestone
-
-v 5.2.0
- - Turbolinks
- - Git over http with ldap credentials
- - Diff with better colors and some spacing on the corners
- - Default values for project features
- - Fixed huge_commit view
- - Restyle project clone panel
- - Move Gitlab::Git code to gitlab_git gem
- - Move update docs in repo
- - Requires gitlab-shell v1.4.0
- - Fixed submodules listing under file tab
- - Fork feature (Angus MacArthur)
- - git version check in gitlab:check
- - Shared deploy keys feature
- - Ability to generate default labels set for issues
- - Improve gfm autocomplete (Harold Luo)
- - Added support for Google Analytics
- - Code search feature (Javier Castro)
-
-v 5.1.0
- - You can login with email or username now
- - Corrected project transfer rollback when repository cannot be moved
- - Move both repo and wiki when project transfer requested
- - Admin area: project editing was removed from admin namespace
- - Access: admin user has now access to any project.
- - Notification settings
- - Gitlab::Git set of objects to abstract from grit library
- - Replace Unicorn web server with Puma
- - Backup/Restore refactored. Backup dump project wiki too now
- - Restyled Issues list. Show milestone version in issue row
- - Restyled Merge Request list
- - Backup now dump/restore uploads
- - Improved performance of dashboard (Andrew Kumanyaev)
- - File history now tracks renames (Akzhan Abdulin)
- - Drop wiki migration tools
- - Drop sqlite migration tools
- - project tagging
- - Paginate users in API
- - Restyled network graph (Hiroyuki Sato)
-
-v 5.0.1
- - Fixed issue with gitlab-grit being overridden by grit
-
-v 5.0.0
- - Replaced gitolite with gitlab-shell
- - Removed gitolite-related libraries
- - State machine added
- - Setup gitlab as git user
- - Internal API
- - Show team tab for empty projects
- - Import repository feature
- - Updated rails
- - Use lambda for scopes
- - Redesign admin area -> users
- - Redesign admin area -> user
- - Secure link to file attachments
- - Add validations for Group and Team names
- - Restyle team page for project
- - Update capybara, rspec-rails, poltergeist to recent versions
- - Wiki on git using Gollum
- - Added Solarized Dark theme for code review
- - Don't show user emails in autocomplete lists, profile pages
- - Added settings tab for group, team, project
- - Replace user popup with icons in header
- - Handle project moving with gitlab-shell
- - Added select2-rails for selectboxes with ajax data load
- - Fixed search field on projects page
- - Added teams to search autocomplete
- - Move groups and teams on dashboard sidebar to sub-tabs
- - API: improved return codes and docs. (Felix Gilcher, Sebastian Ziebell)
- - Redesign wall to be more like chat
- - Snippets, Wall features are disabled by default for new projects
-
-v 4.2.0
- - Teams
- - User show page. Via /u/username
- - Show help contents on pages for better navigation
- - Async gitolite calls
- - added satellites logs
- - can_create_group, can_create_team booleans for User
- - Process webhooks async
- - GFM: Fix images escaped inside links
- - Network graph improved
- - Switchable branches for network graph
- - API: Groups
- - Fixed project download
-
-v 4.1.0
- - Optional Sign-Up
- - Discussions
- - Satellites outside of tmp
- - Line numbers for blame
- - Project public mode
- - Public area with unauthorized access
- - Load dashboard events with ajax
- - remember dashboard filter in cookies
- - replace resque with sidekiq
- - fix routing issues
- - cleanup rake tasks
- - fix backup/restore
- - scss cleanup
- - show preview for note images
- - improved network-graph
- - get rid of app/roles/
- - added new classes Team, Repository
- - Reduce amount of gitolite calls
- - Ability to add user in all group projects
- - remove deprecated configs
- - replaced Korolev font with open font
- - restyled admin/dashboard page
- - restyled admin/projects page
-
-v 4.0.0
- - Remove project code and path from API. Use id instead
- - Return valid cloneable url to repo for webhook
- - Fixed backup issue
- - Reorganized settings
- - Fixed commits compare
- - Refactored scss
- - Improve status checks
- - Validates presence of User#name
- - Fixed postgres support
- - Removed sqlite support
- - Modified post-receive hook
- - Milestones can be closed now
- - Show comment events on dashboard
- - Quick add team members via group#people page
- - [API] expose created date for hooks and SSH keys
- - [API] list, create issue notes
- - [API] list, create snippet notes
- - [API] list, create wall notes
- - Remove project code - use path instead
- - added username field to user
- - rake task to fill usernames based on emails create namespaces for users
- - STI Group < Namespace
- - Project has namespace_id
- - Projects with namespaces also namespaced in gitolite and stored in subdir
- - Moving project to group will move it under group namespace
- - Ability to move project from namespaces to another
- - Fixes commit patches getting escaped (see #2036)
- - Support diff and patch generation for commits and merge request
- - MergeReqest doesn't generate a temporary file for the patch any more
- - Update the UI to allow downloading Patch or Diff
-
-v 3.1.0
- - Updated gems
- - Services: Gitlab CI integration
- - Events filter on dashboard
- - Own namespace for redis/resque
- - Optimized commit diff views
- - add alphabetical order for projects admin page
- - Improved web editor
- - Commit stats page
- - Documentation split and cleanup
- - Link to commit authors everywhere
- - Restyled milestones list
- - added Milestone to Merge Request
- - Restyled Top panel
- - Refactored Satellite Code
- - Added file line links
- - moved from capybara-webkit to poltergeist + phantomjs
-
-v 3.0.3
- - Fixed bug with issues list in Chrome
- - New Feature: Import team from another project
-
-v 3.0.2
- - Fixed gitlab:app:setup
- - Fixed application error on empty project in admin area
- - Restyled last push widget
-
-v 3.0.1
- - Fixed git over http
-
-v 3.0.0
- - Projects groups
- - Web Editor
- - Fixed bug with gitolite keys
- - UI improved
- - Increased performance of application
- - Show user avatar in last commit when browsing Files
- - Refactored Gitlab::Merge
- - Use Font Awesome for icons
- - Separate observing of Note and MergeRequests
- - Milestone "All Issues" filter
- - Fix issue close and reopen button text and styles
- - Fix forward/back while browsing Tree hierarchy
- - Show number of notes for commits and merge requests
- - Added support pg from box and update installation doc
- - Reject ssh keys that break gitolite
- - [API] list one project hook
- - [API] edit project hook
- - [API] list project snippets
- - [API] allow to authorize using private token in HTTP header
- - [API] add user creation
-
-v 2.9.1
- - Fixed resque custom config init
-
-v 2.9.0
- - fixed inline notes bugs
- - refactored rspecs
- - refactored gitolite backend
- - added factory_girl
- - restyled projects list on dashboard
- - ssh keys validation to prevent gitolite crash
- - send notifications if changed permission in project
- - scss refactoring. gitlab_bootstrap/ dir
- - fix git push http body bigger than 112k problem
- - list of labels page under issues tab
- - API for milestones, keys
- - restyled buttons
- - OAuth
- - Comment order changed
-
-v 2.8.1
- - ability to disable gravatars
- - improved MR diff logic
- - ssh key help page
-
-v 2.8.0
- - Gitlab Flavored Markdown
- - Bulk issues update
- - Issues API
- - Cucumber coverage increased
- - Post-receive files fixed
- - UI improved
- - Application cleanup
- - more cucumber
- - capybara-webkit + headless
-
-v 2.7.0
- - Issue Labels
- - Inline diff
- - Git HTTP
- - API
- - UI improved
- - System hooks
- - UI improved
- - Dashboard events endless scroll
- - Source performance increased
-
-v 2.6.0
- - UI polished
- - Improved network graph + keyboard nav
- - Handle huge commits
- - Last Push widget
- - Bugfix
- - Better performance
- - Email in resque
- - Increased test coverage
- - Ability to remove branch with MR accept
- - a lot of code refactored
-
-v 2.5.0
- - UI polished
- - Git blame for file
- - Bugfix
- - Email in resque
- - Better test coverage
-
-v 2.4.0
- - Admin area stats page
- - Ability to block user
- - Simplified dashboard area
- - Improved admin area
- - Bootstrap 2.0
- - Responsive layout
- - Big commits handling
- - Performance improved
- - Milestones
-
-v 2.3.1
- - Issues pagination
- - ssl fixes
- - Merge Request pagination
-
-v 2.3.0
- - Dashboard r1
- - Search r1
- - Project page
- - Close merge request on push
- - Persist MR diff after merge
- - mysql support
- - Documentation
-
-v 2.2.0
- - We’ve added support of LDAP auth
- - Improved permission logic (4 roles system)
- - Protected branches (now only masters can push to protected branches)
- - Usability improved
- - twitter bootstrap integrated
- - compare view between commits
- - wiki feature
- - now you can enable/disable issues, wiki, wall features per project
- - security fixes
- - improved code browsing (ajax branch switch etc)
- - improved per-line commenting
- - git submodules displayed
- - moved to rails 3.2
- - help section improved
-
-v 2.1.0
- - Project tab r1
- - List branches/tags
- - per line comments
- - mass user import
-
-v 2.0.0
- - gitolite as main git host system
- - merge requests
- - project/repo access
- - link to commit/issue feed
- - design tab
- - improved email notifications
- - restyled dashboard
- - bugfix
-
-v 1.2.2
- - common config file gitlab.yml
- - issues restyle
- - snippets restyle
- - clickable news feed header on dashboard
- - bugfix
-
-v 1.2.1
- - bugfix
-
-v 1.2.0
- - new design
- - user dashboard
- - network graph
- - markdown support for comments
- - encoding issues
- - wall like twitter timeline
-
-v 1.1.0
- - project dashboard
- - wall redesigned
- - feature: code snippets
- - fixed horizontal scroll on file preview
- - fixed app crash if commit message has invalid chars
- - bugfix & code cleaning
-
-v 1.0.2
- - fixed bug with empty project
- - added adv validation for project path & code
- - feature: issues can be sortable
- - bugfix
- - username displayed on top panel
-
-v 1.0.1
- - fixed: with invalid source code for commit
- - fixed: lose branch/tag selection when use tree navigation
- - when history clicked - display path
- - bug fix & code cleaning
-
-v 1.0.0
- - bug fix
- - projects preview mode
-
-v 0.9.6
- - css fix
- - new repo empty tree until restart server - fixed
-
-v 0.9.4
- - security improved
- - authorization improved
- - html escaping
- - bug fix
- - increased test coverage
- - design improvements
-
-v 0.9.1
- - increased test coverage
- - design improvements
- - new issue email notification
- - updated app name
- - issue redesigned
- - issue can be edit
-
-v 0.8.0
- - syntax highlight for main file types
- - redesign
- - stability
- - security fixes
- - increased test coverage
- - email notification
+v 7.14.3 through 0.8.0
+ - See changelogs/archive.md
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c77dcd96a7d..549918284c1 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -91,19 +91,7 @@ This was inspired by [an article by Kent C. Dodds][medium-up-for-grabs].
## Implement design & UI elements
-### Design reference
-
-The GitLab design reference can be found in the [gitlab-design] project.
-The designs are made using Antetype (`.atype` files). You can use the
-[free Antetype viewer (Mac OSX only)] or grab an exported PNG from the design
-(the PNG is 1:1).
-
-The current designs can be found in the [`gitlab8.atype` file].
-
-### UI development kit
-
-Implemented UI elements can also be found at https://gitlab.com/help/ui. Please
-note that this page isn't comprehensive at this time.
+Please see the [UI Guide for building GitLab].
## Issue tracker
@@ -489,7 +477,5 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[doc-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide"
[scss-styleguide]: doc/development/scss_styleguide.md "SCSS styleguide"
[newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide"
-[gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design
-[free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12
-[`gitlab8.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/current/
+[UI Guide for building GitLab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/ui_guide.md
[license-finder-doc]: doc/development/licensing.md
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 18091983f59..1545d966571 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-3.4.0
+3.5.0
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index b4d6d12101f..6f4eebdf6f6 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-0.7.11
+0.8.1
diff --git a/Gemfile b/Gemfile
index 81b7002027a..70d8495325f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -26,7 +26,7 @@ gem 'omniauth-auth0', '~> 1.4.1'
gem 'omniauth-azure-oauth2', '~> 0.0.6'
gem 'omniauth-bitbucket', '~> 0.0.2'
gem 'omniauth-cas3', '~> 1.1.2'
-gem 'omniauth-facebook', '~> 3.0.0'
+gem 'omniauth-facebook', '~> 4.0.0'
gem 'omniauth-github', '~> 1.1.1'
gem 'omniauth-gitlab', '~> 1.0.0'
gem 'omniauth-google-oauth2', '~> 0.4.1'
@@ -53,7 +53,7 @@ gem 'browser', '~> 2.2'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
-gem 'gitlab_git', '~> 10.6.3'
+gem 'gitlab_git', '~> 10.6.6'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
@@ -295,9 +295,10 @@ group :development, :test do
gem 'spring-commands-spinach', '~> 1.1.0'
gem 'spring-commands-teaspoon', '~> 0.0.2'
- gem 'rubocop', '~> 0.41.2', require: false
+ gem 'rubocop', '~> 0.42.0', require: false
gem 'rubocop-rspec', '~> 1.5.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false
+ gem 'haml_lint', '~> 0.18.2', require: false
gem 'simplecov', '0.12.0', require: false
gem 'flog', '~> 4.3.2', require: false
gem 'flay', '~> 2.6.1', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index c421713f6a1..e2b5a58d973 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -279,7 +279,7 @@ GEM
diff-lcs (~> 1.1)
mime-types (>= 1.16, < 3)
posix-spawn (~> 0.3)
- gitlab_git (10.6.3)
+ gitlab_git (10.6.6)
activesupport (~> 4.0)
charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0)
@@ -322,11 +322,18 @@ GEM
grape-entity (0.4.8)
activesupport
multi_json (>= 1.3.2)
+ haml (4.0.7)
+ tilt
+ haml_lint (0.18.2)
+ haml (~> 4.0)
+ rake (>= 10, < 12)
+ rubocop (>= 0.36.0)
+ sysexits (~> 1.1)
hamlit (2.6.1)
temple (~> 0.7.6)
thor
tilt
- hashie (3.4.3)
+ hashie (3.4.4)
health_check (2.1.0)
rails (>= 4.0)
hipchat (1.5.2)
@@ -394,7 +401,7 @@ GEM
mime-types (>= 1.16, < 4)
mail_room (0.8.0)
method_source (0.8.2)
- mime-types (2.99.2)
+ mime-types (2.99.3)
mimemagic (0.3.0)
mini_portile2 (2.1.0)
minitest (5.7.0)
@@ -437,7 +444,7 @@ GEM
addressable (~> 2.3)
nokogiri (~> 1.6.6)
omniauth (~> 1.2)
- omniauth-facebook (3.0.0)
+ omniauth-facebook (4.0.0)
omniauth-oauth2 (~> 1.2)
omniauth-github (1.1.2)
omniauth (~> 1.0)
@@ -612,7 +619,7 @@ GEM
rspec-retry (0.4.5)
rspec-core
rspec-support (3.5.0)
- rubocop (0.41.2)
+ rubocop (0.42.0)
parser (>= 2.3.1.1, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
@@ -723,6 +730,7 @@ GEM
stringex (2.5.2)
sys-filesystem (1.1.6)
ffi
+ sysexits (1.2.0)
systemu (2.6.5)
task_list (1.0.2)
html-pipeline
@@ -754,7 +762,7 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.7.2)
- unicode-display_width (1.1.0)
+ unicode-display_width (1.1.1)
unicorn (4.9.0)
kgio (~> 2.6)
rack
@@ -858,7 +866,7 @@ DEPENDENCIES
github-linguist (~> 4.7.0)
github-markup (~> 1.4)
gitlab-flowdock-git-hook (~> 1.0.1)
- gitlab_git (~> 10.6.3)
+ gitlab_git (~> 10.6.6)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.2)
@@ -866,6 +874,7 @@ DEPENDENCIES
gon (~> 6.1.0)
grape (~> 0.15.0)
grape-entity (~> 0.4.2)
+ haml_lint (~> 0.18.2)
hamlit (~> 2.6.1)
health_check (~> 2.1.0)
hipchat (~> 1.5.0)
@@ -900,7 +909,7 @@ DEPENDENCIES
omniauth-azure-oauth2 (~> 0.0.6)
omniauth-bitbucket (~> 0.0.2)
omniauth-cas3 (~> 1.1.2)
- omniauth-facebook (~> 3.0.0)
+ omniauth-facebook (~> 4.0.0)
omniauth-github (~> 1.1.1)
omniauth-gitlab (~> 1.0.0)
omniauth-google-oauth2 (~> 0.4.1)
@@ -935,7 +944,7 @@ DEPENDENCIES
rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.5.0)
rspec-retry (~> 0.4.5)
- rubocop (~> 0.41.2)
+ rubocop (~> 0.42.0)
rubocop-rspec (~> 1.5.0)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.15.9)
diff --git a/app/assets/javascripts/LabelManager.js b/app/assets/javascripts/LabelManager.js
index 151455ce4a3..d4a4c7abaa1 100644
--- a/app/assets/javascripts/LabelManager.js
+++ b/app/assets/javascripts/LabelManager.js
@@ -3,6 +3,7 @@
LabelManager.prototype.errorMessage = 'Unable to update label prioritization at this time';
function LabelManager(opts) {
+ // Defaults
var ref, ref1, ref2;
if (opts == null) {
opts = {};
@@ -28,6 +29,7 @@
$btn = $(e.currentTarget);
$label = $("#" + ($btn.data('domId')));
action = $btn.parents('.js-prioritized-labels').length ? 'remove' : 'add';
+ // Make sure tooltip will hide
$tooltip = $("#" + ($btn.find('.has-tooltip:visible').attr('aria-describedby')));
$tooltip.tooltip('destroy');
return _this.toggleLabelPriority($label, action);
@@ -42,6 +44,7 @@
url = $label.find('.js-toggle-priority').data('url');
$target = this.prioritizedLabels;
$from = this.otherLabels;
+ // Optimistic update
if (action === 'remove') {
$target = this.otherLabels;
$from = this.prioritizedLabels;
@@ -53,6 +56,7 @@
$target.find('.empty-message').addClass('hidden');
}
$label.detach().appendTo($target);
+ // Return if we are not persisting state
if (!persistState) {
return;
}
@@ -61,6 +65,7 @@
url: url,
type: 'DELETE'
});
+ // Restore empty message
if (!$from.find('li').length) {
$from.find('.empty-message').removeClass('hidden');
}
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 84b292e59c6..6df2ecf57a2 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -24,6 +24,8 @@
return callback(group);
});
},
+ // Return groups list. Filtered by query
+ // Only active groups retrieved
groups: function(query, skip_ldap, callback) {
var url = Api.buildUrl(Api.groupsPath);
return $.ajax({
@@ -38,6 +40,7 @@
return callback(groups);
});
},
+ // Return namespaces list. Filtered by query
namespaces: function(query, callback) {
var url = Api.buildUrl(Api.namespacesPath);
return $.ajax({
@@ -52,6 +55,7 @@
return callback(namespaces);
});
},
+ // Return projects list. Filtered by query
projects: function(query, order, callback) {
var url = Api.buildUrl(Api.projectsPath);
return $.ajax({
@@ -82,6 +86,7 @@
return callback(message.responseJSON);
});
},
+ // Return group projects list. Filtered by query
groupProjects: function(group_id, query, callback) {
var url = Api.buildUrl(Api.groupProjectsPath)
.replace(':id', group_id);
@@ -97,6 +102,7 @@
return callback(projects);
});
},
+ // Return text for a specific license
licenseText: function(key, data, callback) {
var url = Api.buildUrl(Api.licensePath)
.replace(':key', key);
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index fea6f41d5e9..31fa508d6c1 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -1,3 +1,9 @@
+// This is a manifest file that'll be compiled into including all the files listed below.
+// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+// be included in the compiled file accessible from http://example.com/assets/application.js
+// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
+// the compiled file.
+//
/*= require jquery2 */
/*= require jquery-ui/autocomplete */
/*= require jquery-ui/datepicker */
@@ -76,6 +82,7 @@
}
};
+ // Disable button if text field is empty
window.disableButtonIfEmptyField = function(field_selector, button_selector) {
var closest_submit, field;
field = $(field_selector);
@@ -92,6 +99,7 @@
});
};
+ // Disable button if any input field with given selector is empty
window.disableButtonIfAnyEmptyField = function(form, form_selector, button_selector) {
var closest_submit, updateButtons;
closest_submit = form.find(button_selector);
@@ -128,6 +136,8 @@
window.addEventListener("hashchange", shiftWindow);
window.onload = function() {
+ // Scroll the window to avoid the topnav bar
+ // https://github.com/twitter/bootstrap/issues/1768
if (location.hash) {
return setTimeout(shiftWindow, 100);
}
@@ -149,6 +159,8 @@
return $(this).select().one('mouseup', function(e) {
return e.preventDefault();
});
+ // Click a .js-select-on-focus field, select the contents
+ // Prevent a mouseup event from deselecting the input
});
$('.remove-row').bind('ajax:success', function() {
$(this).tooltip('destroy')
@@ -163,6 +175,7 @@
});
$('select.select2').select2({
width: 'resolve',
+ // Initialize select2 selects
dropdownAutoWidth: true
});
$('.js-select2').bind('select2-close', function() {
@@ -170,7 +183,9 @@
$('.select2-container-active').removeClass('select2-container-active');
return $(':focus').blur();
}), 1);
+ // Close select2 on escape
});
+ // Initialize tooltips
$body.tooltip({
selector: '.has-tooltip, [data-toggle="tooltip"]',
placement: function(_, el) {
@@ -179,14 +194,17 @@
});
$('.trigger-submit').on('change', function() {
return $(this).parents('form').submit();
+ // Form submitter
});
gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true);
+ // Flash
if ((flash = $(".flash-container")).length > 0) {
flash.click(function() {
return $(this).fadeOut();
});
flash.show();
}
+ // Disable form buttons while a form is submitting
$body.on('ajax:complete, ajax:beforeSend, submit', 'form', function(e) {
var buttons;
buttons = $('[type="submit"]', this);
@@ -207,6 +225,7 @@
}
});
$('.account-box').hover(function() {
+ // Show/Hide the profile menu when hovering the account box
return $(this).toggleClass('hover');
});
$document.on('click', '.diff-content .js-show-suppressed-diff', function() {
@@ -214,6 +233,7 @@
$container = $(this).parent();
$container.next('table').show();
return $container.remove();
+ // Commit show suppressed diff
});
$('.navbar-toggle').on('click', function() {
$('.header-content .title').toggle();
@@ -221,6 +241,7 @@
$('.header-content .navbar-collapse').toggle();
return $('.navbar-toggle').toggleClass('active');
});
+ // Show/hide comments on diff
$body.on("click", ".js-toggle-diff-comments", function(e) {
var $this = $(this);
$this.toggleClass('active');
diff --git a/app/assets/javascripts/autosave.js b/app/assets/javascripts/autosave.js
index 7116512d6b7..a9aec6e8ea4 100644
--- a/app/assets/javascripts/autosave.js
+++ b/app/assets/javascripts/autosave.js
@@ -16,7 +16,7 @@
}
Autosave.prototype.restore = function() {
- var e, error, text;
+ var e, text;
if (window.localStorage == null) {
return;
}
@@ -41,7 +41,7 @@
if ((text != null ? text.length : void 0) > 0) {
try {
return window.localStorage.setItem(this.key, text);
- } catch (undefined) {}
+ } catch (error) {}
} else {
return this.reset();
}
@@ -53,7 +53,7 @@
}
try {
return window.localStorage.removeItem(this.key);
- } catch (undefined) {}
+ } catch (error) {}
};
return Autosave;
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 5ea18ea8b7a..0decc6d09e6 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -86,6 +86,8 @@
AwardsHandler.prototype.positionMenu = function($menu, $addBtn) {
var css, position;
position = $addBtn.data('position');
+ // The menu could potentially be off-screen or in a hidden overflow element
+ // So we position the element absolute in the body
css = {
top: ($addBtn.offset().top + $addBtn.outerHeight()) + "px"
};
@@ -284,6 +286,7 @@
if (emojiIcon.length > 0) {
unicodeName = emojiIcon.data('unicode-name');
} else {
+ // Find by alias
unicodeName = $(".emoji-menu-content [data-aliases*=':" + emoji + ":']").data('unicode-name');
}
return "emoji-" + unicodeName;
@@ -350,8 +353,10 @@
return function(ev) {
var found_emojis, h5, term, ul;
term = $(ev.target).val();
+ // Clean previous search results
$('ul.emoji-menu-search, h5.emoji-search').remove();
if (term) {
+ // Generate a search result block
h5 = $('<h5>').text('Search results');
found_emojis = _this.searchEmojis(term).show();
ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis);
diff --git a/app/assets/javascripts/behaviors/autosize.js b/app/assets/javascripts/behaviors/autosize.js
index f977a1e8a7b..dc8ae601961 100644
--- a/app/assets/javascripts/behaviors/autosize.js
+++ b/app/assets/javascripts/behaviors/autosize.js
@@ -1,7 +1,5 @@
/*= require jquery.ba-resize */
-
-
/*= require autosize */
(function() {
diff --git a/app/assets/javascripts/behaviors/details_behavior.js b/app/assets/javascripts/behaviors/details_behavior.js
index 3631d1b74ac..1df681a4816 100644
--- a/app/assets/javascripts/behaviors/details_behavior.js
+++ b/app/assets/javascripts/behaviors/details_behavior.js
@@ -5,6 +5,12 @@
container = $(this).closest(".js-details-container");
return container.toggleClass("open");
});
+ // Show details content. Hides link after click.
+ //
+ // %div
+ // %a.js-details-expand
+ // %div.js-details-content
+ //
return $("body").on("click", ".js-details-expand", function(e) {
$(this).next('.js-details-content').removeClass("hide");
$(this).hide();
diff --git a/app/assets/javascripts/behaviors/quick_submit.js b/app/assets/javascripts/behaviors/quick_submit.js
index 3527d0a95fc..54b7360ab41 100644
--- a/app/assets/javascripts/behaviors/quick_submit.js
+++ b/app/assets/javascripts/behaviors/quick_submit.js
@@ -1,6 +1,20 @@
-
+// Quick Submit behavior
+//
+// When a child field of a form with a `js-quick-submit` class receives a
+// "Meta+Enter" (Mac) or "Ctrl+Enter" (Linux/Windows) key combination, the form
+// is submitted.
+//
/*= require extensions/jquery */
+//
+// ### Example Markup
+//
+// <form action="/foo" class="js-quick-submit">
+// <input type="text" />
+// <textarea></textarea>
+// <input type="submit" value="Submit" />
+// </form>
+//
(function() {
var isMac, keyCodeIs;
@@ -17,6 +31,7 @@
$(document).on('keydown.quick_submit', '.js-quick-submit', function(e) {
var $form, $submit_button;
+ // Enter
if (!keyCodeIs(e, 13)) {
return;
}
@@ -33,8 +48,11 @@
return $form.submit();
});
+ // If the user tabs to a submit button on a `js-quick-submit` form, display a
+ // tooltip to let them know they could've used the hotkey
$(document).on('keyup.quick_submit', '.js-quick-submit input[type=submit], .js-quick-submit button[type=submit]', function(e) {
var $this, title;
+ // Tab
if (!keyCodeIs(e, 9)) {
return;
}
diff --git a/app/assets/javascripts/behaviors/requires_input.js b/app/assets/javascripts/behaviors/requires_input.js
index db0b36b24e9..894034bdd54 100644
--- a/app/assets/javascripts/behaviors/requires_input.js
+++ b/app/assets/javascripts/behaviors/requires_input.js
@@ -1,6 +1,18 @@
-
+// Requires Input behavior
+//
+// When called on a form with input fields with the `required` attribute, the
+// form's submit button will be disabled until all required fields have values.
+//
/*= require extensions/jquery */
+//
+// ### Example Markup
+//
+// <form class="js-requires-input">
+// <input type="text" required="required">
+// <input type="submit" value="Submit">
+// </form>
+//
(function() {
$.fn.requiresInput = function() {
var $button, $form, fieldSelector, requireInput, required;
@@ -11,14 +23,17 @@
requireInput = function() {
var values;
values = _.map($(fieldSelector, $form), function(field) {
+ // Collect the input values of *all* required fields
return field.value;
});
+ // Disable the button if any required fields are empty
if (values.length && _.any(values, _.isEmpty)) {
return $button.disable();
} else {
return $button.enable();
}
};
+ // Set initial button state
requireInput();
return $form.on('change input', fieldSelector, requireInput);
};
@@ -27,6 +42,8 @@
var $form, hideOrShowHelpBlock;
$form = $('form.js-requires-input');
$form.requiresInput();
+ // Hide or Show the help block when creating a new project
+ // based on the option selected
hideOrShowHelpBlock = function(form) {
var selected;
selected = $('.js-select-namespace option:selected');
diff --git a/app/assets/javascripts/behaviors/toggler_behavior.js b/app/assets/javascripts/behaviors/toggler_behavior.js
index 5467e3edc69..a6ce378d67a 100644
--- a/app/assets/javascripts/behaviors/toggler_behavior.js
+++ b/app/assets/javascripts/behaviors/toggler_behavior.js
@@ -1,5 +1,12 @@
(function(w) {
$(function() {
+ // Toggle button. Show/hide content inside parent container.
+ // Button does not change visibility. If button has icon - it changes chevron style.
+ //
+ // %div.js-toggle-container
+ // %a.js-toggle-button
+ // %div.js-toggle-content
+ //
$('body').on('click', '.js-toggle-button', function(e) {
e.preventDefault();
$(this)
diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js b/app/assets/javascripts/blob/blob_file_dropzone.js
index f4044f22db2..8cca1aa9232 100644
--- a/app/assets/javascripts/blob/blob_file_dropzone.js
+++ b/app/assets/javascripts/blob/blob_file_dropzone.js
@@ -8,6 +8,8 @@
autoDiscover: false,
autoProcessQueue: false,
url: form.attr('action'),
+ // Rails uses a hidden input field for PUT
+ // http://stackoverflow.com/questions/21056482/how-to-set-method-put-in-form-tag-in-rails
method: method,
clickable: true,
uploadMultiple: false,
@@ -36,6 +38,7 @@
formData.append('commit_message', form.find('.js-commit-message').val());
});
},
+ // Override behavior of adding error underneath preview
error: function(file, errorMessage) {
var stripped;
stripped = $("<div/>").html(errorMessage).text();
diff --git a/app/assets/javascripts/blob/template_selector.js b/app/assets/javascripts/blob/template_selector.js
index 4866add779d..95352164d76 100644
--- a/app/assets/javascripts/blob/template_selector.js
+++ b/app/assets/javascripts/blob/template_selector.js
@@ -69,6 +69,9 @@
// be added by all subclasses.
};
+ // To be implemented on the extending class
+ // e.g.
+ // Api.gitignoreText item.name, @requestFileSuccess.bind(@)
TemplateSelector.prototype.requestFileSuccess = function(file, skipFocus) {
this.editor.setValue(file.content, 1);
if (!skipFocus) this.editor.focus();
diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js
index 649c79daee8..b846bab0424 100644
--- a/app/assets/javascripts/blob_edit/edit_blob.js
+++ b/app/assets/javascripts/blob_edit/edit_blob.js
@@ -18,6 +18,8 @@
return function() {
return $("#file-content").val(_this.editor.getValue());
};
+ // Before a form submission, move the content from the Ace editor into the
+ // submitted textarea
})(this));
this.initModePanesAndLinks();
new BlobLicenseSelectors({
diff --git a/app/assets/javascripts/breakpoints.js b/app/assets/javascripts/breakpoints.js
index 1e0148e5798..5fef9725178 100644
--- a/app/assets/javascripts/breakpoints.js
+++ b/app/assets/javascripts/breakpoints.js
@@ -23,6 +23,7 @@
if ($(allDeviceSelector.join(",")).length) {
return;
}
+ // Create all the elements
els = $.map(BREAKPOINTS, function(breakpoint) {
return "<div class='device-" + breakpoint + " visible-" + breakpoint + "'></div>";
});
@@ -40,6 +41,7 @@
BreakpointInstance.prototype.getBreakpointSize = function() {
var $visibleDevice;
$visibleDevice = this.visibleDevice;
+ // the page refreshed via turbolinks
if (!$visibleDevice().length) {
this.setup();
}
diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js
index 4d066f13646..10abeb50f4b 100644
--- a/app/assets/javascripts/build.js
+++ b/app/assets/javascripts/build.js
@@ -16,6 +16,7 @@
this.toggleSidebar = bind(this.toggleSidebar, this);
this.updateDropdown = bind(this.updateDropdown, this);
clearInterval(Build.interval);
+ // Init breakpoint checker
this.bp = Breakpoints.get();
$('.js-build-sidebar').niceScroll();
@@ -42,6 +43,9 @@
$(this).data("state", "enabled");
return $(this).text("disable autoscroll");
}
+ //
+ // Bind autoscroll button to follow build output
+ //
});
Build.interval = setInterval((function(_this) {
return function() {
@@ -49,6 +53,10 @@
return _this.getBuildTrace();
}
};
+ //
+ // Check for new build output if user still watching build page
+ // Only valid for runnig build when output changes during time
+ //
})(this), 4000);
}
}
diff --git a/app/assets/javascripts/commit/image-file.js b/app/assets/javascripts/commit/image-file.js
index c0d0b2d049f..e893491b19b 100644
--- a/app/assets/javascripts/commit/image-file.js
+++ b/app/assets/javascripts/commit/image-file.js
@@ -2,6 +2,7 @@
this.ImageFile = (function() {
var prepareFrames;
+ // Width where images must fits in, for 2-up this gets divided by 2
ImageFile.availWidth = 900;
ImageFile.viewModes = ['two-up', 'swipe'];
@@ -9,6 +10,7 @@
function ImageFile(file) {
this.file = file;
this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), (function(_this) {
+ // Determine if old and new file has same dimensions, if not show 'two-up' view
return function(deletedWidth, deletedHeight) {
return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(width, height) {
if (width === deletedWidth && height === deletedHeight) {
diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js
index 37f168c5190..9132089adcd 100644
--- a/app/assets/javascripts/commits.js
+++ b/app/assets/javascripts/commits.js
@@ -45,6 +45,7 @@
CommitsList.content.html(data.html);
return history.replaceState({
page: commitsUrl
+ // Change url so if user reload a page - search results are saved
}, document.title, commitsUrl);
},
dataType: "json"
diff --git a/app/assets/javascripts/copy_to_clipboard.js b/app/assets/javascripts/copy_to_clipboard.js
index c43af17442b..3e20db7e308 100644
--- a/app/assets/javascripts/copy_to_clipboard.js
+++ b/app/assets/javascripts/copy_to_clipboard.js
@@ -6,14 +6,19 @@
genericSuccess = function(e) {
showTooltip(e.trigger, 'Copied!');
+ // Clear the selection and blur the trigger so it loses its border
e.clearSelection();
return $(e.trigger).blur();
};
+ // Safari doesn't support `execCommand`, so instead we inform the user to
+ // copy manually.
+ //
+ // See http://clipboardjs.com/#browser-support
genericError = function(e) {
var key;
if (/Mac/i.test(navigator.userAgent)) {
- key = '&#8984;';
+ key = '&#8984;'; // Command
} else {
key = 'Ctrl';
}
diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js
index 3dd7ceba92f..c8634b78f2b 100644
--- a/app/assets/javascripts/diff.js
+++ b/app/assets/javascripts/diff.js
@@ -39,6 +39,9 @@
bottom: unfoldBottom,
offset: offset,
unfold: unfold,
+ // indent is used to compensate for single space indent to fit
+ // '+' and '-' prepended to diff lines,
+ // see https://gitlab.com/gitlab-org/gitlab-ce/issues/707
indent: 1,
view: file.data('view')
};
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 179d3bc38a5..99b16f7d59b 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -164,6 +164,8 @@
}
break;
case 'projects:network:show':
+ // Ensure we don't create a particular shortcut handler here. This is
+ // already created, where the network graph is created.
shortcut_handler = true;
break;
case 'projects:forks:new':
@@ -260,12 +262,14 @@
shortcut_handler = new ShortcutsNavigation();
}
}
+ // If we haven't installed a custom shortcut handler, install the default one
if (!shortcut_handler) {
return new Shortcuts();
}
};
Dispatcher.prototype.initSearch = function() {
+ // Only when search form is present
if ($('.search').length) {
return new SearchAutocomplete();
}
diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js
index 5a725a41fd1..bf68b7e3a9b 100644
--- a/app/assets/javascripts/due_date_select.js
+++ b/app/assets/javascripts/due_date_select.js
@@ -2,6 +2,7 @@
this.DueDateSelect = (function() {
function DueDateSelect() {
var $datePicker, $dueDate, $loading;
+ // Milestone edit/new form
$datePicker = $('.datepicker');
if ($datePicker.length) {
$dueDate = $('#milestone_due_date');
@@ -16,6 +17,7 @@
e.preventDefault();
return $.datepicker._clearDate($datePicker);
});
+ // Issuable sidebar
$loading = $('.js-issuable-update .due_date').find('.block-loading').hide();
$('.js-due-date-select').each(function(i, dropdown) {
var $block, $dropdown, $dropdownParent, $selectbox, $sidebarValue, $value, $valueContent, abilityName, addDueDate, fieldName, issueUpdateURL;
@@ -38,6 +40,7 @@
});
addDueDate = function(isDropdown) {
var data, date, mediumDate, value;
+ // Create the post date
value = $("input[name='" + fieldName + "']").val();
if (value !== '') {
date = new Date(value.replace(new RegExp('-', 'g'), ','));
diff --git a/app/assets/javascripts/extensions/jquery.js b/app/assets/javascripts/extensions/jquery.js
index ae3dde63da3..4978e24949c 100644
--- a/app/assets/javascripts/extensions/jquery.js
+++ b/app/assets/javascripts/extensions/jquery.js
@@ -1,3 +1,4 @@
+// Disable an element and add the 'disabled' Bootstrap class
(function() {
$.fn.extend({
disable: function() {
@@ -5,6 +6,7 @@
}
});
+ // Enable an element and remove the 'disabled' Bootstrap class
$.fn.extend({
enable: function() {
return $(this).removeAttr('disabled').removeClass('disabled');
diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6
index 3dca06d36b1..d0786bf0053 100644
--- a/app/assets/javascripts/gfm_auto_complete.js.es6
+++ b/app/assets/javascripts/gfm_auto_complete.js.es6
@@ -1,3 +1,4 @@
+// Creates the variables for setting up GFM auto-completion
(function() {
if (window.GitLab == null) {
window.GitLab = {};
@@ -8,18 +9,22 @@
dataLoaded: false,
cachedData: {},
dataSource: '',
+ // Emoji
Emoji: {
template: '<li>${name} <img alt="${name}" height="20" src="${path}" width="20" /></li>'
},
+ // Team Members
Members: {
template: '<li>${username} <small>${title}</small></li>'
},
Labels: {
template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>'
},
+ // Issues and MergeRequests
Issues: {
template: '<li><small>${id}</small> ${title}</li>'
},
+ // Milestones
Milestones: {
template: '<li>${title}</li>'
},
@@ -48,8 +53,11 @@
}
},
setup: function(input) {
+ // Add GFM auto-completion to all input fields, that accept GFM input.
this.input = input || $('.js-gfm-input');
+ // destroy previous instances
this.destroyAtWho();
+ // set up instances
this.setupAtWho();
if (this.dataSource) {
if (!this.dataLoading && !this.cachedData) {
@@ -63,6 +71,11 @@
return _this.loadData(data);
});
};
+ // We should wait until initializations are done
+ // and only trigger the last .setup since
+ // The previous .dataSource belongs to the previous issuable
+ // and the last one will have the **proper** .dataSource property
+ // TODO: Make this a singleton and turn off events when moving to another page
})(this), 1000);
}
if (this.cachedData != null) {
@@ -71,6 +84,7 @@
}
},
setupAtWho: function() {
+ // Emoji
this.input.atwho({
at: ':',
displayTpl: (function(_this) {
@@ -90,6 +104,7 @@
beforeInsert: this.DefaultOptions.beforeInsert
}
});
+ // Team Members
this.input.atwho({
at: '@',
displayTpl: (function(_this) {
@@ -321,13 +336,22 @@
loadData: function(data) {
this.cachedData = data;
this.dataLoaded = true;
+ // load members
this.input.atwho('load', '@', data.members);
+ // load issues
this.input.atwho('load', 'issues', data.issues);
+ // load milestones
this.input.atwho('load', 'milestones', data.milestones);
+ // load merge requests
this.input.atwho('load', 'mergerequests', data.mergerequests);
+ // load emojis
this.input.atwho('load', ':', data.emojis);
+ // load labels
this.input.atwho('load', '~', data.labels);
+ // load commands
this.input.atwho('load', '/', data.commands);
+ // This trigger at.js again
+ // otherwise we would be stuck with loading until the user types
return $(':focus').trigger('keyup');
}
};
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index 77b2082cba0..bea141bae51 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -21,12 +21,14 @@
$clearButton = $inputContainer.find('.js-dropdown-input-clear');
this.indeterminateIds = [];
$clearButton.on('click', (function(_this) {
+ // Clear click
return function(e) {
e.preventDefault();
e.stopPropagation();
return _this.input.val('').trigger('keyup').focus();
};
})(this));
+ // Key events
timeout = "";
this.input
.on('keydown', function (e) {
@@ -49,6 +51,7 @@
if (keyCode === 13 && !options.elIsInput) {
return false;
}
+ // Only filter asynchronously only if option remote is set
if (this.options.remote) {
clearTimeout(timeout);
return timeout = setTimeout(function() {
@@ -79,11 +82,27 @@
if ((data != null) && !this.options.filterByText) {
results = data;
if (search_text !== '') {
+ // When data is an array of objects therefore [object Array] e.g.
+ // [
+ // { prop: 'foo' },
+ // { prop: 'baz' }
+ // ]
if (_.isArray(data)) {
results = fuzzaldrinPlus.filter(data, search_text, {
key: this.options.keys
});
} else {
+ // If data is grouped therefore an [object Object]. e.g.
+ // {
+ // groupName1: [
+ // { prop: 'foo' },
+ // { prop: 'baz' }
+ // ],
+ // groupName2: [
+ // { prop: 'abc' },
+ // { prop: 'def' }
+ // ]
+ // }
if (gl.utils.isObject(data)) {
results = {};
for (key in data) {
@@ -140,6 +159,7 @@
this.options.beforeSend();
}
return this.dataEndpoint("", (function(_this) {
+ // Fetch the data by calling the data funcfion
return function(data) {
if (_this.options.success) {
_this.options.success(data);
@@ -171,6 +191,7 @@
};
})(this)
});
+ // Fetch the data through ajax if the data is a string
};
return GitLabDropdownRemote;
@@ -209,13 +230,18 @@
self = this;
selector = $(this.el).data("target");
this.dropdown = selector != null ? $(selector) : $(this.el).parent();
+ // Set Defaults
ref = this.options, this.filterInput = (ref1 = ref.filterInput) != null ? ref1 : this.getElement(FILTER_INPUT), this.highlight = (ref2 = ref.highlight) != null ? ref2 : false, this.filterInputBlur = (ref3 = ref.filterInputBlur) != null ? ref3 : true;
+ // If no input is passed create a default one
self = this;
+ // If selector was passed
if (_.isString(this.filterInput)) {
this.filterInput = this.getElement(this.filterInput);
}
searchFields = this.options.search ? this.options.search.fields : [];
if (this.options.data) {
+ // If we provided data
+ // data could be an array of objects or a group of arrays
if (_.isObject(this.options.data) && !_.isFunction(this.options.data)) {
this.fullData = this.options.data;
currentIndex = -1;
@@ -232,10 +258,12 @@
return _this.filter.input.trigger('keyup');
}
};
+ // Remote data
})(this)
});
}
}
+ // Init filterable
if (this.options.filterable) {
this.filter = new GitLabDropdownFilter(this.filterInput, {
elIsInput: $(this.el).is('input'),
@@ -278,12 +306,14 @@
})(this)
});
}
+ // Event listeners
this.dropdown.on("shown.bs.dropdown", this.opened);
this.dropdown.on("hidden.bs.dropdown", this.hidden);
$(this.el).on("update.label", this.updateLabel);
this.dropdown.on("click", ".dropdown-menu, .dropdown-menu-close", this.shouldPropagate);
this.dropdown.on('keyup', (function(_this) {
return function(e) {
+ // Escape key
if (e.which === 27) {
return $('.dropdown-menu-close', _this.dropdown).trigger('click');
}
@@ -327,6 +357,7 @@
}
}
+ // Finds an element inside wrapper element
GitLabDropdown.prototype.getElement = function(selector) {
return this.dropdown.find(selector);
};
@@ -344,6 +375,7 @@
}
}
menu.toggleClass(PAGE_TWO_CLASS);
+ // Focus first visible input on active page
return this.dropdown.find('[class^="dropdown-page-"]:visible :text:visible:first').focus();
};
@@ -351,23 +383,28 @@
var full_html, groupData, html, name;
this.renderedData = data;
if (this.options.filterable && data.length === 0) {
+ // render no matching results
html = [this.noResults()];
} else {
+ // Handle array groups
if (gl.utils.isObject(data)) {
html = [];
for (name in data) {
groupData = data[name];
html.push(this.renderItem({
header: name
+ // Add header for each group
}, name));
this.renderData(groupData, name).map(function(item) {
return html.push(item);
});
}
} else {
+ // Render each row
html = this.renderData(data);
}
}
+ // Render the full menu
full_html = this.renderMenu(html);
return this.appendMenu(full_html);
};
@@ -406,6 +443,7 @@
if (this.options.setActiveIds) {
this.options.setActiveIds.call(this);
}
+ // Makes indeterminate items effective
if (this.fullData && this.dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')) {
this.parseData(this.fullData);
}
@@ -427,6 +465,8 @@
if (this.options.filterable) {
$input.blur().val("");
}
+ // Triggering 'keyup' will re-render the dropdown which is not always required
+ // specially if we want to keep the state of the dropdown needed for bulk-assignment
if (!this.options.persistWhenHide) {
$input.trigger("keyup");
}
@@ -439,6 +479,7 @@
return this.dropdown.trigger('hidden.gl.dropdown');
};
+ // Render the full menu
GitLabDropdown.prototype.renderMenu = function(html) {
var menu_html;
menu_html = "";
@@ -450,6 +491,7 @@
return menu_html;
};
+ // Append the menu into the dropdown
GitLabDropdown.prototype.appendMenu = function(html) {
var selector;
selector = '.dropdown-content';
@@ -465,19 +507,24 @@
group = false;
}
if (index == null) {
+ // Render the row
index = false;
}
html = "";
+ // Divider
if (data === "divider") {
return "<li class='divider'></li>";
}
+ // Separator is a full-width divider
if (data === "separator") {
return "<li class='separator'></li>";
}
+ // Header
if (data.header != null) {
return _.template('<li class="dropdown-header"><%- header %></li>')({ header: data.header });
}
if (this.options.renderRow) {
+ // Call the render function
html = this.options.renderRow.call(this.options, data, this);
} else {
if (!selected) {
@@ -489,11 +536,13 @@
selected = true;
}
}
+ // Set URL
if (this.options.url != null) {
url = this.options.url(data);
} else {
url = data.url != null ? data.url : '#';
}
+ // Set Text
if (this.options.text != null) {
text = this.options.text(data);
} else {
@@ -584,6 +633,7 @@
if (value == null) {
field.remove();
}
+ // Toggle active class for the tick mark
el.addClass(ACTIVE_CLASS);
if (value != null) {
if (!field.length && fieldName) {
@@ -604,6 +654,7 @@
GitLabDropdown.prototype.addInput = function(fieldName, value, selectedObject) {
var $input;
+ // Create hidden input for form
$input = $('<input>').attr('type', 'hidden').attr('name', fieldName).val(value);
if (this.options.inputId != null) {
$input.attr('id', this.options.inputId);
@@ -625,6 +676,7 @@
if (this.dropdown.find(".dropdown-toggle-page").length) {
selector = ".dropdown-page-one " + selector;
}
+ // simulate a click on the first link
$el = $(selector, this.dropdown);
if ($el.length) {
var href = $el.attr('href');
@@ -653,11 +705,15 @@
e.stopImmediatePropagation();
PREV_INDEX = currentIndex;
$listItems = $(selector, _this.dropdown);
+ // if @options.filterable
+ // $input.blur()
if (currentKeyCode === 40) {
+ // Move down
if (currentIndex < ($listItems.length - 1)) {
currentIndex += 1;
}
} else if (currentKeyCode === 38) {
+ // Move up
if (currentIndex > 0) {
currentIndex -= 1;
}
@@ -685,24 +741,32 @@
GitLabDropdown.prototype.highlightRowAtIndex = function($listItems, index) {
var $dropdownContent, $listItem, dropdownContentBottom, dropdownContentHeight, dropdownContentTop, dropdownScrollTop, listItemBottom, listItemHeight, listItemTop;
+ // Remove the class for the previously focused row
$('.is-focused', this.dropdown).removeClass('is-focused');
+ // Update the class for the row at the specific index
$listItem = $listItems.eq(index);
$listItem.find('a:first-child').addClass("is-focused");
+ // Dropdown content scroll area
$dropdownContent = $listItem.closest('.dropdown-content');
dropdownScrollTop = $dropdownContent.scrollTop();
dropdownContentHeight = $dropdownContent.outerHeight();
dropdownContentTop = $dropdownContent.prop('offsetTop');
dropdownContentBottom = dropdownContentTop + dropdownContentHeight;
+ // Get the offset bottom of the list item
listItemHeight = $listItem.outerHeight();
listItemTop = $listItem.prop('offsetTop');
listItemBottom = listItemTop + listItemHeight;
if (!index) {
+ // Scroll the dropdown content to the top
$dropdownContent.scrollTop(0)
} else if (index === ($listItems.length - 1)) {
+ // Scroll the dropdown content to the bottom
$dropdownContent.scrollTop($dropdownContent.prop('scrollHeight'));
} else if (listItemBottom > (dropdownContentBottom + dropdownScrollTop)) {
+ // Scroll the dropdown content down
$dropdownContent.scrollTop(listItemBottom - dropdownContentBottom + CURSOR_SELECT_SCROLL_PADDING);
} else if (listItemTop < (dropdownContentTop + dropdownScrollTop)) {
+ // Scroll the dropdown content up
return $dropdownContent.scrollTop(listItemTop - dropdownContentTop - CURSOR_SELECT_SCROLL_PADDING);
}
};
diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js
index 528a673eb15..2703adc0705 100644
--- a/app/assets/javascripts/gl_form.js
+++ b/app/assets/javascripts/gl_form.js
@@ -3,12 +3,15 @@
function GLForm(form) {
this.form = form;
this.textarea = this.form.find('textarea.js-gfm-input');
+ // Before we start, we should clean up any previous data for this form
this.destroy();
+ // Setup the form
this.setupForm();
this.form.data('gl-form', this);
}
GLForm.prototype.destroy = function() {
+ // Clean form listeners
this.clearEventListeners();
return this.form.data('gl-form', null);
};
@@ -21,12 +24,15 @@
this.form.find('.div-dropzone').remove();
this.form.addClass('gfm-form');
disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button'));
+ // remove notify commit author checkbox for non-commit notes
GitLab.GfmAutoComplete.setup(this.form.find('.js-gfm-input'));
new DropzoneInput(this.form);
autosize(this.textarea);
+ // form and textarea event listeners
this.addEventListeners();
gl.text.init(this.form);
}
+ // hide discard button
this.form.find('.js-note-discard').hide();
return this.form.show();
};
diff --git a/app/assets/javascripts/graphs/graphs_bundle.js b/app/assets/javascripts/graphs/graphs_bundle.js
index b95faadc8e7..4886da9f21f 100644
--- a/app/assets/javascripts/graphs/graphs_bundle.js
+++ b/app/assets/javascripts/graphs/graphs_bundle.js
@@ -1,7 +1,11 @@
-
+// This is a manifest file that'll be compiled into including all the files listed below.
+// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+// be included in the compiled file accessible from http://example.com/assets/application.js
+// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
+// the compiled file.
+//
/*= require_tree . */
(function() {
-
}).call(this);
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
index a646ca1d84f..7d9d4d7c679 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
+++ b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
@@ -204,6 +204,7 @@
function ContributorsAuthorGraph(data1) {
this.data = data1;
+ // Don't split graph size in half for mobile devices.
if ($(window).width() < 768) {
this.width = $('.content').width() - 80;
} else {
diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js
index fd5b6dc0ddd..7c2eebcdd44 100644
--- a/app/assets/javascripts/groups_select.js
+++ b/app/assets/javascripts/groups_select.js
@@ -38,6 +38,7 @@
return _this.formatSelection.apply(_this, args);
},
dropdownCssClass: "ajax-groups-dropdown",
+ // we do not want to escape markup since we are displaying html in results
escapeMarkup: function(m) {
return m;
}
diff --git a/app/assets/javascripts/issuable.js.es6 b/app/assets/javascripts/issuable.js.es6
index 82c14ef0157..53faaa38a0c 100644
--- a/app/assets/javascripts/issuable.js.es6
+++ b/app/assets/javascripts/issuable.js.es6
@@ -38,9 +38,11 @@
return $(document).off('click', '.js-label-filter-remove').on('click', '.js-label-filter-remove', function(e) {
var $button;
$button = $(this);
+ // Remove the label input box
$('input[name="label_name[]"]').filter(function() {
return this.value === $button.data('label');
}).remove();
+ // Submit the form to get new data
Issuable.filterResults($('.filter-form'));
return $('.js-label-select').trigger('update.label');
});
diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js
index e6422602ce8..261bf6137c2 100644
--- a/app/assets/javascripts/issue.js
+++ b/app/assets/javascripts/issue.js
@@ -1,10 +1,6 @@
/*= require flash */
-
-
/*= require jquery.waitforimages */
-
-
/*= require task_list */
(function() {
@@ -13,6 +9,7 @@
this.Issue = (function() {
function Issue() {
this.submitNoteForm = bind(this.submitNoteForm, this);
+ // Prevent duplicate event bindings
this.disableTaskList();
if ($('a.btn-close').length) {
this.initTaskList();
@@ -99,6 +96,8 @@
url: $('form.js-issuable-update').attr('action'),
data: patchData
});
+ // TODO (rspeicher): Make the issue description inline-editable like a note so
+ // that we can re-use its form here
};
Issue.prototype.initMergeRequests = function() {
@@ -128,6 +127,8 @@
Issue.prototype.initCanCreateBranch = function() {
var $container;
$container = $('#new-branch');
+ // If the user doesn't have the required permissions the container isn't
+ // rendered at all.
if ($container.length === 0) {
return;
}
diff --git a/app/assets/javascripts/issues-bulk-assignment.js b/app/assets/javascripts/issues-bulk-assignment.js
index 8ca90490426..62a7fc9a06c 100644
--- a/app/assets/javascripts/issues-bulk-assignment.js
+++ b/app/assets/javascripts/issues-bulk-assignment.js
@@ -1,14 +1,17 @@
(function() {
this.IssuableBulkActions = (function() {
function IssuableBulkActions(opts) {
+ // Set defaults
var ref, ref1, ref2;
if (opts == null) {
opts = {};
}
this.container = (ref = opts.container) != null ? ref : $('.content'), this.form = (ref1 = opts.form) != null ? ref1 : this.getElement('.bulk-update'), this.issues = (ref2 = opts.issues) != null ? ref2 : this.getElement('.issuable-list > li');
+ // Save instance
this.form.data('bulkActions', this);
this.willUpdateLabels = false;
this.bindEvents();
+ // Fixes bulk-assign not working when navigating through pages
Issuable.initChecks();
}
@@ -86,6 +89,7 @@
ref1 = this.getLabelsFromSelection();
for (j = 0, len1 = ref1.length; j < len1; j++) {
id = ref1[j];
+ // Only the ones that we are not going to keep
if (labelsToKeep.indexOf(id) === -1) {
result.push(id);
}
@@ -147,6 +151,8 @@
indeterminatedLabels = this.getUnmarkedIndeterminedLabels();
labelsToApply = this.getLabelsToApply();
indeterminatedLabels.map(function(id) {
+ // We need to exclude label IDs that will be applied
+ // By not doing this will cause issues from selection to not add labels at all
if (labelsToApply.indexOf(id) === -1) {
return result.push(id);
}
diff --git a/app/assets/javascripts/labels.js b/app/assets/javascripts/labels.js
index fe071fca67c..cb16e2ba814 100644
--- a/app/assets/javascripts/labels.js
+++ b/app/assets/javascripts/labels.js
@@ -26,13 +26,16 @@
var previewColor;
previewColor = $('input#label_color').val();
return $('div.label-color-preview').css('background-color', previewColor);
+ // Updates the the preview color with the hex-color input
};
+ // Updates the preview color with a click on a suggested color
Labels.prototype.setSuggestedColor = function(e) {
var color;
color = $(e.currentTarget).data('color');
$('input#label_color').val(color);
this.updateColorPreview();
+ // Notify the form, that color has changed
$('.label-form').trigger('keyup');
return e.preventDefault();
};
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index bab23ff5ac0..29a967a35a0 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -156,11 +156,13 @@
selectedClass.push('is-indeterminate');
}
if (active.indexOf(label.id) !== -1) {
+ // Remove is-indeterminate class if the item will be marked as active
i = selectedClass.indexOf('is-indeterminate');
if (i !== -1) {
selectedClass.splice(i, 1);
}
selectedClass.push('is-active');
+ // Add input manually
instance.addInput(this.fieldName, label.id);
}
}
@@ -172,6 +174,7 @@
}
if (label.duplicate) {
spacing = 100 / label.color.length;
+ // Reduce the colors to 4
label.color = label.color.filter(function(color, i) {
return i < 4;
});
@@ -192,11 +195,13 @@
} else {
colorEl = '';
}
+ // We need to identify which items are actually labels
if (label.id) {
selectedClass.push('label-item');
$a.attr('data-label-id', label.id);
}
$a.addClass(selectedClass.join(' ')).html(colorEl + " " + label.title);
+ // Return generated html
return $li.html($a).prop('outerHTML');
},
persistWhenHide: $dropdown.data('persistWhenHide'),
@@ -238,6 +243,7 @@
isIssueIndex = page === 'projects:issues:index';
isMRIndex = page === 'projects:merge_requests:index';
$selectbox.hide();
+ // display:block overrides the hide-collapse rule
$value.removeAttr('style');
if (page === 'projects:boards:show') {
return;
@@ -255,6 +261,7 @@
}
}
if ($dropdown.hasClass('js-filter-bulk-update')) {
+ // If we are persisting state we need the classes
if (!this.options.persistWhenHide) {
return $dropdown.parent().find('.is-active, .is-indeterminate').removeClass();
}
@@ -324,7 +331,9 @@
if ($('.selected_issue:checked').length) {
return;
}
+ // Remove inputs
$('.issues_bulk_update .labels-filter input[type="hidden"]').remove();
+ // Also restore button text
return $('.issues_bulk_update .labels-filter .dropdown-toggle-text').text('Label');
};
diff --git a/app/assets/javascripts/lib/chart.js b/app/assets/javascripts/lib/chart.js
index 8d5e52286b7..d9b07c10a49 100644
--- a/app/assets/javascripts/lib/chart.js
+++ b/app/assets/javascripts/lib/chart.js
@@ -3,5 +3,4 @@
(function() {
-
}).call(this);
diff --git a/app/assets/javascripts/lib/cropper.js b/app/assets/javascripts/lib/cropper.js
index 8ee81804513..a88e640f298 100644
--- a/app/assets/javascripts/lib/cropper.js
+++ b/app/assets/javascripts/lib/cropper.js
@@ -3,5 +3,4 @@
(function() {
-
}).call(this);
diff --git a/app/assets/javascripts/lib/d3.js b/app/assets/javascripts/lib/d3.js
index 31e6033e756..ee1baf54803 100644
--- a/app/assets/javascripts/lib/d3.js
+++ b/app/assets/javascripts/lib/d3.js
@@ -3,5 +3,4 @@
(function() {
-
}).call(this);
diff --git a/app/assets/javascripts/lib/raphael.js b/app/assets/javascripts/lib/raphael.js
index 923c575dcfe..6df427bc2b1 100644
--- a/app/assets/javascripts/lib/raphael.js
+++ b/app/assets/javascripts/lib/raphael.js
@@ -1,13 +1,8 @@
/*= require raphael */
-
-
/*= require g.raphael */
-
-
/*= require g.bar */
(function() {
-
}).call(this);
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index d4d5927d3b0..8fdf4646cd8 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -29,6 +29,7 @@
if (setTimeago) {
$timeagoEls.timeago();
$timeagoEls.tooltip('destroy');
+ // Recreate with custom template
return $timeagoEls.tooltip({
template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
});
diff --git a/app/assets/javascripts/lib/utils/emoji_aliases.js.coffee.erb b/app/assets/javascripts/lib/utils/emoji_aliases.js.coffee.erb
deleted file mode 100644
index 80f9936b9c2..00000000000
--- a/app/assets/javascripts/lib/utils/emoji_aliases.js.coffee.erb
+++ /dev/null
@@ -1,2 +0,0 @@
-gl.emojiAliases = ->
- JSON.parse('<%= Gitlab::AwardEmoji.aliases.to_json %>')
diff --git a/app/assets/javascripts/lib/utils/emoji_aliases.js.erb b/app/assets/javascripts/lib/utils/emoji_aliases.js.erb
new file mode 100644
index 00000000000..aeb86c9fa5b
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/emoji_aliases.js.erb
@@ -0,0 +1,6 @@
+(function() {
+ gl.emojiAliases = function() {
+ return JSON.parse('<%= Gitlab::AwardEmoji.aliases.to_json %>');
+ };
+
+}).call(this);
diff --git a/app/assets/javascripts/lib/utils/notify.js b/app/assets/javascripts/lib/utils/notify.js
index 42b6ac0589e..5b338b00d76 100644
--- a/app/assets/javascripts/lib/utils/notify.js
+++ b/app/assets/javascripts/lib/utils/notify.js
@@ -6,6 +6,7 @@
notification = new Notification(message, opts);
setTimeout(function() {
return notification.close();
+ // Hide the notification after X amount of seconds
}, 8000);
if (onclick) {
return notification.onclick = onclick;
@@ -22,12 +23,16 @@
body: body,
icon: icon
};
+ // Let's check if the browser supports notifications
if (!('Notification' in window)) {
+ // do nothing
} else if (Notification.permission === 'granted') {
+ // If it's okay let's create a notification
return notificationGranted(message, opts, onclick);
} else if (Notification.permission !== 'denied') {
return Notification.requestPermission(function(permission) {
+ // If the user accepts, let's create a notification
if (permission === 'granted') {
return notificationGranted(message, opts, onclick);
}
diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js
index b6636de5767..d761a844be9 100644
--- a/app/assets/javascripts/lib/utils/text_utility.js
+++ b/app/assets/javascripts/lib/utils/text_utility.js
@@ -29,6 +29,7 @@
lineBefore = this.lineBefore(text, textArea);
lineAfter = this.lineAfter(text, textArea);
if (lineBefore === blockTag && lineAfter === blockTag) {
+ // To remove the block tag we have to select the line before & after
if (blockTag != null) {
textArea.selectionStart = textArea.selectionStart - (blockTag.length + 1);
textArea.selectionEnd = textArea.selectionEnd + (blockTag.length + 1);
@@ -63,11 +64,11 @@
if (!inserted) {
try {
document.execCommand("ms-beginUndoUnit");
- } catch (undefined) {}
+ } catch (error) {}
textArea.value = this.replaceRange(text, textArea.selectionStart, textArea.selectionEnd, insertText);
try {
document.execCommand("ms-endUndoUnit");
- } catch (undefined) {}
+ } catch (error) {}
}
return this.moveCursor(textArea, tag, wrap);
};
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index 533310cc87c..f84a20cf0fe 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -7,6 +7,8 @@
if ((base = w.gl).utils == null) {
base.utils = {};
}
+ // Returns an array containing the value(s) of the
+ // of the key passed as an argument
w.gl.utils.getParameterValues = function(sParam) {
var i, sPageURL, sParameterName, sURLVariables, values;
sPageURL = decodeURIComponent(window.location.search.substring(1));
@@ -23,6 +25,8 @@
}
return values;
};
+ // @param {Object} params - url keys and value to merge
+ // @param {String} url
w.gl.utils.mergeUrlParams = function(params, url) {
var lastChar, newUrl, paramName, paramValue, pattern;
newUrl = decodeURIComponent(url);
@@ -37,12 +41,14 @@
newUrl = "" + newUrl + (newUrl.indexOf('?') > 0 ? '&' : '?') + paramName + "=" + paramValue;
}
}
+ // Remove a trailing ampersand
lastChar = newUrl[newUrl.length - 1];
if (lastChar === '&') {
newUrl = newUrl.slice(0, -1);
}
return newUrl;
};
+ // removes parameter query string from url. returns the modified url
w.gl.utils.removeParamQueryString = function(url, param) {
var urlVariables, variables;
url = decodeURIComponent(url);
diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js
index f145bd3ad74..93daea1dce7 100644
--- a/app/assets/javascripts/line_highlighter.js
+++ b/app/assets/javascripts/line_highlighter.js
@@ -1,17 +1,49 @@
-
+// LineHighlighter
+//
+// Handles single- and multi-line selection and highlight for blob views.
+//
/*= require jquery.scrollTo */
+//
+// ### Example Markup
+//
+// <div id="blob-content-holder">
+// <div class="file-content">
+// <div class="line-numbers">
+// <a href="#L1" id="L1" data-line-number="1">1</a>
+// <a href="#L2" id="L2" data-line-number="2">2</a>
+// <a href="#L3" id="L3" data-line-number="3">3</a>
+// <a href="#L4" id="L4" data-line-number="4">4</a>
+// <a href="#L5" id="L5" data-line-number="5">5</a>
+// </div>
+// <pre class="code highlight">
+// <code>
+// <span id="LC1" class="line">...</span>
+// <span id="LC2" class="line">...</span>
+// <span id="LC3" class="line">...</span>
+// <span id="LC4" class="line">...</span>
+// <span id="LC5" class="line">...</span>
+// </code>
+// </pre>
+// </div>
+// </div>
+//
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
this.LineHighlighter = (function() {
+ // CSS class applied to highlighted lines
LineHighlighter.prototype.highlightClass = 'hll';
+ // Internal copy of location.hash so we're not dependent on `location` in tests
LineHighlighter.prototype._hash = '';
function LineHighlighter(hash) {
var range;
if (hash == null) {
+ // Initialize a LineHighlighter object
+ //
+ // hash - String URL hash for dependency injection in tests
hash = location.hash;
}
this.setHash = bind(this.setHash, this);
@@ -24,6 +56,8 @@
if (range[0]) {
this.highlightRange(range);
$.scrollTo("#L" + range[0], {
+ // Scroll to the first highlighted line on initial load
+ // Offset -50 for the sticky top bar, and another -100 for some context
offset: -150
});
}
@@ -32,6 +66,12 @@
LineHighlighter.prototype.bindEvents = function() {
$('#blob-content-holder').on('mousedown', 'a[data-line-number]', this.clickHandler);
+ // While it may seem odd to bind to the mousedown event and then throw away
+ // the click event, there is a method to our madness.
+ //
+ // If not done this way, the line number anchor will sometimes keep its
+ // active state even when the event is cancelled, resulting in an ugly border
+ // around the link and/or a persisted underline text decoration.
return $('#blob-content-holder').on('click', 'a[data-line-number]', function(event) {
return event.preventDefault();
});
@@ -44,6 +84,8 @@
lineNumber = $(event.target).closest('a').data('line-number');
current = this.hashToRange(this._hash);
if (!(current[0] && event.shiftKey)) {
+ // If there's no current selection, or there is but Shift wasn't held,
+ // treat this like a single-line selection.
this.setHash(lineNumber);
return this.highlightLine(lineNumber);
} else if (event.shiftKey) {
@@ -59,10 +101,23 @@
LineHighlighter.prototype.clearHighlight = function() {
return $("." + this.highlightClass).removeClass(this.highlightClass);
+ // Unhighlight previously highlighted lines
};
+ // Convert a URL hash String into line numbers
+ //
+ // hash - Hash String
+ //
+ // Examples:
+ //
+ // hashToRange('#L5') # => [5, null]
+ // hashToRange('#L5-15') # => [5, 15]
+ // hashToRange('#foo') # => [null, null]
+ //
+ // Returns an Array
LineHighlighter.prototype.hashToRange = function(hash) {
var first, last, matches;
+ //?L(\d+)(?:-(\d+))?$/)
matches = hash.match(/^#?L(\d+)(?:-(\d+))?$/);
if (matches && matches.length) {
first = parseInt(matches[1]);
@@ -73,10 +128,16 @@
}
};
+ // Highlight a single line
+ //
+ // lineNumber - Line number to highlight
LineHighlighter.prototype.highlightLine = function(lineNumber) {
return $("#LC" + lineNumber).addClass(this.highlightClass);
};
+ // Highlight all lines within a range
+ //
+ // range - Array containing the starting and ending line numbers
LineHighlighter.prototype.highlightRange = function(range) {
var i, lineNumber, ref, ref1, results;
if (range[1]) {
@@ -90,6 +151,7 @@
}
};
+ // Set the URL hash string
LineHighlighter.prototype.setHash = function(firstLineNumber, lastLineNumber) {
var hash;
if (lastLineNumber) {
@@ -101,10 +163,15 @@
return this.__setLocationHash__(hash);
};
+ // Make the actual hash change in the browser
+ //
+ // This method is stubbed in tests.
LineHighlighter.prototype.__setLocationHash__ = function(value) {
return history.pushState({
turbolinks: false,
url: value
+ // We're using pushState instead of assigning location.hash directly to
+ // prevent the page from scrolling on the hashchange event
}, document.title, value);
};
diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js
index 56ebf84c4f6..05644b3d03c 100644
--- a/app/assets/javascripts/merge_request.js
+++ b/app/assets/javascripts/merge_request.js
@@ -1,10 +1,6 @@
/*= require jquery.waitforimages */
-
-
/*= require task_list */
-
-
/*= require merge_request_tabs */
(function() {
@@ -12,6 +8,11 @@
this.MergeRequest = (function() {
function MergeRequest(opts) {
+ // Initialize MergeRequest behavior
+ //
+ // Options:
+ // action - String, current controller action
+ //
this.opts = opts != null ? opts : {};
this.submitNoteForm = bind(this.submitNoteForm, this);
this.$el = $('.merge-request');
@@ -21,6 +22,7 @@
};
})(this));
this.initTabs();
+ // Prevent duplicate event bindings
this.disableTaskList();
this.initMRBtnListeners();
if ($("a.btn-close").length) {
@@ -28,14 +30,17 @@
}
}
+ // Local jQuery finder
MergeRequest.prototype.$ = function(selector) {
return this.$el.find(selector);
};
MergeRequest.prototype.initTabs = function() {
if (this.opts.action !== 'new') {
+ // `MergeRequests#new` has no tab-persisting or lazy-loading behavior
window.mrTabs = new MergeRequestTabs(this.opts);
} else {
+ // Show the first tab (Commits)
return $('.merge-request-tabs a[data-toggle="tab"]:first').tab('show');
}
};
@@ -96,6 +101,8 @@
url: $('form.js-issuable-update').attr('action'),
data: patchData
});
+ // TODO (rspeicher): Make the merge request description inline-editable like a
+ // note so that we can re-use its form here
};
return MergeRequest;
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index ad08209d61e..dcba4a8d275 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -1,6 +1,49 @@
-
+// MergeRequestTabs
+//
+// Handles persisting and restoring the current tab selection and lazily-loading
+// content on the MergeRequests#show page.
+//
/*= require jquery.cookie */
+//
+// ### Example Markup
+//
+// <ul class="nav-links merge-request-tabs">
+// <li class="notes-tab active">
+// <a data-action="notes" data-target="#notes" data-toggle="tab" href="/foo/bar/merge_requests/1">
+// Discussion
+// </a>
+// </li>
+// <li class="commits-tab">
+// <a data-action="commits" data-target="#commits" data-toggle="tab" href="/foo/bar/merge_requests/1/commits">
+// Commits
+// </a>
+// </li>
+// <li class="diffs-tab">
+// <a data-action="diffs" data-target="#diffs" data-toggle="tab" href="/foo/bar/merge_requests/1/diffs">
+// Diffs
+// </a>
+// </li>
+// </ul>
+//
+// <div class="tab-content">
+// <div class="notes tab-pane active" id="notes">
+// Notes Content
+// </div>
+// <div class="commits tab-pane" id="commits">
+// Commits Content
+// </div>
+// <div class="diffs tab-pane" id="diffs">
+// Diffs Content
+// </div>
+// </div>
+//
+// <div class="mr-loading-status">
+// <div class="loading">
+// Loading Animation
+// </div>
+// </div>
+//
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -19,6 +62,7 @@
this.setCurrentAction = bind(this.setCurrentAction, this);
this.tabShown = bind(this.tabShown, this);
this.showTab = bind(this.showTab, this);
+ // Store the `location` object, allowing for easier stubbing in tests
this._location = location;
this.bindEvents();
this.activateTab(this.opts.action);
@@ -77,6 +121,7 @@
}
};
+ // Activate a tab based on the current action
MergeRequestTabs.prototype.activateTab = function(action) {
if (action === 'show') {
action = 'notes';
@@ -84,20 +129,48 @@
return $(".merge-request-tabs a[data-action='" + action + "']").tab('show');
};
+ // Replaces the current Merge Request-specific action in the URL with a new one
+ //
+ // If the action is "notes", the URL is reset to the standard
+ // `MergeRequests#show` route.
+ //
+ // Examples:
+ //
+ // location.pathname # => "/namespace/project/merge_requests/1"
+ // setCurrentAction('diffs')
+ // location.pathname # => "/namespace/project/merge_requests/1/diffs"
+ //
+ // location.pathname # => "/namespace/project/merge_requests/1/diffs"
+ // setCurrentAction('notes')
+ // location.pathname # => "/namespace/project/merge_requests/1"
+ //
+ // location.pathname # => "/namespace/project/merge_requests/1/diffs"
+ // setCurrentAction('commits')
+ // location.pathname # => "/namespace/project/merge_requests/1/commits"
+ //
+ // Returns the new URL String
MergeRequestTabs.prototype.setCurrentAction = function(action) {
var new_state;
+ // Normalize action, just to be safe
if (action === 'show') {
action = 'notes';
}
this.currentAction = action;
+ // Remove a trailing '/commits' or '/diffs'
new_state = this._location.pathname.replace(/\/(commits|diffs|builds|pipelines)(\.html)?\/?$/, '');
+ // Append the new action if we're on a tab other than 'notes'
if (action !== 'notes') {
new_state += "/" + action;
}
+ // Ensure parameters and hash come along for the ride
new_state += this._location.search + this._location.hash;
history.replaceState({
turbolinks: true,
url: new_state
+ // Replace the current history state with the new one without breaking
+ // Turbolinks' history.
+ //
+ // See https://github.com/rails/turbolinks/issues/363
}, document.title, new_state);
return new_state;
};
@@ -206,6 +279,9 @@
});
};
+ // Show or hide the loading spinner
+ //
+ // status - Boolean, true to show, false to hide
MergeRequestTabs.prototype.toggleLoading = function(status) {
return $('.mr-loading-status .loading').toggle(status);
};
@@ -232,6 +308,7 @@
MergeRequestTabs.prototype.diffViewType = function() {
return $('.inline-parallel-buttons a.active').data('view-type');
+ // Returns diff view type
};
MergeRequestTabs.prototype.expandViewContainer = function() {
@@ -245,6 +322,8 @@
if ($gutterIcon.is('.fa-angle-double-right')) {
return $gutterIcon.closest('a').trigger('click', [true]);
}
+ // Wait until listeners are set
+ // Only when sidebar is expanded
}, 0);
};
@@ -259,6 +338,9 @@
return $gutterIcon.closest('a').trigger('click', [true]);
}
}, 0);
+ // Expand the issuable sidebar unless the user explicitly collapsed it
+ // Wait until listeners are set
+ // Only when sidebar is collapsed
};
return MergeRequestTabs;
diff --git a/app/assets/javascripts/merge_request_widget.js b/app/assets/javascripts/merge_request_widget.js
index bd35b6f679d..7bbcdf59838 100644
--- a/app/assets/javascripts/merge_request_widget.js
+++ b/app/assets/javascripts/merge_request_widget.js
@@ -3,6 +3,12 @@
this.MergeRequestWidget = (function() {
function MergeRequestWidget(opts) {
+ // Initialize MergeRequestWidget behavior
+ //
+ // check_enable - Boolean, whether to check automerge status
+ // merge_check_url - String, URL to use to check automerge status
+ // ci_status_url - String, URL to use to check CI status
+ //
this.opts = opts;
$('#modal_merge_info').modal({
show: false
@@ -118,6 +124,8 @@
if (data.coverage) {
_this.showCICoverage(data.coverage);
}
+ // The first check should only update the UI, a notification
+ // should only be displayed on status changes
if (showNotification && !_this.firstCICheck) {
status = _this.ciLabelForStatus(data.status);
if (status === "preparing") {
diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js
index e8d51da7d58..bc1a99057d9 100644
--- a/app/assets/javascripts/milestone.js
+++ b/app/assets/javascripts/milestone.js
@@ -110,6 +110,7 @@
},
update: function(event, ui) {
var data;
+ // Prevents sorting from container which element has been removed.
if ($(this).find(ui.item).length > 0) {
data = $(this).sortable("serialize");
return Milestone.sortIssues(data);
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js
index e897ebdf630..c8031174dd2 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -92,6 +92,7 @@
},
hidden: function() {
$selectbox.hide();
+ // display:block overrides the hide-collapse rule
return $value.css('display', '');
},
clicked: function(selected, $el, e) {
diff --git a/app/assets/javascripts/network/branch-graph.js b/app/assets/javascripts/network/branch-graph.js
index c0fec1f8607..91132af273a 100644
--- a/app/assets/javascripts/network/branch-graph.js
+++ b/app/assets/javascripts/network/branch-graph.js
@@ -90,6 +90,7 @@
results = [];
while (k < this.mspace) {
this.colors.push(Raphael.getColor(.8));
+ // Skipping a few colors in the spectrum to get more contrast between colors
Raphael.getColor();
Raphael.getColor();
results.push(k++);
@@ -112,6 +113,7 @@
for (mm = j = 0, len = ref.length; j < len; mm = ++j) {
day = ref[mm];
if (cuday !== day[0] || cumonth !== day[1]) {
+ // Dates
r.text(55, this.offsetY + this.unitTime * mm, day[0]).attr({
font: "12px Monaco, monospace",
fill: "#BBB"
@@ -119,6 +121,7 @@
cuday = day[0];
}
if (cumonth !== day[1]) {
+ // Months
r.text(20, this.offsetY + this.unitTime * mm, day[1]).attr({
font: "12px Monaco, monospace",
fill: "#EEE"
@@ -207,6 +210,7 @@
}
r = this.r;
shortrefs = commit.refs;
+ // Truncate if longer than 15 chars
if (shortrefs.length > 17) {
shortrefs = shortrefs.substr(0, 15) + "…";
}
@@ -217,6 +221,7 @@
title: commit.refs
});
textbox = text.getBBox();
+ // Create rectangle based on the size of the textbox
rect = r.rect(x, y - 7, textbox.width + 5, textbox.height + 5, 4).attr({
fill: "#000",
"fill-opacity": .5,
@@ -229,6 +234,7 @@
});
label = r.set(rect, text);
label.transform(["t", -rect.getBBox().width - 15, 0]);
+ // Set text to front
return text.toFront();
};
@@ -283,11 +289,13 @@
parentY = this.offsetY + this.unitTime * parentCommit.time;
parentX1 = this.offsetX + this.unitSpace * (this.mspace - parentCommit.space);
parentX2 = this.offsetX + this.unitSpace * (this.mspace - parent[1]);
+ // Set line color
if (parentCommit.space <= commit.space) {
color = this.colors[commit.space];
} else {
color = this.colors[parentCommit.space];
}
+ // Build line shape
if (parent[1] === commit.space) {
offset = [0, 5];
arrow = "l-2,5,4,0,-2,-5,0,5";
@@ -298,13 +306,17 @@
offset = [-3, 3];
arrow = "l-5,0,2,4,3,-4,-4,2";
}
+ // Start point
route = ["M", x + offset[0], y + offset[1]];
+ // Add arrow if not first parent
if (i > 0) {
route.push(arrow);
}
+ // Circumvent if overlap
if (commit.space !== parentCommit.space || commit.space !== parent[1]) {
route.push("L", parentX2, y + 10, "L", parentX2, parentY - 5);
}
+ // End point
route.push("L", parentX1, parentY);
results.push(r.path(route).attr({
stroke: color,
@@ -325,6 +337,7 @@
"fill-opacity": .5,
stroke: "none"
});
+ // Displayed in the center
return this.element.scrollTop(y - this.graphHeight / 2);
}
};
diff --git a/app/assets/javascripts/network/network_bundle.js b/app/assets/javascripts/network/network_bundle.js
index 6a7422a7755..67c3e645364 100644
--- a/app/assets/javascripts/network/network_bundle.js
+++ b/app/assets/javascripts/network/network_bundle.js
@@ -1,4 +1,9 @@
-
+// This is a manifest file that'll be compiled into including all the files listed below.
+// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+// be included in the compiled file accessible from http://example.com/assets/application.js
+// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
+// the compiled file.
+//
/*= require_tree . */
(function() {
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index ebe9ff2b8c4..c6854f703fb 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -1,22 +1,10 @@
/*= require autosave */
-
-
/*= require autosize */
-
-
/*= require dropzone */
-
-
/*= require dropzone_input */
-
-
/*= require gfm_auto_complete */
-
-
/*= require jquery.atwho */
-
-
/*= require task_list */
(function() {
@@ -60,26 +48,43 @@
}
Notes.prototype.addBinding = function() {
+ // add note to UI after creation
$(document).on("ajax:success", ".js-main-target-form", this.addNote);
$(document).on("ajax:success", ".js-discussion-note-form", this.addDiscussionNote);
+ // catch note ajax errors
$(document).on("ajax:error", ".js-main-target-form", this.addNoteError);
+ // change note in UI after update
$(document).on("ajax:success", "form.edit-note", this.updateNote);
+ // Edit note link
$(document).on("click", ".js-note-edit", this.showEditForm);
$(document).on("click", ".note-edit-cancel", this.cancelEdit);
+ // Reopen and close actions for Issue/MR combined with note form submit
$(document).on("click", ".js-comment-button", this.updateCloseButton);
$(document).on("keyup input", ".js-note-text", this.updateTargetButtons);
+ // resolve a discussion
$(document).on('click', '.js-comment-resolve-button', this.resolveDiscussion);
+ // remove a note (in general)
$(document).on("click", ".js-note-delete", this.removeNote);
+ // delete note attachment
$(document).on("click", ".js-note-attachment-delete", this.removeAttachment);
+ // reset main target form after submit
$(document).on("ajax:complete", ".js-main-target-form", this.reenableTargetFormSubmitButton);
$(document).on("ajax:success", ".js-main-target-form", this.resetMainTargetForm);
+ // reset main target form when clicking discard
$(document).on("click", ".js-note-discard", this.resetMainTargetForm);
+ // update the file name when an attachment is selected
$(document).on("change", ".js-note-attachment-input", this.updateFormAttachment);
+ // reply to diff/discussion notes
$(document).on("click", ".js-discussion-reply-button", this.replyToDiscussionNote);
+ // add diff note
$(document).on("click", ".js-add-diff-note-button", this.addDiffNote);
+ // hide diff note form
$(document).on("click", ".js-close-discussion-note-form", this.cancelDiscussionForm);
+ // fetch notes when tab becomes visible
$(document).on("visibilitychange", this.visibilityChange);
+ // when issue status changes, we need to refresh data
$(document).on("issuable:change", this.refresh);
+ // when a key is clicked on the notes
return $(document).on("keydown", ".js-note-text", this.keydownNoteText);
};
@@ -112,6 +117,7 @@
return;
}
$textarea = $(e.target);
+ // Edit previous note when UP arrow is hit
switch (e.which) {
case 38:
if ($textarea.val() !== '') {
@@ -123,6 +129,7 @@
return myLastNoteEditBtn.trigger('click', [true, myLastNote]);
}
break;
+ // Cancel creating diff note or editing any note when ESCAPE is hit
case 27:
discussionNoteForm = $textarea.closest('.js-discussion-note-form');
if (discussionNoteForm.length) {
@@ -247,10 +254,13 @@
votesBlock = $('.js-awards-block').eq(0);
gl.awardsHandler.addAwardToEmojiBar(votesBlock, note.name);
return gl.awardsHandler.scrollToAwards();
+ // render note if it not present in loaded list
+ // or skip if rendered
} else if (this.isNewNote(note)) {
this.note_ids.push(note.id);
$notesList = $('ul.main-notes-list');
$notesList.append(note.html).syntaxHighlight();
+ // Update datetime format on the recent note
gl.utils.localTimeAgo($notesList.find("#note_" + note.id + " .js-timeago"), false);
this.initTaskList();
this.refresh();
@@ -291,19 +301,26 @@
row = form.closest("tr");
note_html = $(note.html);
note_html.syntaxHighlight();
+ // is this the first note of discussion?
discussionContainer = $(".notes[data-discussion-id='" + note.discussion_id + "']");
if ((note.original_discussion_id != null) && discussionContainer.length === 0) {
discussionContainer = $(".notes[data-discussion-id='" + note.original_discussion_id + "']");
}
if (discussionContainer.length === 0) {
+ // insert the note and the reply button after the temp row
row.after(note.diff_discussion_html);
+ // remove the note (will be added again below)
row.next().find(".note").remove();
+ // Before that, the container didn't exist
discussionContainer = $(".notes[data-discussion-id='" + note.discussion_id + "']");
+ // Add note to 'Changes' page discussions
discussionContainer.append(note_html);
+ // Init discussion on 'Discussion' page if it is merge request page
if ($('body').attr('data-page').indexOf('projects:merge_request') === 0) {
$('ul.main-notes-list').append(note.discussion_html).syntaxHighlight();
}
} else {
+ // append new note to all matching discussions
discussionContainer.append(note_html);
}
@@ -327,7 +344,9 @@
Notes.prototype.resetMainTargetForm = function(e) {
var form;
form = $(".js-main-target-form");
+ // remove validation errors
form.find(".js-errors").remove();
+ // reset text and preview
form.find(".js-md-write-button").click();
form.find(".js-note-text").val("").trigger("input");
form.find(".js-note-text").data("autosave").reset();
@@ -354,9 +373,13 @@
Notes.prototype.setupMainTargetNoteForm = function() {
var form;
+ // find the form
form = $(".js-new-note-form");
+ // Set a global clone of the form for later cloning
this.formClone = form.clone();
+ // show the form
this.setupNoteForm(form);
+ // fix classes
form.removeClass("js-new-note-form");
form.addClass("js-main-target-form");
form.find("#note_line_code").remove();
@@ -421,6 +444,7 @@
}
this.renderDiscussionNote(note);
+ // cleanup after successfully creating a diff/discussion note
this.removeDiscussionNoteForm($form);
};
@@ -433,10 +457,12 @@
Notes.prototype.updateNote = function(_xhr, note, _status) {
var $html, $note_li;
+ // Convert returned HTML to a jQuery object so we can modify it further
$html = $(note.html);
gl.utils.localTimeAgo($('.js-timeago', $html));
$html.syntaxHighlight();
$html.find('.js-task-list-container').taskList('enable');
+ // Find the note's `li` element by ID and replace it with the updated HTML
$note_li = $('.note-row-' + note.id);
$note_li.replaceWith($html);
@@ -461,15 +487,20 @@
note.addClass("is-editting");
form = note.find(".note-edit-form");
form.addClass('current-note-edit-form');
+ // Show the attachment delete link
note.find(".js-note-attachment-delete").show();
done = function($noteText) {
var noteTextVal;
+ // Neat little trick to put the cursor at the end
noteTextVal = $noteText.val();
+ // Store the original note text in a data attribute to retrieve if a user cancels edit.
form.find('form.edit-note').data('original-note', noteTextVal);
return $noteText.val('').val(noteTextVal);
};
new GLForm(form);
if ((scrollTo != null) && (myLastNote != null)) {
+ // scroll to the bottom
+ // so the open of the last element doesn't make a jump
$('html, body').scrollTop($(document).height());
return $('html, body').animate({
scrollTop: myLastNote.offset().top - 150
@@ -505,6 +536,7 @@
form = note.find(".current-note-edit-form");
note.removeClass("is-editting");
form.removeClass("current-note-edit-form");
+ // Replace markdown textarea text with original note text.
return form.find(".js-note-text").val(form.find('form.edit-note').data('original-note'));
};
@@ -520,6 +552,9 @@
var noteId;
noteId = $(e.currentTarget).closest(".note").attr("id");
$(".note[id='" + noteId + "']").each((function(_this) {
+ // A same note appears in the "Discussion" and in the "Changes" tab, we have
+ // to remove all. Using $(".note[id='noteId']") ensure we get all the notes,
+ // where $("#noteId") would return only one.
return function(i, el) {
var note, notes;
note = $(el);
@@ -533,13 +568,17 @@
}
}
+ // check if this is the last note for this line
if (notes.find(".note").length === 1) {
+ // "Discussions" tab
notes.closest(".timeline-entry").remove();
+ // "Changes" tab / commit view
notes.closest("tr").remove();
}
return note.remove();
};
})(this));
+ // Decrement the "Discussions" counter only once
return this.updateNotesCount(-1);
};
@@ -571,10 +610,12 @@
var form, replyLink;
form = this.formClone.clone();
replyLink = $(e.target).closest(".js-discussion-reply-button");
+ // insert the form after the button
replyLink
.closest('.discussion-reply-holder')
.hide()
.after(form);
+ // show the form
return this.setupDiscussionNoteForm(replyLink, form);
};
@@ -589,6 +630,7 @@
*/
Notes.prototype.setupDiscussionNoteForm = function(dataHolder, form) {
+ // setup note target
form.attr('id', "new-discussion-note-form-" + (dataHolder.data("discussionId")));
form.attr("data-line-code", dataHolder.data("lineCode"));
form.find("#note_type").val(dataHolder.data("noteType"));
@@ -636,6 +678,7 @@
addForm = false;
notesContentSelector = ".notes_content";
rowCssToAdd = "<tr class=\"notes_holder js-temp-notes-holder\"><td class=\"notes_line\" colspan=\"2\"></td><td class=\"notes_content\"><div class=\"content\"></div></td></tr>";
+ // In parallel view, look inside the correct left/right pane
if (this.isParallelView()) {
lineType = $link.data("lineType");
notesContentSelector += "." + lineType;
@@ -652,6 +695,7 @@
e.target = replyButton[0];
$.proxy(this.replyToDiscussionNote, replyButton[0], e).call();
} else {
+ // In parallel view, the form may not be present in one of the panes
noteForm = notesContent.find(".js-discussion-note-form");
if (noteForm.length === 0) {
addForm = true;
@@ -659,6 +703,7 @@
}
}
} else {
+ // add a notes row and insert the form
row.after(rowCssToAdd);
nextRow = row.next();
notesContent = nextRow.find(notesContentSelector);
@@ -667,6 +712,7 @@
if (addForm) {
newForm = this.formClone.clone();
newForm.appendTo(notesContent);
+ // show the form
return this.setupDiscussionNoteForm($link, newForm);
}
};
@@ -685,12 +731,15 @@
glForm = form.data('gl-form');
glForm.destroy();
form.find(".js-note-text").data("autosave").reset();
+ // show the reply button (will only work for replies)
form
.prev('.discussion-reply-holder')
.show();
if (row.is(".js-temp-notes-holder")) {
+ // remove temporary row for diff lines
return row.remove();
} else {
+ // only remove the form
return form.remove();
}
};
@@ -712,6 +761,7 @@
Notes.prototype.updateFormAttachment = function() {
var filename, form;
form = $(this).closest("form");
+ // get only the basename
filename = $(this).val().replace(/^.*[\\\/]/, "");
return form.find(".js-attachment-filename").text(filename);
};
diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js
index 5fd75799640..5200487814f 100644
--- a/app/assets/javascripts/preview_markdown.js
+++ b/app/assets/javascripts/preview_markdown.js
@@ -1,9 +1,15 @@
+// MarkdownPreview
+//
+// Handles toggling the "Write" and "Preview" tab clicks, rendering the preview,
+// and showing a warning when more than `x` users are referenced.
+//
(function() {
var lastTextareaPreviewed, markdownPreview, previewButtonSelector, writeButtonSelector;
this.MarkdownPreview = (function() {
function MarkdownPreview() {}
+ // Minimum number of users referenced before triggering a warning
MarkdownPreview.prototype.referenceThreshold = 10;
MarkdownPreview.prototype.ajaxCache = {};
@@ -101,8 +107,10 @@
return;
}
lastTextareaPreviewed = $form.find('textarea.markdown-area');
+ // toggle tabs
$form.find(writeButtonSelector).parent().removeClass('active');
$form.find(previewButtonSelector).parent().addClass('active');
+ // toggle content
$form.find('.md-write-holder').hide();
$form.find('.md-preview-holder').show();
return markdownPreview.showPreview($form);
@@ -113,8 +121,10 @@
return;
}
lastTextareaPreviewed = null;
+ // toggle tabs
$form.find(writeButtonSelector).parent().addClass('active');
$form.find(previewButtonSelector).parent().removeClass('active');
+ // toggle content
$form.find('.md-write-holder').show();
$form.find('textarea.markdown-area').focus();
return $form.find('.md-preview-holder').hide();
diff --git a/app/assets/javascripts/profile/gl_crop.js b/app/assets/javascripts/profile/gl_crop.js
index a3eea316f67..30cd6f6e470 100644
--- a/app/assets/javascripts/profile/gl_crop.js
+++ b/app/assets/javascripts/profile/gl_crop.js
@@ -5,6 +5,7 @@
GitLabCrop = (function() {
var FILENAMEREGEX;
+ // Matches everything but the file name
FILENAMEREGEX = /^.*[\\\/]/;
function GitLabCrop(input, opts) {
@@ -17,11 +18,18 @@
this.onModalShow = bind(this.onModalShow, this);
this.onPickImageClick = bind(this.onPickImageClick, this);
this.fileInput = $(input);
+ // We should rename to avoid spec to fail
+ // Form will submit the proper input filed with a file using FormData
this.fileInput.attr('name', (this.fileInput.attr('name')) + "-trigger").attr('id', (this.fileInput.attr('id')) + "-trigger");
+ // Set defaults
this.exportWidth = (ref = opts.exportWidth) != null ? ref : 200, this.exportHeight = (ref1 = opts.exportHeight) != null ? ref1 : 200, this.cropBoxWidth = (ref2 = opts.cropBoxWidth) != null ? ref2 : 200, this.cropBoxHeight = (ref3 = opts.cropBoxHeight) != null ? ref3 : 200, this.form = (ref4 = opts.form) != null ? ref4 : this.fileInput.parents('form'), this.filename = opts.filename, this.previewImage = opts.previewImage, this.modalCrop = opts.modalCrop, this.pickImageEl = opts.pickImageEl, this.uploadImageBtn = opts.uploadImageBtn, this.modalCropImg = opts.modalCropImg;
+ // Required params
+ // Ensure needed elements are jquery objects
+ // If selector is provided we will convert them to a jQuery Object
this.filename = this.getElement(this.filename);
this.previewImage = this.getElement(this.previewImage);
this.pickImageEl = this.getElement(this.pickImageEl);
+ // Modal elements usually are outside the @form element
this.modalCrop = _.isString(this.modalCrop) ? $(this.modalCrop) : this.modalCrop;
this.uploadImageBtn = _.isString(this.uploadImageBtn) ? $(this.uploadImageBtn) : this.uploadImageBtn;
this.modalCropImg = _.isString(this.modalCropImg) ? $(this.modalCropImg) : this.modalCropImg;
@@ -93,8 +101,8 @@
return this.modalCropImg.attr('src', '').cropper('destroy');
};
- GitLabCrop.prototype.onUploadImageBtnClick = function(e) {
- e.preventDefault();
+ GitLabCrop.prototype.onUploadImageBtnClick = function(e) { // Remove attached image
+ e.preventDefault(); // Destroy cropper instance
this.setBlob();
this.setPreview();
this.modalCrop.modal('hide');
diff --git a/app/assets/javascripts/profile/profile.js b/app/assets/javascripts/profile/profile.js
index ed1d87abafe..60f9fba5777 100644
--- a/app/assets/javascripts/profile/profile.js
+++ b/app/assets/javascripts/profile/profile.js
@@ -11,9 +11,11 @@
this.form = (ref = opts.form) != null ? ref : $('.edit-user');
$('.js-preferences-form').on('change.preference', 'input[type=radio]', function() {
return $(this).parents('form').submit();
+ // Automatically submit the Preferences form when any of its radio buttons change
});
$('#user_notification_email').on('change', function() {
return $(this).parents('form').submit();
+ // Automatically submit email form when it changes
});
$('.update-username').on('ajax:before', function() {
$('.loading-username').show();
@@ -76,6 +78,7 @@
},
complete: function() {
window.scrollTo(0, 0);
+ // Enable submit button after requests ends
return self.form.find(':input[disabled]').enable();
}
});
@@ -93,6 +96,7 @@
if (comment && comment.length > 1 && $title.val() === '') {
return $title.val(comment[1]).change();
}
+ // Extract the SSH Key title from its comment
});
if (gl.utils.getPagePath() === 'profiles') {
return new Profile();
diff --git a/app/assets/javascripts/profile/profile_bundle.js b/app/assets/javascripts/profile/profile_bundle.js
index b95faadc8e7..d6e4d9f7ad8 100644
--- a/app/assets/javascripts/profile/profile_bundle.js
+++ b/app/assets/javascripts/profile/profile_bundle.js
@@ -3,5 +3,4 @@
(function() {
-
}).call(this);
diff --git a/app/assets/javascripts/project.js b/app/assets/javascripts/project.js
index 66e097c0a28..a6c015299a0 100644
--- a/app/assets/javascripts/project.js
+++ b/app/assets/javascripts/project.js
@@ -11,7 +11,13 @@
url = $("#project_clone").val();
$('#project_clone').val(url);
return $('.clone').text(url);
+ // Git protocol switcher
+ // Remove the active class for all buttons (ssh, http, kerberos if shown)
+ // Add the active class for the clicked button
+ // Update the input field
+ // Update the command line instructions
});
+ // Ref switcher
this.initRefSwitcher();
$('.project-refs-select').on('change', function() {
return $(this).parents('form').submit();
diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js
index 4925f0519f0..5bf900f3e1d 100644
--- a/app/assets/javascripts/project_find_file.js
+++ b/app/assets/javascripts/project_find_file.js
@@ -13,8 +13,11 @@
this.selectRowUp = bind(this.selectRowUp, this);
this.filePaths = {};
this.inputElement = this.element.find(".file-finder-input");
+ // init event
this.initEvent();
+ // focus text input box
this.inputElement.focus();
+ // load file list
this.load(this.options.url);
}
@@ -42,6 +45,7 @@
}
}
});
+ // init event
};
ProjectFindFile.prototype.findFile = function() {
@@ -49,8 +53,10 @@
searchText = this.inputElement.val();
result = searchText.length > 0 ? fuzzaldrinPlus.filter(this.filePaths, searchText) : this.filePaths;
return this.renderList(result, searchText);
+ // find file
};
+ // files pathes load
ProjectFindFile.prototype.load = function(url) {
return $.ajax({
url: url,
@@ -67,6 +73,7 @@
});
};
+ // render result
ProjectFindFile.prototype.renderList = function(filePaths, searchText) {
var blobItemUrl, filePath, html, i, j, len, matches, results;
this.element.find(".tree-table > tbody").empty();
@@ -86,6 +93,7 @@
return results;
};
+ // highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> )
highlighter = function(element, text, matches) {
var highlightText, j, lastIndex, len, matchIndex, matchedChars, unmatched;
lastIndex = 0;
@@ -110,6 +118,7 @@
return element.append(document.createTextNode(text.substring(lastIndex)));
};
+ // make tbody row html
ProjectFindFile.prototype.makeHtml = function(filePath, matches, blobItemUrl) {
var $tr;
$tr = $("<tr class='tree-item'><td class='tree-item-file-name'><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'><a></a></span></td></tr>");
diff --git a/app/assets/javascripts/project_show.js b/app/assets/javascripts/project_show.js
index 8ca4c427912..c8cfc9a9ba8 100644
--- a/app/assets/javascripts/project_show.js
+++ b/app/assets/javascripts/project_show.js
@@ -7,3 +7,5 @@
})();
}).call(this);
+
+// I kept class for future
diff --git a/app/assets/javascripts/projects_list.js b/app/assets/javascripts/projects_list.js
index 4f415b05dbc..04fb49552e8 100644
--- a/app/assets/javascripts/projects_list.js
+++ b/app/assets/javascripts/projects_list.js
@@ -33,6 +33,7 @@
$('.projects-list-holder').replaceWith(data.html);
return history.replaceState({
page: project_filter_url
+ // Change url so if user reload a page - search results are saved
}, document.title, project_filter_url);
},
dataType: "json"
diff --git a/app/assets/javascripts/protected_branch_dropdown.js.es6 b/app/assets/javascripts/protected_branch_dropdown.js.es6
index 6738dc8862d..983322cbecc 100644
--- a/app/assets/javascripts/protected_branch_dropdown.js.es6
+++ b/app/assets/javascripts/protected_branch_dropdown.js.es6
@@ -45,6 +45,7 @@ class ProtectedBranchDropdown {
}
onClickCreateWildcard() {
+ // Refresh the dropdown's data, which ends up calling `getProtectedBranches`
this.$dropdown.data('glDropdown').remote.execute();
this.$dropdown.data('glDropdown').selectRowAtIndex(0);
}
diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js
index 227e8c696b4..8abb09c626f 100644
--- a/app/assets/javascripts/search_autocomplete.js
+++ b/app/assets/javascripts/search_autocomplete.js
@@ -24,6 +24,7 @@
this.onSearchInputKeyUp = bind(this.onSearchInputKeyUp, this);
this.onSearchInputKeyDown = bind(this.onSearchInputKeyDown, this);
this.wrap = (ref = opts.wrap) != null ? ref : $('.search'), this.optsEl = (ref1 = opts.optsEl) != null ? ref1 : this.wrap.find('.search-autocomplete-opts'), this.autocompletePath = (ref2 = opts.autocompletePath) != null ? ref2 : this.optsEl.data('autocomplete-path'), this.projectId = (ref3 = opts.projectId) != null ? ref3 : this.optsEl.data('autocomplete-project-id') || '', this.projectRef = (ref4 = opts.projectRef) != null ? ref4 : this.optsEl.data('autocomplete-project-ref') || '';
+ // Dropdown Element
this.dropdown = this.wrap.find('.dropdown');
this.dropdownContent = this.dropdown.find('.dropdown-content');
this.locationBadgeEl = this.getElement('.location-badge');
@@ -35,6 +36,7 @@
this.repositoryInputEl = this.getElement('#repository_ref');
this.clearInput = this.getElement('.js-clear-input');
this.saveOriginalState();
+ // Only when user is logged in
if (gon.current_user_id) {
this.createAutocomplete();
}
@@ -43,6 +45,7 @@
this.bindEvents();
}
+ // Finds an element inside wrapper element
SearchAutocomplete.prototype.getElement = function(selector) {
return this.wrap.find(selector);
};
@@ -82,6 +85,7 @@
}
return;
}
+ // Prevent multiple ajax calls
if (this.loadingSuggestions) {
return;
}
@@ -92,14 +96,17 @@
term: term
}, function(response) {
var data, firstCategory, i, lastCategory, len, suggestion;
+ // Hide dropdown menu if no suggestions returns
if (!response.length) {
_this.disableAutocomplete();
return;
}
data = [];
+ // List results
firstCategory = true;
for (i = 0, len = response.length; i < len; i++) {
suggestion = response[i];
+ // Add group header before list each group
if (lastCategory !== suggestion.category) {
if (!firstCategory) {
data.push('separator');
@@ -119,6 +126,7 @@
url: suggestion.url
});
}
+ // Add option to proceed with the search
if (data.length) {
data.push('separator');
data.push({
@@ -169,11 +177,13 @@
SearchAutocomplete.prototype.serializeState = function() {
return {
+ // Search Criteria
search_project_id: this.projectInputEl.val(),
group_id: this.groupInputEl.val(),
search_code: this.searchCodeInputEl.val(),
repository_ref: this.repositoryInputEl.val(),
scope: this.scopeInputEl.val(),
+ // Location badge
_location: this.locationBadgeEl.text()
};
};
@@ -194,6 +204,7 @@
SearchAutocomplete.prototype.enableAutocomplete = function() {
var _this;
+ // No need to enable anything if user is not logged in
if (!gon.current_user_id) {
return;
}
@@ -206,18 +217,22 @@
};
SearchAutocomplete.prototype.onSearchInputKeyDown = function() {
+ // Saves last length of the entered text
return this.saveTextLength();
};
SearchAutocomplete.prototype.onSearchInputKeyUp = function(e) {
switch (e.keyCode) {
case KEYCODE.BACKSPACE:
+ // when trying to remove the location badge
if (this.lastTextLength === 0 && this.badgePresent()) {
this.removeLocationBadge();
}
+ // When removing the last character and no badge is present
if (this.lastTextLength === 1) {
this.disableAutocomplete();
}
+ // When removing any character from existin value
if (this.lastTextLength > 1) {
this.enableAutocomplete();
}
@@ -232,9 +247,12 @@
case KEYCODE.DOWN:
return;
default:
+ // Handle the case when deleting the input value other than backspace
+ // e.g. Pressing ctrl + backspace or ctrl + x
if (this.searchInput.val() === '') {
this.disableAutocomplete();
} else {
+ // We should display the menu only when input is not empty
if (e.keyCode !== KEYCODE.ENTER) {
this.enableAutocomplete();
}
@@ -243,7 +261,9 @@
this.wrap.toggleClass('has-value', !!e.target.value);
};
+ // Avoid falsy value to be returned
SearchAutocomplete.prototype.onSearchInputClick = function(e) {
+ // Prevents closing the dropdown menu
return e.stopImmediatePropagation();
};
@@ -267,6 +287,7 @@
SearchAutocomplete.prototype.onSearchInputBlur = function(e) {
this.isFocused = false;
this.wrap.removeClass('search-active');
+ // If input is blank then restore state
if (this.searchInput.val() === '') {
return this.restoreOriginalState();
}
@@ -311,6 +332,7 @@
results = [];
for (i = 0, len = inputs.length; i < len; i++) {
input = inputs[i];
+ // _location isnt a input
if (input === '_location') {
break;
}
diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/shortcuts.js
index 3b28332854a..3aa8536d40a 100644
--- a/app/assets/javascripts/shortcuts.js
+++ b/app/assets/javascripts/shortcuts.js
@@ -86,6 +86,7 @@
var defaultStopCallback;
defaultStopCallback = Mousetrap.stopCallback;
return function(e, element, combo) {
+ // allowed shortcuts if textarea, input, contenteditable are focused
if (['ctrl+shift+p', 'command+shift+p'].indexOf(combo) !== -1) {
return false;
} else {
diff --git a/app/assets/javascripts/shortcuts_find_file.js b/app/assets/javascripts/shortcuts_find_file.js
index 6c78914d338..92ce31969e3 100644
--- a/app/assets/javascripts/shortcuts_find_file.js
+++ b/app/assets/javascripts/shortcuts_find_file.js
@@ -14,8 +14,10 @@
ShortcutsFindFile.__super__.constructor.call(this);
_oldStopCallback = Mousetrap.stopCallback;
Mousetrap.stopCallback = (function(_this) {
+ // override to fire shortcuts action when focus in textbox
return function(event, element, combo) {
if (element === _this.projectFindFile.inputElement[0] && (combo === 'up' || combo === 'down' || combo === 'esc' || combo === 'enter')) {
+ // when press up/down key in textbox, cusor prevent to move to home/end
event.preventDefault();
return false;
}
diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/shortcuts_issuable.js
index 3f3a8a9dfd9..235bf4f95ec 100644
--- a/app/assets/javascripts/shortcuts_issuable.js
+++ b/app/assets/javascripts/shortcuts_issuable.js
@@ -1,7 +1,5 @@
/*= require mousetrap */
-
-
/*= require shortcuts_navigation */
(function() {
@@ -43,16 +41,20 @@
if (selected.trim() === "") {
return;
}
+ // Put a '>' character before each non-empty line in the selection
quote = _.map(selected.split("\n"), function(val) {
if (val.trim() !== '') {
return "> " + val + "\n";
}
});
+ // If replyField already has some content, add a newline before our quote
separator = replyField.val().trim() !== "" && "\n" || '';
replyField.val(function(_, current) {
return current + separator + quote.join('') + "\n";
});
+ // Trigger autosave for the added text
replyField.trigger('input');
+ // Focus the input field
return replyField.focus();
}
};
diff --git a/app/assets/javascripts/syntax_highlight.js b/app/assets/javascripts/syntax_highlight.js
index dba62638c78..2ae7bf5fc15 100644
--- a/app/assets/javascripts/syntax_highlight.js
+++ b/app/assets/javascripts/syntax_highlight.js
@@ -1,9 +1,20 @@
+// Syntax Highlighter
+//
+// Applies a syntax highlighting color scheme CSS class to any element with the
+// `js-syntax-highlight` class
+//
+// ### Example Markup
+//
+// <div class="js-syntax-highlight"></div>
+//
(function() {
$.fn.syntaxHighlight = function() {
var $children;
if ($(this).hasClass('js-syntax-highlight')) {
+ // Given the element itself, apply highlighting
return $(this).addClass(gon.user_color_scheme);
} else {
+ // Given a parent element, recurse to any of its applicable children
$children = $(this).find('.js-syntax-highlight');
if ($children.length) {
return $children.syntaxHighlight();
diff --git a/app/assets/javascripts/todos.js b/app/assets/javascripts/todos.js
index 23eda7d44ca..93421649ac7 100644
--- a/app/assets/javascripts/todos.js
+++ b/app/assets/javascripts/todos.js
@@ -129,16 +129,21 @@
var currPage, currPages, newPages, pageParams, url;
currPages = this.getTotalPages();
currPage = this.getCurrentPage();
+ // Refresh if no remaining Todos
if (!total) {
location.reload();
return;
}
+ // Do nothing if no pagination
if (!currPages) {
return;
}
newPages = Math.ceil(total / this.getTodosPerPage());
+ // Includes query strings
url = location.href;
+ // If new total of pages is different than we have now
if (newPages !== currPages) {
+ // Redirect to previous page if there's one available
if (currPages > 1 && currPage === currPages) {
pageParams = {
page: currPages - 1
@@ -155,6 +160,7 @@
if (!todoLink) {
return;
}
+ // Allow Meta-Click or Mouse3-click to open in a new tab
if (e.metaKey || e.which === 2) {
e.preventDefault();
return window.open(todoLink, '_blank');
diff --git a/app/assets/javascripts/tree.js b/app/assets/javascripts/tree.js
index 78e159a7ed9..9b7be17c4fe 100644
--- a/app/assets/javascripts/tree.js
+++ b/app/assets/javascripts/tree.js
@@ -2,6 +2,8 @@
this.TreeView = (function() {
function TreeView() {
this.initKeyNav();
+ // Code browser tree slider
+ // Make the entire tree-item row clickable, but not if clicking another link (like a commit message)
$(".tree-content-holder .tree-item").on('click', function(e) {
var $clickedEl, path;
$clickedEl = $(e.target);
@@ -15,6 +17,7 @@
}
}
});
+ // Show the "Loading commit data" for only the first element
$('span.log_loading:first').removeClass('hide');
}
diff --git a/app/assets/javascripts/u2f/authenticate.js b/app/assets/javascripts/u2f/authenticate.js
index 9ba847fb0c2..ce2930c7fc7 100644
--- a/app/assets/javascripts/u2f/authenticate.js
+++ b/app/assets/javascripts/u2f/authenticate.js
@@ -1,3 +1,7 @@
+// Authenticate U2F (universal 2nd factor) devices for users to authenticate with.
+//
+// State Flow #1: setup -> in_progress -> authenticated -> POST to server
+// State Flow #2: setup -> in_progress -> error -> setup
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -15,6 +19,17 @@
this.appId = u2fParams.app_id;
this.challenge = u2fParams.challenge;
this.signRequests = u2fParams.sign_requests.map(function(request) {
+ // The U2F Javascript API v1.1 requires a single challenge, with
+ // _no challenges per-request_. The U2F Javascript API v1.0 requires a
+ // challenge per-request, which is done by copying the single challenge
+ // into every request.
+ //
+ // In either case, we don't need the per-request challenges that the server
+ // has generated, so we can remove them.
+ //
+ // Note: The server library fixes this behaviour in (unreleased) version 1.0.0.
+ // This can be removed once we upgrade.
+ // https://github.com/castle/ruby-u2f/commit/103f428071a81cd3d5f80c2e77d522d5029946a4
return _(request).omit('challenge');
});
}
@@ -41,6 +56,7 @@
})(this), 10);
};
+ // Rendering #
U2FAuthenticate.prototype.templates = {
"notSupported": "#js-authenticate-u2f-not-supported",
"setup": '#js-authenticate-u2f-setup',
@@ -75,6 +91,8 @@
U2FAuthenticate.prototype.renderAuthenticated = function(deviceResponse) {
this.renderTemplate('authenticated');
+ // Prefer to do this instead of interpolating using Underscore templates
+ // because of JSON escaping issues.
return this.container.find("#js-device-response").val(deviceResponse);
};
diff --git a/app/assets/javascripts/u2f/register.js b/app/assets/javascripts/u2f/register.js
index c87e0840df3..926912fa988 100644
--- a/app/assets/javascripts/u2f/register.js
+++ b/app/assets/javascripts/u2f/register.js
@@ -1,3 +1,7 @@
+// Register U2F (universal 2nd factor) devices for users to authenticate with.
+//
+// State Flow #1: setup -> in_progress -> registered -> POST to server
+// State Flow #2: setup -> in_progress -> error -> setup
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -39,6 +43,7 @@
})(this), 10);
};
+ // Rendering #
U2FRegister.prototype.templates = {
"notSupported": "#js-register-u2f-not-supported",
"setup": '#js-register-u2f-setup',
@@ -73,6 +78,8 @@
U2FRegister.prototype.renderRegistered = function(deviceResponse) {
this.renderTemplate('registered');
+ // Prefer to do this instead of interpolating using Underscore templates
+ // because of JSON escaping issues.
return this.container.find("#js-device-response").val(deviceResponse);
};
diff --git a/app/assets/javascripts/user_tabs.js b/app/assets/javascripts/user_tabs.js
index e5e75701fee..8a657780eb6 100644
--- a/app/assets/javascripts/user_tabs.js
+++ b/app/assets/javascripts/user_tabs.js
@@ -1,3 +1,61 @@
+// UserTabs
+//
+// Handles persisting and restoring the current tab selection and lazily-loading
+// content on the Users#show page.
+//
+// ### Example Markup
+//
+// <ul class="nav-links">
+// <li class="activity-tab active">
+// <a data-action="activity" data-target="#activity" data-toggle="tab" href="/u/username">
+// Activity
+// </a>
+// </li>
+// <li class="groups-tab">
+// <a data-action="groups" data-target="#groups" data-toggle="tab" href="/u/username/groups">
+// Groups
+// </a>
+// </li>
+// <li class="contributed-tab">
+// <a data-action="contributed" data-target="#contributed" data-toggle="tab" href="/u/username/contributed">
+// Contributed projects
+// </a>
+// </li>
+// <li class="projects-tab">
+// <a data-action="projects" data-target="#projects" data-toggle="tab" href="/u/username/projects">
+// Personal projects
+// </a>
+// </li>
+// <li class="snippets-tab">
+// <a data-action="snippets" data-target="#snippets" data-toggle="tab" href="/u/username/snippets">
+// </a>
+// </li>
+// </ul>
+//
+// <div class="tab-content">
+// <div class="tab-pane" id="activity">
+// Activity Content
+// </div>
+// <div class="tab-pane" id="groups">
+// Groups Content
+// </div>
+// <div class="tab-pane" id="contributed">
+// Contributed projects content
+// </div>
+// <div class="tab-pane" id="projects">
+// Projects content
+// </div>
+// <div class="tab-pane" id="snippets">
+// Snippets content
+// </div>
+// </div>
+//
+// <div class="loading-status">
+// <div class="loading">
+// Loading Animation
+// </div>
+// </div>
+//
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -6,18 +64,23 @@
this.tabShown = bind(this.tabShown, this);
var i, item, len, ref, ref1, ref2, ref3;
this.action = (ref = opts.action) != null ? ref : 'activity', this.defaultAction = (ref1 = opts.defaultAction) != null ? ref1 : 'activity', this.parentEl = (ref2 = opts.parentEl) != null ? ref2 : $(document);
+ // Make jQuery object if selector is provided
if (typeof this.parentEl === 'string') {
this.parentEl = $(this.parentEl);
}
+ // Store the `location` object, allowing for easier stubbing in tests
this._location = location;
+ // Set tab states
this.loaded = {};
ref3 = this.parentEl.find('.nav-links a');
for (i = 0, len = ref3.length; i < len; i++) {
item = ref3[i];
this.loaded[$(item).attr('data-action')] = false;
}
+ // Actions
this.actions = Object.keys(this.loaded);
this.bindEvents();
+ // Set active tab
if (this.action === 'show') {
this.action = this.defaultAction;
}
@@ -25,6 +88,7 @@
}
UserTabs.prototype.bindEvents = function() {
+ // Toggle event listeners
return this.parentEl.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]').on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', this.tabShown);
};
@@ -74,6 +138,7 @@
tabSelector = 'div#' + action;
_this.parentEl.find(tabSelector).html(data.html);
_this.loaded[action] = true;
+ // Fix tooltips
return gl.utils.localTimeAgo($('.js-timeago', tabSelector));
};
})(this)
@@ -97,13 +162,17 @@
UserTabs.prototype.setCurrentAction = function(action) {
var new_state, regExp;
+ // Remove possible actions from URL
regExp = new RegExp('\/(' + this.actions.join('|') + ')(\.html)?\/?$');
new_state = this._location.pathname;
+ // remove trailing slashes
new_state = new_state.replace(/\/+$/, "");
new_state = new_state.replace(regExp, '');
+ // Append the new action if we're on a tab other than 'activity'
if (action !== this.defaultAction) {
new_state += "/" + action;
}
+ // Ensure parameters and hash come along for the ride
new_state += this._location.search + this._location.hash;
history.replaceState({
turbolinks: true,
diff --git a/app/assets/javascripts/users/calendar.js b/app/assets/javascripts/users/calendar.js
index 90cf551b32e..b8da7c4f297 100644
--- a/app/assets/javascripts/users/calendar.js
+++ b/app/assets/javascripts/users/calendar.js
@@ -11,6 +11,8 @@
this.daySizeWithSpace = this.daySize + (this.daySpace * 2);
this.monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
this.months = [];
+ // Loop through the timestamps to create a group of objects
+ // The group of objects will be grouped based on the day of the week they are
this.timestampsTmp = [];
var group = 0;
@@ -29,12 +31,15 @@
var day = date.getDay();
var count = timestamps[date.getTime() * 0.001];
+ // Create a new group array if this is the first day of the week
+ // or if is first object
if ((day === 0 && i !== 0) || i === 0) {
this.timestampsTmp.push([]);
group++;
}
var innerArray = this.timestampsTmp[group - 1];
+ // Push to the inner array the values that will be used to render map
innerArray.push({
count: count || 0,
date: date,
@@ -42,8 +47,10 @@
});
}
+ // Init color functions
this.colorKey = this.initColorKey();
this.color = this.initColor();
+ // Init the svg element
this.renderSvg(group);
this.renderDays();
this.renderMonths();
diff --git a/app/assets/javascripts/users/users_bundle.js b/app/assets/javascripts/users/users_bundle.js
index b95faadc8e7..d6e4d9f7ad8 100644
--- a/app/assets/javascripts/users/users_bundle.js
+++ b/app/assets/javascripts/users/users_bundle.js
@@ -3,5 +3,4 @@
(function() {
-
}).call(this);
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index bad82868ab0..9c277998db4 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -81,6 +81,7 @@
if (term.length === 0) {
showDivider = 0;
if (firstUser) {
+ // Move current user to the front of the list
for (index = j = 0, len = users.length; j < len; index = ++j) {
obj = users[index];
if (obj.username === firstUser) {
@@ -115,6 +116,7 @@
if (showDivider) {
users.splice(showDivider, 0, "divider");
}
+ // Send the data back
return callback(users);
});
},
@@ -139,6 +141,7 @@
inputId: 'issue_assignee_id',
hidden: function(e) {
$selectbox.hide();
+ // display:block overrides the hide-collapse rule
return $value.css('display', '');
},
clicked: function(user, $el, e) {
@@ -177,6 +180,7 @@
img = "<img src='" + avatar + "' class='avatar avatar-inline' width='30' />";
}
}
+ // split into three parts so we can remove the username section if nessesary
listWithName = "<li> <a href='#' class='dropdown-menu-user-link " + selected + "'> " + img + " <strong class='dropdown-menu-user-full-name'> " + user.name + " </strong>";
listWithUserName = "<span class='dropdown-menu-user-username'> " + username + " </span>";
listClosingTags = "</a> </li>";
@@ -215,6 +219,7 @@
};
if (query.term.length === 0) {
if (firstUser) {
+ // Move current user to the front of the list
ref = data.results;
for (index = j = 0, len = ref.length; j < len; index = ++j) {
obj = ref[index];
@@ -271,6 +276,7 @@
return _this.formatSelection.apply(_this, args);
},
dropdownCssClass: "ajax-users-dropdown",
+ // we do not want to escape markup since we are displaying html in results
escapeMarkup: function(m) {
return m;
}
@@ -318,6 +324,8 @@
});
};
+ // Return users list. Filtered by query
+ // Only active users retrieved
UsersSelect.prototype.users = function(query, options, callback) {
var url;
url = this.buildUrl(this.usersPath);
diff --git a/app/assets/javascripts/zen_mode.js b/app/assets/javascripts/zen_mode.js
index 71236c6a27d..777b32b41c9 100644
--- a/app/assets/javascripts/zen_mode.js
+++ b/app/assets/javascripts/zen_mode.js
@@ -1,21 +1,34 @@
-
+// Zen Mode (full screen) textarea
+//
/*= provides zen_mode:enter */
-
-
/*= provides zen_mode:leave */
-
-
+//
/*= require jquery.scrollTo */
-
-
/*= require dropzone */
-
-
/*= require mousetrap */
-
-
/*= require mousetrap/pause */
+//
+// ### Events
+//
+// `zen_mode:enter`
+//
+// Fired when the "Edit in fullscreen" link is clicked.
+//
+// **Synchronicity** Sync
+// **Bubbles** Yes
+// **Cancelable** No
+// **Target** a.js-zen-enter
+//
+// `zen_mode:leave`
+//
+// Fired when the "Leave Fullscreen" link is clicked.
+//
+// **Synchronicity** Sync
+// **Bubbles** Yes
+// **Cancelable** No
+// **Target** a.js-zen-leave
+//
(function() {
this.ZenMode = (function() {
function ZenMode() {
@@ -40,6 +53,7 @@
};
})(this));
$(document).on('keydown', function(e) {
+ // Esc
if (e.keyCode === 27) {
e.preventDefault();
return $(document).trigger('zen_mode:leave');
@@ -52,6 +66,7 @@
this.active_backdrop = $(backdrop);
this.active_backdrop.addClass('fullscreen');
this.active_textarea = this.active_backdrop.find('textarea');
+ // Prevent a user-resized textarea from persisting to fullscreen
this.active_textarea.removeAttr('style');
return this.active_textarea.focus();
};
diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss
index 367c7d01944..76b93b23b95 100644
--- a/app/assets/stylesheets/framework/mobile.scss
+++ b/app/assets/stylesheets/framework/mobile.scss
@@ -79,10 +79,6 @@
padding-left: 15px !important;
}
- .issue-info, .merge-request-info {
- display: none;
- }
-
.nav-links, .nav-links {
li a {
font-size: 14px;
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 63845e6b9ba..41079b6eeb5 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -206,7 +206,7 @@
padding-top: 0;
.block {
- width: $sidebar_collapsed_width - 1px;
+ width: $sidebar_collapsed_width - 2px;
margin-left: -19px;
padding: 15px 0 0;
border-bottom: none;
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 7a26b7ad497..60a0d50ba73 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -37,6 +37,15 @@ form.edit-issue {
margin: 0;
}
+ul.related-merge-requests > li {
+ display: -ms-flexbox;
+ display: -webkit-flex;
+ display: flex;
+ .merge-request-id {
+ flex-shrink: 0;
+ }
+}
+
.merge-requests-title, .related-branches-title {
font-size: 16px;
font-weight: 600;
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 3e6e50375f6..db46d8072ce 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -334,6 +334,10 @@ a.deploy-project-label {
a {
color: $gl-dark-link-color;
}
+
+ .dropdown-menu {
+ width: 240px;
+ }
}
.last-push-widget {
diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb
index ba07cea569c..d5a8a962662 100644
--- a/app/controllers/concerns/authenticates_with_two_factor.rb
+++ b/app/controllers/concerns/authenticates_with_two_factor.rb
@@ -62,6 +62,7 @@ module AuthenticatesWithTwoFactor
session.delete(:otp_user_id)
session.delete(:challenges)
+ remember_me(user) if user_params[:remember_me] == '1'
sign_in(user)
else
flash.now[:alert] = 'Authentication via U2F device failed.'
diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb
index f2b8f297bc2..dacb5679dd3 100644
--- a/app/controllers/concerns/creates_commit.rb
+++ b/app/controllers/concerns/creates_commit.rb
@@ -7,8 +7,7 @@ module CreatesCommit
commit_params = @commit_params.merge(
source_project: @project,
source_branch: @ref,
- target_branch: @target_branch,
- previous_path: @previous_path
+ target_branch: @target_branch
)
result = service.new(@tree_edit_project, current_user, commit_params).execute
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index cdf9a04bacf..b78cc6585ba 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -38,12 +38,7 @@ class Projects::BlobController < Projects::ApplicationController
end
def update
- if params[:file_path].present?
- @previous_path = @path
- @path = params[:file_path]
- @commit_params[:file_path] = @path
- end
-
+ @path = params[:file_path] if params[:file_path].present?
after_edit_path =
if from_merge_request && @target_branch == @ref
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
@@ -143,6 +138,8 @@ class Projects::BlobController < Projects::ApplicationController
params[:file_name] = params[:file].original_filename
end
File.join(@path, params[:file_name])
+ elsif params[:file_path].present?
+ params[:file_path]
else
@path
end
@@ -155,6 +152,7 @@ class Projects::BlobController < Projects::ApplicationController
@commit_params = {
file_path: @file_path,
commit_message: params[:commit_message],
+ previous_path: @path,
file_content: params[:content],
file_content_encoding: params[:encoding],
last_commit_sha: params[:last_commit_sha]
diff --git a/app/controllers/projects/git_http_client_controller.rb b/app/controllers/projects/git_http_client_controller.rb
index a5b4031c30f..f5ce63fdfed 100644
--- a/app/controllers/projects/git_http_client_controller.rb
+++ b/app/controllers/projects/git_http_client_controller.rb
@@ -117,4 +117,8 @@ class Projects::GitHttpClientController < Projects::ApplicationController
def ci?
@ci.present?
end
+
+ def verify_workhorse_api!
+ Gitlab::Workhorse.verify_api_request!(request.headers)
+ end
end
diff --git a/app/controllers/projects/git_http_controller.rb b/app/controllers/projects/git_http_controller.rb
index b4373ef89ef..9805705c4e3 100644
--- a/app/controllers/projects/git_http_controller.rb
+++ b/app/controllers/projects/git_http_controller.rb
@@ -1,6 +1,8 @@
# This file should be identical in GitLab Community Edition and Enterprise Edition
class Projects::GitHttpController < Projects::GitHttpClientController
+ before_action :verify_workhorse_api!
+
# GET /foo/bar.git/info/refs?service=git-upload-pack (git pull)
# GET /foo/bar.git/info/refs?service=git-receive-pack (git push)
def info_refs
@@ -56,6 +58,7 @@ class Projects::GitHttpController < Projects::GitHttpClientController
end
def render_ok
+ set_workhorse_internal_api_content_type
render json: Gitlab::Workhorse.git_http_ok(repository, user)
end
diff --git a/app/controllers/projects/lfs_storage_controller.rb b/app/controllers/projects/lfs_storage_controller.rb
index 69066cb40e6..9005b104e90 100644
--- a/app/controllers/projects/lfs_storage_controller.rb
+++ b/app/controllers/projects/lfs_storage_controller.rb
@@ -3,6 +3,7 @@ class Projects::LfsStorageController < Projects::GitHttpClientController
before_action :require_lfs_enabled!
before_action :lfs_check_access!
+ before_action :verify_workhorse_api!, only: [:upload_authorize]
def download
lfs_object = LfsObject.find_by_oid(oid)
@@ -15,14 +16,8 @@ class Projects::LfsStorageController < Projects::GitHttpClientController
end
def upload_authorize
- render(
- json: {
- StoreLFSPath: "#{Gitlab.config.lfs.storage_path}/tmp/upload",
- LfsOid: oid,
- LfsSize: size,
- },
- content_type: 'application/json; charset=utf-8'
- )
+ set_workhorse_internal_api_content_type
+ render json: Gitlab::Workhorse.lfs_upload_ok(oid, size)
end
def upload_finalize
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 8895cb955bd..aa8645ba8cc 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -428,17 +428,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def validates_merge_request
- # If source project was removed (Ex. mr from fork to origin)
- return invalid_mr unless @merge_request.source_project
-
# Show git not found page
# if there is no saved commits between source & target branch
if @merge_request.commits.blank?
# and if target branch doesn't exist
return invalid_mr unless @merge_request.target_branch_exists?
-
- # or if source branch doesn't exist
- return invalid_mr unless @merge_request.source_branch_exists?
end
end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index e523c46e879..8a7446b7cc7 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -30,6 +30,37 @@ module SearchHelper
"Showing #{from} - #{to} of #{count} #{scope.humanize(capitalize: false)} for \"#{term}\""
end
+ def parse_search_result(result)
+ ref = nil
+ filename = nil
+ basename = nil
+ startline = 0
+
+ result.each_line.each_with_index do |line, index|
+ if line =~ /^.*:.*:\d+:/
+ ref, filename, startline = line.split(':')
+ startline = startline.to_i - index
+ extname = Regexp.escape(File.extname(filename))
+ basename = filename.sub(/#{extname}$/, '')
+ break
+ end
+ end
+
+ data = ""
+
+ result.each_line do |line|
+ data << line.sub(ref, '').sub(filename, '').sub(/^:-\d+-/, '').sub(/^::\d+:/, '')
+ end
+
+ OpenStruct.new(
+ filename: filename,
+ basename: basename,
+ ref: ref,
+ startline: startline,
+ data: data
+ )
+ end
+
private
# Autocomplete results for various settings pages
diff --git a/app/helpers/workhorse_helper.rb b/app/helpers/workhorse_helper.rb
index d887cdadc34..88f374be1e5 100644
--- a/app/helpers/workhorse_helper.rb
+++ b/app/helpers/workhorse_helper.rb
@@ -34,4 +34,8 @@ module WorkhorseHelper
headers.store(*Gitlab::Workhorse.send_artifacts_entry(build, entry))
head :ok
end
+
+ def set_workhorse_internal_api_content_type
+ headers['Content-Type'] = Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
+ end
end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 246477ffe88..55d2e07de08 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -146,7 +146,7 @@ class ApplicationSetting < ActiveRecord::Base
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
domain_whitelist: Settings.gitlab['domain_whitelist'],
- import_sources: %w[github bitbucket gitlab google_code fogbugz git gitlab_project],
+ import_sources: Gitlab::ImportSources.values,
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
max_artifacts_size: Settings.artifacts['max_size'],
require_two_factor_authentication: false,
diff --git a/app/models/blob.rb b/app/models/blob.rb
index 12cc5aaafba..ab92e820335 100644
--- a/app/models/blob.rb
+++ b/app/models/blob.rb
@@ -22,6 +22,18 @@ class Blob < SimpleDelegator
new(blob)
end
+ # Returns the data of the blob.
+ #
+ # If the blob is a text based blob the content is converted to UTF-8 and any
+ # invalid byte sequences are replaced.
+ def data
+ if binary?
+ super
+ else
+ @data ||= super.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
+ end
+ end
+
def no_highlighting?
size && size > 1.megabyte
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 61052437318..fb16bc06d71 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -148,6 +148,7 @@ module Ci
variables += runner.predefined_variables if runner
variables += project.container_registry_variables
variables += yaml_variables
+ variables += user_variables
variables += project.secret_variables
variables += trigger_request.user_variables if trigger_request
variables
@@ -434,6 +435,15 @@ module Ci
read_attribute(:yaml_variables) || build_attributes_from_config[:yaml_variables] || []
end
+ def user_variables
+ return [] if user.blank?
+
+ [
+ { key: 'GITLAB_USER_ID', value: user.id.to_s, public: true },
+ { key: 'GITLAB_USER_EMAIL', value: user.email, public: true }
+ ]
+ end
+
private
def update_artifacts_size
@@ -469,6 +479,7 @@ module Ci
]
variables << { key: 'CI_BUILD_TAG', value: ref, public: true } if tag?
variables << { key: 'CI_BUILD_TRIGGERED', value: 'true', public: true } if trigger_request
+ variables << { key: 'CI_BUILD_MANUAL', value: 'true', public: true } if manual?
variables
end
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 4a628924499..438f807b118 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -69,17 +69,15 @@ class CommitStatus < ActiveRecord::Base
commit_status.update_attributes finished_at: Time.now
end
- # We use around_transition to process pipeline on next stages as soon as possible, before the `after_*` is executed
- around_transition any => [:success, :failed, :canceled] do |commit_status, block|
- block.call
-
- commit_status.pipeline.try(:process!)
- end
-
after_transition do |commit_status, transition|
commit_status.pipeline.try(:build_updated) unless transition.loopback?
end
+ after_transition any => [:success, :failed, :canceled] do |commit_status|
+ commit_status.pipeline.try(:process!)
+ true
+ end
+
after_transition [:created, :pending, :running] => :success do |commit_status|
MergeRequests::MergeWhenBuildSucceedsService.new(commit_status.pipeline.project, nil).trigger(commit_status)
end
diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb
index f7b8352405c..d658552f695 100644
--- a/app/models/concerns/has_status.rb
+++ b/app/models/concerns/has_status.rb
@@ -8,8 +8,9 @@ module HasStatus
class_methods do
def status_sql
- scope = all.relevant
+ scope = all
builds = scope.select('count(*)').to_sql
+ created = scope.created.select('count(*)').to_sql
success = scope.success.select('count(*)').to_sql
ignored = scope.ignored.select('count(*)').to_sql if scope.respond_to?(:ignored)
ignored ||= '0'
@@ -19,12 +20,12 @@ module HasStatus
skipped = scope.skipped.select('count(*)').to_sql
deduce_status = "(CASE
- WHEN (#{builds})=0 THEN NULL
+ WHEN (#{builds})=(#{created}) THEN NULL
WHEN (#{builds})=(#{skipped}) THEN 'skipped'
WHEN (#{builds})=(#{success})+(#{ignored})+(#{skipped}) THEN 'success'
- WHEN (#{builds})=(#{pending})+(#{skipped}) THEN 'pending'
+ WHEN (#{builds})=(#{created})+(#{pending})+(#{skipped}) THEN 'pending'
WHEN (#{builds})=(#{canceled})+(#{success})+(#{ignored})+(#{skipped}) THEN 'canceled'
- WHEN (#{running})+(#{pending})>0 THEN 'running'
+ WHEN (#{running})+(#{pending})+(#{created})>0 THEN 'running'
ELSE 'failed'
END)"
diff --git a/app/models/member.rb b/app/models/member.rb
index 64e0d33fb20..69406379948 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -28,17 +28,34 @@ class Member < ActiveRecord::Base
allow_nil: true
}
+ # This scope encapsulates (most of) the conditions a row in the member table
+ # must satisfy if it is a valid permission. Of particular note:
+ #
+ # * Access requests must be excluded
+ # * Blocked users must be excluded
+ # * Invitations take effect immediately
+ # * expires_at is not implemented. A background worker purges expired rows
+ scope :active, -> do
+ is_external_invite = arel_table[:user_id].eq(nil).and(arel_table[:invite_token].not_eq(nil))
+ user_is_active = User.arel_table[:state].eq(:active)
+
+ includes(:user).references(:users)
+ .where(is_external_invite.or(user_is_active))
+ .where(requested_at: nil)
+ end
+
scope :invite, -> { where.not(invite_token: nil) }
scope :non_invite, -> { where(invite_token: nil) }
scope :request, -> { where.not(requested_at: nil) }
- scope :has_access, -> { where('access_level > 0') }
-
- scope :guests, -> { where(access_level: GUEST) }
- scope :reporters, -> { where(access_level: REPORTER) }
- scope :developers, -> { where(access_level: DEVELOPER) }
- scope :masters, -> { where(access_level: MASTER) }
- scope :owners, -> { where(access_level: OWNER) }
- scope :owners_and_masters, -> { where(access_level: [OWNER, MASTER]) }
+
+ scope :has_access, -> { active.where('access_level > 0') }
+
+ scope :guests, -> { active.where(access_level: GUEST) }
+ scope :reporters, -> { active.where(access_level: REPORTER) }
+ scope :developers, -> { active.where(access_level: DEVELOPER) }
+ scope :masters, -> { active.where(access_level: MASTER) }
+ scope :owners, -> { active.where(access_level: OWNER) }
+ scope :owners_and_masters, -> { active.where(access_level: [OWNER, MASTER]) }
before_validation :generate_invite_token, on: :create, if: -> (member) { member.invite_email.present? }
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index b0b1313f94a..f7d1253d957 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -316,6 +316,10 @@ class MergeRequest < ActiveRecord::Base
closed? && forked_source_project_missing?
end
+ def closed_without_source_project?
+ closed? && !source_project
+ end
+
def forked_source_project_missing?
return false unless for_fork?
return true unless source_project
@@ -323,6 +327,12 @@ class MergeRequest < ActiveRecord::Base
!source_project.forked_from?(target_project)
end
+ def reopenable?
+ return false if closed_without_fork? || closed_without_source_project? || merged?
+
+ closed?
+ end
+
def ensure_merge_request_diff
merge_request_diff || create_merge_request_diff
end
diff --git a/app/models/project.rb b/app/models/project.rb
index a6de2c48071..f3f3ffbbd28 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -58,7 +58,7 @@ class Project < ActiveRecord::Base
# Relations
belongs_to :creator, foreign_key: 'creator_id', class_name: 'User'
- belongs_to :group, -> { where(type: Group) }, foreign_key: 'namespace_id'
+ belongs_to :group, -> { where(type: 'Group') }, foreign_key: 'namespace_id'
belongs_to :namespace
has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event', foreign_key: 'project_id'
@@ -1288,8 +1288,24 @@ class Project < ActiveRecord::Base
end
end
+ def pushes_since_gc
+ Gitlab::Redis.with { |redis| redis.get(pushes_since_gc_redis_key).to_i }
+ end
+
+ def increment_pushes_since_gc
+ Gitlab::Redis.with { |redis| redis.incr(pushes_since_gc_redis_key) }
+ end
+
+ def reset_pushes_since_gc
+ Gitlab::Redis.with { |redis| redis.del(pushes_since_gc_redis_key) }
+ end
+
private
+ def pushes_since_gc_redis_key
+ "projects/#{id}/pushes_since_gc"
+ end
+
# Prevents the creation of project_feature record for every project
def setup_project_feature
build_project_feature unless project_feature
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 7b7090b8a73..c69e5a22a69 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -813,7 +813,7 @@ class Repository
update: true
}
- if previous_path
+ if previous_path && previous_path != path
options[:file][:previous_path] = previous_path
Gitlab::Git::Blob.rename(raw_repository, options)
else
@@ -990,37 +990,6 @@ class Repository
Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/)
end
- def parse_search_result(result)
- ref = nil
- filename = nil
- basename = nil
- startline = 0
-
- result.each_line.each_with_index do |line, index|
- if line =~ /^.*:.*:\d+:/
- ref, filename, startline = line.split(':')
- startline = startline.to_i - index
- extname = Regexp.escape(File.extname(filename))
- basename = filename.sub(/#{extname}$/, '')
- break
- end
- end
-
- data = ""
-
- result.each_line do |line|
- data << line.sub(ref, '').sub(filename, '').sub(/^:-\d+-/, '').sub(/^::\d+:/, '')
- end
-
- OpenStruct.new(
- filename: filename,
- basename: basename,
- ref: ref,
- startline: startline,
- data: data
- )
- end
-
def fetch_ref(source_path, source_ref, target_ref)
args = %W(#{Gitlab.config.git.bin_path} fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref})
Gitlab::Popen.popen(args, path_to_repo)
@@ -1048,7 +1017,7 @@ class Repository
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
update_ref!(ref, newrev, oldrev)
-
+
if was_empty || !target_branch
# If repo was empty expire cache
after_create if was_empty
diff --git a/app/models/service.rb b/app/models/service.rb
index 198e7247838..80de7175565 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -12,6 +12,7 @@ class Service < ActiveRecord::Base
default_value_for :tag_push_events, true
default_value_for :note_events, true
default_value_for :build_events, true
+ default_value_for :pipeline_events, true
default_value_for :wiki_page_events, true
after_initialize :initialize_properties
diff --git a/app/services/ci/process_pipeline_service.rb b/app/services/ci/process_pipeline_service.rb
index de48a50774e..36c93dddadb 100644
--- a/app/services/ci/process_pipeline_service.rb
+++ b/app/services/ci/process_pipeline_service.rb
@@ -31,13 +31,13 @@ module Ci
current_status = status_for_prior_stages(index)
created_builds_in_stage(index).select do |build|
- process_build(build, current_status)
+ if HasStatus::COMPLETED_STATUSES.include?(current_status)
+ process_build(build, current_status)
+ end
end
end
def process_build(build, current_status)
- return false unless HasStatus::COMPLETED_STATUSES.include?(current_status)
-
if valid_statuses_for_when(build.when).include?(current_status)
build.enqueue
true
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index 8a53f65aec1..a08c6fcd94b 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -27,6 +27,8 @@ module Projects
# Git data (e.g. a list of branch names).
flush_caches(project, wiki_path)
+ Projects::UnlinkForkService.new(project, current_user).execute
+
Project.transaction do
project.destroy!
diff --git a/app/services/projects/housekeeping_service.rb b/app/services/projects/housekeeping_service.rb
index 29b3981f49f..c3dfc8cfbe8 100644
--- a/app/services/projects/housekeeping_service.rb
+++ b/app/services/projects/housekeeping_service.rb
@@ -30,10 +30,8 @@ module Projects
end
def increment!
- if Gitlab::ExclusiveLease.new("project_housekeeping:increment!:#{@project.id}", timeout: 60).try_obtain
- Gitlab::Metrics.measure(:increment_pushes_since_gc) do
- update_pushes_since_gc(@project.pushes_since_gc + 1)
- end
+ Gitlab::Metrics.measure(:increment_pushes_since_gc) do
+ @project.increment_pushes_since_gc
end
end
@@ -43,14 +41,10 @@ module Projects
GitGarbageCollectWorker.perform_async(@project.id)
ensure
Gitlab::Metrics.measure(:reset_pushes_since_gc) do
- update_pushes_since_gc(0)
+ @project.reset_pushes_since_gc
end
end
- def update_pushes_since_gc(new_value)
- @project.update_column(:pushes_since_gc, new_value)
- end
-
def try_obtain_lease
Gitlab::Metrics.measure(:obtain_housekeeping_lease) do
lease = ::Gitlab::ExclusiveLease.new("project_housekeeping:#{@project.id}", timeout: LEASE_TIMEOUT)
diff --git a/app/views/admin/builds/_build.html.haml b/app/views/admin/builds/_build.html.haml
deleted file mode 100644
index f29d9c94441..00000000000
--- a/app/views/admin/builds/_build.html.haml
+++ /dev/null
@@ -1,77 +0,0 @@
-- project = build.project
-%tr.build.commit
- %td.status
- = ci_status_with_icon(build.status)
-
- %td
- .branch-commit
- - if can?(current_user, :read_build, build.project)
- = link_to namespace_project_build_url(build.project.namespace, build.project, build) do
- %span.build-link ##{build.id}
- - else
- %span.build-link ##{build.id}
-
- - if build.ref
- .icon-container
- = build.tag? ? icon('tag') : icon('code-fork')
- = link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref), class: "monospace branch-name"
- - else
- .light none
- .icon-container
- = custom_icon("icon_commit")
-
- = link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace commit-id"
- - if build.stuck?
- %i.fa.fa-warning.text-warning
-
- .label-container
- - if build.tags.any?
- - build.tags.each do |tag|
- %span.label.label-primary
- = tag
- - if build.try(:trigger_request)
- %span.label.label-info triggered
- - if build.try(:allow_failure)
- %span.label.label-danger allowed to fail
-
- %td
- - if project
- = link_to project.name_with_namespace, admin_namespace_project_path(project.namespace, project)
-
- %td
- - if build.try(:runner)
- = runner_link(build.runner)
- - else
- .light none
-
- %td
- #{build.stage} / #{build.name}
-
- %td
- - if build.duration
- %p.duration
- = custom_icon("icon_timer")
- = duration_in_numbers(build.duration)
-
- - if build.finished_at
- %p.finished-at
- = icon("calendar")
- %span #{time_ago_with_tooltip(build.finished_at)}
-
- - if defined?(coverage) && coverage
- %td.coverage
- - if build.try(:coverage)
- #{build.coverage}%
-
- %td
- .pull-right
- - if can?(current_user, :read_build, project) && build.artifacts?
- = link_to download_namespace_project_build_artifacts_path(build.project.namespace, build.project, build), title: 'Download artifacts', class: 'btn btn-build' do
- %i.fa.fa-download
- - if can?(current_user, :update_build, build.project)
- - if build.active?
- = link_to cancel_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Cancel', class: 'btn btn-build' do
- %i.fa.fa-remove.cred
- - elsif defined?(allow_retry) && allow_retry && build.retryable?
- = link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-build' do
- %i.fa.fa-refresh
diff --git a/app/views/admin/builds/index.html.haml b/app/views/admin/builds/index.html.haml
index 3d77634d8fa..26a8846b609 100644
--- a/app/views/admin/builds/index.html.haml
+++ b/app/views/admin/builds/index.html.haml
@@ -4,26 +4,8 @@
%div{ class: container_class }
.top-area
- %ul.nav-links
- %li{class: ('active' if @scope.nil?)}
- = link_to admin_builds_path do
- All
- %span.badge.js-totalbuilds-count= @all_builds.count(:id)
-
- %li{class: ('active' if @scope == 'pending')}
- = link_to admin_builds_path(scope: :pending) do
- Pending
- %span.badge= number_with_delimiter(@all_builds.pending.count(:id))
-
- %li{class: ('active' if @scope == 'running')}
- = link_to admin_builds_path(scope: :running) do
- Running
- %span.badge= number_with_delimiter(@all_builds.running.count(:id))
-
- %li{class: ('active' if @scope == 'finished')}
- = link_to admin_builds_path(scope: :finished) do
- Finished
- %span.badge= number_with_delimiter(@all_builds.finished.count(:id))
+ - build_path_proc = ->(scope) { admin_builds_path(scope: scope) }
+ = render "shared/builds/tabs", build_path_proc: build_path_proc, all_builds: @all_builds, scope: @scope
.nav-controls
- if @all_builds.running_or_pending.any?
@@ -33,23 +15,4 @@
#{(@scope || 'all').capitalize} builds
%ul.content-list.builds-content-list
- - if @builds.blank?
- %li
- .nothing-here-block No builds to show
- - else
- .table-holder
- %table.table.builds
- %thead
- %tr
- %th Status
- %th Commit
- %th Project
- %th Runner
- %th Name
- %th
- %th
-
- - @builds.each do |build|
- = render "admin/builds/build", build: build
-
- = paginate @builds, theme: 'gitlab'
+ = render "projects/builds/table", builds: @builds, admin: true
diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml
index 4debd3d608f..e623f7cff88 100644
--- a/app/views/devise/sessions/two_factor.html.haml
+++ b/app/views/devise/sessions/two_factor.html.haml
@@ -18,6 +18,5 @@
= f.submit "Verify code", class: "btn btn-save"
- if @user.two_factor_u2f_enabled?
-
%hr
- = render "u2f/authenticate"
+ = render "u2f/authenticate", locals: { params: params, resource: resource, resource_name: resource_name }
diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml
index a42b3b8eb38..93187873501 100644
--- a/app/views/profiles/keys/index.html.haml
+++ b/app/views/profiles/keys/index.html.haml
@@ -1,4 +1,5 @@
- page_title "SSH Keys"
+= render 'profiles/head'
.row.prepend-top-default
.col-lg-3.profile-settings-sidebar
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index 808e6b95746..5217b8bf028 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -3,6 +3,7 @@
- diverging_commit_counts = @repository.diverging_commit_counts(branch)
- number_commits_behind = diverging_commit_counts[:behind]
- number_commits_ahead = diverging_commit_counts[:ahead]
+- merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
%li(class="js-branch-#{branch.name}")
%div
= link_to namespace_project_tree_path(@project.namespace, @project, branch.name), class: 'item-title str-truncated' do
@@ -19,12 +20,12 @@
%i.fa.fa-lock
protected
.controls.hidden-xs
- - if create_mr_button?(@repository.root_ref, branch.name)
+ - if merge_project && create_mr_button?(@repository.root_ref, branch.name)
= link_to create_mr_path(@repository.root_ref, branch.name), class: 'btn btn-default' do
Merge Request
- if branch.name != @repository.root_ref
- = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: branch.name), class: 'btn btn-default', method: :post, title: "Compare" do
+ = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: branch.name), class: "btn btn-default #{'prepend-left-10' unless merge_project}", method: :post, title: "Compare" do
Compare
= render 'projects/buttons/download', project: @project, ref: branch.name
diff --git a/app/views/projects/builds/_table.html.haml b/app/views/projects/builds/_table.html.haml
new file mode 100644
index 00000000000..61eff73da26
--- /dev/null
+++ b/app/views/projects/builds/_table.html.haml
@@ -0,0 +1,24 @@
+- admin = local_assigns.fetch(:admin, false)
+
+- if builds.blank?
+ %li
+ .nothing-here-block No builds to show
+- else
+ .table-holder
+ %table.table.builds
+ %thead
+ %tr
+ %th Status
+ %th Commit
+ - if admin
+ %th Project
+ %th Runner
+ %th Stage
+ %th Name
+ %th
+ %th Coverage
+ %th
+
+ = render partial: "projects/ci/builds/build", collection: builds, as: :build, locals: { commit_sha: true, ref: true, stage: true, allow_retry: true, coverage: admin || project.build_coverage_enabled?, admin: admin }
+
+ = paginate builds, theme: 'gitlab'
diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml
index 2af625f69cd..5c60b7a7364 100644
--- a/app/views/projects/builds/index.html.haml
+++ b/app/views/projects/builds/index.html.haml
@@ -4,30 +4,8 @@
%div{ class: container_class }
.top-area
- %ul.nav-links
- %li{class: ('active' if @scope.nil?)}
- = link_to project_builds_path(@project) do
- All
- %span.badge.js-totalbuilds-count
- = number_with_delimiter(@all_builds.count(:id))
-
- %li{class: ('active' if @scope == 'pending')}
- = link_to project_builds_path(@project, scope: :pending) do
- Pending
- %span.badge
- = number_with_delimiter(@all_builds.pending.count(:id))
-
- %li{class: ('active' if @scope == 'running')}
- = link_to project_builds_path(@project, scope: :running) do
- Running
- %span.badge
- = number_with_delimiter(@all_builds.running.count(:id))
-
- %li{class: ('active' if @scope == 'finished')}
- = link_to project_builds_path(@project, scope: :finished) do
- Finished
- %span.badge
- = number_with_delimiter(@all_builds.finished.count(:id))
+ - build_path_proc = ->(scope) { project_builds_path(@project, scope: scope) }
+ = render "shared/builds/tabs", build_path_proc: build_path_proc, all_builds: @all_builds, scope: @scope
.nav-controls
- if can?(current_user, :update_build, @project)
@@ -42,23 +20,4 @@
%span CI Lint
%ul.content-list.builds-content-list
- - if @builds.blank?
- %li
- .nothing-here-block No builds to show
- - else
- .table-holder
- %table.table.builds
- %thead
- %tr
- %th Status
- %th Commit
- %th Stage
- %th Name
- %th
- - if @project.build_coverage_enabled?
- %th Coverage
- %th
-
- = render @builds, commit_sha: true, ref: true, stage: true, allow_retry: true, coverage: @project.build_coverage_enabled?
-
- = paginate @builds, theme: 'gitlab'
+ = render "table", builds: @builds, project: @project
diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml
index 73de8abe55b..75192c48188 100644
--- a/app/views/projects/ci/builds/_build.html.haml
+++ b/app/views/projects/ci/builds/_build.html.haml
@@ -1,3 +1,11 @@
+- admin = local_assigns.fetch(:admin, false)
+- ref = local_assigns.fetch(:ref, nil)
+- commit_sha = local_assigns.fetch(:commit_sha, nil)
+- retried = local_assigns.fetch(:retried, false)
+- stage = local_assigns.fetch(:stage, false)
+- coverage = local_assigns.fetch(:coverage, false)
+- allow_retry = local_assigns.fetch(:allow_retry, false)
+
%tr.build.commit
%td.status
- if can?(current_user, :read_build, build)
@@ -9,11 +17,11 @@
.branch-commit
- if can?(current_user, :read_build, build)
= link_to namespace_project_build_url(build.project.namespace, build.project, build) do
- %span ##{build.id}
+ %span.build-link ##{build.id}
- else
- %span ##{build.id}
+ %span.build-link ##{build.id}
- - if defined?(ref) && ref
+ - if ref
- if build.ref
.icon-container
= build.tag? ? icon('tag') : icon('code-fork')
@@ -23,12 +31,12 @@
.icon-container
= custom_icon("icon_commit")
- - if defined?(commit_sha) && commit_sha
+ - if commit_sha
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "commit-id monospace"
- if build.stuck?
= icon('warning', class: 'text-warning has-tooltip', title: 'Build is stuck. Check runners.')
- - if defined?(retried) && retried
+ - if retried
= icon('warning', class: 'text-warning has-tooltip', title: 'Build was retried.')
.label-container
@@ -40,19 +48,24 @@
%span.label.label-info triggered
- if build.try(:allow_failure)
%span.label.label-danger allowed to fail
- - if defined?(retried) && retried
+ - if retried
%span.label.label-warning retried
- if build.manual?
%span.label.label-info manual
- - if defined?(runner) && runner
+ - if admin
+ %td
+ - if build.project
+ = link_to build.project.name_with_namespace, admin_namespace_project_path(build.project.namespace, build.project)
+
+ - if admin
%td
- if build.try(:runner)
= runner_link(build.runner)
- else
.light none
- - if defined?(stage) && stage
+ - if stage
%td
= build.stage
@@ -64,13 +77,14 @@
%p.duration
= custom_icon("icon_timer")
= duration_in_numbers(build.duration)
+
- if build.finished_at
%p.finished-at
= icon("calendar")
%span #{time_ago_with_tooltip(build.finished_at)}
- - if defined?(coverage) && coverage
- %td.coverage
+ %td.coverage
+ - if coverage
- if build.try(:coverage)
#{build.coverage}%
@@ -83,10 +97,10 @@
- if build.active?
= link_to cancel_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Cancel', class: 'btn btn-build' do
= icon('remove', class: 'cred')
- - elsif defined?(allow_retry) && allow_retry
+ - elsif allow_retry
- if build.retryable?
= link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-build' do
= icon('repeat')
- - elsif build.playable?
+ - elsif build.playable? && !admin
= link_to play_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Play', class: 'btn btn-build' do
= custom_icon('icon_play')
diff --git a/app/views/projects/issues/_merge_requests.html.haml b/app/views/projects/issues/_merge_requests.html.haml
index d8075371853..31d3ec23276 100644
--- a/app/views/projects/issues/_merge_requests.html.haml
+++ b/app/views/projects/issues/_merge_requests.html.haml
@@ -1,7 +1,7 @@
- if @merge_requests.any?
%h2.merge-requests-title
= pluralize(@merge_requests.count, 'Related Merge Request')
- %ul.unstyled-list
+ %ul.unstyled-list.related-merge-requests
- has_any_ci = @merge_requests.any?(&:pipeline)
- @merge_requests.each do |merge_request|
%li
diff --git a/app/views/projects/issues/_related_branches.html.haml b/app/views/projects/issues/_related_branches.html.haml
index a8eeab3e55e..44683c8bcdb 100644
--- a/app/views/projects/issues/_related_branches.html.haml
+++ b/app/views/projects/issues/_related_branches.html.haml
@@ -1,7 +1,7 @@
- if @related_branches.any?
%h2.related-branches-title
= pluralize(@related_branches.count, 'Related Branch')
- %ul.unstyled-list
+ %ul.unstyled-list.related-merge-requests
- @related_branches.each do |branch|
%li
- target = @project.repository.find_branch(branch).target
diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml
index d070979bcfe..3900b4f6f17 100644
--- a/app/views/projects/merge_requests/_discussion.html.haml
+++ b/app/views/projects/merge_requests/_discussion.html.haml
@@ -2,7 +2,7 @@
- if can?(current_user, :update_merge_request, @merge_request)
- if @merge_request.open?
= link_to 'Close merge request', merge_request_path(@merge_request, merge_request: {state_event: :close }), method: :put, class: "btn btn-nr btn-comment btn-close close-mr-link js-note-target-close", title: "Close merge request", data: {original_text: "Close merge request", alternative_text: "Comment & close merge request"}
- - if @merge_request.closed?
+ - if @merge_request.reopenable?
= link_to 'Reopen merge request', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-nr btn-comment btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request", data: {original_text: "Reopen merge request", alternative_text: "Comment & reopen merge request"}
%comment-and-resolve-btn{ "inline-template" => true, ":discussion-id" => "" }
%button.btn.btn-nr.btn-default.append-right-10.js-comment-resolve-button{ "v-if" => "showButton", type: "submit", data: { namespace_path: "#{@merge_request.project.namespace.path}", project_path: "#{@merge_request.project.path}" } }
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index 4b4d418e8ec..d03ff9ec7e8 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -29,17 +29,19 @@
%ul.dropdown-menu.dropdown-menu-align-right
%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
- %span.label-branch
- = link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch)
- - if @merge_request.open? && @merge_request.diverged_from_target_branch?
- %span (#{pluralize(@merge_request.diverged_commits_count, 'commit')} behind)
+ - unless @merge_request.closed_without_fork?
+ .normal
+ %span Request to merge
+ %span.label-branch= source_branch_with_namespace(@merge_request)
+ %span into
+ %span.label-branch
+ = link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch)
+ - if @merge_request.open? && @merge_request.diverged_from_target_branch?
+ %span (#{pluralize(@merge_request.diverged_commits_count, 'commit')} behind)
- = render "projects/merge_requests/show/how_to_merge"
- = render "projects/merge_requests/widget/show.html.haml"
+ - unless @merge_request.closed_without_source_project?
+ = render "projects/merge_requests/show/how_to_merge"
+ = render "projects/merge_requests/widget/show.html.haml"
- if @merge_request.source_branch_exists? && @merge_request.mergeable? && @merge_request.can_be_merged_by?(current_user)
.light.prepend-top-default.append-bottom-default
@@ -53,10 +55,11 @@
= 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_count
+ - unless @merge_request.closed_without_source_project?
+ %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_count
- if @pipeline
%li.pipelines-tab
= link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index 7c82177f9ea..9ec17cf6e76 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -1,6 +1,5 @@
- return unless note.author
- return if note.cross_reference_not_visible_for?(current_user)
-- can_resolve = can?(current_user, :resolve_note, note)
- note_editable = note_editable?(note)
%li.timeline-entry{ id: dom_id(note), class: ["note", "note-row-#{note.id}", ('system-note' if note.system)], data: {author_id: note.author.id, editable: note_editable} }
@@ -24,6 +23,8 @@
%span.note-role.hidden-xs= access
- if note.resolvable?
+ - can_resolve = can?(current_user, :resolve_note, note)
+
%resolve-btn{ ":namespace-path" => "'#{note.project.namespace.path}'",
":project-path" => "'#{note.project.path}'",
":discussion-id" => "'#{note.discussion_id}'",
diff --git a/app/views/search/results/_blob.html.haml b/app/views/search/results/_blob.html.haml
index 290743feb4a..6f0a0ea36ec 100644
--- a/app/views/search/results/_blob.html.haml
+++ b/app/views/search/results/_blob.html.haml
@@ -1,4 +1,4 @@
-- blob = @project.repository.parse_search_result(blob)
+- blob = parse_search_result(blob)
.blob-result
.file-holder
.file-title
diff --git a/app/views/search/results/_wiki_blob.html.haml b/app/views/search/results/_wiki_blob.html.haml
index 235106c4f74..648d0bd76cb 100644
--- a/app/views/search/results/_wiki_blob.html.haml
+++ b/app/views/search/results/_wiki_blob.html.haml
@@ -1,4 +1,4 @@
-- wiki_blob = @project.repository.parse_search_result(wiki_blob)
+- wiki_blob = parse_search_result(wiki_blob)
.blob-result
.file-holder
.file-title
diff --git a/app/views/shared/builds/_tabs.html.haml b/app/views/shared/builds/_tabs.html.haml
new file mode 100644
index 00000000000..60353aee7f1
--- /dev/null
+++ b/app/views/shared/builds/_tabs.html.haml
@@ -0,0 +1,24 @@
+%ul.nav-links
+ %li{ class: ('active' if scope.nil?) }
+ = link_to build_path_proc.call(nil) do
+ All
+ %span.badge.js-totalbuilds-count
+ = number_with_delimiter(all_builds.count(:id))
+
+ %li{ class: ('active' if scope == 'pending') }
+ = link_to build_path_proc.call('pending') do
+ Pending
+ %span.badge
+ = number_with_delimiter(all_builds.pending.count(:id))
+
+ %li{ class: ('active' if scope == 'running') }
+ = link_to build_path_proc.call('running') do
+ Running
+ %span.badge
+ = number_with_delimiter(all_builds.running.count(:id))
+
+ %li{ class: ('active' if scope == 'finished') }
+ = link_to build_path_proc.call('finished') do
+ Finished
+ %span.badge
+ = number_with_delimiter(all_builds.finished.count(:id))
diff --git a/app/views/u2f/_authenticate.html.haml b/app/views/u2f/_authenticate.html.haml
index 75fb0e303ad..9657101ace5 100644
--- a/app/views/u2f/_authenticate.html.haml
+++ b/app/views/u2f/_authenticate.html.haml
@@ -20,6 +20,8 @@
%div
%p We heard back from your U2F device. Click this button to authenticate with the GitLab server.
= form_tag(new_user_session_path, method: :post) do |f|
+ - resource_params = params[resource_name].presence || params
+ = hidden_field_tag 'user[remember_me]', resource_params.fetch(:remember_me, 0)
= hidden_field_tag 'user[device_response]', nil, class: 'form-control', required: true, id: "js-device-response"
= submit_tag "Authenticate via U2F Device", class: "btn btn-success"
diff --git a/changelogs/archive.md b/changelogs/archive.md
new file mode 100644
index 00000000000..c68ab694d39
--- /dev/null
+++ b/changelogs/archive.md
@@ -0,0 +1,1810 @@
+## 7.14.3
+
+- No changes
+
+## 7.14.2
+
+- Upgrade gitlab_git to 7.2.15 to fix `git blame` errors with ISO-encoded files (Stan Hu)
+- Allow configuration of LDAP attributes GitLab will use for the new user account.
+
+## 7.14.1
+
+- Improve abuse reports management from admin area
+- Fix "Reload with full diff" URL button in compare branch view (Stan Hu)
+- Disabled DNS lookups for SSH in docker image (Rowan Wookey)
+- Only include base URL in OmniAuth full_host parameter (Stan Hu)
+- Fix Error 500 in API when accessing a group that has an avatar (Stan Hu)
+- Ability to enable SSL verification for Webhooks
+
+## 7.14.0
+
+- Fix bug where non-project members of the target project could set labels on new merge requests.
+- Update default robots.txt rules to disallow crawling of irrelevant pages (Ben Bodenmiller)
+- Fix redirection after sign in when using auto_sign_in_with_provider
+- Upgrade gitlab_git to 7.2.14 to ignore CRLFs in .gitmodules (Stan Hu)
+- Clear cache to prevent listing deleted branches after MR removes source branch (Stan Hu)
+- Provide more feedback what went wrong if HipChat service failed test (Stan Hu)
+- Fix bug where backslashes in inline diffs could be dropped (Stan Hu)
+- Disable turbolinks when linking to Bitbucket import status (Stan Hu)
+- Fix broken code import and display error messages if something went wrong with creating project (Stan Hu)
+- Fix corrupted binary files when using API files endpoint (Stan Hu)
+- Bump Haml to 4.0.7 to speed up textarea rendering (Stan Hu)
+- Show incompatible projects in Bitbucket import status (Stan Hu)
+- Fix coloring of diffs on MR Discussion-tab (Gert Goet)
+- Fix "Network" and "Graphs" pages for branches with encoded slashes (Stan Hu)
+- Fix errors deleting and creating branches with encoded slashes (Stan Hu)
+- Always add current user to autocomplete controller to support filter by "Me" (Stan Hu)
+- Fix multi-line syntax highlighting (Stan Hu)
+- Fix network graph when branch name has single quotes (Stan Hu)
+- Add "Confirm user" button in user admin page (Stan Hu)
+- Upgrade gitlab_git to version 7.2.6 to fix Error 500 when creating network graphs (Stan Hu)
+- Add support for Unicode filenames in relative links (Hiroyuki Sato)
+- Fix URL used for refreshing notes if relative_url is present (Bartłomiej Święcki)
+- Fix commit data retrieval when branch name has single quotes (Stan Hu)
+- Check that project was actually created rather than just validated in import:repos task (Stan Hu)
+- Fix full screen mode for snippet comments (Daniel Gerhardt)
+- Fix 404 error in files view after deleting the last file in a repository (Stan Hu)
+- Fix the "Reload with full diff" URL button (Stan Hu)
+- Fix label read access for unauthenticated users (Daniel Gerhardt)
+- Fix access to disabled features for unauthenticated users (Daniel Gerhardt)
+- Fix OAuth provider bug where GitLab would not go return to the redirect_uri after sign-in (Stan Hu)
+- Fix file upload dialog for comment editing (Daniel Gerhardt)
+- Set OmniAuth full_host parameter to ensure redirect URIs are correct (Stan Hu)
+- Return comments in created order in merge request API (Stan Hu)
+- Disable internal issue tracker controller if external tracker is used (Stan Hu)
+- Expire Rails cache entries after two weeks to prevent endless Redis growth
+- Add support for destroying project milestones (Stan Hu)
+- Allow custom backup archive permissions
+- Add project star and fork count, group avatar URL and user/group web URL attributes to API
+- Show who last edited a comment if it wasn't the original author
+- Send notification to all participants when MR is merged.
+- Add ability to manage user email addresses via the API.
+- Show buttons to add license, changelog and contribution guide if they're missing.
+- Tweak project page buttons.
+- Disabled autocapitalize and autocorrect on login field (Daryl Chan)
+- Mention group and project name in creation, update and deletion notices (Achilleas Pipinellis)
+- Update gravatar link on profile page to link to configured gravatar host (Ben Bodenmiller)
+- Remove redis-store TTL monkey patch
+- Add support for CI skipped status
+- Fetch code from forks to refs/merge-requests/:id/head when merge request created
+- Remove comments and email addresses when publicly exposing ssh keys (Zeger-Jan van de Weg)
+- Add "Check out branch" button to the MR page.
+- Improve MR merge widget text and UI consistency.
+- Improve text in MR "How To Merge" modal.
+- Cache all events
+- Order commits by date when comparing branches
+- Fix bug causing error when the target branch of a symbolic ref was deleted
+- Include branch/tag name in archive file and directory name
+- Add dropzone upload progress
+- Add a label for merged branches on branches page (Florent Baldino)
+- Detect .mkd and .mkdn files as markdown (Ben Boeckel)
+- Fix: User search feature in admin area does not respect filters
+- Set max-width for README, issue and merge request description for easier read on big screens
+- Update Flowdock integration to support new Flowdock API (Boyan Tabakov)
+- Remove author from files view (Sven Strickroth)
+- Fix infinite loop when SAML was incorrectly configured.
+
+## 7.13.5
+
+- Satellites reverted
+
+## 7.13.4
+
+- Allow users to send abuse reports
+
+## 7.13.3
+
+- Fix bug causing Bitbucket importer to crash when OAuth application had been removed.
+- Allow users to send abuse reports
+- Remove satellites
+- Link username to profile on Group Members page (Tom Webster)
+
+## 7.13.2
+
+- Fix randomly failed spec
+- Create project services on Project creation
+- Add admin_merge_request ability to Developer level and up
+- Fix Error 500 when browsing projects with no HEAD (Stan Hu)
+- Fix labels / assignee / milestone for the merge requests when issues are disabled
+- Show the first tab automatically on MergeRequests#new
+- Add rake task 'gitlab:update_commit_count' (Daniel Gerhardt)
+- Fix Gmail Actions
+
+## 7.13.1
+
+- Fix: Label modifications are not reflected in existing notes and in the issue list
+- Fix: Label not shown in the Issue list, although it's set through web interface
+- Fix: Group/project references are linked incorrectly
+- Improve documentation
+- Fix of migration: Check if session_expire_delay column exists before adding the column
+- Fix: ActionView::Template::Error
+- Fix: "Create Merge Request" isn't always shown in event for newly pushed branch
+- Fix bug causing "Remove source-branch" option not to work for merge requests from the same project.
+- Render Note field hints consistently for "new" and "edit" forms
+
+## 7.13.0
+
+- Remove repository graph log to fix slow cache updates after push event (Stan Hu)
+- Only enable HSTS header for HTTPS and port 443 (Stan Hu)
+- Fix user autocomplete for unauthenticated users accessing public projects (Stan Hu)
+- Fix redirection to home page URL for unauthorized users (Daniel Gerhardt)
+- Add branch switching support for graphs (Daniel Gerhardt)
+- Fix external issue tracker hook/test for HTTPS URLs (Daniel Gerhardt)
+- Remove link leading to a 404 error in Deploy Keys page (Stan Hu)
+- Add support for unlocking users in admin settings (Stan Hu)
+- Add Irker service configuration options (Stan Hu)
+- Fix order of issues imported from GitHub (Hiroyuki Sato)
+- Bump rugments to 1.0.0beta8 to fix C prototype function highlighting (Jonathon Reinhart)
+- Fix Merge Request webhook to properly fire "merge" action when accepted from the web UI
+- Add `two_factor_enabled` field to admin user API (Stan Hu)
+- Fix invalid timestamps in RSS feeds (Rowan Wookey)
+- Fix downloading of patches on public merge requests when user logged out (Stan Hu)
+- Fix Error 500 when relative submodule resolves to a namespace that has a different name from its path (Stan Hu)
+- Extract the longest-matching ref from a commit path when multiple matches occur (Stan Hu)
+- Update maintenance documentation to explain no need to recompile asssets for omnibus installations (Stan Hu)
+- Support commenting on diffs in side-by-side mode (Stan Hu)
+- Fix JavaScript error when clicking on the comment button on a diff line that has a comment already (Stan Hu)
+- Return 40x error codes if branch could not be deleted in UI (Stan Hu)
+- Remove project visibility icons from dashboard projects list
+- Rename "Design" profile settings page to "Preferences".
+- Allow users to customize their default Dashboard page.
+- Update ssl_ciphers in Nginx example to remove DHE settings. This will deny forward secrecy for Android 2.3.7, Java 6 and OpenSSL 0.9.8
+- Admin can edit and remove user identities
+- Convert CRLF newlines to LF when committing using the web editor.
+- API request /projects/:project_id/merge_requests?state=closed will return only closed merge requests without merged one. If you need ones that were merged - use state=merged.
+- Allow Administrators to filter the user list by those with or without Two-factor Authentication enabled.
+- Show a user's Two-factor Authentication status in the administration area.
+- Explicit error when commit not found in the CI
+- Improve performance for issue and merge request pages
+- Users with guest access level can not set assignee, labels or milestones for issue and merge request
+- Reporter role can manage issue tracker now: edit any issue, set assignee or milestone and manage labels
+- Better performance for pages with events list, issues list and commits list
+- Faster automerge check and merge itself when source and target branches are in same repository
+- Correctly show anonymous authorized applications under Profile > Applications.
+- Query Optimization in MySQL.
+- Allow users to be blocked and unblocked via the API
+- Use native Postgres database cleaning during backup restore
+- Redesign project page. Show README as default instead of activity. Move project activity to separate page
+- Make left menu more hierarchical and less contextual by adding back item at top
+- A fork can’t have a visibility level that is greater than the original project.
+- Faster code search in repository and wiki. Fixes search page timeout for big repositories
+- Allow administrators to disable 2FA for a specific user
+- Add error message for SSH key linebreaks
+- Store commits count in database (will populate with valid values only after first push)
+- Rebuild cache after push to repository in background job
+- Fix transferring of project to another group using the API.
+
+## 7.12.2
+
+- Correctly show anonymous authorized applications under Profile > Applications.
+- Faster automerge check and merge itself when source and target branches are in same repository
+- Audit log for user authentication
+- Allow custom label to be set for authentication providers.
+
+## 7.12.1
+
+- Fix error when deleting a user who has projects (Stan Hu)
+- Fix post-receive errors on a push when an external issue tracker is configured (Stan Hu)
+- Add SAML to list of social_provider (Matt Firtion)
+- Fix merge requests API scope to keep compatibility in 7.12.x patch release (Dmitriy Zaporozhets)
+- Fix closed merge request scope at milestone page (Dmitriy Zaporozhets)
+- Revert merge request states renaming
+- Fix hooks for web based events with external issue references (Daniel Gerhardt)
+- Improve performance for issue and merge request pages
+- Compress database dumps to reduce backup size
+
+## 7.12.0
+
+- Fix Error 500 when one user attempts to access a personal, internal snippet (Stan Hu)
+- Disable changing of target branch in new merge request page when a branch has already been specified (Stan Hu)
+- Fix post-receive errors on a push when an external issue tracker is configured (Stan Hu)
+- Update oauth button logos for Twitter and Google to recommended assets
+- Update browser gem to version 0.8.0 for IE11 support (Stan Hu)
+- Fix timeout when rendering file with thousands of lines.
+- Add "Remember me" checkbox to LDAP signin form.
+- Add session expiration delay configuration through UI application settings
+- Don't notify users mentioned in code blocks or blockquotes.
+- Omit link to generate labels if user does not have access to create them (Stan Hu)
+- Show warning when a comment will add 10 or more people to the discussion.
+- Disable changing of the source branch in merge request update API (Stan Hu)
+- Shorten merge request WIP text.
+- Add option to disallow users from registering any application to use GitLab as an OAuth provider
+- Support editing target branch of merge request (Stan Hu)
+- Refactor permission checks with issues and merge requests project settings (Stan Hu)
+- Fix Markdown preview not working in Edit Milestone page (Stan Hu)
+- Fix Zen Mode not closing with ESC key (Stan Hu)
+- Allow HipChat API version to be blank and default to v2 (Stan Hu)
+- Add file attachment support in Milestone description (Stan Hu)
+- Fix milestone "Browse Issues" button.
+- Set milestone on new issue when creating issue from index with milestone filter active.
+- Make namespace API available to all users (Stan Hu)
+- Add webhook support for note events (Stan Hu)
+- Disable "New Issue" and "New Merge Request" buttons when features are disabled in project settings (Stan Hu)
+- Remove Rack Attack monkey patches and bump to version 4.3.0 (Stan Hu)
+- Fix clone URL losing selection after a single click in Safari and Chrome (Stan Hu)
+- Fix git blame syntax highlighting when different commits break up lines (Stan Hu)
+- Add "Resend confirmation e-mail" link in profile settings (Stan Hu)
+- Allow to configure location of the `.gitlab_shell_secret` file. (Jakub Jirutka)
+- Disabled expansion of top/bottom blobs for new file diffs
+- Update Asciidoctor gem to version 1.5.2. (Jakub Jirutka)
+- Fix resolving of relative links to repository files in AsciiDoc documents. (Jakub Jirutka)
+- Use the user list from the target project in a merge request (Stan Hu)
+- Default extention for wiki pages is now .md instead of .markdown (Jeroen van Baarsen)
+- Add validation to wiki page creation (only [a-zA-Z0-9/_-] are allowed) (Jeroen van Baarsen)
+- Fix new/empty milestones showing 100% completion value (Jonah Bishop)
+- Add a note when an Issue or Merge Request's title changes
+- Consistently refer to MRs as either Merged or Closed.
+- Add Merged tab to MR lists.
+- Prefix EmailsOnPush email subject with `[Git]`.
+- Group project contributions by both name and email.
+- Clarify navigation labels for Project Settings and Group Settings.
+- Move user avatar and logout button to sidebar
+- You can not remove user if he/she is an only owner of group
+- User should be able to leave group. If not - show him proper message
+- User has ability to leave project
+- Add SAML support as an omniauth provider
+- Allow to configure a URL to show after sign out
+- Add an option to automatically sign-in with an Omniauth provider
+- GitLab CI service sends .gitlab-ci.yml in each push call
+- When remove project - move repository and schedule it removal
+- Improve group removing logic
+- Trigger create-hooks on backup restore task
+- Add option to automatically link omniauth and LDAP identities
+- Allow special character in users bio. I.e.: I <3 GitLab
+
+## 7.11.4
+
+- Fix missing bullets when creating lists
+- Set rel="nofollow" on external links
+
+## 7.11.3
+
+- no changes
+- Fix upgrader script (Martins Polakovs)
+
+## 7.11.2
+
+- no changes
+
+## 7.11.1
+
+- no changes
+
+## 7.11.0
+
+- Fall back to Plaintext when Syntaxhighlighting doesn't work. Fixes some buggy lexers (Hannes Rosenögger)
+- Get editing comments to work in Chrome 43 again.
+- Fix broken view when viewing history of a file that includes a path that used to be another file (Stan Hu)
+- Don't show duplicate deploy keys
+- Fix commit time being displayed in the wrong timezone in some cases (Hannes Rosenögger)
+- Make the first branch pushed to an empty repository the default HEAD (Stan Hu)
+- Fix broken view when using a tag to display a tree that contains git submodules (Stan Hu)
+- Make Reply-To config apply to change e-mail confirmation and other Devise notifications (Stan Hu)
+- Add application setting to restrict user signups to e-mail domains (Stan Hu)
+- Don't allow a merge request to be merged when its title starts with "WIP".
+- Add a page title to every page.
+- Allow primary email to be set to an email that you've already added.
+- Fix clone URL field and X11 Primary selection (Dmitry Medvinsky)
+- Ignore invalid lines in .gitmodules
+- Fix "Cannot move project" error message from popping up after a successful transfer (Stan Hu)
+- Redirect to sign in page after signing out.
+- Fix "Hello @username." references not working by no longer allowing usernames to end in period.
+- Fix "Revspec not found" errors when viewing diffs in a forked project with submodules (Stan Hu)
+- Improve project page UI
+- Fix broken file browsing with relative submodule in personal projects (Stan Hu)
+- Add "Reply quoting selected text" shortcut key (`r`)
+- Fix bug causing `@whatever` inside an issue's first code block to be picked up as a user mention.
+- Fix bug causing `@whatever` inside an inline code snippet (backtick-style) to be picked up as a user mention.
+- When use change branches link at MR form - save source branch selection instead of target one
+- Improve handling of large diffs
+- Added GitLab Event header for project hooks
+- Add Two-factor authentication (2FA) for GitLab logins
+- Show Atom feed buttons everywhere where applicable.
+- Add project activity atom feed.
+- Don't crash when an MR from a fork has a cross-reference comment from the target project on one of its commits.
+- Explain how to get a new password reset token in welcome emails
+- Include commit comments in MR from a forked project.
+- Group milestones by title in the dashboard and all other issue views.
+- Query issues, merge requests and milestones with their IID through API (Julien Bianchi)
+- Add default project and snippet visibility settings to the admin web UI.
+- Show incompatible projects in Google Code import status (Stan Hu)
+- Fix bug where commit data would not appear in some subdirectories (Stan Hu)
+- Task lists are now usable in comments, and will show up in Markdown previews.
+- Fix bug where avatar filenames were not actually deleted from the database during removal (Stan Hu)
+- Fix bug where Slack service channel was not saved in admin template settings. (Stan Hu)
+- Protect OmniAuth request phase against CSRF.
+- Don't send notifications to mentioned users that don't have access to the project in question.
+- Add search issues/MR by number
+- Change plots to bar graphs in commit statistics screen
+- Move snippets UI to fluid layout
+- Improve UI for sidebar. Increase separation between navigation and content
+- Improve new project command options (Ben Bodenmiller)
+- Add common method to force UTF-8 and use it to properly handle non-ascii OAuth user properties (Onur Küçük)
+- Prevent sending empty messages to HipChat (Chulki Lee)
+- Improve UI for mobile phones on dashboard and project pages
+- Add room notification and message color option for HipChat
+- Allow to use non-ASCII letters and dashes in project and namespace name. (Jakub Jirutka)
+- Add footnotes support to Markdown (Guillaume Delbergue)
+- Add current_sign_in_at to UserFull REST api.
+- Make Sidekiq MemoryKiller shutdown signal configurable
+- Add "Create Merge Request" buttons to commits and branches pages and push event.
+- Show user roles by comments.
+- Fix automatic blocking of auto-created users from Active Directory.
+- Call merge request webhook for each new commits (Arthur Gautier)
+- Use SIGKILL by default in Sidekiq::MemoryKiller
+- Fix mentioning of private groups.
+- Add style for <kbd> element in markdown
+- Spin spinner icon next to "Checking for CI status..." on MR page.
+- Fix reference links in dashboard activity and ATOM feeds.
+- Ensure that the first added admin performs repository imports
+
+## 7.10.4
+
+- Fix migrations broken in 7.10.2
+- Make tags for GitLab installations running on MySQL case sensitive
+- Get Gitorious importer to work again.
+- Fix adding new group members from admin area
+- Fix DB error when trying to tag a repository (Stan Hu)
+- Fix Error 500 when searching Wiki pages (Stan Hu)
+- Unescape branch names in compare commit (Stan Hu)
+- Order commit comments chronologically in API.
+
+## 7.10.2
+
+- Fix CI links on MR page
+
+## 7.10.0
+
+- Ignore submodules that are defined in .gitmodules but are checked in as directories.
+- Allow projects to be imported from Google Code.
+- Remove access control for uploaded images to fix broken images in emails (Hannes Rosenögger)
+- Allow users to be invited by email to join a group or project.
+- Don't crash when project repository doesn't exist.
+- Add config var to block auto-created LDAP users.
+- Don't use HTML ellipsis in EmailsOnPush subject truncated commit message.
+- Set EmailsOnPush reply-to address to committer email when enabled.
+- Fix broken file browsing with a submodule that contains a relative link (Stan Hu)
+- Fix persistent XSS vulnerability around profile website URLs.
+- Fix project import URL regex to prevent arbitary local repos from being imported.
+- Fix directory traversal vulnerability around uploads routes.
+- Fix directory traversal vulnerability around help pages.
+- Don't leak existence of project via search autocomplete.
+- Don't leak existence of group or project via search.
+- Fix bug where Wiki pages that included a '/' were no longer accessible (Stan Hu)
+- Fix bug where error messages from Dropzone would not be displayed on the issues page (Stan Hu)
+- Add a rake task to check repository integrity with `git fsck`
+- Add ability to configure Reply-To address in gitlab.yml (Stan Hu)
+- Move current user to the top of the list in assignee/author filters (Stan Hu)
+- Fix broken side-by-side diff view on merge request page (Stan Hu)
+- Set Application controller default URL options to ensure all url_for calls are consistent (Stan Hu)
+- Allow HTML tags in Markdown input
+- Fix code unfold not working on Compare commits page (Stan Hu)
+- Fix generating SSH key fingerprints with OpenSSH 6.8. (Sašo Stanovnik)
+- Fix "Import projects from" button to show the correct instructions (Stan Hu)
+- Fix dots in Wiki slugs causing errors (Stan Hu)
+- Make maximum attachment size configurable via Application Settings (Stan Hu)
+- Update poltergeist to version 1.6.0 to support PhantomJS 2.0 (Zeger-Jan van de Weg)
+- Fix cross references when usernames, milestones, or project names contain underscores (Stan Hu)
+- Disable reference creation for comments surrounded by code/preformatted blocks (Stan Hu)
+- Reduce Rack Attack false positives causing 403 errors during HTTP authentication (Stan Hu)
+- enable line wrapping per default and remove the checkbox to toggle it (Hannes Rosenögger)
+- Fix a link in the patch update guide
+- Add a service to support external wikis (Hannes Rosenögger)
+- Omit the "email patches" link and fix plain diff view for merge commits
+- List new commits for newly pushed branch in activity view.
+- Add sidetiq gem dependency to match EE
+- Add changelog, license and contribution guide links to project tab bar.
+- Improve diff UI
+- Fix alignment of navbar toggle button (Cody Mize)
+- Fix checkbox rendering for nested task lists
+- Identical look of selectboxes in UI
+- Upgrade the gitlab_git gem to version 7.1.3
+- Move "Import existing repository by URL" option to button.
+- Improve error message when save profile has error.
+- Passing the name of pushed ref to CI service (requires GitLab CI 7.9+)
+- Add location field to user profile
+- Fix print view for markdown files and wiki pages
+- Fix errors when deleting old backups
+- Improve GitLab performance when working with git repositories
+- Add tag message and last commit to tag hook (Kamil Trzciński)
+- Restrict permissions on backup files
+- Improve oauth accounts UI in profile page
+- Add ability to unlink connected accounts
+- Replace commits calendar with faster contribution calendar that includes issues and merge requests
+- Add inifinite scroll to user page activity
+- Don't include system notes in issue/MR comment count.
+- Don't mark merge request as updated when merge status relative to target branch changes.
+- Link note avatar to user.
+- Make Git-over-SSH errors more descriptive.
+- Fix EmailsOnPush.
+- Refactor issue filtering
+- AJAX selectbox for issue assignee and author filters
+- Fix issue with missing options in issue filtering dropdown if selected one
+- Prevent holding Control-Enter or Command-Enter from posting comment multiple times.
+- Prevent note form from being cleared when submitting failed.
+- Improve file icons rendering on tree (Sullivan Sénéchal)
+- API: Add pagination to project events
+- Get issue links in notification mail to work again.
+- Don't show commit comment button when user is not signed in.
+- Fix admin user projects lists.
+- Don't leak private group existence by redirecting from namespace controller to group controller.
+- Ability to skip some items from backup (database, respositories or uploads)
+- Archive repositories in background worker.
+- Import GitHub, Bitbucket or GitLab.com projects owned by authenticated user into current namespace.
+- Project labels are now available over the API under the "tag_list" field (Cristian Medina)
+- Fixed link paths for HTTP and SSH on the admin project view (Jeremy Maziarz)
+- Fix and improve help rendering (Sullivan Sénéchal)
+- Fix final line in EmailsOnPush email diff being rendered as error.
+- Prevent duplicate Buildkite service creation.
+- Fix git over ssh errors 'fatal: protocol error: bad line length character'
+- Automatically setup GitLab CI project for forks if origin project has GitLab CI enabled
+- Bust group page project list cache when namespace name or path changes.
+- Explicitly set image alt-attribute to prevent graphical glitches if gravatars could not be loaded
+- Allow user to choose a public email to show on public profile
+- Remove truncation from issue titles on milestone page (Jason Blanchard)
+- Fix stuck Merge Request merging events from old installations (Ben Bodenmiller)
+- Fix merge request comments on files with multiple commits
+- Fix Resource Owner Password Authentication Flow
+- Add icons to Add dropdown items.
+- Allow admin to create public deploy keys that are accessible to any project.
+- Warn when gitlab-shell version doesn't match requirement.
+- Skip email confirmation when set by admin or via LDAP.
+- Only allow users to reference groups, projects, issues, MRs, commits they have access to.
+
+## 7.9.4
+
+- Security: Fix project import URL regex to prevent arbitary local repos from being imported
+- Fixed issue where only 25 commits would load in file listings
+- Fix LDAP identities after config update
+
+## 7.9.3
+
+- Contains no changes
+
+## 7.9.2
+
+- Contains no changes
+
+## 7.9.1
+
+- Include missing events and fix save functionality in admin service template settings form (Stan Hu)
+- Fix "Import projects from" button to show the correct instructions (Stan Hu)
+- Fix OAuth2 issue importing a new project from GitHub and GitLab (Stan Hu)
+- Fix for LDAP with commas in DN
+- Fix missing events and in admin Slack service template settings form (Stan Hu)
+- Don't show commit comment button when user is not signed in.
+- Downgrade gemnasium-gitlab-service gem
+
+## 7.9.0
+
+- Add HipChat integration documentation (Stan Hu)
+- Update documentation for object_kind field in Webhook push and tag push Webhooks (Stan Hu)
+- Fix broken email images (Hannes Rosenögger)
+- Automatically config git if user forgot, where possible (Zeger-Jan van de Weg)
+- Fix mass SQL statements on initial push (Hannes Rosenögger)
+- Add tag push notifications and normalize HipChat and Slack messages to be consistent (Stan Hu)
+- Add comment notification events to HipChat and Slack services (Stan Hu)
+- Add issue and merge request events to HipChat and Slack services (Stan Hu)
+- Fix merge request URL passed to Webhooks. (Stan Hu)
+- Fix bug that caused a server error when editing a comment to "+1" or "-1" (Stan Hu)
+- Fix code preview theme setting for comments, issues, merge requests, and snippets (Stan Hu)
+- Move labels/milestones tabs to sidebar
+- Upgrade Rails gem to version 4.1.9.
+- Improve error messages for file edit failures
+- Improve UI for commits, issues and merge request lists
+- Fix commit comments on first line of diff not rendering in Merge Request Discussion view.
+- Allow admins to override restricted project visibility settings.
+- Move restricted visibility settings from gitlab.yml into the web UI.
+- Improve trigger merge request hook when source project branch has been updated (Kirill Zaitsev)
+- Save web edit in new branch
+- Fix ordering of imported but unchanged projects (Marco Wessel)
+- Mobile UI improvements: make aside content expandable
+- Expose avatar_url in projects API
+- Fix checkbox alignment on the application settings page.
+- Generalize image upload in drag and drop in markdown to all files (Hannes Rosenögger)
+- Fix mass-unassignment of issues (Robert Speicher)
+- Fix hidden diff comments in merge request discussion view
+- Allow user confirmation to be skipped for new users via API
+- Add a service to send updates to an Irker gateway (Romain Coltel)
+- Add brakeman (security scanner for Ruby on Rails)
+- Slack username and channel options
+- Add grouped milestones from all projects to dashboard.
+- Webhook sends pusher email as well as commiter
+- Add Bitbucket omniauth provider.
+- Add Bitbucket importer.
+- Support referencing issues to a project whose name starts with a digit
+- Condense commits already in target branch when updating merge request source branch.
+- Send notifications and leave system comments when bulk updating issues.
+- Automatically link commit ranges to compare page: sha1...sha4 or sha1..sha4 (includes sha1 in comparison)
+- Move groups page from profile to dashboard
+- Starred projects page at dashboard
+- Blocking user does not remove him/her from project/groups but show blocked label
+- Change subject of EmailsOnPush emails to include namespace, project and branch.
+- Change subject of EmailsOnPush emails to include first commit message when multiple were pushed.
+- Remove confusing footer from EmailsOnPush mail body.
+- Add list of changed files to EmailsOnPush emails.
+- Add option to send EmailsOnPush emails from committer email if domain matches.
+- Add option to disable code diffs in EmailOnPush emails.
+- Wrap commit message in EmailsOnPush email.
+- Send EmailsOnPush emails when deleting commits using force push.
+- Fix EmailsOnPush email comparison link to include first commit.
+- Fix highliht of selected lines in file
+- Reject access to group/project avatar if the user doesn't have access.
+- Add database migration to clean group duplicates with same path and name (Make sure you have a backup before update)
+- Add GitLab active users count to rake gitlab:check
+- Starred projects page at dashboard
+- Make email display name configurable
+- Improve json validation in hook data
+- Use Emoji One
+- Updated emoji help documentation to properly reference EmojiOne.
+- Fix missing GitHub organisation repositories on import page.
+- Added blue theme
+- Remove annoying notice messages when create/update merge request
+- Allow smb:// links in Markdown text.
+- Filter merge request by title or description at Merge Requests page
+- Block user if he/she was blocked in Active Directory
+- Fix import pages not working after first load.
+- Use custom LDAP label in LDAP signin form.
+- Execute hooks and services when branch or tag is created or deleted through web interface.
+- Block and unblock user if he/she was blocked/unblocked in Active Directory
+- Raise recommended number of unicorn workers from 2 to 3
+- Use same layout and interactivity for project members as group members.
+- Prevent gitlab-shell character encoding issues by receiving its changes as raw data.
+- Ability to unsubscribe/subscribe to issue or merge request
+- Delete deploy key when last connection to a project is destroyed.
+- Fix invalid Atom feeds when using emoji, horizontal rules, or images (Christian Walther)
+- Backup of repositories with tar instead of git bundle (only now are git-annex files included in the backup)
+- Add canceled status for CI
+- Send EmailsOnPush email when branch or tag is created or deleted.
+- Faster merge request processing for large repository
+- Prevent doubling AJAX request with each commit visit via Turbolink
+- Prevent unnecessary doubling of js events on import pages and user calendar
+
+## 7.8.4
+
+- Fix issue_tracker_id substitution in custom issue trackers
+- Fix path and name duplication in namespaces
+
+## 7.8.3
+
+- Bump version of gitlab_git fixing annotated tags without message
+
+## 7.8.2
+
+- Fix service migration issue when upgrading from versions prior to 7.3
+- Fix setting of the default use project limit via admin UI
+- Fix showing of already imported projects for GitLab and Gitorious importers
+- Fix response of push to repository to return "Not found" if user doesn't have access
+- Fix check if user is allowed to view the file attachment
+- Fix import check for case sensetive namespaces
+- Increase timeout for Git-over-HTTP requests to 1 hour since large pulls/pushes can take a long time.
+- Properly handle autosave local storage exceptions.
+- Escape wildcards when searching LDAP by username.
+
+## 7.8.1
+
+- Fix run of custom post receive hooks
+- Fix migration that caused issues when upgrading to version 7.8 from versions prior to 7.3
+- Fix the warning for LDAP users about need to set password
+- Fix avatars which were not shown for non logged in users
+- Fix urls for the issues when relative url was enabled
+
+## 7.8.0
+
+- Fix access control and protection against XSS for note attachments and other uploads.
+- Replace highlight.js with rouge-fork rugments (Stefan Tatschner)
+- Make project search case insensitive (Hannes Rosenögger)
+- Include issue/mr participants in list of recipients for reassign/close/reopen emails
+- Expose description in groups API
+- Better UI for project services page
+- Cleaner UI for web editor
+- Add diff syntax highlighting in email-on-push service notifications (Hannes Rosenögger)
+- Add API endpoint to fetch all changes on a MergeRequest (Jeroen van Baarsen)
+- View note image attachments in new tab when clicked instead of downloading them
+- Improve sorting logic in UI and API. Explicitly define what sorting method is used by default
+- Fix overflow at sidebar when have several items
+- Add notes for label changes in issue and merge requests
+- Show tags in commit view (Hannes Rosenögger)
+- Only count a user's vote once on a merge request or issue (Michael Clarke)
+- Increase font size when browse source files and diffs
+- Service Templates now let you set default values for all services
+- Create new file in empty repository using GitLab UI
+- Ability to clone project using oauth2 token
+- Upgrade Sidekiq gem to version 3.3.0
+- Stop git zombie creation during force push check
+- Show success/error messages for test setting button in services
+- Added Rubocop for code style checks
+- Fix commits pagination
+- Async load a branch information at the commit page
+- Disable blacklist validation for project names
+- Allow configuring protection of the default branch upon first push (Marco Wessel)
+- Add gitlab.com importer
+- Add an ability to login with gitlab.com
+- Add a commit calendar to the user profile (Hannes Rosenögger)
+- Submit comment on command-enter
+- Notify all members of a group when that group is mentioned in a comment, for example: `@gitlab-org` or `@sales`.
+- Extend issue clossing pattern to include "Resolve", "Resolves", "Resolved", "Resolving" and "Close" (Julien Bianchi and Hannes Rosenögger)
+- Fix long broadcast message cut-off on left sidebar (Visay Keo)
+- Add Project Avatars (Steven Thonus and Hannes Rosenögger)
+- Password reset token validity increased from 2 hours to 2 days since it is also send on account creation.
+- Edit group members via API
+- Enable raw image paste from clipboard, currently Chrome only (Marco Cyriacks)
+- Add action property to merge request hook (Julien Bianchi)
+- Remove duplicates from group milestone participants list.
+- Add a new API function that retrieves all issues assigned to a single milestone (Justin Whear and Hannes Rosenögger)
+- API: Access groups with their path (Julien Bianchi)
+- Added link to milestone and keeping resource context on smaller viewports for issues and merge requests (Jason Blanchard)
+- Allow notification email to be set separately from primary email.
+- API: Add support for editing an existing project (Mika Mäenpää and Hannes Rosenögger)
+- Don't have Markdown preview fail for long comments/wiki pages.
+- When test webhook - show error message instead of 500 error page if connection to hook url was reset
+- Added support for firing system hooks on group create/destroy and adding/removing users to group (Boyan Tabakov)
+- Added persistent collapse button for left side nav bar (Jason Blanchard)
+- Prevent losing unsaved comments by automatically restoring them when comment page is loaded again.
+- Don't allow page to be scaled on mobile.
+- Clean the username acquired from OAuth/LDAP so it doesn't fail username validation and block signing up.
+- Show assignees in merge request index page (Kelvin Mutuma)
+- Link head panel titles to relevant root page.
+- Allow users that signed up via OAuth to set their password in order to use Git over HTTP(S).
+- Show users button to share their newly created public or internal projects on twitter
+- Add quick help links to the GitLab pricing and feature comparison pages.
+- Fix duplicate authorized applications in user profile and incorrect application client count in admin area.
+- Make sure Markdown previews always use the same styling as the eventual destination.
+- Remove deprecated Group#owner_id from API
+- Show projects user contributed to on user page. Show stars near project on user page.
+- Improve database performance for GitLab
+- Add Asana service (Jeremy Benoist)
+- Improve project webhooks with extra data
+
+## 7.7.2
+
+- Update GitLab Shell to version 2.4.2 that fixes a bug when developers can push to protected branch
+- Fix issue when LDAP user can't login with existing GitLab account
+
+## 7.7.1
+
+- Improve mention autocomplete performance
+- Show setup instructions for GitHub import if disabled
+- Allow use http for OAuth applications
+
+## 7.7.0
+
+- Import from GitHub.com feature
+- Add Jetbrains Teamcity CI service (Jason Lippert)
+- Mention notification level
+- Markdown preview in wiki (Yuriy Glukhov)
+- Raise group avatar filesize limit to 200kb
+- OAuth applications feature
+- Show user SSH keys in admin area
+- Developer can push to protected branches option
+- Set project path instead of project name in create form
+- Block Git HTTP access after 10 failed authentication attempts
+- Updates to the messages returned by API (sponsored by O'Reilly Media)
+- New UI layout with side navigation
+- Add alert message in case of outdated browser (IE < 10)
+- Added API support for sorting projects
+- Update gitlab_git to version 7.0.0.rc14
+- Add API project search filter option for authorized projects
+- Fix File blame not respecting branch selection
+- Change some of application settings on fly in admin area UI
+- Redesign signin/signup pages
+- Close standard input in Gitlab::Popen.popen
+- Trigger GitLab CI when push tags
+- When accept merge request - do merge using sidaekiq job
+- Enable web signups by default
+- Fixes for diff comments: drag-n-drop images, selecting images
+- Fixes for edit comments: drag-n-drop images, preview mode, selecting images, save & update
+- Remove password strength indicator
+
+## 7.6.0
+
+- Fork repository to groups
+- New rugged version
+- Add CRON=1 backup setting for quiet backups
+- Fix failing wiki restore
+- Add optional Sidekiq MemoryKiller middleware (enabled via SIDEKIQ_MAX_RSS env variable)
+- Monokai highlighting style now more faithful to original design (Mark Riedesel)
+- Create project with repository in synchrony
+- Added ability to create empty repo or import existing one if project does not have repository
+- Reactivate highlight.js language autodetection
+- Mobile UI improvements
+- Change maximum avatar file size from 100KB to 200KB
+- Strict validation for snippet file names
+- Enable Markdown preview for issues, merge requests, milestones, and notes (Vinnie Okada)
+- In the docker directory is a container template based on the Omnibus packages.
+- Update Sidekiq to version 2.17.8
+- Add author filter to project issues and merge requests pages
+- Atom feed for user activity
+- Support multiple omniauth providers for the same user
+- Rendering cross reference in issue title and tooltip for merge request
+- Show username in comments
+- Possibility to create Milestones or Labels when Issues are disabled
+- Fix bug with showing gpg signature in tag
+
+## 7.5.3
+
+- Bump gitlab_git to 7.0.0.rc12 (includes Rugged 0.21.2)
+
+## 7.5.2
+
+- Don't log Sidekiq arguments by default
+- Fix restore of wiki repositories from backups
+
+## 7.5.1
+
+- Add missing timestamps to 'members' table
+
+## 7.5.0
+
+- API: Add support for Hipchat (Kevin Houdebert)
+- Add time zone configuration in gitlab.yml (Sullivan Senechal)
+- Fix LDAP authentication for Git HTTP access
+- Run 'GC.start' after every EmailsOnPushWorker job
+- Fix LDAP config lookup for provider 'ldap'
+- Drop all sequences during Postgres database restore
+- Project title links to project homepage (Ben Bodenmiller)
+- Add Atlassian Bamboo CI service (Drew Blessing)
+- Mentioned @user will receive email even if he is not participating in issue or commit
+- Session API: Use case-insensitive authentication like in UI (Andrey Krivko)
+- Tie up loose ends with annotated tags: API & UI (Sean Edge)
+- Return valid json for deleting branch via API (sponsored by O'Reilly Media)
+- Expose username in project events API (sponsored by O'Reilly Media)
+- Adds comments to commits in the API
+- Performance improvements
+- Fix post-receive issue for projects with deleted forks
+- New gitlab-shell version with custom hooks support
+- Improve code
+- GitLab CI 5.2+ support (does not support older versions)
+- Fixed bug when you can not push commits starting with 000000 to protected branches
+- Added a password strength indicator
+- Change project name and path in one form
+- Display renamed files in diff views (Vinnie Okada)
+- Fix raw view for public snippets
+- Use secret token with GitLab internal API.
+- Add missing timestamps to 'members' table
+
+## 7.4.5
+
+- Bump gitlab_git to 7.0.0.rc12 (includes Rugged 0.21.2)
+
+## 7.4.4
+
+- No changes
+
+## 7.4.3
+
+- Fix raw snippets view
+- Fix security issue for member api
+- Fix buildbox integration
+
+## 7.4.2
+
+- Fix internal snippet exposing for unauthenticated users
+
+## 7.4.1
+
+- Fix LDAP authentication for Git HTTP access
+- Fix LDAP config lookup for provider 'ldap'
+- Fix public snippets
+- Fix 500 error on projects with nested submodules
+
+## 7.4.0
+
+- Refactored membership logic
+- Improve error reporting on users API (Julien Bianchi)
+- Refactor test coverage tools usage. Use SIMPLECOV=true to generate it locally
+- Default branch is protected by default
+- Increase unicorn timeout to 60 seconds
+- Sort search autocomplete projects by stars count so most popular go first
+- Add README to tab on project show page
+- Do not delete tmp/repositories itself during clean-up, only its contents
+- Support for backup uploads to remote storage
+- Prevent notes polling when there are not notes
+- Internal ForkService: Prepare support for fork to a given namespace
+- API: Add support for forking a project via the API (Bernhard Kaindl)
+- API: filter project issues by milestone (Julien Bianchi)
+- Fail harder in the backup script
+- Changes to Slack service structure, only webhook url needed
+- Zen mode for wiki and milestones (Robert Schilling)
+- Move Emoji parsing to html-pipeline-gitlab (Robert Schilling)
+- Font Awesome 4.2 integration (Sullivan Senechal)
+- Add Pushover service integration (Sullivan Senechal)
+- Add select field type for services options (Sullivan Senechal)
+- Add cross-project references to the Markdown parser (Vinnie Okada)
+- Add task lists to issue and merge request descriptions (Vinnie Okada)
+- Snippets can be public, internal or private
+- Improve danger zone: ask project path to confirm data-loss action
+- Raise exception on forgery
+- Show build coverage in Merge Requests (requires GitLab CI v5.1)
+- New milestone and label links on issue edit form
+- Improved repository graphs
+- Improve event note display in dashboard and project activity views (Vinnie Okada)
+- Add users sorting to admin area
+- UI improvements
+- Fix ambiguous sha problem with mentioned commit
+- Fixed bug with apostrophe when at mentioning users
+- Add active directory ldap option
+- Developers can push to wiki repo. Protected branches does not affect wiki repo any more
+- Faster rev list
+- Fix branch removal
+
+## 7.3.2
+
+- Fix creating new file via web editor
+- Use gitlab-shell v2.0.1
+
+## 7.3.1
+
+- Fix ref parsing in Gitlab::GitAccess
+- Fix error 500 when viewing diff on a file with changed permissions
+- Fix adding comments to MR when source branch is master
+- Fix error 500 when searching description contains relative link
+
+## 7.3.0
+
+- Always set the 'origin' remote in satellite actions
+- Write authorized_keys in tmp/ during tests
+- Use sockets to connect to Redis
+- Add dormant New Relic gem (can be enabled via environment variables)
+- Expire Rack sessions after 1 week
+- Cleaner signin/signup pages
+- Improved comments UI
+- Better search with filtering, pagination etc
+- Added a checkbox to toggle line wrapping in diff (Yuriy Glukhov)
+- Prevent project stars duplication when fork project
+- Use the default Unicorn socket backlog value of 1024
+- Support Unix domain sockets for Redis
+- Store session Redis keys in 'session:gitlab:' namespace
+- Deprecate LDAP account takeover based on partial LDAP email / GitLab username match
+- Use /bin/sh instead of Bash in bin/web, bin/background_jobs (Pavel Novitskiy)
+- Keyboard shortcuts for productivity (Robert Schilling)
+- API: filter issues by state (Julien Bianchi)
+- API: filter issues by labels (Julien Bianchi)
+- Add system hook for ssh key changes
+- Add blob permalink link (Ciro Santilli)
+- Create annotated tags through UI and API (Sean Edge)
+- Snippets search (Charles Bushong)
+- Comment new push to existing MR
+- Add 'ci' to the blacklist of forbidden names
+- Improve text filtering on issues page
+- Comment & Close button
+- Process git push --all much faster
+- Don't allow edit of system notes
+- Project wiki search (Ralf Seidler)
+- Enabled Shibboleth authentication support (Matus Banas)
+- Zen mode (fullscreen) for issues/MR/notes (Robert Schilling)
+- Add ability to configure webhook timeout via gitlab.yml (Wes Gurney)
+- Sort project merge requests in asc or desc order for updated_at or created_at field (sponsored by O'Reilly Media)
+- Add Redis socket support to 'rake gitlab:shell:install'
+
+## 7.2.1
+
+- Delete orphaned labels during label migration (James Brooks)
+- Security: prevent XSS with stricter MIME types for raw repo files
+
+## 7.2.0
+
+- Explore page
+- Add project stars (Ciro Santilli)
+- Log Sidekiq arguments
+- Better labels: colors, ability to rename and remove
+- Improve the way merge request collects diffs
+- Improve compare page for large diffs
+- Expose the full commit message via API
+- Fix 500 error on repository rename
+- Fix bug when MR download patch return invalid diff
+- Test gitlab-shell integration
+- Repository import timeout increased from 2 to 4 minutes allowing larger repos to be imported
+- API for labels (Robert Schilling)
+- API: ability to set an import url when creating project for specific user
+
+## 7.1.1
+
+- Fix cpu usage issue in Firefox
+- Fix redirect loop when changing password by new user
+- Fix 500 error on new merge request page
+
+## 7.1.0
+
+- Remove observers
+- Improve MR discussions
+- Filter by description on Issues#index page
+- Fix bug with namespace select when create new project page
+- Show README link after description for non-master members
+- Add @all mention for comments
+- Dont show reply button if user is not signed in
+- Expose more information for issues with webhook
+- Add a mention of the merge request into the default merge request commit message
+- Improve code highlight, introduce support for more languages like Go, Clojure, Erlang etc
+- Fix concurrency issue in repository download
+- Dont allow repository name start with ?
+- Improve email threading (Pierre de La Morinerie)
+- Cleaner help page
+- Group milestones
+- Improved email notifications
+- Contributors API (sponsored by Mobbr)
+- Fix LDAP TLS authentication (Boris HUISGEN)
+- Show VERSION information on project sidebar
+- Improve branch removal logic when accept MR
+- Fix bug where comment form is spawned inside the Reply button
+- Remove Dir.chdir from Satellite#lock for thread-safety
+- Increased default git max_size value from 5MB to 20MB in gitlab.yml. Please update your configs!
+- Show error message in case of timeout in satellite when create MR
+- Show first 100 files for huge diff instead of hiding all
+- Change default admin email from admin@local.host to admin@example.com
+
+## 7.0.0
+
+- The CPU no longer overheats when you hold down the spacebar
+- Improve edit file UI
+- Add ability to upload group avatar when create
+- Protected branch cannot be removed
+- Developers can remove normal branches with UI
+- Remove branch via API (sponsored by O'Reilly Media)
+- Move protected branches page to Project settings area
+- Redirect to Files view when create new branch via UI
+- Drag and drop upload of image in every markdown-area (Earle Randolph Bunao and Neil Francis Calabroso)
+- Refactor the markdown relative links processing
+- Make it easier to implement other CI services for GitLab
+- Group masters can create projects in group
+- Deprecate ruby 1.9.3 support
+- Only masters can rewrite/remove git tags
+- Add X-Frame-Options SAMEORIGIN to Nginx config so Sidekiq admin is visible
+- UI improvements
+- Case-insensetive search for issues
+- Update to rails 4.1
+- Improve performance of application for projects and groups with a lot of members
+- Formally support Ruby 2.1
+- Include Nginx gitlab-ssl config
+- Add manual language detection for highlight.js
+- Added example.com/:username routing
+- Show notice if your profile is public
+- UI improvements for mobile devices
+- Improve diff rendering performance
+- Drag-n-drop for issues and merge requests between states at milestone page
+- Fix '0 commits' message for huge repositories on project home page
+- Prevent 500 error page when visit commit page from large repo
+- Add notice about huge push over http to unicorn config
+- File action in satellites uses default 30 seconds timeout instead of old 10 seconds one
+- Overall performance improvements
+- Skip init script check on omnibus-gitlab
+- Be more selective when killing stray Sidekiqs
+- Check LDAP user filter during sign-in
+- Remove wall feature (no data loss - you can take it from database)
+- Dont expose user emails via API unless you are admin
+- Detect issues closed by Merge Request description
+- Better email subject lines from email on push service (Alex Elman)
+- Enable identicon for gravatar be default
+
+## 6.9.2
+
+- Revert the commit that broke the LDAP user filter
+
+## 6.9.1
+
+- Fix scroll to highlighted line
+- Fix the pagination on load for commits page
+
+## 6.9.0
+
+- Store Rails cache data in the Redis `cache:gitlab` namespace
+- Adjust MySQL limits for existing installations
+- Add db index on project_id+iid column. This prevents duplicate on iid (During migration duplicates will be removed)
+- Markdown preview or diff during editing via web editor (Evgeniy Sokovikov)
+- Give the Rails cache its own Redis namespace
+- Add ability to set different ssh host, if different from http/https
+- Fix syntax highlighting for code comments blocks
+- Improve comments loading logic
+- Stop refreshing comments when the tab is hidden
+- Improve issue and merge request mobile UI (Drew Blessing)
+- Document how to convert a backup to PostgreSQL
+- Fix locale bug in backup manager
+- Fix can not automerge when MR description is too long
+- Fix wiki backup skip bug
+- Two Step MR creation process
+- Remove unwanted files from satellite working directory with git clean -fdx
+- Accept merge request via API (sponsored by O'Reilly Media)
+- Add more access checks during API calls
+- Block SSH access for 'disabled' Active Directory users
+- Labels for merge requests (Drew Blessing)
+- Threaded emails by setting a Message-ID (Philip Blatter)
+
+## 6.8.0
+
+- Ability to at mention users that are participating in issue and merge req. discussion
+- Enabled GZip Compression for assets in example Nginx, make sure that Nginx is compiled with --with-http_gzip_static_module flag (this is default in Ubuntu)
+- Make user search case-insensitive (Christopher Arnold)
+- Remove omniauth-ldap nickname bug workaround
+- Drop all tables before restoring a Postgres backup
+- Make the repository downloads path configurable
+- Create branches via API (sponsored by O'Reilly Media)
+- Changed permission of gitlab-satellites directory not to be world accessible
+- Protected branch does not allow force push
+- Fix popen bug in `rake gitlab:satellites:create`
+- Disable connection reaping for MySQL
+- Allow oauth signup without email for twitter and github
+- Fix faulty namespace names that caused 500 on user creation
+- Option to disable standard login
+- Clean old created archives from repository downloads directory
+- Fix download link for huge MR diffs
+- Expose event and mergerequest timestamps in API
+- Fix emails on push service when only one commit is pushed
+
+## 6.7.3
+
+- Fix the merge notification email not being sent (Pierre de La Morinerie)
+- Drop all tables before restoring a Postgres backup
+- Remove yanked modernizr gem
+
+## 6.7.2
+
+- Fix upgrader script
+
+## 6.7.1
+
+- Fix GitLab CI integration
+
+## 6.7.0
+
+- Increased the example Nginx client_max_body_size from 5MB to 20MB, consider updating it manually on existing installations
+- Add support for Gemnasium as a Project Service (Olivier Gonzalez)
+- Add edit file button to MergeRequest diff
+- Public groups (Jason Hollingsworth)
+- Cleaner headers in Notification Emails (Pierre de La Morinerie)
+- Blob and tree gfm links to anchors work
+- Piwik Integration (Sebastian Winkler)
+- Show contribution guide link for new issue form (Jeroen van Baarsen)
+- Fix CI status for merge requests from fork
+- Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard)
+- New page load indicator that includes a spinner that scrolls with the page
+- Converted all the help sections into markdown
+- LDAP user filters
+- Streamline the content of notification emails (Pierre de La Morinerie)
+- Fixes a bug with group member administration (Matt DeTullio)
+- Sort tag names using VersionSorter (Robert Speicher)
+- Add GFM autocompletion for MergeRequests (Robert Speicher)
+- Add webhook when a new tag is pushed (Jeroen van Baarsen)
+- Add button for toggling inline comments in diff view
+- Add retry feature for repository import
+- Reuse the GitLab LDAP connection within each request
+- Changed markdown new line behaviour to conform to markdown standards
+- Fix global search
+- Faster authorized_keys rebuilding in `rake gitlab:shell:setup` (requires gitlab-shell 1.8.5)
+- Create and Update MR calls now support the description parameter (Greg Messner)
+- Markdown relative links in the wiki link to wiki pages, markdown relative links in repositories link to files in the repository
+- Added Slack service integration (Federico Ravasio)
+- Better API responses for access_levels (sponsored by O'Reilly Media)
+- Requires at least 2 unicorn workers
+- Requires gitlab-shell v1.9+
+- Replaced gemoji(due to closed licencing problem) with Phantom Open Emoji library(combined SIL Open Font License, MIT License and the CC 3.0 License)
+- Fix `/:username.keys` response content type (Dmitry Medvinsky)
+
+## 6.6.5
+
+- Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard)
+- Hide mr close button for comment form if merge request was closed or inline comment
+- Adds ability to reopen closed merge request
+
+## 6.6.4
+
+- Add missing html escape for highlighted code blocks in comments, issues
+
+## 6.6.3
+
+- Fix 500 error when edit yourself from admin area
+- Hide private groups for public profiles
+
+## 6.6.2
+
+- Fix 500 error on branch/tag create or remove via UI
+
+## 6.6.1
+
+- Fix 500 error on files tab if submodules presents
+
+## 6.6.0
+
+- Retrieving user ssh keys publically(github style): http://__HOST__/__USERNAME__.keys
+- Permissions: Developer now can manage issue tracker (modify any issue)
+- Improve Code Compare page performance
+- Group avatar
+- Pygments.rb replaced with highlight.js
+- Improve Merge request diff store logic
+- Improve render performnace for MR show page
+- Fixed Assembla hardcoded project name
+- Jira integration documentation
+- Refactored app/services
+- Remove snippet expiration
+- Mobile UI improvements (Drew Blessing)
+- Fix block/remove UI for admin::users#show page
+- Show users' group membership on users' activity page (Robert Djurasaj)
+- User pages are visible without login if user is authorized to a public project
+- Markdown rendered headers have id derived from their name and link to their id
+- Improve application to work faster with large groups (100+ members)
+- Multiple emails per user
+- Show last commit for file when view file source
+- Restyle Issue#show page and MR#show page
+- Ability to filter by multiple labels for Issues page
+- Rails version to 4.0.3
+- Fixed attachment identifier displaying underneath note text (Jason Blanchard)
+
+## 6.5.1
+
+- Fix branch selectbox when create merge request from fork
+
+## 6.5.0
+
+- Dropdown menus on issue#show page for assignee and milestone (Jason Blanchard)
+- Add color custimization and previewing to broadcast messages
+- Fixed notes anchors
+- Load new comments in issues dynamically
+- Added sort options to Public page
+- New filters (assigned/authored/all) for Dashboard#issues/merge_requests (sponsored by Say Media)
+- Add project visibility icons to dashboard
+- Enable secure cookies if https used
+- Protect users/confirmation with rack_attack
+- Default HTTP headers to protect against MIME-sniffing, force https if enabled
+- Bootstrap 3 with responsive UI
+- New repository download formats: tar.bz2, zip, tar (Jason Hollingsworth)
+- Restyled accept widgets for MR
+- SCSS refactored
+- Use jquery timeago plugin
+- Fix 500 error for rdoc files
+- Ability to customize merge commit message (sponsored by Say Media)
+- Search autocomplete via ajax
+- Add website url to user profile
+- Files API supports base64 encoded content (sponsored by O'Reilly Media)
+- Added support for Go's repository retrieval (Bruno Albuquerque)
+
+## 6.4.3
+
+- Don't use unicorn worker killer if PhusionPassenger is defined
+
+## 6.4.2
+
+- Fixed wrong behaviour of script/upgrade.rb
+
+## 6.4.1
+
+- Fixed bug with repository rename
+- Fixed bug with project transfer
+
+## 6.4.0
+
+- Added sorting to project issues page (Jason Blanchard)
+- Assembla integration (Carlos Paramio)
+- Fixed another 500 error with submodules
+- UI: More compact issues page
+- Minimal password length increased to 8 symbols
+- Side-by-side diff view (Steven Thonus)
+- Internal projects (Jason Hollingsworth)
+- Allow removal of avatar (Drew Blessing)
+- Project webhooks now support issues and merge request events
+- Visiting project page while not logged in will redirect to sign-in instead of 404 (Jason Hollingsworth)
+- Expire event cache on avatar creation/removal (Drew Blessing)
+- Archiving old projects (Steven Thonus)
+- Rails 4
+- Add time ago tooltips to show actual date/time
+- UI: Fixed UI for admin system hooks
+- Ruby script for easier GitLab upgrade
+- Do not remove Merge requests if fork project was removed
+- Improve sign-in/signup UX
+- Add resend confirmation link to sign-in page
+- Set noreply@HOSTNAME for reply_to field in all emails
+- Show GitLab API version on Admin#dashboard
+- API Cross-origin resource sharing
+- Show READMe link at project home page
+- Show repo size for projects in Admin area
+
+## 6.3.0
+
+- API for adding gitlab-ci service
+- Init script now waits for pids to appear after (re)starting before reporting status (Rovanion Luckey)
+- Restyle project home page
+- Grammar fixes
+- Show branches list (which branches contains commit) on commit page (Andrew Kumanyaev)
+- Security improvements
+- Added support for GitLab CI 4.0
+- Fixed issue with 500 error when group did not exist
+- Ability to leave project
+- You can create file in repo using UI
+- You can remove file from repo using UI
+- API: dropped default_branch attribute from project during creation
+- Project default_branch is not stored in db any more. It takes from repo now.
+- Admin broadcast messages
+- UI improvements
+- Dont show last push widget if user removed this branch
+- Fix 500 error for repos with newline in file name
+- Extended html titles
+- API: create/update/delete repo files
+- Admin can transfer project to any namespace
+- API: projects/all for admin users
+- Fix recent branches order
+
+## 6.2.4
+
+- Security: Cast API private_token to string (CVE-2013-4580)
+- Security: Require gitlab-shell 1.7.8 (CVE-2013-4581, CVE-2013-4582, CVE-2013-4583)
+- Fix for Git SSH access for LDAP users
+
+## 6.2.3
+
+- Security: More protection against CVE-2013-4489
+- Security: Require gitlab-shell 1.7.4 (CVE-2013-4490, CVE-2013-4546)
+- Fix sidekiq rake tasks
+
+## 6.2.2
+
+- Security: Update gitlab_git (CVE-2013-4489)
+
+## 6.2.1
+
+- Security: Fix issue with generated passwords for new users
+
+## 6.2.0
+
+- Public project pages are now visible to everyone (files, issues, wik, etc.)
+ THIS MEANS YOUR ISSUES AND WIKI FOR PUBLIC PROJECTS ARE PUBLICLY VISIBLE AFTER THE UPGRADE
+- Add group access to permissions page
+- Require current password to change one
+- Group owner or admin can remove other group owners
+- Remove group transfer since we have multiple owners
+- Respect authorization in Repository API
+- Improve UI for Project#files page
+- Add more security specs
+- Added search for projects by name to api (Izaak Alpert)
+- Make default user theme configurable (Izaak Alpert)
+- Update logic for validates_merge_request for tree of MR (Andrew Kumanyaev)
+- Rake tasks for webhooks management (Jonhnny Weslley)
+- Extended User API to expose admin and can_create_group for user creation/updating (Boyan Tabakov)
+- API: Remove group
+- API: Remove project
+- Avatar upload on profile page with a maximum of 100KB (Steven Thonus)
+- Store the sessions in Redis instead of the cookie store
+- Fixed relative links in markdown
+- User must confirm their email if signup enabled
+- User must confirm changed email
+
+## 6.1.0
+
+- Project specific IDs for issues, mr, milestones
+ Above items will get a new id and for example all bookmarked issue urls will change.
+ Old issue urls are redirected to the new one if the issue id is too high for an internal id.
+- Description field added to Merge Request
+- API: Sudo api calls (Izaak Alpert)
+- API: Group membership api (Izaak Alpert)
+- Improved commit diff
+- Improved large commit handling (Boyan Tabakov)
+- Rewrite: Init script now less prone to errors and keeps better track of the service (Rovanion Luckey)
+- Link issues, merge requests, and commits when they reference each other with GFM (Ash Wilson)
+- Close issues automatically when pushing commits with a special message
+- Improve user removal from admin area
+- Invalidate events cache when project was moved
+- Remove deprecated classes and rake tasks
+- Add event filter for group and project show pages
+- Add links to create branch/tag from project home page
+- Add public-project? checkbox to new-project view
+- Improved compare page. Added link to proceed into Merge Request
+- Send an email to a user when they are added to group
+- New landing page when you have 0 projects
+
+## 6.0.0
+
+- Feature: Replace teams with group membership
+ We introduce group membership in 6.0 as a replacement for teams.
+ The old combination of groups and teams was confusing for a lot of people.
+ And when the members of a team where changed this wasn't reflected in the project permissions.
+ In GitLab 6.0 you will be able to add members to a group with a permission level for each member.
+ These group members will have access to the projects in that group.
+ Any changes to group members will immediately be reflected in the project permissions.
+ You can even have multiple owners for a group, greatly simplifying administration.
+- Feature: Ability to have multiple owners for group
+- Feature: Merge Requests between fork and project (Izaak Alpert)
+- Feature: Generate fingerprint for ssh keys
+- Feature: Ability to create and remove branches with UI
+- Feature: Ability to create and remove git tags with UI
+- Feature: Groups page in profile. You can leave group there
+- API: Allow login with LDAP credentials
+- Redesign: project settings navigation
+- Redesign: snippets area
+- Redesign: ssh keys page
+- Redesign: buttons, blocks and other ui elements
+- Add comment title to rss feed
+- You can use arrows to navigate at tree view
+- Add project filter on dashboard
+- Cache project graph
+- Drop support of root namespaces
+- Default theme is classic now
+- Cache result of methods like authorize_projects, project.team.members etc
+- Remove $.ready events
+- Fix onclick events being double binded
+- Add notification level to group membership
+- Move all project controllers/views under Projects:: module
+- Move all profile controllers/views under Profiles:: module
+- Apply user project limit only for personal projects
+- Unicorn is default web server again
+- Store satellites lock files inside satellites dir
+- Disabled threadsafety mode in rails
+- Fixed bug with loosing MR comments
+- Improved MR comments logic
+- Render readme file for projects in public area
+
+## 5.4.2
+
+- Security: Cast API private_token to string (CVE-2013-4580)
+- Security: Require gitlab-shell 1.7.8 (CVE-2013-4581, CVE-2013-4582, CVE-2013-4583)
+
+## 5.4.1
+
+- Security: Fixes for CVE-2013-4489
+- Security: Require gitlab-shell 1.7.4 (CVE-2013-4490, CVE-2013-4546)
+
+## 5.4.0
+
+- Ability to edit own comments
+- Documentation improvements
+- Improve dashboard projects page
+- Fixed nav for empty repos
+- GitLab Markdown help page
+- Misspelling fixes
+- Added support of unicorn and fog gems
+- Added client list to API doc
+- Fix PostgreSQL database restoration problem
+- Increase snippet content column size
+- allow project import via git:// url
+- Show participants on issues, including mentions
+- Notify mentioned users with email
+
+## 5.3.0
+
+- Refactored services
+- Campfire service added
+- HipChat service added
+- Fixed bug with LDAP + git over http
+- Fixed bug with google analytics code being ignored
+- Improve sign-in page if ldap enabled
+- Respect newlines in wall messages
+- Generate the Rails secret token on first run
+- Rename repo feature
+- Init.d: remove gitlab.socket on service start
+- Api: added teams api
+- Api: Prevent blob content being escaped
+- Api: Smart deploy key add behaviour
+- Api: projects/owned.json return user owned project
+- Fix bug with team assignation on project from #4109
+- Advanced snippets: public/private, project/personal (Andrew Kulakov)
+- Repository Graphs (Karlo Nicholas T. Soriano)
+- Fix dashboard lost if comment on commit
+- Update gitlab-grack. Fixes issue with --depth option
+- Fix project events duplicate on project page
+- Fix postgres error when displaying network graph.
+- Fix dashboard event filter when navigate via turbolinks
+- init.d: Ensure socket is removed before starting service
+- Admin area: Style teams:index, group:show pages
+- Own page for failed forking
+- Scrum view for milestone
+
+## 5.2.0
+
+- Turbolinks
+- Git over http with ldap credentials
+- Diff with better colors and some spacing on the corners
+- Default values for project features
+- Fixed huge_commit view
+- Restyle project clone panel
+- Move Gitlab::Git code to gitlab_git gem
+- Move update docs in repo
+- Requires gitlab-shell v1.4.0
+- Fixed submodules listing under file tab
+- Fork feature (Angus MacArthur)
+- git version check in gitlab:check
+- Shared deploy keys feature
+- Ability to generate default labels set for issues
+- Improve gfm autocomplete (Harold Luo)
+- Added support for Google Analytics
+- Code search feature (Javier Castro)
+
+## 5.1.0
+
+- You can login with email or username now
+- Corrected project transfer rollback when repository cannot be moved
+- Move both repo and wiki when project transfer requested
+- Admin area: project editing was removed from admin namespace
+- Access: admin user has now access to any project.
+- Notification settings
+- Gitlab::Git set of objects to abstract from grit library
+- Replace Unicorn web server with Puma
+- Backup/Restore refactored. Backup dump project wiki too now
+- Restyled Issues list. Show milestone version in issue row
+- Restyled Merge Request list
+- Backup now dump/restore uploads
+- Improved performance of dashboard (Andrew Kumanyaev)
+- File history now tracks renames (Akzhan Abdulin)
+- Drop wiki migration tools
+- Drop sqlite migration tools
+- project tagging
+- Paginate users in API
+- Restyled network graph (Hiroyuki Sato)
+
+## 5.0.1
+
+- Fixed issue with gitlab-grit being overridden by grit
+
+## 5.0.0
+
+- Replaced gitolite with gitlab-shell
+- Removed gitolite-related libraries
+- State machine added
+- Setup gitlab as git user
+- Internal API
+- Show team tab for empty projects
+- Import repository feature
+- Updated rails
+- Use lambda for scopes
+- Redesign admin area -> users
+- Redesign admin area -> user
+- Secure link to file attachments
+- Add validations for Group and Team names
+- Restyle team page for project
+- Update capybara, rspec-rails, poltergeist to recent versions
+- Wiki on git using Gollum
+- Added Solarized Dark theme for code review
+- Don't show user emails in autocomplete lists, profile pages
+- Added settings tab for group, team, project
+- Replace user popup with icons in header
+- Handle project moving with gitlab-shell
+- Added select2-rails for selectboxes with ajax data load
+- Fixed search field on projects page
+- Added teams to search autocomplete
+- Move groups and teams on dashboard sidebar to sub-tabs
+- API: improved return codes and docs. (Felix Gilcher, Sebastian Ziebell)
+- Redesign wall to be more like chat
+- Snippets, Wall features are disabled by default for new projects
+
+## 4.2.0
+
+- Teams
+- User show page. Via /u/username
+- Show help contents on pages for better navigation
+- Async gitolite calls
+- added satellites logs
+- can_create_group, can_create_team booleans for User
+- Process webhooks async
+- GFM: Fix images escaped inside links
+- Network graph improved
+- Switchable branches for network graph
+- API: Groups
+- Fixed project download
+
+## 4.1.0
+
+- Optional Sign-Up
+- Discussions
+- Satellites outside of tmp
+- Line numbers for blame
+- Project public mode
+- Public area with unauthorized access
+- Load dashboard events with ajax
+- remember dashboard filter in cookies
+- replace resque with sidekiq
+- fix routing issues
+- cleanup rake tasks
+- fix backup/restore
+- scss cleanup
+- show preview for note images
+- improved network-graph
+- get rid of app/roles/
+- added new classes Team, Repository
+- Reduce amount of gitolite calls
+- Ability to add user in all group projects
+- remove deprecated configs
+- replaced Korolev font with open font
+- restyled admin/dashboard page
+- restyled admin/projects page
+
+## 4.0.0
+
+- Remove project code and path from API. Use id instead
+- Return valid cloneable url to repo for webhook
+- Fixed backup issue
+- Reorganized settings
+- Fixed commits compare
+- Refactored scss
+- Improve status checks
+- Validates presence of User#name
+- Fixed postgres support
+- Removed sqlite support
+- Modified post-receive hook
+- Milestones can be closed now
+- Show comment events on dashboard
+- Quick add team members via group#people page
+- [API] expose created date for hooks and SSH keys
+- [API] list, create issue notes
+- [API] list, create snippet notes
+- [API] list, create wall notes
+- Remove project code - use path instead
+- added username field to user
+- rake task to fill usernames based on emails create namespaces for users
+- STI Group < Namespace
+- Project has namespace_id
+- Projects with namespaces also namespaced in gitolite and stored in subdir
+- Moving project to group will move it under group namespace
+- Ability to move project from namespaces to another
+- Fixes commit patches getting escaped (see #2036)
+- Support diff and patch generation for commits and merge request
+- MergeReqest doesn't generate a temporary file for the patch any more
+- Update the UI to allow downloading Patch or Diff
+
+## 3.1.0
+
+- Updated gems
+- Services: Gitlab CI integration
+- Events filter on dashboard
+- Own namespace for redis/resque
+- Optimized commit diff views
+- add alphabetical order for projects admin page
+- Improved web editor
+- Commit stats page
+- Documentation split and cleanup
+- Link to commit authors everywhere
+- Restyled milestones list
+- added Milestone to Merge Request
+- Restyled Top panel
+- Refactored Satellite Code
+- Added file line links
+- moved from capybara-webkit to poltergeist + phantomjs
+
+## 3.0.3
+
+- Fixed bug with issues list in Chrome
+- New Feature: Import team from another project
+
+## 3.0.2
+
+- Fixed gitlab:app:setup
+- Fixed application error on empty project in admin area
+- Restyled last push widget
+
+## 3.0.1
+
+- Fixed git over http
+
+## 3.0.0
+
+- Projects groups
+- Web Editor
+- Fixed bug with gitolite keys
+- UI improved
+- Increased performance of application
+- Show user avatar in last commit when browsing Files
+- Refactored Gitlab::Merge
+- Use Font Awesome for icons
+- Separate observing of Note and MergeRequests
+- Milestone "All Issues" filter
+- Fix issue close and reopen button text and styles
+- Fix forward/back while browsing Tree hierarchy
+- Show number of notes for commits and merge requests
+- Added support pg from box and update installation doc
+- Reject ssh keys that break gitolite
+- [API] list one project hook
+- [API] edit project hook
+- [API] list project snippets
+- [API] allow to authorize using private token in HTTP header
+- [API] add user creation
+
+## 2.9.1
+
+- Fixed resque custom config init
+
+## 2.9.0
+
+- fixed inline notes bugs
+- refactored rspecs
+- refactored gitolite backend
+- added factory_girl
+- restyled projects list on dashboard
+- ssh keys validation to prevent gitolite crash
+- send notifications if changed permission in project
+- scss refactoring. gitlab_bootstrap/ dir
+- fix git push http body bigger than 112k problem
+- list of labels page under issues tab
+- API for milestones, keys
+- restyled buttons
+- OAuth
+- Comment order changed
+
+## 2.8.1
+
+- ability to disable gravatars
+- improved MR diff logic
+- ssh key help page
+
+## 2.8.0
+
+- Gitlab Flavored Markdown
+- Bulk issues update
+- Issues API
+- Cucumber coverage increased
+- Post-receive files fixed
+- UI improved
+- Application cleanup
+- more cucumber
+- capybara-webkit + headless
+
+## 2.7.0
+
+- Issue Labels
+- Inline diff
+- Git HTTP
+- API
+- UI improved
+- System hooks
+- UI improved
+- Dashboard events endless scroll
+- Source performance increased
+
+## 2.6.0
+
+- UI polished
+- Improved network graph + keyboard nav
+- Handle huge commits
+- Last Push widget
+- Bugfix
+- Better performance
+- Email in resque
+- Increased test coverage
+- Ability to remove branch with MR accept
+- a lot of code refactored
+
+## 2.5.0
+
+- UI polished
+- Git blame for file
+- Bugfix
+- Email in resque
+- Better test coverage
+
+## 2.4.0
+
+- Admin area stats page
+- Ability to block user
+- Simplified dashboard area
+- Improved admin area
+- Bootstrap 2.0
+- Responsive layout
+- Big commits handling
+- Performance improved
+- Milestones
+
+## 2.3.1
+
+- Issues pagination
+- ssl fixes
+- Merge Request pagination
+
+## 2.3.0
+
+- Dashboard r1
+- Search r1
+- Project page
+- Close merge request on push
+- Persist MR diff after merge
+- mysql support
+- Documentation
+
+## 2.2.0
+
+- We’ve added support of LDAP auth
+- Improved permission logic (4 roles system)
+- Protected branches (now only masters can push to protected branches)
+- Usability improved
+- twitter bootstrap integrated
+- compare view between commits
+- wiki feature
+- now you can enable/disable issues, wiki, wall features per project
+- security fixes
+- improved code browsing (ajax branch switch etc)
+- improved per-line commenting
+- git submodules displayed
+- moved to rails 3.2
+- help section improved
+
+## 2.1.0
+
+- Project tab r1
+- List branches/tags
+- per line comments
+- mass user import
+
+## 2.0.0
+
+- gitolite as main git host system
+- merge requests
+- project/repo access
+- link to commit/issue feed
+- design tab
+- improved email notifications
+- restyled dashboard
+- bugfix
+
+## 1.2.2
+
+- common config file gitlab.yml
+- issues restyle
+- snippets restyle
+- clickable news feed header on dashboard
+- bugfix
+
+## 1.2.1
+
+- bugfix
+
+## 1.2.0
+
+- new design
+- user dashboard
+- network graph
+- markdown support for comments
+- encoding issues
+- wall like twitter timeline
+
+## 1.1.0
+
+- project dashboard
+- wall redesigned
+- feature: code snippets
+- fixed horizontal scroll on file preview
+- fixed app crash if commit message has invalid chars
+- bugfix & code cleaning
+
+## 1.0.2
+
+- fixed bug with empty project
+- added adv validation for project path & code
+- feature: issues can be sortable
+- bugfix
+- username displayed on top panel
+
+## 1.0.1
+
+- fixed: with invalid source code for commit
+- fixed: lose branch/tag selection when use tree navigation
+- when history clicked - display path
+- bug fix & code cleaning
+
+## 1.0.0
+
+- bug fix
+- projects preview mode
+
+## 0.9.6
+
+- css fix
+- new repo empty tree until restart server - fixed
+
+## 0.9.4
+
+- security improved
+- authorization improved
+- html escaping
+- bug fix
+- increased test coverage
+- design improvements
+
+## 0.9.1
+
+- increased test coverage
+- design improvements
+- new issue email notification
+- updated app name
+- issue redesigned
+- issue can be edit
+
+## 0.8.0
+
+- syntax highlight for main file types
+- redesign
+- stability
+- security fixes
+- increased test coverage
+- email notification
diff --git a/changelogs/unreleased/.gitkeep b/changelogs/unreleased/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/changelogs/unreleased/.gitkeep
diff --git a/config/initializers/gitlab_workhorse_secret.rb b/config/initializers/gitlab_workhorse_secret.rb
new file mode 100644
index 00000000000..ed54dc11098
--- /dev/null
+++ b/config/initializers/gitlab_workhorse_secret.rb
@@ -0,0 +1,8 @@
+begin
+ Gitlab::Workhorse.secret
+rescue
+ Gitlab::Workhorse.write_secret
+end
+
+# Try a second time. If it does not work this will raise.
+Gitlab::Workhorse.secret
diff --git a/config/initializers/metrics.rb b/config/initializers/metrics.rb
index 52522e099e7..be22085b0df 100644
--- a/config/initializers/metrics.rb
+++ b/config/initializers/metrics.rb
@@ -68,7 +68,8 @@ if Gitlab::Metrics.enabled?
['app', 'mailers', 'emails'] => ['app', 'mailers'],
['app', 'services', '**'] => ['app', 'services'],
['lib', 'gitlab', 'diff'] => ['lib'],
- ['lib', 'gitlab', 'email', 'message'] => ['lib']
+ ['lib', 'gitlab', 'email', 'message'] => ['lib'],
+ ['lib', 'gitlab', 'checks'] => ['lib']
}
paths_to_instrument.each do |(path, prefix)|
diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb
index f498732feca..5e3e4c966cb 100644
--- a/config/initializers/mime_types.rb
+++ b/config/initializers/mime_types.rb
@@ -13,9 +13,5 @@ Mime::Type.register "video/mp4", :mp4, [], [:m4v, :mov]
Mime::Type.register "video/webm", :webm
Mime::Type.register "video/ogg", :ogv
-middlewares = Gitlab::Application.config.middleware
-middlewares.swap(ActionDispatch::ParamsParser, ActionDispatch::ParamsParser, {
- Mime::Type.lookup('application/vnd.git-lfs+json') => lambda do |body|
- ActiveSupport::JSON.decode(body)
- end
-})
+Mime::Type.unregister :json
+Mime::Type.register 'application/json', :json, %w(application/vnd.git-lfs+json application/json)
diff --git a/db/migrate/20160725104020_merge_request_diff_remove_uniq.rb b/db/migrate/20160725104020_merge_request_diff_remove_uniq.rb
index c8cbd2718ff..75a3eb15124 100644
--- a/db/migrate/20160725104020_merge_request_diff_remove_uniq.rb
+++ b/db/migrate/20160725104020_merge_request_diff_remove_uniq.rb
@@ -8,14 +8,28 @@ class MergeRequestDiffRemoveUniq < ActiveRecord::Migration
DOWNTIME = false
def up
- if index_exists?(:merge_request_diffs, :merge_request_id)
- remove_index :merge_request_diffs, :merge_request_id
+ constraint_name = 'merge_request_diffs_merge_request_id_key'
+
+ transaction do
+ if index_exists?(:merge_request_diffs, :merge_request_id)
+ remove_index(:merge_request_diffs, :merge_request_id)
+ end
+
+ # In some bizarre cases PostgreSQL might have a separate unique constraint
+ # that we'll need to drop.
+ if constraint_exists?(constraint_name) && Gitlab::Database.postgresql?
+ execute("ALTER TABLE merge_request_diffs DROP CONSTRAINT IF EXISTS #{constraint_name};")
+ end
end
end
def down
unless index_exists?(:merge_request_diffs, :merge_request_id)
- add_concurrent_index :merge_request_diffs, :merge_request_id, unique: true
+ add_concurrent_index(:merge_request_diffs, :merge_request_id, unique: true)
end
end
+
+ def constraint_exists?(name)
+ indexes(:merge_request_diffs).map(&:name).include?(name)
+ end
end
diff --git a/db/migrate/20160913162434_remove_projects_pushes_since_gc.rb b/db/migrate/20160913162434_remove_projects_pushes_since_gc.rb
new file mode 100644
index 00000000000..c5b8c35e961
--- /dev/null
+++ b/db/migrate/20160913162434_remove_projects_pushes_since_gc.rb
@@ -0,0 +1,19 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class RemoveProjectsPushesSinceGc < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = true
+ DOWNTIME_REASON = 'This migration removes an existing column'
+
+ disable_ddl_transaction!
+
+ def up
+ remove_column :projects, :pushes_since_gc
+ end
+
+ def down
+ add_column_with_default! :projects, :pushes_since_gc, :integer, default: 0
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 5c283141084..61873e38113 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20160902122721) do
+ActiveRecord::Schema.define(version: 20160913162434) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -824,7 +824,6 @@ ActiveRecord::Schema.define(version: 20160902122721) do
t.integer "build_timeout", default: 3600, null: false
t.boolean "pending_delete", default: false
t.boolean "public_builds", default: true, null: false
- t.integer "pushes_since_gc", default: 0
t.boolean "last_repository_check_failed"
t.datetime "last_repository_check_at"
t.boolean "container_registry_enabled"
diff --git a/doc/administration/container_registry.md b/doc/administration/container_registry.md
index 28c4c7c86ca..c5611e2a121 100644
--- a/doc/administration/container_registry.md
+++ b/doc/administration/container_registry.md
@@ -406,7 +406,8 @@ To configure the storage driver in Omnibus:
's3' => {
'accesskey' => 's3-access-key',
'secretkey' => 's3-secret-key-for-access-key',
- 'bucket' => 'your-s3-bucket'
+ 'bucket' => 'your-s3-bucket',
+ 'region' => 'your-s3-region'
}
}
```
@@ -428,6 +429,7 @@ storage:
accesskey: 'AKIAKIAKI'
secretkey: 'secret123'
bucket: 'gitlab-registry-bucket-AKIAKIAKI'
+ region: 'your-s3-region'
cache:
blobdescriptor: inmemory
delete:
diff --git a/doc/api/README.md b/doc/api/README.md
index e12070dc1ce..7661e1eea02 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -27,6 +27,7 @@ following locations:
- [Open source license templates](licenses.md)
- [Namespaces](namespaces.md)
- [Notes](notes.md) (comments)
+- [Notification settings](notification_settings.md)
- [Pipelines](pipelines.md)
- [Projects](projects.md) including setting Webhooks
- [Project Access Requests](access_requests.md)
diff --git a/doc/api/ci/builds.md b/doc/api/ci/builds.md
index 2a71b087f19..b6d79706a84 100644
--- a/doc/api/ci/builds.md
+++ b/doc/api/ci/builds.md
@@ -38,6 +38,15 @@ POST /ci/api/v1/builds/register
curl --request POST "https://gitlab.example.com/ci/api/v1/builds/register" --form "token=t0k3n"
```
+**Responses:**
+
+| Status | Data |Description |
+|--------|------|---------------------------------------------------------------------------|
+| `201` | yes | When a build is scheduled for a runner |
+| `204` | no | When no builds are scheduled for a runner (for GitLab Runner >= `v1.3.0`) |
+| `403` | no | When invalid token is used or no token is sent |
+| `404` | no | When no builds are scheduled for a runner (for GitLab Runner < `v1.3.0`) **or** when the runner is set to `paused` in GitLab runner's configuration page |
+
### Update details of an existing build
```
diff --git a/doc/api/notification_settings.md b/doc/api/notification_settings.md
new file mode 100644
index 00000000000..ff6c9e4931c
--- /dev/null
+++ b/doc/api/notification_settings.md
@@ -0,0 +1,169 @@
+# Notification settings
+
+>**Note:** This feature was [introduced][ce-5632] in GitLab 8.12.
+
+**Valid notification levels**
+
+The notification levels are defined in the `NotificationSetting::level` model enumeration. Currently, these levels are recognized:
+
+```
+disabled
+participating
+watch
+global
+mention
+custom
+```
+
+If the `custom` level is used, specific email events can be controlled. Notification email events are defined in the `NotificationSetting::EMAIL_EVENTS` model variable. Currently, these events are recognized:
+
+```
+new_note
+new_issue
+reopen_issue
+close_issue
+reassign_issue
+new_merge_request
+reopen_merge_request
+close_merge_request
+reassign_merge_request
+merge_merge_request
+```
+
+## Global notification settings
+
+Get current notification settings and email address.
+
+```
+GET /notification_settings
+```
+
+```bash
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/notification_settings
+```
+
+Example response:
+
+```json
+{
+ "level": "participating",
+ "notification_email": "admin@example.com"
+}
+```
+
+## Update global notification settings
+
+Update current notification settings and email address.
+
+```
+PUT /notification_settings
+```
+
+```bash
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/notification_settings?level=watch
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `level` | string | no | The global notification level |
+| `notification_email` | string | no | The email address to send notifications |
+| `new_note` | boolean | no | Enable/disable this notification |
+| `new_issue` | boolean | no | Enable/disable this notification |
+| `reopen_issue` | boolean | no | Enable/disable this notification |
+| `close_issue` | boolean | no | Enable/disable this notification |
+| `reassign_issue` | boolean | no | Enable/disable this notification |
+| `new_merge_request` | boolean | no | Enable/disable this notification |
+| `reopen_merge_request` | boolean | no | Enable/disable this notification |
+| `close_merge_request` | boolean | no | Enable/disable this notification |
+| `reassign_merge_request` | boolean | no | Enable/disable this notification |
+| `merge_merge_request` | boolean | no | Enable/disable this notification |
+
+Example response:
+
+```json
+{
+ "level": "watch",
+ "notification_email": "admin@example.com"
+}
+```
+
+## Group / project level notification settings
+
+Get current group or project notification settings.
+
+```
+GET /groups/:id/notification_settings
+GET /projects/:id/notification_settings
+```
+
+```bash
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/5/notification_settings
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/8/notification_settings
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The group/project ID or path |
+
+Example response:
+
+```json
+{
+ "level": "global"
+}
+```
+
+## Update group/project level notification settings
+
+Update current group/project notification settings.
+
+```
+PUT /groups/:id/notification_settings
+PUT /projects/:id/notification_settings
+```
+
+```bash
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/5/notification_settings?level=watch
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/8/notification_settings?level=custom&new_note=true
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The group/project ID or path |
+| `level` | string | no | The global notification level |
+| `new_note` | boolean | no | Enable/disable this notification |
+| `new_issue` | boolean | no | Enable/disable this notification |
+| `reopen_issue` | boolean | no | Enable/disable this notification |
+| `close_issue` | boolean | no | Enable/disable this notification |
+| `reassign_issue` | boolean | no | Enable/disable this notification |
+| `new_merge_request` | boolean | no | Enable/disable this notification |
+| `reopen_merge_request` | boolean | no | Enable/disable this notification |
+| `close_merge_request` | boolean | no | Enable/disable this notification |
+| `reassign_merge_request` | boolean | no | Enable/disable this notification |
+| `merge_merge_request` | boolean | no | Enable/disable this notification |
+
+Example responses:
+
+```json
+{
+ "level": "watch"
+}
+
+{
+ "level": "custom",
+ "events": {
+ "new_note": true,
+ "new_issue": false,
+ "reopen_issue": false,
+ "close_issue": false,
+ "reassign_issue": false,
+ "new_merge_request": false,
+ "reopen_merge_request": false,
+ "close_merge_request": false,
+ "reassign_merge_request": false,
+ "merge_merge_request": false
+ }
+}
+```
+
+[ce-5632]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5632
diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md
index c134106bfd0..406396deaaa 100644
--- a/doc/ci/examples/README.md
+++ b/doc/ci/examples/README.md
@@ -1,17 +1,19 @@
# CI Examples
+A collection of `.gitlab-ci.yml` files is maintained at the [GitLab CI Yml project][gitlab-ci-templates].
+If your favorite programming language or framework are missing we would love your help by sending a merge request
+with a `.gitlab-ci.yml`.
+
+Apart from those, here is an collection of tutorials and guides on setting up your CI pipeline:
+
- [Testing a PHP application](php.md)
- [Test and deploy a Ruby application to Heroku](test-and-deploy-ruby-application-to-heroku.md)
- [Test and deploy a Python application to Heroku](test-and-deploy-python-application-to-heroku.md)
- [Test a Clojure application](test-clojure-application.md)
- [Test a Scala application](test-scala-application.md)
- [Using `dpl` as deployment tool](deployment/README.md)
-- Help your favorite programming language and GitLab by sending a merge request
- with a guide for that language.
-
-## Outside the documentation
-
- [Blog post about using GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/)
- [Repo's with examples for various languages](https://gitlab.com/groups/gitlab-examples)
- [The .gitlab-ci.yml file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml)
-- [A collection of useful .gitlab-ci.yml templates](https://gitlab.com/gitlab-org/gitlab-ci-yml)
+
+[gitlab-ci-templates][https://gitlab.com/gitlab-org/gitlab-ci-yml]
diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md
index c835ebc2d44..c40cdd55ea5 100644
--- a/doc/ci/quick_start/README.md
+++ b/doc/ci/quick_start/README.md
@@ -105,7 +105,8 @@ What is important is that each job is run independently from each other.
If you want to check whether your `.gitlab-ci.yml` file is valid, there is a
Lint tool under the page `/ci/lint` of your GitLab instance. You can also find
-the link under **Settings > CI settings** in your project.
+a "CI Lint" button to go to this page under **Pipelines > Pipelines** and
+**Pipelines > Builds** in your project.
For more information and a complete `.gitlab-ci.yml` syntax, please read
[the documentation on .gitlab-ci.yml](../yaml/README.md).
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index c32831d3aaa..6a971c3ae87 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -34,6 +34,7 @@ The `API_TOKEN` will take the Secure Variable value: `SECURE`.
| **CI_BUILD_REF_NAME** | all | all | The branch or tag name for which project is built |
| **CI_BUILD_REPO** | all | all | The URL to clone the Git repository |
| **CI_BUILD_TRIGGERED** | all | 0.5 | The flag to indicate that build was [triggered] |
+| **CI_BUILD_MANUAL** | 8.12 | all | The flag to indicate that build was manually started |
| **CI_BUILD_TOKEN** | all | 1.2 | Token used for authenticating with the GitLab Container Registry |
| **CI_PIPELINE_ID** | 8.10 | 0.5 | The unique id of the current pipeline that GitLab CI uses internally |
| **CI_PROJECT_ID** | all | all | The unique id of the current project that GitLab CI uses internally |
@@ -47,6 +48,8 @@ The `API_TOKEN` will take the Secure Variable value: `SECURE`.
| **CI_RUNNER_ID** | 8.10 | 0.5 | The unique id of runner being used |
| **CI_RUNNER_DESCRIPTION** | 8.10 | 0.5 | The description of the runner as saved in GitLab |
| **CI_RUNNER_TAGS** | 8.10 | 0.5 | The defined runner tags |
+| **GITLAB_USER_ID** | 8.12 | all | The id of the user who started the build |
+| **GITLAB_USER_EMAIL** | 8.12 | all | The email of the user who started the build |
**Some of the variables are only available when using runner with at least defined version.**
@@ -60,6 +63,7 @@ export CI_BUILD_REPO="https://gitab-ci-token:abcde-1234ABCD5678ef@gitlab.com/git
export CI_BUILD_TAG="1.0.0"
export CI_BUILD_NAME="spec:other"
export CI_BUILD_STAGE="test"
+export CI_BUILD_MANUAL="true"
export CI_BUILD_TRIGGERED="true"
export CI_BUILD_TOKEN="abcde-1234ABCD5678ef"
export CI_PIPELINE_ID="1000"
@@ -78,6 +82,8 @@ export CI_SERVER="yes"
export CI_SERVER_NAME="GitLab"
export CI_SERVER_REVISION="70606bf"
export CI_SERVER_VERSION="8.9.0"
+export GITLAB_USER_ID="42"
+export GITLAB_USER_EMAIL="alexzander@sporer.com"
```
### YAML-defined variables
diff --git a/doc/development/instrumentation.md b/doc/development/instrumentation.md
index c2272ab0a2b..105e2f1242a 100644
--- a/doc/development/instrumentation.md
+++ b/doc/development/instrumentation.md
@@ -137,3 +137,18 @@ end
```
Here the final value of `sleep_real_time` will be `3`, _not_ `1`.
+
+## Tracking Custom Events
+
+Besides instrumenting code GitLab Performance Monitoring also supports tracking
+of custom events. This is primarily intended to be used for tracking business
+metrics such as the number of Git pushes, repository imports, and so on.
+
+To track a custom event simply call `Gitlab::Metrics.add_event` passing it an
+event name and a custom set of (optional) tags. For example:
+
+```ruby
+Gitlab::Metrics.add_event(:user_login, email: current_user.email)
+```
+
+Event names should be verbs such as `push_repository` and `remove_branch`.
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 9522c3e7170..df98655c396 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -400,7 +400,7 @@ If you are not using Linux you may have to run `gmake` instead of
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 v0.7.11
+ sudo -u git -H git checkout v0.8.1
sudo -u git -H make
### Initialize Database and Activate Advanced Features
diff --git a/doc/update/8.11-to-8.12.md b/doc/update/8.11-to-8.12.md
index c8ca42f97bc..8017c36587e 100644
--- a/doc/update/8.11-to-8.12.md
+++ b/doc/update/8.11-to-8.12.md
@@ -70,7 +70,7 @@ sudo -u git -H git checkout 8-12-stable-ee
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch --all --tags
-sudo -u git -H git checkout v3.4.0
+sudo -u git -H git checkout v3.5.0
```
### 6. Update gitlab-workhorse
@@ -82,7 +82,7 @@ GitLab 8.1.
```bash
cd /home/git/gitlab-workhorse
sudo -u git -H git fetch --all
-sudo -u git -H git checkout v0.7.11
+sudo -u git -H git checkout v0.8.1
sudo -u git -H make
```
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 1498cb361c8..f1b75298180 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -63,7 +63,7 @@ The following table depicts the various user permission levels in a project.
| Force push to protected branches [^2] | | | | | |
| Remove protected branches [^2] | | | | | |
-[^1]: If **Allow guest to access builds** is enabled in CI settings
+[^1]: If **Public pipelines** is enabled in **Project Settings > CI/CD Pipelines**
[^2]: Not allowed for Guest, Reporter, Developer, Master, or Owner
## Group
diff --git a/doc/user/project/merge_requests.md b/doc/user/project/merge_requests.md
index f79535d1542..5af9a5d049c 100644
--- a/doc/user/project/merge_requests.md
+++ b/doc/user/project/merge_requests.md
@@ -93,6 +93,9 @@ A merge request contains all the history from a repository, plus the additional
commits added to the branch associated with the merge request. Here's a few
tricks to checkout a merge request locally.
+Please note that you can checkout a merge request locally even if the source
+project is a fork (even a private fork) of the target project.
+
#### Checkout locally by adding a git alias
Add the following alias to your `~/.gitconfig`:
diff --git a/doc/workflow/importing/import_projects_from_github.md b/doc/workflow/importing/import_projects_from_github.md
index 306caabf6e6..370d885d366 100644
--- a/doc/workflow/importing/import_projects_from_github.md
+++ b/doc/workflow/importing/import_projects_from_github.md
@@ -15,6 +15,7 @@ At its current state, GitHub importer can import:
- the wiki pages (introduced in GitLab 8.4)
- the milestones (introduced in GitLab 8.7)
- the labels (introduced in GitLab 8.7)
+- the release note descriptions (introduced in GitLab 8.12)
With GitLab 8.7+, references to pull requests and issues are preserved.
diff --git a/lib/api/api.rb b/lib/api/api.rb
index a08fb056049..74ca4728695 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -51,6 +51,7 @@ module API
mount ::API::Milestones
mount ::API::Namespaces
mount ::API::Notes
+ mount ::API::NotificationSettings
mount ::API::Pipelines
mount ::API::ProjectHooks
mount ::API::ProjectSnippets
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index 5e3c9563703..dfbdd597d29 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -37,7 +37,7 @@ module API
# id (required) - The ID of a project
# sha (required) - The commit hash
# ref (optional) - The ref
- # state (required) - The state of the status. Can be: pending, running, success, error or failure
+ # state (required) - The state of the status. Can be: pending, running, success, failed or canceled
# target_url (optional) - The target URL to associate with this status
# description (optional) - A short description of the status
# name or context (optional) - A string label to differentiate this status from the status of other systems. Default: "default"
@@ -46,7 +46,7 @@ module API
post ':id/statuses/:sha' do
authorize! :create_commit_status, user_project
required_attributes! [:state]
- attrs = attributes_for_keys [:ref, :target_url, :description, :context, :name]
+ attrs = attributes_for_keys [:target_url, :description]
commit = @project.commit(params[:sha])
not_found! 'Commit' unless commit
@@ -58,36 +58,38 @@ module API
# the first found branch on that commit
ref = params[:ref]
- unless ref
- branches = @project.repository.branch_names_contains(commit.sha)
- not_found! 'References for commit' if branches.none?
- ref = branches.first
- end
+ ref ||= @project.repository.branch_names_contains(commit.sha).first
+ not_found! 'References for commit' unless ref
+
+ name = params[:name] || params[:context] || 'default'
pipeline = @project.ensure_pipeline(ref, commit.sha, current_user)
- name = params[:name] || params[:context]
- status = GenericCommitStatus.running_or_pending.find_by(pipeline: pipeline, name: name, ref: params[:ref])
- status ||= GenericCommitStatus.new(project: @project, pipeline: pipeline, user: current_user)
- status.update(attrs)
+ status = GenericCommitStatus.running_or_pending.find_or_initialize_by(
+ project: @project, pipeline: pipeline,
+ user: current_user, name: name, ref: ref)
+ status.attributes = attrs
- case params[:state].to_s
- when 'running'
- status.run
- when 'success'
- status.success
- when 'failed'
- status.drop
- when 'canceled'
- status.cancel
- else
- status.status = params[:state].to_s
- end
+ begin
+ case params[:state].to_s
+ when 'pending'
+ status.enqueue!
+ when 'running'
+ status.enqueue
+ status.run!
+ when 'success'
+ status.success!
+ when 'failed'
+ status.drop!
+ when 'canceled'
+ status.cancel!
+ else
+ render_api_error!('invalid state', 400)
+ end
- if status.save
present status, with: Entities::CommitStatus
- else
- render_validation_error!(status)
+ rescue StateMachines::InvalidTransition => e
+ render_api_error!(e.message, 400)
end
end
end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 3faba79415b..4f736e4ec2b 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -375,7 +375,7 @@ module API
expose :access_level
expose :notification_level do |member, options|
if member.notification_setting
- NotificationSetting.levels[member.notification_setting.level]
+ ::NotificationSetting.levels[member.notification_setting.level]
end
end
end
@@ -386,6 +386,21 @@ module API
class GroupAccess < MemberAccess
end
+ class NotificationSetting < Grape::Entity
+ expose :level
+ expose :events, if: ->(notification_setting, _) { notification_setting.custom? } do
+ ::NotificationSetting::EMAIL_EVENTS.each do |event|
+ expose event
+ end
+ end
+ end
+
+ class GlobalNotificationSetting < NotificationSetting
+ expose :notification_email do |notification_setting, options|
+ notification_setting.user.notification_email
+ end
+ end
+
class ProjectService < Grape::Entity
expose :id, :title, :created_at, :updated_at, :active
expose :push_events, :issues_events, :merge_requests_events
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 6a20ba95a79..150875ed4f0 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -269,6 +269,10 @@ module API
render_api_error!('304 Not Modified', 304)
end
+ def no_content!
+ render_api_error!('204 No Content', 204)
+ end
+
def render_validation_error!(model)
if model.errors.any?
render_api_error!(model.errors.messages || '400 Bad Request', 400)
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 556684187d8..c9689e6f8ef 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -41,7 +41,8 @@ module API
issues = current_user.issues.inc_notes_with_associations
issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
- issues.reorder(issuable_order_by => issuable_sort)
+ issues = issues.reorder(issuable_order_by => issuable_sort)
+
present paginate(issues), with: Entities::Issue, current_user: current_user
end
end
@@ -73,7 +74,11 @@ module API
params[:group_id] = group.id
params[:milestone_title] = params.delete(:milestone)
params[:label_name] = params.delete(:labels)
- params[:sort] = "#{params.delete(:order_by)}_#{params.delete(:sort)}" if params[:order_by] && params[:sort]
+
+ if params[:order_by] || params[:sort]
+ # The Sortable concern takes 'created_desc', not 'created_at_desc' (for example)
+ params[:sort] = "#{issuable_order_by.sub('_at', '')}_#{issuable_sort}"
+ end
issues = IssuesFinder.new(current_user, params).execute
@@ -113,7 +118,8 @@ module API
issues = filter_issues_milestone(issues, params[:milestone])
end
- issues.reorder(issuable_order_by => issuable_sort)
+ issues = issues.reorder(issuable_order_by => issuable_sort)
+
present paginate(issues), with: Entities::Issue, current_user: current_user
end
diff --git a/lib/api/notification_settings.rb b/lib/api/notification_settings.rb
new file mode 100644
index 00000000000..a70a7e71073
--- /dev/null
+++ b/lib/api/notification_settings.rb
@@ -0,0 +1,97 @@
+module API
+ # notification_settings API
+ class NotificationSettings < Grape::API
+ before { authenticate! }
+
+ helpers ::API::Helpers::MembersHelpers
+
+ resource :notification_settings do
+ desc 'Get global notification level settings and email, defaults to Participate' do
+ detail 'This feature was introduced in GitLab 8.12'
+ success Entities::GlobalNotificationSetting
+ end
+ get do
+ notification_setting = current_user.global_notification_setting
+
+ present notification_setting, with: Entities::GlobalNotificationSetting
+ end
+
+ desc 'Update global notification level settings and email, defaults to Participate' do
+ detail 'This feature was introduced in GitLab 8.12'
+ success Entities::GlobalNotificationSetting
+ end
+ params do
+ optional :level, type: String, desc: 'The global notification level'
+ optional :notification_email, type: String, desc: 'The email address to send notifications'
+ NotificationSetting::EMAIL_EVENTS.each do |event|
+ optional event, type: Boolean, desc: 'Enable/disable this notification'
+ end
+ end
+ put do
+ notification_setting = current_user.global_notification_setting
+
+ begin
+ notification_setting.transaction do
+ new_notification_email = params.delete(:notification_email)
+ declared_params = declared(params, include_missing: false).to_h
+
+ current_user.update(notification_email: new_notification_email) if new_notification_email
+ notification_setting.update(declared_params)
+ end
+ rescue ArgumentError => e # catch level enum error
+ render_api_error! e.to_s, 400
+ end
+
+ render_validation_error! current_user
+ render_validation_error! notification_setting
+ present notification_setting, with: Entities::GlobalNotificationSetting
+ end
+ end
+
+ %w[group project].each do |source_type|
+ resource source_type.pluralize do
+ desc "Get #{source_type} level notification level settings, defaults to Global" do
+ detail 'This feature was introduced in GitLab 8.12'
+ success Entities::NotificationSetting
+ end
+ params do
+ requires :id, type: String, desc: 'The group ID or project ID or project NAMESPACE/PROJECT_NAME'
+ end
+ get ":id/notification_settings" do
+ source = find_source(source_type, params[:id])
+
+ notification_setting = current_user.notification_settings_for(source)
+
+ present notification_setting, with: Entities::NotificationSetting
+ end
+
+ desc "Update #{source_type} level notification level settings, defaults to Global" do
+ detail 'This feature was introduced in GitLab 8.12'
+ success Entities::NotificationSetting
+ end
+ params do
+ requires :id, type: String, desc: 'The group ID or project ID or project NAMESPACE/PROJECT_NAME'
+ optional :level, type: String, desc: "The #{source_type} notification level"
+ NotificationSetting::EMAIL_EVENTS.each do |event|
+ optional event, type: Boolean, desc: 'Enable/disable this notification'
+ end
+ end
+ put ":id/notification_settings" do
+ source = find_source(source_type, params.delete(:id))
+ notification_setting = current_user.notification_settings_for(source)
+
+ begin
+ declared_params = declared(params, include_missing: false).to_h
+
+ notification_setting.update(declared_params)
+ rescue ArgumentError => e # catch level enum error
+ render_api_error! e.to_s, 400
+ end
+
+ render_validation_error! notification_setting
+ present notification_setting, with: Entities::NotificationSetting
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 4033f597859..644d836ed0b 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -428,18 +428,9 @@ module API
# Example Request:
# GET /projects/search/:query
get "/search/:query" do
- ids = current_user.authorized_projects.map(&:id)
- visibility_levels = [ Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC ]
- projects = Project.where("(id in (?) OR visibility_level in (?)) AND (name LIKE (?))", ids, visibility_levels, "%#{params[:query]}%")
- sort = params[:sort] == 'desc' ? 'desc' : 'asc'
-
- projects = case params["order_by"]
- when 'id' then projects.order("id #{sort}")
- when 'name' then projects.order("name #{sort}")
- when 'created_at' then projects.order("created_at #{sort}")
- when 'last_activity_at' then projects.order("last_activity_at #{sort}")
- else projects
- end
+ search_service = Search::GlobalService.new(current_user, search: params[:query]).execute
+ projects = search_service.objects('projects', params[:page])
+ projects = projects.reorder(project_order_by => project_sort)
present paginate(projects), with: Entities::Project
end
diff --git a/lib/banzai/filter/wiki_link_filter/rewriter.rb b/lib/banzai/filter/wiki_link_filter/rewriter.rb
index 2e2c8da311e..e7a1ec8457d 100644
--- a/lib/banzai/filter/wiki_link_filter/rewriter.rb
+++ b/lib/banzai/filter/wiki_link_filter/rewriter.rb
@@ -31,6 +31,7 @@ module Banzai
def apply_relative_link_rules!
if @uri.relative? && @uri.path.present?
link = ::File.join(@wiki_base_path, @uri.path)
+ link = "#{link}##{@uri.fragment}" if @uri.fragment
@uri = Addressable::URI.parse(link)
end
end
diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb
index eb4947cdbf1..59f85416ee5 100644
--- a/lib/ci/api/builds.rb
+++ b/lib/ci/api/builds.rb
@@ -27,7 +27,7 @@ module Ci
else
Gitlab::Metrics.add_event(:build_not_found)
- not_found!
+ build_not_found!
end
end
@@ -101,6 +101,7 @@ module Ci
# POST /builds/:id/artifacts/authorize
post ":id/artifacts/authorize" do
require_gitlab_workhorse!
+ Gitlab::Workhorse.verify_api_request!(headers)
not_allowed! unless Gitlab.config.artifacts.enabled
build = Ci::Build.find_by_id(params[:id])
not_found! unless build
@@ -113,7 +114,8 @@ module Ci
end
status 200
- { TempPath: ArtifactUploader.artifacts_upload_path }
+ content_type Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
+ Gitlab::Workhorse.artifact_upload_ok
end
# Upload artifacts to build - Runners only
diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb
index bcabf7a21b2..ba80c89df78 100644
--- a/lib/ci/api/helpers.rb
+++ b/lib/ci/api/helpers.rb
@@ -32,6 +32,14 @@ module Ci
end
end
+ def build_not_found!
+ if headers['User-Agent'].match(/gitlab-ci-multi-runner \d+\.\d+\.\d+(~beta\.\d+\.g[0-9a-f]+)? /)
+ no_content!
+ else
+ not_found!
+ end
+ end
+
def current_runner
@runner ||= Runner.find_by_token(params[:token].to_s)
end
diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb
index 839a4fa30d5..c412249a01e 100644
--- a/lib/gitlab/backend/shell.rb
+++ b/lib/gitlab/backend/shell.rb
@@ -195,7 +195,7 @@ module Gitlab
# Create (if necessary) and link the secret token file
def generate_and_link_secret_token
secret_file = Gitlab.config.gitlab_shell.secret_file
- unless File.exist? secret_file
+ unless File.size?(secret_file)
# Generate a new token of 16 random hexadecimal characters and store it in secret_file.
token = SecureRandom.hex(16)
File.write(secret_file, token)
diff --git a/lib/gitlab/checks/change_access.rb b/lib/gitlab/checks/change_access.rb
index 4b32eb966aa..cb1065223d4 100644
--- a/lib/gitlab/checks/change_access.rb
+++ b/lib/gitlab/checks/change_access.rb
@@ -23,6 +23,7 @@ module Gitlab
protected
def protected_branch_checks
+ return unless @branch_name
return unless project.protected_branch?(@branch_name)
if forced_push? && user_access.cannot_do_action?(:force_push_code_to_protected_branches)
diff --git a/lib/gitlab/git/hook.rb b/lib/gitlab/git/hook.rb
index 9b681e636c7..bd90d24a2ec 100644
--- a/lib/gitlab/git/hook.rb
+++ b/lib/gitlab/git/hook.rb
@@ -17,11 +17,13 @@ module Gitlab
def trigger(gl_id, oldrev, newrev, ref)
return [true, nil] unless exists?
- case name
- when "pre-receive", "post-receive"
- call_receive_hook(gl_id, oldrev, newrev, ref)
- when "update"
- call_update_hook(gl_id, oldrev, newrev, ref)
+ Bundler.with_clean_env do
+ case name
+ when "pre-receive", "post-receive"
+ call_receive_hook(gl_id, oldrev, newrev, ref)
+ when "update"
+ call_update_hook(gl_id, oldrev, newrev, ref)
+ end
end
end
diff --git a/lib/gitlab/github_import/base_formatter.rb b/lib/gitlab/github_import/base_formatter.rb
index d546e102c63..8cacf4f4925 100644
--- a/lib/gitlab/github_import/base_formatter.rb
+++ b/lib/gitlab/github_import/base_formatter.rb
@@ -20,6 +20,11 @@ module Gitlab
find_by("identities.extern_uid = ? AND identities.provider = 'github'", github_id.to_s).
try(:id)
end
+
+ def gitlab_author_id
+ return @gitlab_author_id if defined?(@gitlab_author_id)
+ @gitlab_author_id = gitlab_user_id(raw_data.user.id)
+ end
end
end
end
diff --git a/lib/gitlab/github_import/comment_formatter.rb b/lib/gitlab/github_import/comment_formatter.rb
index 1c7c1a73c77..2bddcde2b7c 100644
--- a/lib/gitlab/github_import/comment_formatter.rb
+++ b/lib/gitlab/github_import/comment_formatter.rb
@@ -21,7 +21,7 @@ module Gitlab
end
def author_id
- gitlab_user_id(raw_data.user.id) || project.creator_id
+ gitlab_author_id || project.creator_id
end
def body
@@ -52,7 +52,11 @@ module Gitlab
end
def note
- formatter.author_line(author) + body
+ if gitlab_author_id
+ body
+ else
+ formatter.author_line(author) + body
+ end
end
def type
diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb
index 4fdc2f46be0..d35ee2a1c65 100644
--- a/lib/gitlab/github_import/importer.rb
+++ b/lib/gitlab/github_import/importer.rb
@@ -24,6 +24,7 @@ module Gitlab
import_issues
import_pull_requests
import_wiki
+ import_releases
handle_errors
true
@@ -133,8 +134,7 @@ module Gitlab
if issue.labels.count > 0
label_ids = issue.labels
- .map { |raw| LabelFormatter.new(project, raw).attributes }
- .map { |attrs| Label.find_by(attrs).try(:id) }
+ .map { |attrs| project.labels.find_by(title: attrs.name).try(:id) }
.compact
issuable.update_attribute(:label_ids, label_ids)
@@ -178,6 +178,18 @@ module Gitlab
errors << { type: :wiki, errors: e.message }
end
end
+
+ def import_releases
+ releases = client.releases(repo, per_page: 100)
+ releases.each do |raw|
+ begin
+ gh_release = ReleaseFormatter.new(project, raw)
+ gh_release.create! if gh_release.valid?
+ rescue => e
+ errors << { type: :release, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message }
+ end
+ end
+ end
end
end
end
diff --git a/lib/gitlab/github_import/issue_formatter.rb b/lib/gitlab/github_import/issue_formatter.rb
index ad4f1d8ae99..77621de9f4c 100644
--- a/lib/gitlab/github_import/issue_formatter.rb
+++ b/lib/gitlab/github_import/issue_formatter.rb
@@ -49,7 +49,7 @@ module Gitlab
end
def author_id
- gitlab_user_id(raw_data.user.id) || project.creator_id
+ gitlab_author_id || project.creator_id
end
def body
@@ -57,7 +57,11 @@ module Gitlab
end
def description
- @formatter.author_line(author) + body
+ if gitlab_author_id
+ body
+ else
+ formatter.author_line(author) + body
+ end
end
def milestone
diff --git a/lib/gitlab/github_import/label_formatter.rb b/lib/gitlab/github_import/label_formatter.rb
index 9f18244e7d7..2cad7fca88e 100644
--- a/lib/gitlab/github_import/label_formatter.rb
+++ b/lib/gitlab/github_import/label_formatter.rb
@@ -13,6 +13,12 @@ module Gitlab
Label
end
+ def create!
+ project.labels.find_or_create_by!(title: title) do |label|
+ label.color = color
+ end
+ end
+
private
def color
diff --git a/lib/gitlab/github_import/pull_request_formatter.rb b/lib/gitlab/github_import/pull_request_formatter.rb
index 87e031b27f8..1408683100f 100644
--- a/lib/gitlab/github_import/pull_request_formatter.rb
+++ b/lib/gitlab/github_import/pull_request_formatter.rb
@@ -77,7 +77,7 @@ module Gitlab
end
def author_id
- gitlab_user_id(raw_data.user.id) || project.creator_id
+ gitlab_author_id || project.creator_id
end
def body
@@ -85,7 +85,11 @@ module Gitlab
end
def description
- formatter.author_line(author) + body
+ if gitlab_author_id
+ body
+ else
+ formatter.author_line(author) + body
+ end
end
def milestone
diff --git a/lib/gitlab/github_import/release_formatter.rb b/lib/gitlab/github_import/release_formatter.rb
new file mode 100644
index 00000000000..73d643b00ad
--- /dev/null
+++ b/lib/gitlab/github_import/release_formatter.rb
@@ -0,0 +1,23 @@
+module Gitlab
+ module GithubImport
+ class ReleaseFormatter < BaseFormatter
+ def attributes
+ {
+ project: project,
+ tag: raw_data.tag_name,
+ description: raw_data.body,
+ created_at: raw_data.created_at,
+ updated_at: raw_data.created_at
+ }
+ end
+
+ def klass
+ Release
+ end
+
+ def valid?
+ !raw_data.draft
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ldap/adapter.rb b/lib/gitlab/ldap/adapter.rb
index 9a5bcfb5c9b..9100719da87 100644
--- a/lib/gitlab/ldap/adapter.rb
+++ b/lib/gitlab/ldap/adapter.rb
@@ -23,31 +23,7 @@ module Gitlab
end
def users(field, value, limit = nil)
- if field.to_sym == :dn
- options = {
- base: value,
- scope: Net::LDAP::SearchScope_BaseObject
- }
- else
- options = {
- base: config.base,
- filter: Net::LDAP::Filter.eq(field, value)
- }
- end
-
- if config.user_filter.present?
- user_filter = Net::LDAP::Filter.construct(config.user_filter)
-
- options[:filter] = if options[:filter]
- Net::LDAP::Filter.join(options[:filter], user_filter)
- else
- user_filter
- end
- end
-
- if limit.present?
- options.merge!(size: limit)
- end
+ options = user_options(field, value, limit)
entries = ldap_search(options).select do |entry|
entry.respond_to? config.uid
@@ -90,6 +66,38 @@ module Gitlab
Rails.logger.warn("LDAP search timed out after #{config.timeout} seconds")
[]
end
+
+ private
+
+ def user_options(field, value, limit)
+ options = { attributes: %W(#{config.uid} cn mail dn) }
+ options[:size] = limit if limit
+
+ if field.to_sym == :dn
+ options[:base] = value
+ options[:scope] = Net::LDAP::SearchScope_BaseObject
+ options[:filter] = user_filter
+ else
+ options[:base] = config.base
+ options[:filter] = user_filter(Net::LDAP::Filter.eq(field, value))
+ end
+
+ options
+ end
+
+ def user_filter(filter = nil)
+ if config.user_filter.present?
+ user_filter = Net::LDAP::Filter.construct(config.user_filter)
+ end
+
+ if user_filter && filter
+ Net::LDAP::Filter.join(filter, user_filter)
+ elsif user_filter
+ user_filter
+ else
+ filter
+ end
+ end
end
end
end
diff --git a/lib/gitlab/popen.rb b/lib/gitlab/popen.rb
index a0fd41161a5..cc74bb29087 100644
--- a/lib/gitlab/popen.rb
+++ b/lib/gitlab/popen.rb
@@ -18,18 +18,18 @@ module Gitlab
FileUtils.mkdir_p(path)
end
- @cmd_output = ""
- @cmd_status = 0
+ cmd_output = ""
+ cmd_status = 0
Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
yield(stdin) if block_given?
stdin.close
- @cmd_output << stdout.read
- @cmd_output << stderr.read
- @cmd_status = wait_thr.value.exitstatus
+ cmd_output << stdout.read
+ cmd_output << stderr.read
+ cmd_status = wait_thr.value.exitstatus
end
- [@cmd_output, @cmd_status]
+ [cmd_output, cmd_status]
end
end
end
diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb
index c6826a09bd2..60aae541d46 100644
--- a/lib/gitlab/workhorse.rb
+++ b/lib/gitlab/workhorse.rb
@@ -1,19 +1,38 @@
require 'base64'
require 'json'
+require 'securerandom'
module Gitlab
class Workhorse
SEND_DATA_HEADER = 'Gitlab-Workhorse-Send-Data'
VERSION_FILE = 'GITLAB_WORKHORSE_VERSION'
+ INTERNAL_API_CONTENT_TYPE = 'application/vnd.gitlab-workhorse+json'
+ INTERNAL_API_REQUEST_HEADER = 'Gitlab-Workhorse-Api-Request'
+
+ # Supposedly the effective key size for HMAC-SHA256 is 256 bits, i.e. 32
+ # bytes https://tools.ietf.org/html/rfc4868#section-2.6
+ SECRET_LENGTH = 32
class << self
def git_http_ok(repository, user)
{
- 'GL_ID' => Gitlab::GlId.gl_id(user),
- 'RepoPath' => repository.path_to_repo,
+ GL_ID: Gitlab::GlId.gl_id(user),
+ RepoPath: repository.path_to_repo,
}
end
+ def lfs_upload_ok(oid, size)
+ {
+ StoreLFSPath: "#{Gitlab.config.lfs.storage_path}/tmp/upload",
+ LfsOid: oid,
+ LfsSize: size,
+ }
+ end
+
+ def artifact_upload_ok
+ { TempPath: ArtifactUploader.artifacts_upload_path }
+ end
+
def send_git_blob(repository, blob)
params = {
'RepoPath' => repository.path_to_repo,
@@ -81,6 +100,35 @@ module Gitlab
path.readable? ? path.read.chomp : 'unknown'
end
+ def secret
+ @secret ||= begin
+ bytes = Base64.strict_decode64(File.read(secret_path).chomp)
+ raise "#{secret_path} does not contain #{SECRET_LENGTH} bytes" if bytes.length != SECRET_LENGTH
+ bytes
+ end
+ end
+
+ def write_secret
+ bytes = SecureRandom.random_bytes(SECRET_LENGTH)
+ File.open(secret_path, 'w:BINARY', 0600) do |f|
+ f.chmod(0600)
+ f.write(Base64.strict_encode64(bytes))
+ end
+ end
+
+ def verify_api_request!(request_headers)
+ JWT.decode(
+ request_headers[INTERNAL_API_REQUEST_HEADER],
+ secret,
+ true,
+ { iss: 'gitlab-workhorse', verify_iss: true, algorithm: 'HS256' },
+ )
+ end
+
+ def secret_path
+ Rails.root.join('.gitlab_workhorse_secret')
+ end
+
protected
def encode(hash)
diff --git a/lib/tasks/haml-lint.rake b/lib/tasks/haml-lint.rake
new file mode 100644
index 00000000000..609dfaa48e3
--- /dev/null
+++ b/lib/tasks/haml-lint.rake
@@ -0,0 +1,5 @@
+unless Rails.env.production?
+ require 'haml_lint/rake_task'
+
+ HamlLint::RakeTask.new
+end
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index ffe0641ddd7..b0f740f48f7 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -181,6 +181,25 @@ describe ProjectsController do
expect(response).to have_http_status(302)
expect(response).to redirect_to(dashboard_projects_path)
end
+
+ context "when the project is forked" do
+ let(:project) { create(:project) }
+ let(:fork_project) { create(:project, forked_from_project: project) }
+ let(:merge_request) do
+ create(:merge_request,
+ source_project: fork_project,
+ target_project: project)
+ end
+
+ it "closes all related merge requests" do
+ project.merge_requests << merge_request
+ sign_in(admin)
+
+ delete :destroy, namespace_id: fork_project.namespace.path, id: fork_project.path
+
+ expect(merge_request.reload.state).to eq('closed')
+ end
+ end
end
describe "POST #toggle_star" do
diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb
index 4e9bfb0c69b..8f27e616c3e 100644
--- a/spec/controllers/sessions_controller_spec.rb
+++ b/spec/controllers/sessions_controller_spec.rb
@@ -136,6 +136,29 @@ describe SessionsController do
post(:create, { user: user_params }, { otp_user_id: user.id })
end
+ context 'remember_me field' do
+ it 'sets a remember_user_token cookie when enabled' do
+ allow(U2fRegistration).to receive(:authenticate).and_return(true)
+ allow(controller).to receive(:find_user).and_return(user)
+ expect(controller).
+ to receive(:remember_me).with(user).and_call_original
+
+ authenticate_2fa_u2f(remember_me: '1', login: user.username, device_response: "{}")
+
+ expect(response.cookies['remember_user_token']).to be_present
+ end
+
+ it 'does nothing when disabled' do
+ allow(U2fRegistration).to receive(:authenticate).and_return(true)
+ allow(controller).to receive(:find_user).and_return(user)
+ expect(controller).not_to receive(:remember_me)
+
+ authenticate_2fa_u2f(remember_me: '0', login: user.username, device_response: "{}")
+
+ expect(response.cookies['remember_user_token']).to be_nil
+ end
+ end
+
it "creates an audit log record" do
allow(U2fRegistration).to receive(:authenticate).and_return(true)
expect { authenticate_2fa_u2f(login: user.username, device_response: "{}") }.to change { SecurityEvent.count }.by(1)
diff --git a/spec/factories/ci/runners.rb b/spec/factories/ci/runners.rb
index 5b645fab32e..45eaebb2576 100644
--- a/spec/factories/ci/runners.rb
+++ b/spec/factories/ci/runners.rb
@@ -30,5 +30,9 @@ FactoryGirl.define do
trait :shared do
is_shared true
end
+
+ trait :inactive do
+ active false
+ end
end
end
diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb
index 2c0a2dd94ca..2b4670be468 100644
--- a/spec/factories/issues.rb
+++ b/spec/factories/issues.rb
@@ -1,4 +1,8 @@
FactoryGirl.define do
+ sequence :issue_created_at do |n|
+ 4.hours.ago + ( 2 * n ).seconds
+ end
+
factory :issue do
title
author
diff --git a/spec/features/profiles/keys_spec.rb b/spec/features/profiles/keys_spec.rb
new file mode 100644
index 00000000000..3b20d38c520
--- /dev/null
+++ b/spec/features/profiles/keys_spec.rb
@@ -0,0 +1,18 @@
+require 'rails_helper'
+
+describe 'Profile > SSH Keys', feature: true do
+ let(:user) { create(:user) }
+
+ before do
+ login_as(user)
+ visit profile_keys_path
+ end
+
+ describe 'User adds an SSH key' do
+ it 'auto-populates the title', js: true do
+ fill_in('Key', with: attributes_for(:key).fetch(:key))
+
+ expect(find_field('Title').value).to eq 'dummy@gitlab.com'
+ end
+ end
+end
diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb
index 1b14945bf0a..d26a0caf036 100644
--- a/spec/features/projects/branches_spec.rb
+++ b/spec/features/projects/branches_spec.rb
@@ -1,32 +1,46 @@
require 'spec_helper'
describe 'Branches', feature: true do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :public) }
let(:repository) { project.repository }
- before do
- login_as :user
- project.team << [@user, :developer]
- end
+ context 'logged in' do
+ before do
+ login_as :user
+ project.team << [@user, :developer]
+ end
- describe 'Initial branches page' do
- it 'shows all the branches' do
- visit namespace_project_branches_path(project.namespace, project)
+ describe 'Initial branches page' do
+ it 'shows all the branches' do
+ visit namespace_project_branches_path(project.namespace, project)
- repository.branches { |branch| expect(page).to have_content("#{branch.name}") }
- expect(page).to have_content("Protected branches can be managed in project settings")
+ repository.branches { |branch| expect(page).to have_content("#{branch.name}") }
+ expect(page).to have_content("Protected branches can be managed in project settings")
+ end
+ end
+
+ describe 'Find branches' do
+ it 'shows filtered branches', js: true do
+ visit namespace_project_branches_path(project.namespace, project)
+
+ fill_in 'branch-search', with: 'fix'
+ find('#branch-search').native.send_keys(:enter)
+
+ expect(page).to have_content('fix')
+ expect(find('.all-branches')).to have_selector('li', count: 1)
+ end
end
end
- describe 'Find branches' do
- it 'shows filtered branches', js: true do
+ context 'logged out' do
+ before do
visit namespace_project_branches_path(project.namespace, project)
+ end
- fill_in 'branch-search', with: 'fix'
- find('#branch-search').native.send_keys(:enter)
-
- expect(page).to have_content('fix')
- expect(find('.all-branches')).to have_selector('li', count: 1)
+ it 'does not show merge request button' do
+ page.within first('.all-branches li') do
+ expect(page).not_to have_content 'Merge Request'
+ end
end
end
end
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index e00d85904d5..2242cb6236a 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -57,7 +57,7 @@ feature 'Project', feature: true do
describe 'removal', js: true do
let(:user) { create(:user) }
- let(:project) { create(:project, namespace: user.namespace) }
+ let(:project) { create(:project, namespace: user.namespace, name: 'project1') }
before do
login_with(user)
@@ -65,8 +65,12 @@ feature 'Project', feature: true do
visit edit_namespace_project_path(project.namespace, project)
end
- it 'removes project' do
+ it 'removes a project' do
expect { remove_with_confirm('Remove project', project.path) }.to change {Project.count}.by(-1)
+ expect(page).to have_content "Project 'project1' will be deleted."
+ expect(Project.all.count).to be_zero
+ expect(project.issues).to be_empty
+ expect(project.merge_requests).to be_empty
end
end
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
index a46e48c76ed..ff6933dc8d9 100644
--- a/spec/features/u2f_spec.rb
+++ b/spec/features/u2f_spec.rb
@@ -156,6 +156,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
describe "when 2FA via OTP is disabled" do
it "allows logging in with the U2F device" do
+ user.update_attribute(:otp_required_for_login, false)
login_with(user)
@u2f_device.respond_to_u2f_authentication
@@ -181,6 +182,19 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
end
end
+ it 'persists remember_me value via hidden field' do
+ login_with(user, remember: true)
+
+ @u2f_device.respond_to_u2f_authentication
+ click_on "Login Via U2F Device"
+ expect(page.body).to match('We heard back from your U2F device')
+
+ within 'div#js-authenticate-u2f' do
+ field = first('input#user_remember_me', visible: false)
+ expect(field.value).to eq '1'
+ end
+ end
+
describe "when a given U2F device has already been registered by another user" do
describe "but not the current user" do
it "does not allow logging in with that particular device" do
diff --git a/spec/finders/pipelines_finder_spec.rb b/spec/finders/pipelines_finder_spec.rb
index 7100266ab55..b0811d134fa 100644
--- a/spec/finders/pipelines_finder_spec.rb
+++ b/spec/finders/pipelines_finder_spec.rb
@@ -43,9 +43,10 @@ describe PipelinesFinder do
let(:params) { { scope: 'running' } }
it 'orders in descending order on ID' do
- create(:ci_pipeline, project: project, ref: 'feature')
+ feature_pipeline = create(:ci_pipeline, project: project, ref: 'feature')
- expect(subject.map(&:id)).to eq [3, 2, 1]
+ expected_ids = [feature_pipeline.id, branch_pipeline.id, tag_pipeline.id].sort.reverse
+ expect(subject.map(&:id)).to eq expected_ids
end
end
end
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index 4b2ca3514f8..c5b5aa8c445 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -6,6 +6,38 @@ describe SearchHelper do
str
end
+ describe 'parsing result' do
+ let(:project) { create(:project) }
+ let(:repository) { project.repository }
+ let(:results) { repository.search_files('feature', 'master') }
+ let(:search_result) { results.first }
+
+ subject { helper.parse_search_result(search_result) }
+
+ it "returns a valid OpenStruct object" do
+ is_expected.to be_an OpenStruct
+ expect(subject.filename).to eq('CHANGELOG')
+ expect(subject.basename).to eq('CHANGELOG')
+ expect(subject.ref).to eq('master')
+ expect(subject.startline).to eq(186)
+ expect(subject.data.lines[2]).to eq(" - Feature: Replace teams with group membership\n")
+ end
+
+ context "when filename has extension" do
+ let(:search_result) { "master:CONTRIBUTE.md:5:- [Contribute to GitLab](#contribute-to-gitlab)\n" }
+
+ it { expect(subject.filename).to eq('CONTRIBUTE.md') }
+ it { expect(subject.basename).to eq('CONTRIBUTE') }
+ end
+
+ context "when file under directory" do
+ let(:search_result) { "master:a/b/c.md:5:a b c\n" }
+
+ it { expect(subject.filename).to eq('a/b/c.md') }
+ it { expect(subject.basename).to eq('a/b/c') }
+ end
+ end
+
describe 'search_autocomplete_source' do
context "with no current user" do
before do
diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js
index c1c12b57b53..019ce3b0702 100644
--- a/spec/javascripts/awards_handler_spec.js
+++ b/spec/javascripts/awards_handler_spec.js
@@ -1,13 +1,7 @@
/*= require awards_handler */
-
-
/*= require jquery */
-
-
/*= require jquery.cookie */
-
-
/*= require ./fixtures/emoji_menu */
(function() {
@@ -33,6 +27,7 @@
return setTimeout(function() {
assertFn();
return done();
+ // Maybe jasmine.clock here?
}, 333);
};
diff --git a/spec/javascripts/behaviors/quick_submit_spec.js b/spec/javascripts/behaviors/quick_submit_spec.js
index 4c52ecd903d..13babb5bfdb 100644
--- a/spec/javascripts/behaviors/quick_submit_spec.js
+++ b/spec/javascripts/behaviors/quick_submit_spec.js
@@ -8,6 +8,7 @@
beforeEach(function() {
fixture.load('behaviors/quick_submit.html');
$('form').submit(function(e) {
+ // Prevent a form submit from moving us off the testing page
return e.preventDefault();
});
return this.spies = {
@@ -38,6 +39,8 @@
expect($('input[type=submit]')).toBeDisabled();
return expect($('button[type=submit]')).toBeDisabled();
});
+ // We cannot stub `navigator.userAgent` for CI's `rake teaspoon` task, so we'll
+ // only run the tests that apply to the current platform
if (navigator.userAgent.match(/Macintosh/)) {
it('responds to Meta+Enter', function() {
$('input.quick-submit-input').trigger(keydownEvent());
diff --git a/spec/javascripts/fixtures/u2f/authenticate.html.haml b/spec/javascripts/fixtures/u2f/authenticate.html.haml
index 859e79a6c9e..779d6429a5f 100644
--- a/spec/javascripts/fixtures/u2f/authenticate.html.haml
+++ b/spec/javascripts/fixtures/u2f/authenticate.html.haml
@@ -1 +1 @@
-= render partial: "u2f/authenticate", locals: { new_user_session_path: "/users/sign_in" }
+= render partial: "u2f/authenticate", locals: { new_user_session_path: "/users/sign_in", params: {}, resource_name: "user" }
diff --git a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
index 82ee1954a59..d5401fbb0d1 100644
--- a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
+++ b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
@@ -7,7 +7,7 @@ describe("ContributorsGraph", function () {
expect(ContributorsGraph.prototype.x_domain).toEqual(20)
})
})
-
+
describe("#set_y_domain", function () {
it("sets the y_domain", function () {
ContributorsGraph.set_y_domain([{commits: 30}])
@@ -89,7 +89,7 @@ describe("ContributorsGraph", function () {
})
describe("ContributorsMasterGraph", function () {
-
+
// TODO: fix or remove
//describe("#process_dates", function () {
//it("gets and parses dates", function () {
@@ -103,7 +103,7 @@ describe("ContributorsMasterGraph", function () {
//expect(graph.get_dates).toHaveBeenCalledWith(data)
//expect(ContributorsGraph.set_dates).toHaveBeenCalledWith("get")
//})
- //})
+ //})
describe("#get_dates", function () {
it("plucks the date field from data collection", function () {
@@ -124,5 +124,5 @@ describe("ContributorsMasterGraph", function () {
})
})
-
+
})
diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js
index dc6231ebb38..33690c7a5f3 100644
--- a/spec/javascripts/issue_spec.js
+++ b/spec/javascripts/issue_spec.js
@@ -1,7 +1,5 @@
/*= require lib/utils/text_utility */
-
-
/*= require issue */
(function() {
diff --git a/spec/javascripts/new_branch_spec.js b/spec/javascripts/new_branch_spec.js
index 25d3f5b6c04..f09596bd36d 100644
--- a/spec/javascripts/new_branch_spec.js
+++ b/spec/javascripts/new_branch_spec.js
@@ -1,7 +1,5 @@
/*= require jquery-ui/autocomplete */
-
-
/*= require new_branch_form */
(function() {
diff --git a/spec/javascripts/project_title_spec.js b/spec/javascripts/project_title_spec.js
index ffe49828492..51eb12b41d4 100644
--- a/spec/javascripts/project_title_spec.js
+++ b/spec/javascripts/project_title_spec.js
@@ -1,22 +1,10 @@
/*= require bootstrap */
-
-
/*= require select2 */
-
-
/*= require lib/utils/type_utility */
-
-
/*= require gl_dropdown */
-
-
/*= require api */
-
-
/*= require project_select */
-
-
/*= require project */
(function() {
diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js
index 38b3b2653ec..c937a4706f7 100644
--- a/spec/javascripts/right_sidebar_spec.js
+++ b/spec/javascripts/right_sidebar_spec.js
@@ -1,10 +1,6 @@
/*= require right_sidebar */
-
-
/*= require jquery */
-
-
/*= require jquery.cookie */
(function() {
diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js
index 324f5152780..00d9fc1302a 100644
--- a/spec/javascripts/search_autocomplete_spec.js
+++ b/spec/javascripts/search_autocomplete_spec.js
@@ -1,19 +1,9 @@
/*= require gl_dropdown */
-
-
/*= require search_autocomplete */
-
-
/*= require jquery */
-
-
/*= require lib/utils/common_utils */
-
-
/*= require lib/utils/type_utility */
-
-
/*= require fuzzaldrin-plus */
(function() {
@@ -43,6 +33,8 @@
groupName = 'Gitlab Org';
+ // Add required attributes to body before starting the test.
+ // section would be dashboard|group|project
addBodyAttributes = function(section) {
var $body;
if (section == null) {
@@ -64,6 +56,7 @@
}
};
+ // Mock `gl` object in window for dashboard specific page. App code will need it.
mockDashboardOptions = function() {
window.gl || (window.gl = {});
return window.gl.dashboardOptions = {
@@ -72,6 +65,7 @@
};
};
+ // Mock `gl` object in window for project specific page. App code will need it.
mockProjectOptions = function() {
window.gl || (window.gl = {});
return window.gl.projectOptions = {
diff --git a/spec/javascripts/shortcuts_issuable_spec.js b/spec/javascripts/shortcuts_issuable_spec.js
index 7b6b55fe545..04ccf246052 100644
--- a/spec/javascripts/shortcuts_issuable_spec.js
+++ b/spec/javascripts/shortcuts_issuable_spec.js
@@ -10,6 +10,7 @@
});
return describe('#replyWithSelectedText', function() {
var stubSelection;
+ // Stub window.getSelection to return the provided String.
stubSelection = function(text) {
return window.getSelection = function() {
return text;
diff --git a/spec/javascripts/spec_helper.js b/spec/javascripts/spec_helper.js
index 7d91ed0f855..8801c297887 100644
--- a/spec/javascripts/spec_helper.js
+++ b/spec/javascripts/spec_helper.js
@@ -1,21 +1,41 @@
-
+// PhantomJS (Teaspoons default driver) doesn't have support for
+// Function.prototype.bind, which has caused confusion. Use this polyfill to
+// avoid the confusion.
/*= require support/bind-poly */
-
+// You can require your own javascript files here. By default this will include
+// everything in application, however you may get better load performance if you
+// require the specific files that are being used in the spec that tests them.
/*= require jquery */
-
-
/*= require jquery.turbolinks */
-
-
/*= require bootstrap */
-
-
/*= require underscore */
-
+// Teaspoon includes some support files, but you can use anything from your own
+// support path too.
+// require support/jasmine-jquery-1.7.0
+// require support/jasmine-jquery-2.0.0
/*= require support/jasmine-jquery-2.1.0 */
+// require support/sinon
+// require support/your-support-file
+// Deferring execution
+// If you're using CommonJS, RequireJS or some other asynchronous library you can
+// defer execution. Call Teaspoon.execute() after everything has been loaded.
+// Simple example of a timeout:
+// Teaspoon.defer = true
+// setTimeout(Teaspoon.execute, 1000)
+// Matching files
+// By default Teaspoon will look for files that match
+// _spec.{js,js.coffee,.coffee}. Add a filename_spec.js file in your spec path
+// and it'll be included in the default suite automatically. If you want to
+// customize suites, check out the configuration in teaspoon_env.rb
+// Manifest
+// If you'd rather require your spec files manually (to control order for
+// instance) you can disable the suite matcher in the configuration and use this
+// file as a manifest.
+// For more information: http://github.com/modeset/teaspoon
+
(function() {
diff --git a/spec/javascripts/u2f/authenticate_spec.js b/spec/javascripts/u2f/authenticate_spec.js
index e008ce956ad..7ce3884f844 100644
--- a/spec/javascripts/u2f/authenticate_spec.js
+++ b/spec/javascripts/u2f/authenticate_spec.js
@@ -1,16 +1,8 @@
/*= require u2f/authenticate */
-
-
/*= require u2f/util */
-
-
/*= require u2f/error */
-
-
/*= require u2f */
-
-
/*= require ./mock_u2f_device */
(function() {
diff --git a/spec/javascripts/u2f/register_spec.js b/spec/javascripts/u2f/register_spec.js
index 21c5266c60e..01d6b7a8961 100644
--- a/spec/javascripts/u2f/register_spec.js
+++ b/spec/javascripts/u2f/register_spec.js
@@ -1,16 +1,8 @@
/*= require u2f/register */
-
-
/*= require u2f/util */
-
-
/*= require u2f/error */
-
-
/*= require u2f */
-
-
/*= require ./mock_u2f_device */
(function() {
diff --git a/spec/javascripts/zen_mode_spec.js b/spec/javascripts/zen_mode_spec.js
index 3d680ec8ea3..0c1266800d7 100644
--- a/spec/javascripts/zen_mode_spec.js
+++ b/spec/javascripts/zen_mode_spec.js
@@ -14,8 +14,10 @@
return true;
}
};
+ // Stub Dropzone.forElement(...).enable()
});
this.zen = new ZenMode();
+ // Set this manually because we can't actually scroll the window
return this.zen.scroll_position = 456;
});
describe('on enter', function() {
@@ -60,7 +62,7 @@
return $('a.js-zen-enter').click();
};
- exitZen = function() {
+ exitZen = function() { // Ohmmmmmmm
return $('a.js-zen-leave').click();
};
diff --git a/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb b/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb
index 51c89ac4889..ac9bde6baf1 100644
--- a/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb
@@ -127,6 +127,13 @@ describe Banzai::Pipeline::WikiPipeline do
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/page.md\"")
end
+
+ it 'rewrites links with anchor' do
+ markdown = '[Link to Header](start-page#title)'
+ output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
+
+ expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/start-page#title\"")
+ end
end
describe "when creating root links" do
diff --git a/spec/lib/gitlab/github_import/comment_formatter_spec.rb b/spec/lib/gitlab/github_import/comment_formatter_spec.rb
index 9ae02a6c45f..c520a9c53ad 100644
--- a/spec/lib/gitlab/github_import/comment_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/comment_formatter_spec.rb
@@ -73,6 +73,12 @@ describe Gitlab::GithubImport::CommentFormatter, lib: true do
gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
expect(comment.attributes.fetch(:author_id)).to eq gl_user.id
end
+
+ it 'returns note without created at tag line' do
+ create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
+
+ expect(comment.attributes.fetch(:note)).to eq("I'm having a problem with this.")
+ end
end
end
end
diff --git a/spec/lib/gitlab/github_import/importer_spec.rb b/spec/lib/gitlab/github_import/importer_spec.rb
index 3fb8de81545..553c849c9b4 100644
--- a/spec/lib/gitlab/github_import/importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer_spec.rb
@@ -13,7 +13,7 @@ describe Gitlab::GithubImport::Importer, lib: true do
let(:target_sha) { create(:commit, project: project, git_commit: RepoHelpers.another_sample_commit).id }
let(:target_branch) { double(ref: 'master', repo: repository, sha: target_sha) }
- let(:label) do
+ let(:label1) do
double(
name: 'Bug',
color: 'ff0000',
@@ -21,6 +21,14 @@ describe Gitlab::GithubImport::Importer, lib: true do
)
end
+ let(:label2) do
+ double(
+ name: nil,
+ color: 'ff0000',
+ url: 'https://api.github.com/repos/octocat/Hello-World/labels/bug'
+ )
+ end
+
let(:milestone) do
double(
number: 1347,
@@ -90,14 +98,39 @@ describe Gitlab::GithubImport::Importer, lib: true do
)
end
+ let(:release1) do
+ double(
+ tag_name: 'v1.0.0',
+ name: 'First release',
+ body: 'Release v1.0.0',
+ draft: false,
+ created_at: created_at,
+ updated_at: updated_at,
+ url: 'https://api.github.com/repos/octocat/Hello-World/releases/1'
+ )
+ end
+
+ let(:release2) do
+ double(
+ tag_name: 'v2.0.0',
+ name: 'Second release',
+ body: nil,
+ draft: false,
+ created_at: created_at,
+ updated_at: updated_at,
+ url: 'https://api.github.com/repos/octocat/Hello-World/releases/2'
+ )
+ end
+
before do
allow(project).to receive(:import_data).and_return(double.as_null_object)
allow_any_instance_of(Octokit::Client).to receive(:rate_limit!).and_raise(Octokit::NotFound)
- allow_any_instance_of(Octokit::Client).to receive(:labels).and_return([label, label])
+ allow_any_instance_of(Octokit::Client).to receive(:labels).and_return([label1, label2])
allow_any_instance_of(Octokit::Client).to receive(:milestones).and_return([milestone, milestone])
allow_any_instance_of(Octokit::Client).to receive(:issues).and_return([issue1, issue2])
allow_any_instance_of(Octokit::Client).to receive(:pull_requests).and_return([pull_request, pull_request])
allow_any_instance_of(Octokit::Client).to receive(:last_response).and_return(double(rels: { next: nil }))
+ allow_any_instance_of(Octokit::Client).to receive(:releases).and_return([release1, release2])
allow_any_instance_of(Gitlab::Shell).to receive(:import_repository).and_raise(Gitlab::Shell::Error)
end
@@ -113,14 +146,15 @@ describe Gitlab::GithubImport::Importer, lib: true do
error = {
message: 'The remote data could not be fully imported.',
errors: [
- { type: :label, url: "https://api.github.com/repos/octocat/Hello-World/labels/bug", errors: "Validation failed: Title has already been taken" },
+ { type: :label, url: "https://api.github.com/repos/octocat/Hello-World/labels/bug", errors: "Validation failed: Title can't be blank, Title is invalid" },
{ type: :milestone, url: "https://api.github.com/repos/octocat/Hello-World/milestones/1", errors: "Validation failed: Title has already been taken" },
{ type: :issue, url: "https://api.github.com/repos/octocat/Hello-World/issues/1347", errors: "Invalid Repository. Use user/repo format." },
{ type: :issue, url: "https://api.github.com/repos/octocat/Hello-World/issues/1348", errors: "Validation failed: Title can't be blank, Title is too short (minimum is 0 characters)" },
{ type: :pull_request, url: "https://api.github.com/repos/octocat/Hello-World/pulls/1347", errors: "Invalid Repository. Use user/repo format." },
{ type: :pull_request, url: "https://api.github.com/repos/octocat/Hello-World/pulls/1347", errors: "Validation failed: Validate branches Cannot Create: This merge request already exists: [\"New feature\"]" },
- { type: :wiki, errors: "Gitlab::Shell::Error" }
- ]
+ { type: :wiki, errors: "Gitlab::Shell::Error" },
+ { type: :release, url: 'https://api.github.com/repos/octocat/Hello-World/releases/2', errors: "Validation failed: Description can't be blank" }
+ ]
}
described_class.new(project).execute
diff --git a/spec/lib/gitlab/github_import/issue_formatter_spec.rb b/spec/lib/gitlab/github_import/issue_formatter_spec.rb
index d60c4111e99..c2f1f6b91a1 100644
--- a/spec/lib/gitlab/github_import/issue_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/issue_formatter_spec.rb
@@ -109,6 +109,12 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
expect(issue.attributes.fetch(:author_id)).to eq gl_user.id
end
+
+ it 'returns description without created at tag line' do
+ create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
+
+ expect(issue.attributes.fetch(:description)).to eq("I'm having a problem with this.")
+ end
end
end
diff --git a/spec/lib/gitlab/github_import/label_formatter_spec.rb b/spec/lib/gitlab/github_import/label_formatter_spec.rb
index 87593e32db0..8098754d735 100644
--- a/spec/lib/gitlab/github_import/label_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/label_formatter_spec.rb
@@ -1,18 +1,34 @@
require 'spec_helper'
describe Gitlab::GithubImport::LabelFormatter, lib: true do
- describe '#attributes' do
- it 'returns formatted attributes' do
- project = create(:project)
- raw = double(name: 'improvements', color: 'e6e6e6')
+ let(:project) { create(:project) }
+ let(:raw) { double(name: 'improvements', color: 'e6e6e6') }
- formatter = described_class.new(project, raw)
+ subject { described_class.new(project, raw) }
- expect(formatter.attributes).to eq({
+ describe '#attributes' do
+ it 'returns formatted attributes' do
+ expect(subject.attributes).to eq({
project: project,
title: 'improvements',
color: '#e6e6e6'
})
end
end
+
+ describe '#create!' do
+ context 'when label does not exist' do
+ it 'creates a new label' do
+ expect { subject.create! }.to change(Label, :count).by(1)
+ end
+ end
+
+ context 'when label exists' do
+ it 'does not create a new label' do
+ project.labels.create(name: raw.name)
+
+ expect { subject.create! }.not_to change(Label, :count)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
index edfc6ad81c6..302f0fc0623 100644
--- a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
@@ -140,6 +140,12 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
expect(pull_request.attributes.fetch(:author_id)).to eq gl_user.id
end
+
+ it 'returns description without created at tag line' do
+ create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
+
+ expect(pull_request.attributes.fetch(:description)).to eq('Please pull these awesome changes')
+ end
end
context 'when it has a milestone' do
diff --git a/spec/lib/gitlab/github_import/release_formatter_spec.rb b/spec/lib/gitlab/github_import/release_formatter_spec.rb
new file mode 100644
index 00000000000..793128c6ab9
--- /dev/null
+++ b/spec/lib/gitlab/github_import/release_formatter_spec.rb
@@ -0,0 +1,54 @@
+require 'spec_helper'
+
+describe Gitlab::GithubImport::ReleaseFormatter, lib: true do
+ let!(:project) { create(:project, namespace: create(:namespace, path: 'octocat')) }
+ let(:octocat) { double(id: 123456, login: 'octocat') }
+ let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
+
+ let(:base_data) do
+ {
+ tag_name: 'v1.0.0',
+ name: 'First release',
+ draft: false,
+ created_at: created_at,
+ published_at: created_at,
+ body: 'Release v1.0.0'
+ }
+ end
+
+ subject(:release) { described_class.new(project, raw_data) }
+
+ describe '#attributes' do
+ let(:raw_data) { double(base_data) }
+
+ it 'returns formatted attributes' do
+ expected = {
+ project: project,
+ tag: 'v1.0.0',
+ description: 'Release v1.0.0',
+ created_at: created_at,
+ updated_at: created_at
+ }
+
+ expect(release.attributes).to eq(expected)
+ end
+ end
+
+ describe '#valid' do
+ context 'when release is not a draft' do
+ let(:raw_data) { double(base_data) }
+
+ it 'returns true' do
+ expect(release.valid?).to eq true
+ end
+ end
+
+ context 'when release is draft' do
+ let(:raw_data) { double(base_data.merge(draft: true)) }
+
+ it 'returns false' do
+ expect(release.valid?).to eq false
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ldap/adapter_spec.rb b/spec/lib/gitlab/ldap/adapter_spec.rb
index 4847b5f3b0e..0600893f4cf 100644
--- a/spec/lib/gitlab/ldap/adapter_spec.rb
+++ b/spec/lib/gitlab/ldap/adapter_spec.rb
@@ -1,12 +1,77 @@
require 'spec_helper'
describe Gitlab::LDAP::Adapter, lib: true do
- let(:adapter) { Gitlab::LDAP::Adapter.new 'ldapmain' }
+ include LdapHelpers
+
+ let(:ldap) { double(:ldap) }
+ let(:adapter) { ldap_adapter('ldapmain', ldap) }
+
+ describe '#users' do
+ before do
+ stub_ldap_config(base: 'dc=example,dc=com')
+ end
+
+ it 'searches with the proper options when searching by uid' do
+ # Requires this expectation style to match the filter
+ expect(adapter).to receive(:ldap_search) do |arg|
+ expect(arg[:filter].to_s).to eq('(uid=johndoe)')
+ expect(arg[:base]).to eq('dc=example,dc=com')
+ expect(arg[:attributes]).to match(%w{uid cn mail dn})
+ end.and_return({})
+
+ adapter.users('uid', 'johndoe')
+ end
+
+ it 'searches with the proper options when searching by dn' do
+ expect(adapter).to receive(:ldap_search).with(
+ base: 'uid=johndoe,ou=users,dc=example,dc=com',
+ scope: Net::LDAP::SearchScope_BaseObject,
+ attributes: %w{uid cn mail dn},
+ filter: nil
+ ).and_return({})
+
+ adapter.users('dn', 'uid=johndoe,ou=users,dc=example,dc=com')
+ end
+
+ it 'searches with the proper options when searching with a limit' do
+ expect(adapter)
+ .to receive(:ldap_search).with(hash_including(size: 100)).and_return({})
+
+ adapter.users('uid', 'johndoe', 100)
+ end
+
+ it 'returns an LDAP::Person if search returns a result' do
+ entry = ldap_user_entry('johndoe')
+ allow(adapter).to receive(:ldap_search).and_return([entry])
+
+ results = adapter.users('uid', 'johndoe')
+
+ expect(results.size).to eq(1)
+ expect(results.first.uid).to eq('johndoe')
+ end
+
+ it 'returns empty array if search entry does not respond to uid' do
+ entry = Net::LDAP::Entry.new
+ entry['dn'] = user_dn('johndoe')
+ allow(adapter).to receive(:ldap_search).and_return([entry])
+
+ results = adapter.users('uid', 'johndoe')
+
+ expect(results).to be_empty
+ end
+
+ it 'uses the right uid attribute when non-default' do
+ stub_ldap_config(uid: 'sAMAccountName')
+ expect(adapter).to receive(:ldap_search).with(
+ hash_including(attributes: %w{sAMAccountName cn mail dn})
+ ).and_return({})
+
+ adapter.users('sAMAccountName', 'johndoe')
+ end
+ end
describe '#dn_matches_filter?' do
- let(:ldap) { double(:ldap) }
subject { adapter.dn_matches_filter?(:dn, :filter) }
- before { allow(adapter).to receive(:ldap).and_return(ldap) }
context "when the search is successful" do
context "and the result is non-empty" do
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index c5c1402e8fc..6c7fa7e7c15 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -4,7 +4,7 @@ describe Gitlab::Workhorse, lib: true do
let(:project) { create(:project) }
let(:subject) { Gitlab::Workhorse }
- describe "#send_git_archive" do
+ describe ".send_git_archive" do
context "when the repository doesn't have an archive file path" do
before do
allow(project.repository).to receive(:archive_metadata).and_return(Hash.new)
@@ -15,4 +15,93 @@ describe Gitlab::Workhorse, lib: true do
end
end
end
+
+ describe ".secret" do
+ subject { described_class.secret }
+
+ before do
+ described_class.instance_variable_set(:@secret, nil)
+ described_class.write_secret
+ end
+
+ it 'returns 32 bytes' do
+ expect(subject).to be_a(String)
+ expect(subject.length).to eq(32)
+ expect(subject.encoding).to eq(Encoding::ASCII_8BIT)
+ end
+
+ it 'accepts a trailing newline' do
+ open(described_class.secret_path, 'a') { |f| f.write "\n" }
+ expect(subject.length).to eq(32)
+ end
+
+ it 'raises an exception if the secret file cannot be read' do
+ File.delete(described_class.secret_path)
+ expect { subject }.to raise_exception(Errno::ENOENT)
+ end
+
+ it 'raises an exception if the secret file contains the wrong number of bytes' do
+ File.truncate(described_class.secret_path, 0)
+ expect { subject }.to raise_exception(RuntimeError)
+ end
+ end
+
+ describe ".write_secret" do
+ let(:secret_path) { described_class.secret_path }
+ before do
+ begin
+ File.delete(secret_path)
+ rescue Errno::ENOENT
+ end
+
+ described_class.write_secret
+ end
+
+ it 'uses mode 0600' do
+ expect(File.stat(secret_path).mode & 0777).to eq(0600)
+ end
+
+ it 'writes base64 data' do
+ bytes = Base64.strict_decode64(File.read(secret_path))
+ expect(bytes).not_to be_empty
+ end
+ end
+
+ describe '#verify_api_request!' do
+ let(:header_key) { described_class::INTERNAL_API_REQUEST_HEADER }
+ let(:payload) { { 'iss' => 'gitlab-workhorse' } }
+
+ it 'accepts a correct header' do
+ headers = { header_key => JWT.encode(payload, described_class.secret, 'HS256') }
+ expect { call_verify(headers) }.not_to raise_error
+ end
+
+ it 'raises an error when the header is not set' do
+ expect { call_verify({}) }.to raise_jwt_error
+ end
+
+ it 'raises an error when the header is not signed' do
+ headers = { header_key => JWT.encode(payload, nil, 'none') }
+ expect { call_verify(headers) }.to raise_jwt_error
+ end
+
+ it 'raises an error when the header is signed with the wrong key' do
+ headers = { header_key => JWT.encode(payload, 'wrongkey', 'HS256') }
+ expect { call_verify(headers) }.to raise_jwt_error
+ end
+
+ it 'raises an error when the issuer is incorrect' do
+ payload['iss'] = 'somebody else'
+ headers = { header_key => JWT.encode(payload, described_class.secret, 'HS256') }
+ expect { call_verify(headers) }.to raise_jwt_error
+ end
+
+ def raise_jwt_error
+ raise_error(JWT::DecodeError)
+ end
+
+ def call_verify(headers)
+ described_class.verify_api_request!(headers)
+ end
+ end
end
diff --git a/spec/models/blob_spec.rb b/spec/models/blob_spec.rb
index cee20234e1f..03d02b4d382 100644
--- a/spec/models/blob_spec.rb
+++ b/spec/models/blob_spec.rb
@@ -1,3 +1,4 @@
+# encoding: utf-8
require 'rails_helper'
describe Blob do
@@ -7,6 +8,25 @@ describe Blob do
end
end
+ describe '#data' do
+ context 'using a binary blob' do
+ it 'returns the data as-is' do
+ data = "\n\xFF\xB9\xC3"
+ blob = described_class.new(double(binary?: true, data: data))
+
+ expect(blob.data).to eq(data)
+ end
+ end
+
+ context 'using a text blob' do
+ it 'converts the data to UTF-8' do
+ blob = described_class.new(double(binary?: false, data: "\n\xFF\xB9\xC3"))
+
+ expect(blob.data).to eq("\n���")
+ end
+ end
+ end
+
describe '#svg?' do
it 'is falsey when not text' do
git_blob = double(text?: false)
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index c45c2635cf4..8eab4281bc7 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -231,6 +231,34 @@ describe Ci::Build, models: true do
it { is_expected.to eq(predefined_variables) }
end
+ context 'when build has user' do
+ let(:user) { create(:user, username: 'starter') }
+ let(:user_variables) do
+ [
+ { key: 'GITLAB_USER_ID', value: user.id.to_s, public: true },
+ { key: 'GITLAB_USER_EMAIL', value: user.email, public: true }
+ ]
+ end
+
+ before do
+ build.update_attributes(user: user)
+ end
+
+ it { user_variables.each { |v| is_expected.to include(v) } }
+ end
+
+ context 'when build started manually' do
+ before do
+ build.update_attributes(when: :manual)
+ end
+
+ let(:manual_variable) do
+ { key: 'CI_BUILD_MANUAL', value: 'true', public: true }
+ end
+
+ it { is_expected.to include(manual_variable) }
+ end
+
context 'when build is for tag' do
let(:tag_variable) do
{ key: 'CI_BUILD_TAG', value: 'master', public: true }
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index fbf945c757c..f1857f846dc 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -373,8 +373,8 @@ describe Ci::Pipeline, models: true do
end
describe '#execute_hooks' do
- let!(:build_a) { create_build('a') }
- let!(:build_b) { create_build('b') }
+ let!(:build_a) { create_build('a', 0) }
+ let!(:build_b) { create_build('b', 1) }
let!(:hook) do
create(:project_hook, project: project, pipeline_events: enabled)
@@ -398,7 +398,7 @@ describe Ci::Pipeline, models: true do
build_b.enqueue
end
- it 'receive a pending event once' do
+ it 'receives a pending event once' do
expect(WebMock).to have_requested_pipeline_hook('pending').once
end
end
@@ -411,7 +411,7 @@ describe Ci::Pipeline, models: true do
build_b.run
end
- it 'receive a running event once' do
+ it 'receives a running event once' do
expect(WebMock).to have_requested_pipeline_hook('running').once
end
end
@@ -422,11 +422,21 @@ describe Ci::Pipeline, models: true do
build_b.success
end
- it 'receive a success event once' do
+ it 'receives a success event once' do
expect(WebMock).to have_requested_pipeline_hook('success').once
end
end
+ context 'when stage one failed' do
+ before do
+ build_a.drop
+ end
+
+ it 'receives a failed event once' do
+ expect(WebMock).to have_requested_pipeline_hook('failed').once
+ end
+ end
+
def have_requested_pipeline_hook(status)
have_requested(:post, hook.url).with do |req|
json_body = JSON.parse(req.body)
@@ -450,8 +460,12 @@ describe Ci::Pipeline, models: true do
end
end
- def create_build(name)
- create(:ci_build, :created, pipeline: pipeline, name: name)
+ def create_build(name, stage_idx)
+ create(:ci_build,
+ :created,
+ pipeline: pipeline,
+ name: name,
+ stage_idx: stage_idx)
end
end
end
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index fcfa3138ce5..6355f636bb9 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -40,7 +40,7 @@ describe CommitStatus, models: true do
it { is_expected.to be_falsey }
end
- %w(running success failed).each do |status|
+ %w[running success failed].each do |status|
context "if commit status is #{status}" do
before { commit_status.status = status }
@@ -48,7 +48,7 @@ describe CommitStatus, models: true do
end
end
- %w(pending canceled).each do |status|
+ %w[pending canceled].each do |status|
context "if commit status is #{status}" do
before { commit_status.status = status }
@@ -60,7 +60,7 @@ describe CommitStatus, models: true do
describe '#active?' do
subject { commit_status.active? }
- %w(pending running).each do |state|
+ %w[pending running].each do |state|
context "if commit_status.status is #{state}" do
before { commit_status.status = state }
@@ -68,7 +68,7 @@ describe CommitStatus, models: true do
end
end
- %w(success failed canceled).each do |state|
+ %w[success failed canceled].each do |state|
context "if commit_status.status is #{state}" do
before { commit_status.status = state }
@@ -80,7 +80,7 @@ describe CommitStatus, models: true do
describe '#complete?' do
subject { commit_status.complete? }
- %w(success failed canceled).each do |state|
+ %w[success failed canceled].each do |state|
context "if commit_status.status is #{state}" do
before { commit_status.status = state }
@@ -88,7 +88,7 @@ describe CommitStatus, models: true do
end
end
- %w(pending running).each do |state|
+ %w[pending running].each do |state|
context "if commit_status.status is #{state}" do
before { commit_status.status = state }
@@ -187,7 +187,7 @@ describe CommitStatus, models: true do
subject { CommitStatus.where(pipeline: pipeline).stages }
it 'returns ordered list of stages' do
- is_expected.to eq(%w(build test deploy))
+ is_expected.to eq(%w[build test deploy])
end
end
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index fef90d9b5cb..0b1634f654a 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -57,7 +57,7 @@ describe Member, models: true do
describe 'Scopes & finders' do
before do
- project = create(:project)
+ project = create(:empty_project)
group = create(:group)
@owner_user = create(:user).tap { |u| group.add_owner(u) }
@owner = group.members.find_by(user_id: @owner_user.id)
@@ -65,6 +65,15 @@ describe Member, models: true do
@master_user = create(:user).tap { |u| project.team << [u, :master] }
@master = project.members.find_by(user_id: @master_user.id)
+ @blocked_user = create(:user).tap do |u|
+ project.team << [u, :master]
+ project.team << [u, :developer]
+
+ u.block!
+ end
+ @blocked_master = project.members.find_by(user_id: @blocked_user.id, access_level: Gitlab::Access::MASTER)
+ @blocked_developer = project.members.find_by(user_id: @blocked_user.id, access_level: Gitlab::Access::DEVELOPER)
+
Member.add_user(
project.members,
'toto1@example.com',
@@ -73,7 +82,7 @@ describe Member, models: true do
)
@invited_member = project.members.invite.find_by_invite_email('toto1@example.com')
- accepted_invite_user = build(:user)
+ accepted_invite_user = build(:user, state: :active)
Member.add_user(
project.members,
'toto2@example.com',
@@ -91,7 +100,7 @@ describe Member, models: true do
describe '.access_for_user_ids' do
it 'returns the right access levels' do
- users = [@owner_user.id, @master_user.id]
+ users = [@owner_user.id, @master_user.id, @blocked_user.id]
expected = {
@owner_user.id => Gitlab::Access::OWNER,
@master_user.id => Gitlab::Access::MASTER
@@ -125,6 +134,19 @@ describe Member, models: true do
it { expect(described_class.request).not_to include @accepted_request_member }
end
+ describe '.developers' do
+ subject { described_class.developers.to_a }
+
+ it { is_expected.not_to include @owner }
+ it { is_expected.not_to include @master }
+ it { is_expected.to include @invited_member }
+ it { is_expected.to include @accepted_invite_member }
+ it { is_expected.not_to include @requested_member }
+ it { is_expected.to include @accepted_request_member }
+ it { is_expected.not_to include @blocked_master }
+ it { is_expected.not_to include @blocked_developer }
+ end
+
describe '.owners_and_masters' do
it { expect(described_class.owners_and_masters).to include @owner }
it { expect(described_class.owners_and_masters).to include @master }
@@ -132,6 +154,20 @@ describe Member, models: true do
it { expect(described_class.owners_and_masters).not_to include @accepted_invite_member }
it { expect(described_class.owners_and_masters).not_to include @requested_member }
it { expect(described_class.owners_and_masters).not_to include @accepted_request_member }
+ it { expect(described_class.owners_and_masters).not_to include @blocked_master }
+ end
+
+ describe '.has_access' do
+ subject { described_class.has_access.to_a }
+
+ it { is_expected.to include @owner }
+ it { is_expected.to include @master }
+ it { is_expected.to include @invited_member }
+ it { is_expected.to include @accepted_invite_member }
+ it { is_expected.not_to include @requested_member }
+ it { is_expected.to include @accepted_request_member }
+ it { is_expected.not_to include @blocked_master }
+ it { is_expected.not_to include @blocked_developer }
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 5bf3b8e609e..3b815ded2d3 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -1038,4 +1038,81 @@ describe MergeRequest, models: true do
end
end
end
+
+ describe '#closed_without_source_project?' do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:fork_project) { create(:project, forked_from_project: project, namespace: user.namespace) }
+ let(:destroy_service) { Projects::DestroyService.new(fork_project, user) }
+
+ context 'when the merge request is closed' do
+ let(:closed_merge_request) do
+ create(:closed_merge_request,
+ source_project: fork_project,
+ target_project: project)
+ end
+
+ it 'returns false if the source project exists' do
+ expect(closed_merge_request.closed_without_source_project?).to be_falsey
+ end
+
+ it 'returns true if the source project does not exist' do
+ destroy_service.execute
+ closed_merge_request.reload
+
+ expect(closed_merge_request.closed_without_source_project?).to be_truthy
+ end
+ end
+
+ context 'when the merge request is open' do
+ it 'returns false' do
+ expect(subject.closed_without_source_project?).to be_falsey
+ end
+ end
+ end
+
+ describe '#reopenable?' do
+ context 'when the merge request is closed' do
+ it 'returns true' do
+ subject.close
+
+ expect(subject.reopenable?).to be_truthy
+ end
+
+ context 'forked project' do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:fork_project) { create(:project, forked_from_project: project, namespace: user.namespace) }
+ let(:merge_request) do
+ create(:closed_merge_request,
+ source_project: fork_project,
+ target_project: project)
+ end
+
+ it 'returns false if unforked' do
+ Projects::UnlinkForkService.new(fork_project, user).execute
+
+ expect(merge_request.reload.reopenable?).to be_falsey
+ end
+
+ it 'returns false if the source project is deleted' do
+ Projects::DestroyService.new(fork_project, user).execute
+
+ expect(merge_request.reload.reopenable?).to be_falsey
+ end
+
+ it 'returns false if the merge request is merged' do
+ merge_request.update_attributes(state: 'merged')
+
+ expect(merge_request.reload.reopenable?).to be_falsey
+ end
+ end
+ end
+
+ context 'when the merge request is opened' do
+ it 'returns false' do
+ expect(subject.reopenable?).to be_falsey
+ end
+ end
+ end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 4a41fafb84d..f6e811828fc 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -6,6 +6,7 @@ describe Project, models: true do
it { is_expected.to belong_to(:namespace) }
it { is_expected.to belong_to(:creator).class_name('User') }
it { is_expected.to have_many(:users) }
+ it { is_expected.to have_many(:services) }
it { is_expected.to have_many(:events).dependent(:destroy) }
it { is_expected.to have_many(:merge_requests).dependent(:destroy) }
it { is_expected.to have_many(:issues).dependent(:destroy) }
@@ -24,6 +25,30 @@ describe Project, models: true do
it { is_expected.to have_one(:pushover_service).dependent(:destroy) }
it { is_expected.to have_one(:asana_service).dependent(:destroy) }
it { is_expected.to have_one(:board).dependent(:destroy) }
+ it { is_expected.to have_one(:campfire_service).dependent(:destroy) }
+ it { is_expected.to have_one(:drone_ci_service).dependent(:destroy) }
+ it { is_expected.to have_one(:emails_on_push_service).dependent(:destroy) }
+ it { is_expected.to have_one(:builds_email_service).dependent(:destroy) }
+ it { is_expected.to have_one(:emails_on_push_service).dependent(:destroy) }
+ it { is_expected.to have_one(:irker_service).dependent(:destroy) }
+ it { is_expected.to have_one(:pivotaltracker_service).dependent(:destroy) }
+ it { is_expected.to have_one(:hipchat_service).dependent(:destroy) }
+ it { is_expected.to have_one(:flowdock_service).dependent(:destroy) }
+ it { is_expected.to have_one(:assembla_service).dependent(:destroy) }
+ it { is_expected.to have_one(:gemnasium_service).dependent(:destroy) }
+ it { is_expected.to have_one(:buildkite_service).dependent(:destroy) }
+ it { is_expected.to have_one(:bamboo_service).dependent(:destroy) }
+ it { is_expected.to have_one(:teamcity_service).dependent(:destroy) }
+ it { is_expected.to have_one(:jira_service).dependent(:destroy) }
+ it { is_expected.to have_one(:redmine_service).dependent(:destroy) }
+ it { is_expected.to have_one(:custom_issue_tracker_service).dependent(:destroy) }
+ it { is_expected.to have_one(:bugzilla_service).dependent(:destroy) }
+ it { is_expected.to have_one(:gitlab_issue_tracker_service).dependent(:destroy) }
+ it { is_expected.to have_one(:external_wiki_service).dependent(:destroy) }
+ it { is_expected.to have_one(:project_feature).dependent(:destroy) }
+ it { is_expected.to have_one(:import_data).class_name('ProjectImportData').dependent(:destroy) }
+ it { is_expected.to have_one(:last_event).class_name('Event') }
+ it { is_expected.to have_one(:forked_from_project).through(:forked_project_link) }
it { is_expected.to have_many(:commit_statuses) }
it { is_expected.to have_many(:pipelines) }
it { is_expected.to have_many(:builds) }
@@ -31,9 +56,16 @@ describe Project, models: true do
it { is_expected.to have_many(:runners) }
it { is_expected.to have_many(:variables) }
it { is_expected.to have_many(:triggers) }
+ it { is_expected.to have_many(:labels).dependent(:destroy) }
+ it { is_expected.to have_many(:users_star_projects).dependent(:destroy) }
it { is_expected.to have_many(:environments).dependent(:destroy) }
it { is_expected.to have_many(:deployments).dependent(:destroy) }
it { is_expected.to have_many(:todos).dependent(:destroy) }
+ it { is_expected.to have_many(:releases).dependent(:destroy) }
+ it { is_expected.to have_many(:lfs_objects_projects).dependent(:destroy) }
+ it { is_expected.to have_many(:project_group_links).dependent(:destroy) }
+ it { is_expected.to have_many(:notification_settings).dependent(:destroy) }
+ it { is_expected.to have_many(:forks).through(:forked_project_links) }
describe '#members & #requesters' do
let(:project) { create(:project) }
@@ -178,7 +210,7 @@ describe Project, models: true do
expect(project.runners_token).not_to eq('')
end
- it 'does not set an random toke if one provided' do
+ it 'does not set an random token if one provided' do
project = FactoryGirl.create :empty_project, runners_token: 'my-token'
expect(project.runners_token).to eq('my-token')
end
@@ -1497,4 +1529,56 @@ describe Project, models: true do
project.change_head(project.default_branch)
end
end
+
+ describe '#pushes_since_gc' do
+ let(:project) { create(:project) }
+
+ after do
+ project.reset_pushes_since_gc
+ end
+
+ context 'without any pushes' do
+ it 'returns 0' do
+ expect(project.pushes_since_gc).to eq(0)
+ end
+ end
+
+ context 'with a number of pushes' do
+ it 'returns the number of pushes' do
+ 3.times { project.increment_pushes_since_gc }
+
+ expect(project.pushes_since_gc).to eq(3)
+ end
+ end
+ end
+
+ describe '#increment_pushes_since_gc' do
+ let(:project) { create(:project) }
+
+ after do
+ project.reset_pushes_since_gc
+ end
+
+ it 'increments the number of pushes since the last GC' do
+ 3.times { project.increment_pushes_since_gc }
+
+ expect(project.pushes_since_gc).to eq(3)
+ end
+ end
+
+ describe '#reset_pushes_since_gc' do
+ let(:project) { create(:project) }
+
+ after do
+ project.reset_pushes_since_gc
+ end
+
+ it 'resets the number of pushes since the last GC' do
+ 3.times { project.increment_pushes_since_gc }
+
+ project.reset_pushes_since_gc
+
+ expect(project.pushes_since_gc).to eq(0)
+ end
+ end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 7624050878e..94681004c96 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -186,32 +186,6 @@ describe Repository, models: true do
it { is_expected.to be_an String }
it { expect(subject.lines[2]).to eq("master:CHANGELOG:188: - Feature: Replace teams with group membership\n") }
end
-
- describe 'parsing result' do
- subject { repository.parse_search_result(search_result) }
- let(:search_result) { results.first }
-
- it { is_expected.to be_an OpenStruct }
- it { expect(subject.filename).to eq('CHANGELOG') }
- it { expect(subject.basename).to eq('CHANGELOG') }
- it { expect(subject.ref).to eq('master') }
- it { expect(subject.startline).to eq(186) }
- it { expect(subject.data.lines[2]).to eq(" - Feature: Replace teams with group membership\n") }
-
- context "when filename has extension" do
- let(:search_result) { "master:CONTRIBUTE.md:5:- [Contribute to GitLab](#contribute-to-gitlab)\n" }
-
- it { expect(subject.filename).to eq('CONTRIBUTE.md') }
- it { expect(subject.basename).to eq('CONTRIBUTE') }
- end
-
- context "when file under directory" do
- let(:search_result) { "master:a/b/c.md:5:a b c\n" }
-
- it { expect(subject.filename).to eq('a/b/c.md') }
- it { expect(subject.basename).to eq('a/b/c') }
- end
- end
end
describe "#changelog" do
diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb
index 2d6093fec7a..7aa7e85a9e2 100644
--- a/spec/requests/api/commit_statuses_spec.rb
+++ b/spec/requests/api/commit_statuses_spec.rb
@@ -117,17 +117,36 @@ describe API::CommitStatuses, api: true do
let(:post_url) { "/projects/#{project.id}/statuses/#{sha}" }
context 'developer user' do
- context 'only required parameters' do
- before { post api(post_url, developer), state: 'success' }
+ %w[pending running success failed canceled].each do |status|
+ context "for #{status}" do
+ context 'uses only required parameters' do
+ it 'creates commit status' do
+ post api(post_url, developer), state: status
+
+ expect(response).to have_http_status(201)
+ expect(json_response['sha']).to eq(commit.id)
+ expect(json_response['status']).to eq(status)
+ expect(json_response['name']).to eq('default')
+ expect(json_response['ref']).not_to be_empty
+ expect(json_response['target_url']).to be_nil
+ expect(json_response['description']).to be_nil
+ end
+ end
+ end
+ end
- it 'creates commit status' do
- expect(response).to have_http_status(201)
- expect(json_response['sha']).to eq(commit.id)
- expect(json_response['status']).to eq('success')
- expect(json_response['name']).to eq('default')
- expect(json_response['ref']).to be_nil
- expect(json_response['target_url']).to be_nil
- expect(json_response['description']).to be_nil
+ context 'transitions status from pending' do
+ before do
+ post api(post_url, developer), state: 'pending'
+ end
+
+ %w[running success failed canceled].each do |status|
+ it "to #{status}" do
+ expect { post api(post_url, developer), state: status }.not_to change { CommitStatus.count }
+
+ expect(response).to have_http_status(201)
+ expect(json_response['status']).to eq(status)
+ end
end
end
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 47344a13b5e..f840778ae9b 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -17,21 +17,27 @@ describe API::API, api: true do
assignee: user,
project: project,
state: :closed,
- milestone: milestone
+ milestone: milestone,
+ created_at: generate(:issue_created_at),
+ updated_at: 3.hours.ago
end
let!(:confidential_issue) do
create :issue,
:confidential,
project: project,
author: author,
- assignee: assignee
+ assignee: assignee,
+ created_at: generate(:issue_created_at),
+ updated_at: 2.hours.ago
end
let!(:issue) do
create :issue,
author: user,
assignee: user,
project: project,
- milestone: milestone
+ milestone: milestone,
+ created_at: generate(:issue_created_at),
+ updated_at: 1.hour.ago
end
let!(:label) do
create(:label, title: 'label', color: '#FFAABB', project: project)
@@ -135,6 +141,42 @@ describe API::API, api: true do
expect(json_response).to be_an Array
expect(json_response.length).to eq(0)
end
+
+ it 'sorts by created_at descending by default' do
+ get api('/issues', user)
+ response_dates = json_response.map { |issue| issue['created_at'] }
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_dates).to eq(response_dates.sort.reverse)
+ end
+
+ it 'sorts ascending when requested' do
+ get api('/issues?sort=asc', user)
+ response_dates = json_response.map { |issue| issue['created_at'] }
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_dates).to eq(response_dates.sort)
+ end
+
+ it 'sorts by updated_at descending when requested' do
+ get api('/issues?order_by=updated_at', user)
+ response_dates = json_response.map { |issue| issue['updated_at'] }
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_dates).to eq(response_dates.sort.reverse)
+ end
+
+ it 'sorts by updated_at ascending when requested' do
+ get api('/issues?order_by=updated_at&sort=asc', user)
+ response_dates = json_response.map { |issue| issue['updated_at'] }
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_dates).to eq(response_dates.sort)
+ end
end
end
@@ -147,21 +189,24 @@ describe API::API, api: true do
assignee: user,
project: group_project,
state: :closed,
- milestone: group_milestone
+ milestone: group_milestone,
+ updated_at: 3.hours.ago
end
let!(:group_confidential_issue) do
create :issue,
:confidential,
project: group_project,
author: author,
- assignee: assignee
+ assignee: assignee,
+ updated_at: 2.hours.ago
end
let!(:group_issue) do
create :issue,
author: user,
assignee: user,
project: group_project,
- milestone: group_milestone
+ milestone: group_milestone,
+ updated_at: 1.hour.ago
end
let!(:group_label) do
create(:label, title: 'group_lbl', color: '#FFAABB', project: group_project)
@@ -278,6 +323,42 @@ describe API::API, api: true do
expect(json_response.length).to eq(1)
expect(json_response.first['id']).to eq(group_closed_issue.id)
end
+
+ it 'sorts by created_at descending by default' do
+ get api(base_url, user)
+ response_dates = json_response.map { |issue| issue['created_at'] }
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_dates).to eq(response_dates.sort.reverse)
+ end
+
+ it 'sorts ascending when requested' do
+ get api("#{base_url}?sort=asc", user)
+ response_dates = json_response.map { |issue| issue['created_at'] }
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_dates).to eq(response_dates.sort)
+ end
+
+ it 'sorts by updated_at descending when requested' do
+ get api("#{base_url}?order_by=updated_at", user)
+ response_dates = json_response.map { |issue| issue['updated_at'] }
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_dates).to eq(response_dates.sort.reverse)
+ end
+
+ it 'sorts by updated_at ascending when requested' do
+ get api("#{base_url}?order_by=updated_at&sort=asc", user)
+ response_dates = json_response.map { |issue| issue['updated_at'] }
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_dates).to eq(response_dates.sort)
+ end
end
describe "GET /projects/:id/issues" do
@@ -386,6 +467,42 @@ describe API::API, api: true do
expect(json_response.length).to eq(1)
expect(json_response.first['id']).to eq(closed_issue.id)
end
+
+ it 'sorts by created_at descending by default' do
+ get api("#{base_url}/issues", user)
+ response_dates = json_response.map { |issue| issue['created_at'] }
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_dates).to eq(response_dates.sort.reverse)
+ end
+
+ it 'sorts ascending when requested' do
+ get api("#{base_url}/issues?sort=asc", user)
+ response_dates = json_response.map { |issue| issue['created_at'] }
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_dates).to eq(response_dates.sort)
+ end
+
+ it 'sorts by updated_at descending when requested' do
+ get api("#{base_url}/issues?order_by=updated_at", user)
+ response_dates = json_response.map { |issue| issue['updated_at'] }
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_dates).to eq(response_dates.sort.reverse)
+ end
+
+ it 'sorts by updated_at ascending when requested' do
+ get api("#{base_url}/issues?order_by=updated_at&sort=asc", user)
+ response_dates = json_response.map { |issue| issue['updated_at'] }
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_dates).to eq(response_dates.sort)
+ end
end
describe "GET /projects/:id/issues/:issue_id" do
diff --git a/spec/requests/api/notification_settings_spec.rb b/spec/requests/api/notification_settings_spec.rb
new file mode 100644
index 00000000000..e6d8a5ee954
--- /dev/null
+++ b/spec/requests/api/notification_settings_spec.rb
@@ -0,0 +1,89 @@
+require 'spec_helper'
+
+describe API::API, api: true do
+ include ApiHelpers
+
+ let(:user) { create(:user) }
+ let!(:group) { create(:group) }
+ let!(:project) { create(:project, :public, creator_id: user.id, namespace: group) }
+
+ describe "GET /notification_settings" do
+ it "returns global notification settings for the current user" do
+ get api("/notification_settings", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_a Hash
+ expect(json_response['notification_email']).to eq(user.notification_email)
+ expect(json_response['level']).to eq(user.global_notification_setting.level)
+ end
+ end
+
+ describe "PUT /notification_settings" do
+ let(:email) { create(:email, user: user) }
+
+ it "updates global notification settings for the current user" do
+ put api("/notification_settings", user), { level: 'watch', notification_email: email.email }
+
+ expect(response).to have_http_status(200)
+ expect(json_response['notification_email']).to eq(email.email)
+ expect(user.reload.notification_email).to eq(email.email)
+ expect(json_response['level']).to eq(user.reload.global_notification_setting.level)
+ end
+ end
+
+ describe "PUT /notification_settings" do
+ it "fails on non-user email address" do
+ put api("/notification_settings", user), { notification_email: 'invalid@example.com' }
+
+ expect(response).to have_http_status(400)
+ end
+ end
+
+ describe "GET /groups/:id/notification_settings" do
+ it "returns group level notification settings for the current user" do
+ get api("/groups/#{group.id}/notification_settings", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_a Hash
+ expect(json_response['level']).to eq(user.notification_settings_for(group).level)
+ end
+ end
+
+ describe "PUT /groups/:id/notification_settings" do
+ it "updates group level notification settings for the current user" do
+ put api("/groups/#{group.id}/notification_settings", user), { level: 'watch' }
+
+ expect(response).to have_http_status(200)
+ expect(json_response['level']).to eq(user.reload.notification_settings_for(group).level)
+ end
+ end
+
+ describe "GET /projects/:id/notification_settings" do
+ it "returns project level notification settings for the current user" do
+ get api("/projects/#{project.id}/notification_settings", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_a Hash
+ expect(json_response['level']).to eq(user.notification_settings_for(project).level)
+ end
+ end
+
+ describe "PUT /projects/:id/notification_settings" do
+ it "updates project level notification settings for the current user" do
+ put api("/projects/#{project.id}/notification_settings", user), { level: 'custom', new_note: true }
+
+ expect(response).to have_http_status(200)
+ expect(json_response['level']).to eq(user.reload.notification_settings_for(project).level)
+ expect(json_response['events']['new_note']).to eq(true)
+ expect(json_response['events']['new_issue']).to eq(false)
+ end
+ end
+
+ describe "PUT /projects/:id/notification_settings" do
+ it "fails on invalid level" do
+ put api("/projects/#{project.id}/notification_settings", user), { level: 'invalid' }
+
+ expect(response).to have_http_status(400)
+ end
+ end
+end
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index ca7932dc5da..780bd7f2859 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -15,6 +15,25 @@ describe Ci::API::API do
describe "POST /builds/register" do
let!(:build) { create(:ci_build, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
+ let(:user_agent) { 'gitlab-ci-multi-runner 1.5.2 (1-5-stable; go1.6.3; linux/amd64)' }
+
+ shared_examples 'no builds available' do
+ context 'when runner sends version in User-Agent' do
+ context 'for stable version' do
+ it { expect(response).to have_http_status(204) }
+ end
+
+ context 'for beta version' do
+ let(:user_agent) { 'gitlab-ci-multi-runner 1.6.0~beta.167.g2b2bacc (1-5-stable; go1.6.3; linux/amd64)' }
+ it { expect(response).to have_http_status(204) }
+ end
+ end
+
+ context "when runner doesn't send version in User-Agent" do
+ let(:user_agent) { 'Go-http-client/1.1' }
+ it { expect(response).to have_http_status(404) }
+ end
+ end
it "starts a build" do
register_builds info: { platform: :darwin }
@@ -33,36 +52,30 @@ describe Ci::API::API do
context 'when builds are finished' do
before do
build.success
- end
-
- it "returns 404 error if no builds for specific runner" do
register_builds
-
- expect(response).to have_http_status(404)
end
+
+ it_behaves_like 'no builds available'
end
context 'for other project with builds' do
before do
build.success
create(:ci_build, :pending)
- end
-
- it "returns 404 error if no builds for shared runner" do
register_builds
-
- expect(response).to have_http_status(404)
end
+
+ it_behaves_like 'no builds available'
end
context 'for shared runner' do
let(:shared_runner) { create(:ci_runner, token: "SharedRunner") }
- it "should return 404 error if no builds for shared runner" do
+ before do
register_builds shared_runner.token
-
- expect(response).to have_http_status(404)
end
+
+ it_behaves_like 'no builds available'
end
context 'for triggered build' do
@@ -136,18 +149,27 @@ describe Ci::API::API do
end
context 'when runner is not allowed to pick untagged builds' do
- before { runner.update_column(:run_untagged, false) }
-
- it 'does not pick build' do
+ before do
+ runner.update_column(:run_untagged, false)
register_builds
-
- expect(response).to have_http_status 404
end
+
+ it_behaves_like 'no builds available'
+ end
+ end
+
+ context 'when runner is paused' do
+ let(:inactive_runner) { create(:ci_runner, :inactive, token: "InactiveRunner") }
+
+ before do
+ register_builds inactive_runner.token
end
+
+ it { expect(response).to have_http_status 404 }
end
def register_builds(token = runner.token, **params)
- post ci_api("/builds/register"), params.merge(token: token)
+ post ci_api("/builds/register"), params.merge(token: token), { 'User-Agent' => user_agent }
end
end
@@ -230,7 +252,8 @@ describe Ci::API::API do
let(:post_url) { ci_api("/builds/#{build.id}/artifacts") }
let(:delete_url) { ci_api("/builds/#{build.id}/artifacts") }
let(:get_url) { ci_api("/builds/#{build.id}/artifacts") }
- let(:headers) { { "GitLab-Workhorse" => "1.0" } }
+ let(:jwt_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
+ let(:headers) { { "GitLab-Workhorse" => "1.0", Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => jwt_token } }
let(:headers_with_token) { headers.merge(Ci::API::Helpers::BUILD_TOKEN_HEADER => build.token) }
before { build.run! }
@@ -240,14 +263,22 @@ describe Ci::API::API do
it "using token as parameter" do
post authorize_url, { token: build.token }, headers
expect(response).to have_http_status(200)
+ expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
expect(json_response["TempPath"]).not_to be_nil
end
it "using token as header" do
post authorize_url, {}, headers_with_token
expect(response).to have_http_status(200)
+ expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
expect(json_response["TempPath"]).not_to be_nil
end
+
+ it "reject requests that did not go through gitlab-workhorse" do
+ headers.delete(Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER)
+ post authorize_url, { token: build.token }, headers
+ expect(response).to have_http_status(500)
+ end
end
context "should fail to post too large artifact" do
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 9ca3b021aa2..b7001fede40 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -1,6 +1,8 @@
require "spec_helper"
describe 'Git HTTP requests', lib: true do
+ include WorkhorseHelpers
+
let(:user) { create(:user) }
let(:project) { create(:project, path: 'project.git-project') }
@@ -48,6 +50,7 @@ describe 'Git HTTP requests', lib: true do
expect(response).to have_http_status(200)
expect(json_body['RepoPath']).to include(wiki.repository.path_with_namespace)
+ expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
end
end
end
@@ -63,6 +66,7 @@ describe 'Git HTTP requests', lib: true do
it "downloads get status 200" do
download(path, {}) do |response|
expect(response).to have_http_status(200)
+ expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
end
end
@@ -101,6 +105,14 @@ describe 'Git HTTP requests', lib: true do
end
end
end
+
+ context 'when the request is not from gitlab-workhorse' do
+ it 'raises an exception' do
+ expect do
+ get("/#{project.path_with_namespace}.git/info/refs?service=git-upload-pack")
+ end.to raise_error(JWT::DecodeError)
+ end
+ end
end
context "when the project is private" do
@@ -170,11 +182,13 @@ describe 'Git HTTP requests', lib: true do
clone_get(path, env)
expect(response).to have_http_status(200)
+ expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
end
it "uploads get status 200" do
upload(path, env) do |response|
expect(response).to have_http_status(200)
+ expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
end
end
end
@@ -189,6 +203,7 @@ describe 'Git HTTP requests', lib: true do
clone_get "#{project.path_with_namespace}.git", user: 'oauth2', password: @token.token
expect(response).to have_http_status(200)
+ expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
end
it "uploads get status 401 (no project existence information leak)" do
@@ -297,6 +312,7 @@ describe 'Git HTTP requests', lib: true do
clone_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: token
expect(response).to have_http_status(200)
+ expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
end
it "uploads get status 401 (no project existence information leak)" do
@@ -426,7 +442,7 @@ describe 'Git HTTP requests', lib: true do
end
def auth_env(user, password, spnego_request_token)
- env = {}
+ env = workhorse_internal_api_request_header
if user && password
env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(user, password)
elsif spnego_request_token
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index fcd6521317a..6e551bb65fa 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe 'Git LFS API and storage' do
+ include WorkhorseHelpers
+
let(:user) { create(:user) }
let!(:lfs_object) { create(:lfs_object, :with_file) }
@@ -715,6 +717,12 @@ describe 'Git LFS API and storage' do
project.team << [user, :developer]
end
+ context 'and the request bypassed workhorse' do
+ it 'raises an exception' do
+ expect { put_authorize(verified: false) }.to raise_error JWT::DecodeError
+ end
+ end
+
context 'and request is sent by gitlab-workhorse to authorize the request' do
before do
put_authorize
@@ -724,6 +732,10 @@ describe 'Git LFS API and storage' do
expect(response).to have_http_status(200)
end
+ it 'uses the gitlab-workhorse content type' do
+ expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ end
+
it 'responds with status 200, location of lfs store and object details' do
expect(json_response['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
expect(json_response['LfsOid']).to eq(sample_oid)
@@ -863,8 +875,11 @@ describe 'Git LFS API and storage' do
end
end
- def put_authorize
- put "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}/authorize", nil, headers
+ def put_authorize(verified: true)
+ authorize_headers = headers
+ authorize_headers.merge!(workhorse_internal_api_request_header) if verified
+
+ put "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}/authorize", nil, authorize_headers
end
def put_finalize(lfs_tmp = lfs_tmp_file)
diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb
index ad0d58672b3..c6160f4fa57 100644
--- a/spec/services/projects/housekeeping_service_spec.rb
+++ b/spec/services/projects/housekeeping_service_spec.rb
@@ -4,12 +4,11 @@ describe Projects::HousekeepingService do
subject { Projects::HousekeepingService.new(project) }
let(:project) { create :project }
- describe 'execute' do
- before do
- project.pushes_since_gc = 3
- project.save!
- end
+ after do
+ project.reset_pushes_since_gc
+ end
+ describe '#execute' do
it 'enqueues a sidekiq job' do
expect(subject).to receive(:try_obtain_lease).and_return(true)
expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id)
@@ -32,12 +31,12 @@ describe Projects::HousekeepingService do
it 'does not reset pushes_since_gc' do
expect do
expect { subject.execute }.to raise_error(Projects::HousekeepingService::LeaseTaken)
- end.not_to change { project.pushes_since_gc }.from(3)
+ end.not_to change { project.pushes_since_gc }
end
end
end
- describe 'needed?' do
+ describe '#needed?' do
it 'when the count is low enough' do
expect(subject.needed?).to eq(false)
end
@@ -48,25 +47,11 @@ describe Projects::HousekeepingService do
end
end
- describe 'increment!' do
- let(:lease_key) { "project_housekeeping:increment!:#{project.id}" }
-
+ describe '#increment!' do
it 'increments the pushes_since_gc counter' do
- lease = double(:lease, try_obtain: true)
- expect(Gitlab::ExclusiveLease).to receive(:new).with(lease_key, anything).and_return(lease)
-
expect do
subject.increment!
end.to change { project.pushes_since_gc }.from(0).to(1)
end
-
- it 'does not increment when no lease can be obtained' do
- lease = double(:lease, try_obtain: false)
- expect(Gitlab::ExclusiveLease).to receive(:new).with(lease_key, anything).and_return(lease)
-
- expect do
- subject.increment!
- end.not_to change { project.pushes_since_gc }
- end
end
end
diff --git a/spec/support/ldap_helpers.rb b/spec/support/ldap_helpers.rb
new file mode 100644
index 00000000000..079f244475c
--- /dev/null
+++ b/spec/support/ldap_helpers.rb
@@ -0,0 +1,47 @@
+module LdapHelpers
+ def ldap_adapter(provider = 'ldapmain', ldap = double(:ldap))
+ ::Gitlab::LDAP::Adapter.new(provider, ldap)
+ end
+
+ def user_dn(uid)
+ "uid=#{uid},ou=users,dc=example,dc=com"
+ end
+
+ # Accepts a hash of Gitlab::LDAP::Config keys and values.
+ #
+ # Example:
+ # stub_ldap_config(
+ # group_base: 'ou=groups,dc=example,dc=com',
+ # admin_group: 'my-admin-group'
+ # )
+ def stub_ldap_config(messages)
+ messages.each do |config, value|
+ allow_any_instance_of(::Gitlab::LDAP::Config)
+ .to receive(config.to_sym).and_return(value)
+ end
+ end
+
+ # Stub an LDAP person search and provide the return entry. Specify `nil` for
+ # `entry` to simulate when an LDAP person is not found
+ #
+ # Example:
+ # adapter = ::Gitlab::LDAP::Adapter.new('ldapmain', double(:ldap))
+ # ldap_user_entry = ldap_user_entry('john_doe')
+ #
+ # stub_ldap_person_find_by_uid('john_doe', ldap_user_entry, adapter)
+ def stub_ldap_person_find_by_uid(uid, entry, provider = 'ldapmain')
+ return_value = ::Gitlab::LDAP::Person.new(entry, provider) if entry.present?
+
+ allow(::Gitlab::LDAP::Person)
+ .to receive(:find_by_uid).with(uid, any_args).and_return(return_value)
+ end
+
+ # Create a simple LDAP user entry.
+ def ldap_user_entry(uid)
+ entry = Net::LDAP::Entry.new
+ entry['dn'] = user_dn(uid)
+ entry['uid'] = uid
+
+ entry
+ end
+end
diff --git a/spec/support/workhorse_helpers.rb b/spec/support/workhorse_helpers.rb
index 107b6e30924..47673cd4c3a 100644
--- a/spec/support/workhorse_helpers.rb
+++ b/spec/support/workhorse_helpers.rb
@@ -13,4 +13,9 @@ module WorkhorseHelpers
]
end
end
+
+ def workhorse_internal_api_request_header
+ jwt_token = JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256')
+ { 'HTTP_' + Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER.upcase.tr('-', '_') => jwt_token }
+ end
end
diff --git a/vendor/assets/javascripts/task_list.js b/vendor/assets/javascripts/task_list.js
index bc451506b6a..9fbfef03f6d 100644
--- a/vendor/assets/javascripts/task_list.js
+++ b/vendor/assets/javascripts/task_list.js
@@ -1,15 +1,118 @@
-
+// The MIT License (MIT)
+//
+// Copyright (c) 2014 GitHub, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+// TaskList Behavior
+//
/*= provides tasklist:enabled */
-
-
/*= provides tasklist:disabled */
-
-
/*= provides tasklist:change */
-
-
/*= provides tasklist:changed */
-
+//
+//
+// Enables Task List update behavior.
+//
+// ### Example Markup
+//
+// <div class="js-task-list-container">
+// <ul class="task-list">
+// <li class="task-list-item">
+// <input type="checkbox" class="js-task-list-item-checkbox" disabled />
+// text
+// </li>
+// </ul>
+// <form>
+// <textarea class="js-task-list-field">- [ ] text</textarea>
+// </form>
+// </div>
+//
+// ### Specification
+//
+// TaskLists MUST be contained in a `(div).js-task-list-container`.
+//
+// TaskList Items SHOULD be an a list (`UL`/`OL`) element.
+//
+// Task list items MUST match `(input).task-list-item-checkbox` and MUST be
+// `disabled` by default.
+//
+// TaskLists MUST have a `(textarea).js-task-list-field` form element whose
+// `value` attribute is the source (Markdown) to be udpated. The source MUST
+// follow the syntax guidelines.
+//
+// TaskList updates trigger `tasklist:change` events. If the change is
+// successful, `tasklist:changed` is fired. The change can be canceled.
+//
+// jQuery is required.
+//
+// ### Methods
+//
+// `.taskList('enable')` or `.taskList()`
+//
+// Enables TaskList updates for the container.
+//
+// `.taskList('disable')`
+//
+// Disables TaskList updates for the container.
+//
+//# ### Events
+//
+// `tasklist:enabled`
+//
+// Fired when the TaskList is enabled.
+//
+// * **Synchronicity** Sync
+// * **Bubbles** Yes
+// * **Cancelable** No
+// * **Target** `.js-task-list-container`
+//
+// `tasklist:disabled`
+//
+// Fired when the TaskList is disabled.
+//
+// * **Synchronicity** Sync
+// * **Bubbles** Yes
+// * **Cancelable** No
+// * **Target** `.js-task-list-container`
+//
+// `tasklist:change`
+//
+// Fired before the TaskList item change takes affect.
+//
+// * **Synchronicity** Sync
+// * **Bubbles** Yes
+// * **Cancelable** Yes
+// * **Target** `.js-task-list-field`
+//
+// `tasklist:changed`
+//
+// Fired once the TaskList item change has taken affect.
+//
+// * **Synchronicity** Sync
+// * **Bubbles** Yes
+// * **Cancelable** No
+// * **Target** `.js-task-list-field`
+//
+// ### NOTE
+//
+// Task list checkboxes are rendered as disabled by default because rendered
+// user content is cached without regard for the viewer.
(function() {
var codeFencesPattern, complete, completePattern, disableTaskList, disableTaskLists, enableTaskList, enableTaskLists, escapePattern, incomplete, incompletePattern, itemPattern, itemsInParasPattern, updateTaskList, updateTaskListItem,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
@@ -18,20 +121,48 @@
complete = "[x]";
+ // Escapes the String for regular expression matching.
escapePattern = function(str) {
return str.replace(/([\[\]])/g, "\\$1").replace(/\s/, "\\s").replace("x", "[xX]");
};
- incompletePattern = RegExp("" + (escapePattern(incomplete)));
-
- completePattern = RegExp("" + (escapePattern(complete)));
+ incompletePattern = RegExp("" + (escapePattern(incomplete))); // escape square brackets
+ // match all white space
+ completePattern = RegExp("" + (escapePattern(complete))); // match all cases
+ // Pattern used to identify all task list items.
+ // Useful when you need iterate over all items.
itemPattern = RegExp("^(?:\\s*(?:>\\s*)*(?:[-+*]|(?:\\d+\\.)))\\s*(" + (escapePattern(complete)) + "|" + (escapePattern(incomplete)) + ")\\s+(?!\\(.*?\\))(?=(?:\\[.*?\\]\\s*(?:\\[.*?\\]|\\(.*?\\))\\s*)*(?:[^\\[]|$))");
+ // prefix, consisting of
+ // optional leading whitespace
+ // zero or more blockquotes
+ // list item indicator
+ // optional whitespace prefix
+ // checkbox
+ // is followed by whitespace
+ // is not part of a [foo](url) link
+ // and is followed by zero or more links
+ // and either a non-link or the end of the string
+ // Used to filter out code fences from the source for comparison only.
+ // http://rubular.com/r/x5EwZVrloI
+ // Modified slightly due to issues with JS
codeFencesPattern = /^`{3}(?:\s*\w+)?[\S\s].*[\S\s]^`{3}$/mg;
+ // ```
+ // followed by optional language
+ // whitespace
+ // code
+ // whitespace
+ // ```
+ // Used to filter out potential mismatches (items not in lists).
+ // http://rubular.com/r/OInl6CiePy
itemsInParasPattern = RegExp("^(" + (escapePattern(complete)) + "|" + (escapePattern(incomplete)) + ").+$", "g");
+ // Given the source text, updates the appropriate task list item to match the
+ // given checked value.
+ //
+ // Returns the updated String text.
updateTaskListItem = function(source, itemIndex, checked) {
var clean, index, line, result;
clean = source.replace(/\r/g, '').replace(codeFencesPattern, '').replace(itemsInParasPattern, '').split("\n");
@@ -55,6 +186,9 @@
return result.join("\n");
};
+ // Updates the $field value to reflect the state of $item.
+ // Triggers the `tasklist:change` event before the value has changed, and fires
+ // a `tasklist:changed` event once the value has changed.
updateTaskList = function($item) {
var $container, $field, checked, event, index;
$container = $item.closest('.js-task-list-container');
@@ -70,10 +204,12 @@
}
};
+ // When the task list item checkbox is updated, submit the change
$(document).on('change', '.task-list-item-checkbox', function() {
return updateTaskList($(this));
});
+ // Enables TaskList item changes.
enableTaskList = function($container) {
if ($container.find('.js-task-list-field').length > 0) {
$container.find('.task-list-item').addClass('enabled').find('.task-list-item-checkbox').attr('disabled', null);
@@ -81,6 +217,7 @@
}
};
+ // Enables a collection of TaskList containers.
enableTaskLists = function($containers) {
var container, i, len, results;
results = [];
@@ -91,11 +228,13 @@
return results;
};
+ // Disable TaskList item changes.
disableTaskList = function($container) {
$container.find('.task-list-item').removeClass('enabled').find('.task-list-item-checkbox').attr('disabled', 'disabled');
return $container.removeClass('is-task-list-enabled').trigger('tasklist:disabled');
};
+ // Disables a collection of TaskList containers.
disableTaskLists = function($containers) {
var container, i, len, results;
results = [];
diff --git a/vendor/gitignore/Global/NetBeans.gitignore b/vendor/gitignore/Global/NetBeans.gitignore
index 520d91ff584..254108cd23b 100644
--- a/vendor/gitignore/Global/NetBeans.gitignore
+++ b/vendor/gitignore/Global/NetBeans.gitignore
@@ -3,5 +3,4 @@ build/
nbbuild/
dist/
nbdist/
-nbactions.xml
.nb-gradle/
diff --git a/vendor/gitignore/Global/Tags.gitignore b/vendor/gitignore/Global/Tags.gitignore
index c0318165a27..91927af4cd6 100644
--- a/vendor/gitignore/Global/Tags.gitignore
+++ b/vendor/gitignore/Global/Tags.gitignore
@@ -9,6 +9,7 @@ gtags.files
GTAGS
GRTAGS
GPATH
+GSYMS
cscope.files
cscope.out
cscope.in.out
diff --git a/vendor/gitignore/Global/OSX.gitignore b/vendor/gitignore/Global/macOS.gitignore
index 5972fe50f66..828a509a137 100644
--- a/vendor/gitignore/Global/OSX.gitignore
+++ b/vendor/gitignore/Global/macOS.gitignore
@@ -3,7 +3,8 @@
.LSOverride
# Icon must end with two \r
-Icon
+Icon
+
# Thumbnails
._*
diff --git a/vendor/gitignore/Haskell.gitignore b/vendor/gitignore/Haskell.gitignore
index a4ee41ab62b..450f32ec40c 100644
--- a/vendor/gitignore/Haskell.gitignore
+++ b/vendor/gitignore/Haskell.gitignore
@@ -17,3 +17,4 @@ cabal.sandbox.config
*.eventlog
.stack-work/
cabal.project.local
+.HTF/
diff --git a/vendor/gitignore/Joomla.gitignore b/vendor/gitignore/Joomla.gitignore
index 0d7a0de298f..93103fdbe77 100644
--- a/vendor/gitignore/Joomla.gitignore
+++ b/vendor/gitignore/Joomla.gitignore
@@ -52,6 +52,7 @@
/administrator/language/en-GB/en-GB.plg_content_contact.sys.ini
/administrator/language/en-GB/en-GB.plg_content_finder.ini
/administrator/language/en-GB/en-GB.plg_content_finder.sys.ini
+/administrator/language/en-GB/en-GB.plg_editors-xtd_module*
/administrator/language/en-GB/en-GB.plg_finder_categories.ini
/administrator/language/en-GB/en-GB.plg_finder_categories.sys.ini
/administrator/language/en-GB/en-GB.plg_finder_contacts.ini
@@ -64,6 +65,10 @@
/administrator/language/en-GB/en-GB.plg_finder_tags.sys.ini
/administrator/language/en-GB/en-GB.plg_finder_weblinks.ini
/administrator/language/en-GB/en-GB.plg_finder_weblinks.sys.ini
+/administrator/language/en-GB/en-GB.plg_installer_folderinstaller*
+/administrator/language/en-GB/en-GB.plg_installer_packageinstaller*
+/administrator/language/en-GB/en-GB.plg_installer_packageinstaller
+/administrator/language/en-GB/en-GB.plg_installer_urlinstaller*
/administrator/language/en-GB/en-GB.plg_installer_webinstaller.ini
/administrator/language/en-GB/en-GB.plg_installer_webinstaller.sys.ini
/administrator/language/en-GB/en-GB.plg_quickicon_joomlaupdate.ini
@@ -72,6 +77,8 @@
/administrator/language/en-GB/en-GB.plg_search_tags.sys.ini
/administrator/language/en-GB/en-GB.plg_system_languagecode.ini
/administrator/language/en-GB/en-GB.plg_system_languagecode.sys.ini
+/administrator/language/en-GB/en-GB.plg_system_stats*
+/administrator/language/en-GB/en-GB.plg_system_updatenotification*
/administrator/language/en-GB/en-GB.plg_twofactorauth_totp.ini
/administrator/language/en-GB/en-GB.plg_twofactorauth_totp.sys.ini
/administrator/language/en-GB/en-GB.plg_twofactorauth_yubikey.ini
@@ -249,8 +256,10 @@
/administrator/language/en-GB/en-GB.tpl_hathor.sys.ini
/administrator/language/en-GB/en-GB.xml
/administrator/language/en-GB/index.html
+/administrator/language/ru-RU/index.html
/administrator/language/overrides/*
/administrator/language/index.html
+/administrator/logs/index.html
/administrator/manifests/*
/administrator/modules/mod_custom/*
/administrator/modules/mod_feed/*
@@ -289,6 +298,7 @@
/components/com_finder/*
/components/com_mailto/*
/components/com_media/*
+/components/com_modules/*
/components/com_newsfeeds/*
/components/com_search/*
/components/com_users/*
@@ -407,6 +417,7 @@
/libraries/idna_convert/*
/libraries/joomla/*
/libraries/legacy/*
+/libraries/php-encryption/*
/libraries/phpass/*
/libraries/phpmailer/*
/libraries/phputf8/*
@@ -431,9 +442,11 @@
/media/media/*
/media/mod_languages/*
/media/overrider/*
+/media/plg_captcha_recaptcha/*
/media/plg_quickicon_extensionupdate/*
/media/plg_quickicon_joomlaupdate/*
/media/plg_system_highlight/*
+/media/plg_system_stats/*
/media/system/*
/media/index.html
/modules/mod_articles_archive/*
@@ -486,6 +499,7 @@
/plugins/editors/none/*
/plugins/editors/tinymce/*
/plugins/editors/index.html
+/plugins/editors-xtd/module/*
/plugins/editors-xtd/article/*
/plugins/editors-xtd/image/*
/plugins/editors-xtd/pagebreak/*
@@ -523,6 +537,8 @@
/plugins/system/redirect/*
/plugins/system/remember/*
/plugins/system/sef/*
+/plugins/system/stats/*
+/plugins/system/updatenotification/*
/plugins/system/index.html
/plugins/twofactorauth/*
/plugins/user/contactcreator/*
diff --git a/vendor/gitignore/Node.gitignore b/vendor/gitignore/Node.gitignore
index aea5294de9d..bf7525f9912 100644
--- a/vendor/gitignore/Node.gitignore
+++ b/vendor/gitignore/Node.gitignore
@@ -34,5 +34,8 @@ jspm_packages
# Optional npm cache directory
.npm
+# Optional eslint cache
+.eslintcache
+
# Optional REPL history
.node_repl_history
diff --git a/vendor/gitignore/Objective-C.gitignore b/vendor/gitignore/Objective-C.gitignore
index 20592083931..58c51ecaed4 100644
--- a/vendor/gitignore/Objective-C.gitignore
+++ b/vendor/gitignore/Objective-C.gitignore
@@ -50,7 +50,9 @@ Carthage/Build
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
fastlane/report.xml
+fastlane/Preview.html
fastlane/screenshots
+fastlane/test_output
# Code Injection
#
diff --git a/vendor/gitignore/Python.gitignore b/vendor/gitignore/Python.gitignore
index 72364f99fe4..37fc9d40817 100644
--- a/vendor/gitignore/Python.gitignore
+++ b/vendor/gitignore/Python.gitignore
@@ -79,6 +79,7 @@ celerybeat-schedule
.env
# virtualenv
+.venv/
venv/
ENV/
diff --git a/vendor/gitignore/Rails.gitignore b/vendor/gitignore/Rails.gitignore
index d8c256c1925..e97427608c1 100644
--- a/vendor/gitignore/Rails.gitignore
+++ b/vendor/gitignore/Rails.gitignore
@@ -12,9 +12,11 @@ capybara-*.html
rerun.txt
pickle-email-*.html
-# TODO Comment out these rules if you are OK with secrets being uploaded to the repo
+# TODO Comment out this rule if you are OK with secrets being uploaded to the repo
config/initializers/secret_token.rb
-config/secrets.yml
+
+# Only include if you have production secrets in this file, which is no longer a Rails default
+# config/secrets.yml
# dotenv
# TODO Comment out this rule if environment variables can be committed
diff --git a/vendor/gitignore/VisualStudio.gitignore b/vendor/gitignore/VisualStudio.gitignore
index 67acbf42f5e..d56f8b53288 100644
--- a/vendor/gitignore/VisualStudio.gitignore
+++ b/vendor/gitignore/VisualStudio.gitignore
@@ -251,3 +251,10 @@ paket-files/
# JetBrains Rider
.idea/
*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
diff --git a/vendor/gitlab-ci-yml/Docker.gitlab-ci.yml b/vendor/gitlab-ci-yml/Docker.gitlab-ci.yml
index 396d3f1b042..f3fa3949656 100644
--- a/vendor/gitlab-ci-yml/Docker.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Docker.gitlab-ci.yml
@@ -1,7 +1,12 @@
# Official docker image.
image: docker:latest
+services:
+ - docker:dind
+
build:
stage: build
script:
- - docker build -t test .
+ - docker login -u "gitlab-ci-token" -p "$CI_BUILD_TOKEN" $CI_REGISTRY
+ - docker build --pull -t "$CI_REGISTRY_IMAGE:$CI_BUILD_REF_NAME" .
+ - docker push "$CI_REGISTRY_IMAGE:$CI_BUILD_REF_NAME"
diff --git a/vendor/gitlab-ci-yml/Ruby.gitlab-ci.yml b/vendor/gitlab-ci-yml/Ruby.gitlab-ci.yml
index 166f146ee05..08b57c8c0ac 100644
--- a/vendor/gitlab-ci-yml/Ruby.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Ruby.gitlab-ci.yml
@@ -43,3 +43,12 @@ rails:
- bundle exec rake db:migrate
- bundle exec rake db:seed
- bundle exec rake test
+
+# This deploy job uses a simple deploy flow to Heroku, other providers, e.g. AWS Elastic Beanstalk
+# are supported too: https://github.com/travis-ci/dpl
+deploy:
+ type: deploy
+ environment: production
+ script:
+ - gem install dpl
+ - dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_PRODUCTION_KEY
diff --git a/vendor/gitlab-ci-yml/Swift.gitlab-ci.yml b/vendor/gitlab-ci-yml/Swift.gitlab-ci.yml
new file mode 100644
index 00000000000..c9c35906d1c
--- /dev/null
+++ b/vendor/gitlab-ci-yml/Swift.gitlab-ci.yml
@@ -0,0 +1,30 @@
+# Lifted from: https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/
+# This file assumes an own GitLab CI runner, setup on an OS X system.
+stages:
+ - build
+ - archive
+
+build_project:
+ stage: build
+ script:
+ - xcodebuild clean -project ProjectName.xcodeproj -scheme SchemeName | xcpretty
+ - xcodebuild test -project ProjectName.xcodeproj -scheme SchemeName -destination 'platform=iOS Simulator,name=iPhone 6s,OS=9.2' | xcpretty -s
+ tags:
+ - ios_9-2
+ - xcode_7-2
+ - osx_10-11
+
+archive_project:
+ stage: archive
+ script:
+ - xcodebuild clean archive -archivePath build/ProjectName -scheme SchemeName
+ - xcodebuild -exportArchive -exportFormat ipa -archivePath "build/ProjectName.xcarchive" -exportPath "build/ProjectName.ipa" -exportProvisioningProfile "ProvisioningProfileName"
+ only:
+ - master
+ artifacts:
+ paths:
+ - build/ProjectName.ipa
+ tags:
+ - ios_9-2
+ - xcode_7-2
+ - osx_10-11