summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKamil Trzcinski <ayufan@ayufan.eu>2016-07-18 10:40:22 +0200
committerKamil Trzcinski <ayufan@ayufan.eu>2016-07-18 10:40:22 +0200
commitffea9c46000ade225852ee32e90cb8ad0f4f8316 (patch)
treec798d483a2ccab175d2341fc6e4828a5b5859d66
parent22c2814b09960255d9ece0c620858500719858cb (diff)
parentf1083ba1f6e4ff0c01d5e08b2a6c0b1f9b5a9b9a (diff)
downloadgitlab-ce-store-variables-and-when-in-builds-table.tar.gz
Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into store-variables-and-when-in-builds-tablestore-variables-and-when-in-builds-table
# Conflicts: # db/schema.rb
-rw-r--r--.rubocop.yml361
-rw-r--r--.rubocop_todo.yml462
-rw-r--r--CHANGELOG16
-rw-r--r--Gemfile8
-rw-r--r--Gemfile.lock30
-rw-r--r--app/assets/javascripts/application.js.coffee1
-rw-r--r--app/assets/javascripts/dispatcher.js.coffee2
-rw-r--r--app/assets/javascripts/labels_select.js.coffee31
-rw-r--r--app/assets/javascripts/milestone_select.js.coffee2
-rw-r--r--app/assets/javascripts/u2f/authenticate.js.coffee18
-rw-r--r--app/assets/javascripts/u2f/util.js.coffee3
-rw-r--r--app/assets/javascripts/u2f/util.js.coffee.erb15
-rw-r--r--app/assets/javascripts/users_select.js.coffee9
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss2
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss4
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss2
-rw-r--r--app/controllers/application_controller.rb4
-rw-r--r--app/controllers/concerns/authenticates_with_two_factor.rb10
-rw-r--r--app/controllers/profiles/two_factor_auths_controller.rb3
-rw-r--r--app/controllers/projects/issues_controller.rb6
-rw-r--r--app/controllers/projects/merge_requests_controller.rb5
-rw-r--r--app/helpers/blob_helper.rb9
-rw-r--r--app/helpers/issuables_helper.rb2
-rw-r--r--app/helpers/u2f_helper.rb5
-rw-r--r--app/models/ability.rb3
-rw-r--r--app/models/ci/build.rb1
-rw-r--r--app/models/ci/pipeline.rb4
-rw-r--r--app/models/concerns/awardable.rb9
-rw-r--r--app/models/concerns/issuable.rb6
-rw-r--r--app/models/concerns/mentionable.rb8
-rw-r--r--app/models/concerns/participable.rb7
-rw-r--r--app/models/legacy_diff_note.rb4
-rw-r--r--app/models/note.rb3
-rw-r--r--app/models/project.rb8
-rw-r--r--app/models/user.rb1
-rw-r--r--app/services/ci/create_pipeline_service.rb1
-rw-r--r--app/services/create_commit_builds_service.rb8
-rw-r--r--app/services/notes/create_service.rb1
-rw-r--r--app/services/projects/import_export/export_service.rb2
-rw-r--r--app/services/todo_service.rb3
-rw-r--r--app/views/devise/sessions/two_factor.html.haml4
-rw-r--r--app/views/layouts/_search.html.haml2
-rw-r--r--app/views/notify/note_merge_request_email.html.haml2
-rw-r--r--app/views/profiles/two_factor_auths/show.html.haml4
-rw-r--r--app/views/projects/ci/pipelines/_pipeline.html.haml15
-rw-r--r--app/views/shared/issuable/_filter.html.haml4
-rw-r--r--app/views/shared/issuable/_form.html.haml45
-rw-r--r--app/views/shared/issuable/_label_dropdown.html.haml14
-rw-r--r--app/views/shared/issuable/_milestone_dropdown.html.haml8
-rw-r--r--app/workers/project_export_worker.rb2
-rw-r--r--config/application.rb1
-rw-r--r--db/migrate/20160707104333_add_lock_to_issuables.rb17
-rw-r--r--db/migrate/20160715132507_add_user_id_to_pipeline.rb7
-rw-r--r--db/migrate/20160715134306_add_index_for_pipeline_user_id.rb7
-rw-r--r--db/schema.rb24
-rw-r--r--doc/ci/yaml/README.md4
-rw-r--r--doc/development/doc_styleguide.md2
-rw-r--r--features/project/issues/issues.feature1
-rw-r--r--features/project/merge_requests.feature2
-rw-r--r--features/steps/project/forked_merge_requests.rb12
-rw-r--r--features/steps/project/issues/issues.rb3
-rw-r--r--lib/api/award_emoji.rb4
-rw-r--r--lib/api/commit_statuses.rb2
-rw-r--r--lib/api/internal.rb7
-rw-r--r--lib/banzai/filter/syntax_highlight_filter.rb15
-rw-r--r--lib/banzai/filter/user_reference_filter.rb10
-rw-r--r--lib/gitlab/backend/grack_auth.rb2
-rw-r--r--lib/gitlab/database/migration_helpers.rb10
-rw-r--r--lib/gitlab/gitlab_import/importer.rb53
-rw-r--r--lib/gitlab/highlight.rb43
-rw-r--r--lib/gitlab/import_export.rb7
-rw-r--r--lib/gitlab/import_export/project_tree_restorer.rb5
-rw-r--r--lib/gitlab/import_export/relation_factory.rb8
-rw-r--r--lib/gitlab/import_export/saver.rb5
-rw-r--r--lib/gitlab/lfs/response.rb2
-rw-r--r--lib/gitlab/lfs/router.rb2
-rw-r--r--lib/rouge/formatters/html_gitlab.rb168
-rw-r--r--spec/features/groups_spec.rb20
-rw-r--r--spec/features/issues/move_spec.rb2
-rw-r--r--spec/features/issues_spec.rb16
-rw-r--r--spec/features/merge_requests/edit_mr_spec.rb11
-rw-r--r--spec/features/security/project/internal_access_spec.rb19
-rw-r--r--spec/features/security/project/private_access_spec.rb19
-rw-r--r--spec/features/security/project/public_access_spec.rb19
-rw-r--r--spec/features/u2f_spec.rb55
-rw-r--r--spec/fixtures/parallel_diff_result.yml12
-rw-r--r--spec/helpers/blob_helper_spec.rb32
-rw-r--r--spec/helpers/events_helper_spec.rb2
-rw-r--r--spec/javascripts/u2f/authenticate_spec.coffee3
-rw-r--r--spec/javascripts/u2f/register_spec.js.coffee1
-rw-r--r--spec/lib/banzai/filter/syntax_highlight_filter_spec.rb34
-rw-r--r--spec/lib/gitlab/bitbucket_import/client_spec.rb2
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb4
-rw-r--r--spec/lib/gitlab/diff/highlight_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/import_export_spec.rb21
-rw-r--r--spec/lib/gitlab/import_export/project.json1
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb8
-rw-r--r--spec/lib/gitlab/lfs/lfs_router_spec.rb730
-rw-r--r--spec/models/ci/pipeline_spec.rb3
-rw-r--r--spec/models/commit_status_spec.rb24
-rw-r--r--spec/models/legacy_diff_note_spec.rb8
-rw-r--r--spec/models/project_spec.rb4
-rw-r--r--spec/models/user_spec.rb2
-rw-r--r--spec/requests/api/award_emoji_spec.rb32
-rw-r--r--spec/requests/api/internal_spec.rb14
-rw-r--r--spec/requests/lfs_http_spec.rb768
-rw-r--r--spec/services/auth/container_registry_authentication_service_spec.rb104
-rw-r--r--spec/services/create_commit_builds_service_spec.rb3
-rw-r--r--spec/services/create_deployment_service_spec.rb16
-rw-r--r--spec/services/todo_service_spec.rb53
-rw-r--r--spec/support/fake_u2f_device.rb4
111 files changed, 1979 insertions, 1673 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
index 3aac8401848..db0bcfadcf4 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -2,6 +2,8 @@ require:
- rubocop-rspec
- ./rubocop/rubocop
+inherit_from: .rubocop_todo.yml
+
AllCops:
TargetRubyVersion: 2.1
# Cop names are not displayed in offense messages by default. Change behavior
@@ -52,14 +54,6 @@ Style/AlignArray:
Style/AlignHash:
Enabled: true
-# Align the parameters of a method call if they span more than one line.
-Style/AlignParameters:
- Enabled: false
-
-# Use &&/|| instead of and/or.
-Style/AndOr:
- Enabled: false
-
# Use `Array#join` instead of `Array#*`.
Style/ArrayJoin:
Enabled: true
@@ -80,10 +74,6 @@ Style/Attr:
Style/BeginBlock:
Enabled: true
-# Checks if usage of %() or %Q() matches configuration.
-Style/BarePercentLiterals:
- Enabled: false
-
# Do not use block comments.
Style/BlockComments:
Enabled: true
@@ -97,14 +87,6 @@ Style/BlockEndNewline:
Style/BlockDelimiters:
Enabled: true
-# Enforce braces style around hash parameters.
-Style/BracesAroundHashParameters:
- Enabled: false
-
-# Avoid explicit use of the case equality operator(===).
-Style/CaseEquality:
- Enabled: false
-
# Indentation of when in a case/when/[else/]end.
Style/CaseIndentation:
Enabled: true
@@ -133,24 +115,10 @@ Style/ClassMethods:
Style/ClassVars:
Enabled: true
-# Do not use :: for method call.
-Style/ColonMethodCall:
- Enabled: false
-
-# Checks formatting of special comments (TODO, FIXME, OPTIMIZE, HACK, REVIEW).
-Style/CommentAnnotation:
- Enabled: false
-
# Indentation of comments.
Style/CommentIndentation:
Enabled: true
-# Use the return value of `if` and `case` statements for assignment to a
-# variable and variable comparison instead of assigning that variable
-# inside of each branch.
-Style/ConditionalAssignment:
- Enabled: false
-
# Constants should use SCREAMING_SNAKE_CASE.
Style/ConstantName:
Enabled: true
@@ -159,34 +127,14 @@ Style/ConstantName:
Style/DefWithParentheses:
Enabled: true
-# Checks for use of deprecated Hash methods.
-Style/DeprecatedHashMethods:
- Enabled: false
-
# Document classes and non-namespace modules.
Style/Documentation:
Enabled: false
-# Checks the position of the dot in multi-line method calls.
-Style/DotPosition:
- Enabled: false
-
-# Checks for uses of double negation (!!).
-Style/DoubleNegation:
- Enabled: false
-
-# Prefer `each_with_object` over `inject` or `reduce`.
-Style/EachWithObject:
- Enabled: false
-
# Align elses and elsifs correctly.
Style/ElseAlignment:
Enabled: true
-# Avoid empty else-clauses.
-Style/EmptyElse:
- Enabled: false
-
# Use empty lines between defs.
Style/EmptyLineBetweenDefs:
Enabled: false
@@ -215,10 +163,6 @@ Style/EmptyLinesAroundModuleBody:
Style/EmptyLinesAroundMethodBody:
Enabled: false
-# Prefer literals to Array.new/Hash.new/String.new.
-Style/EmptyLiteral:
- Enabled: false
-
# Avoid the use of END blocks.
Style/EndBlock:
Enabled: true
@@ -231,10 +175,6 @@ Style/EndOfLine:
Style/EvenOdd:
Enabled: true
-# Do not use unnecessary spacing.
-Style/ExtraSpacing:
- Enabled: false
-
# Use snake_case for source file names.
Style/FileName:
Enabled: true
@@ -252,31 +192,15 @@ Style/FlipFlop:
Style/For:
Enabled: true
-# Enforce the use of Kernel#sprintf, Kernel#format or String#%.
-Style/FormatString:
- Enabled: false
-
# Do not introduce global variables.
Style/GlobalVars:
Enabled: true
-# Check for conditionals that can be replaced with guard clauses.
-Style/GuardClause:
- Enabled: false
-
# Prefer Ruby 1.9 hash syntax `{ a: 1, b: 2 }`
# over 1.8 syntax `{ :a => 1, :b => 2 }`.
Style/HashSyntax:
Enabled: true
-# Finds if nodes inside else, which can be converted to elsif.
-Style/IfInsideElse:
- Enabled: false
-
-# Favor modifier if/unless usage when you have a single-line body.
-Style/IfUnlessModifier:
- Enabled: false
-
# Do not use if x; .... Use the ternary operator instead.
Style/IfWithSemicolon:
Enabled: true
@@ -299,22 +223,10 @@ Style/IndentationConsistency:
Style/IndentationWidth:
Enabled: true
-# Checks the indentation of the first element in an array literal.
-Style/IndentArray:
- Enabled: false
-
-# Checks the indentation of the first key in a hash literal.
-Style/IndentHash:
- Enabled: false
-
# Use Kernel#loop for infinite loops.
Style/InfiniteLoop:
Enabled: true
-# Use the new lambda literal syntax for single-line blocks.
-Style/Lambda:
- Enabled: false
-
# Use lambda.call(...) instead of lambda.(...).
Style/LambdaCall:
Enabled: true
@@ -323,14 +235,6 @@ Style/LambdaCall:
Style/LeadingCommentSpace:
Enabled: true
-# Use \ instead of + or << to concatenate two string literals at line end.
-Style/LineEndConcatenation:
- Enabled: false
-
-# Do not use parentheses for method calls with no arguments.
-Style/MethodCallParentheses:
- Enabled: false
-
# Checks if the method definitions have or don't have parentheses.
Style/MethodDefParentheses:
Enabled: true
@@ -387,39 +291,18 @@ Style/MultilineMethodDefinitionBraceLayout:
Style/MultilineOperationIndentation:
Enabled: false
-# Avoid multi-line `? :` (the ternary operator), use if/unless instead.
-Style/MultilineTernaryOperator:
- Enabled: false
-
-# Do not assign mutable objects to constants.
-Style/MutableConstant:
- Enabled: false
-
# Favor unless over if for negative conditions (or control flow or).
Style/NegatedIf:
Enabled: true
-# Favor until over while for negative conditions.
-Style/NegatedWhile:
- Enabled: false
-
# Avoid using nested modifiers.
Style/NestedModifier:
Enabled: true
-# Parenthesize method calls which are nested inside the argument list of
-# another parenthesized method call.
-Style/NestedParenthesizedCalls:
- Enabled: false
-
# Use one expression per branch in a ternary operator.
Style/NestedTernaryOperator:
Enabled: true
-# Use `next` to skip iteration instead of a condition at the end.
-Style/Next:
- Enabled: false
-
# Prefer x.nil? to x == nil.
Style/NilComparison:
Enabled: true
@@ -444,51 +327,10 @@ Style/OneLineConditional:
Style/OpMethod:
Enabled: true
-# Check for simple usages of parallel assignment. It will only warn when
-# the number of variables matches on both sides of the assignment.
-Style/ParallelAssignment:
- Enabled: false
-
# Don't use parentheses around the condition of an if/unless/while.
Style/ParenthesesAroundCondition:
Enabled: true
-# Use `%`-literal delimiters consistently.
-Style/PercentLiteralDelimiters:
- Enabled: false
-
-# Checks if uses of %Q/%q match the configured preference.
-Style/PercentQLiterals:
- Enabled: false
-
-# Avoid Perl-style regex back references.
-Style/PerlBackrefs:
- Enabled: false
-
-# Check the names of predicate methods.
-Style/PredicateName:
- Enabled: false
-
-# Use proc instead of Proc.new.
-Style/Proc:
- Enabled: false
-
-# Checks the arguments passed to raise/fail.
-Style/RaiseArgs:
- Enabled: false
-
-# Don't use begin blocks when they are not needed.
-Style/RedundantBegin:
- Enabled: false
-
-# Checks for an obsolete RuntimeException argument in raise/fail.
-Style/RedundantException:
- Enabled: false
-
-# Checks usages of Object#freeze on immutable objects.
-Style/RedundantFreeze:
- Enabled: false
-
# Checks for parentheses that seem not to serve any purpose.
Style/RedundantParentheses:
Enabled: true
@@ -497,24 +339,6 @@ Style/RedundantParentheses:
Style/RedundantReturn:
Enabled: true
-# Don't use self where it's not needed.
-Style/RedundantSelf:
- Enabled: false
-
-# Use %r for regular expressions matching more than `MaxSlashes` '/'
-# characters. Use %r only for regular expressions matching more
-# than `MaxSlashes` '/' character.
-Style/RegexpLiteral:
- Enabled: false
-
-# Avoid using rescue in its modifier form.
-Style/RescueModifier:
- Enabled: false
-
-# Checks for places where self-assignment shorthand should have been used.
-Style/SelfAssignment:
- Enabled: false
-
# Don't use semicolons to terminate expressions.
Style/Semicolon:
Enabled: true
@@ -524,14 +348,6 @@ Style/SignalException:
EnforcedStyle: only_raise
Enabled: true
-# Enforces the names of some block params.
-Style/SingleLineBlockParams:
- Enabled: false
-
-# Avoid single-line methods.
-Style/SingleLineMethods:
- Enabled: false
-
# Use spaces after colons.
Style/SpaceAfterColon:
Enabled: true
@@ -553,11 +369,6 @@ Style/SpaceAfterNot:
Style/SpaceAfterSemicolon:
Enabled: true
-# Checks that the equals signs in parameter default assignments have or don't
-# have surrounding space depending on configuration.
-Style/SpaceAroundEqualsInParameterDefault:
- Enabled: false
-
# Use a space around keywords if appropriate.
Style/SpaceAroundKeyword:
Enabled: true
@@ -566,10 +377,6 @@ Style/SpaceAroundKeyword:
Style/SpaceAroundOperators:
Enabled: true
-# Checks that the left block brace has or doesn't have space before it.
-Style/SpaceBeforeBlockBraces:
- Enabled: false
-
# No spaces before commas.
Style/SpaceBeforeComma:
Enabled: true
@@ -578,33 +385,14 @@ Style/SpaceBeforeComma:
Style/SpaceBeforeComment:
Enabled: true
-# Checks that exactly one space is used between a method name and the first
-# argument for method calls without parentheses.
-Style/SpaceBeforeFirstArg:
- Enabled: false
-
# No spaces before semicolons.
Style/SpaceBeforeSemicolon:
Enabled: true
-# Checks that block braces have or don't have surrounding space.
-# For blocks taking parameters, checks that the left brace has or doesn't
-# have trailing space.
-Style/SpaceInsideBlockBraces:
- Enabled: false
-
-# No spaces after [ or before ].
-Style/SpaceInsideBrackets:
- Enabled: false
-
# Use spaces inside hash literal braces - or don't.
Style/SpaceInsideHashLiteralBraces:
Enabled: true
-# No spaces after ( or before ).
-Style/SpaceInsideParens:
- Enabled: false
-
# No spaces inside range literals.
Style/SpaceInsideRangeLiteral:
Enabled: true
@@ -614,10 +402,6 @@ Style/SpaceInsideStringInterpolation:
EnforcedStyle: no_space
Enabled: true
-# Avoid Perl-style global variables.
-Style/SpecialGlobalVars:
- Enabled: false
-
# Check for the usage of parentheses around stabby lambda arguments.
Style/StabbyLambdaParentheses:
EnforcedStyle: require_parentheses
@@ -627,25 +411,12 @@ Style/StabbyLambdaParentheses:
Style/StringLiterals:
Enabled: false
-# Checks if uses of quotes inside expressions in interpolated strings match the
-# configured preference.
-Style/StringLiteralsInInterpolation:
- Enabled: false
-
# Checks if configured preferred methods are used over non-preferred.
Style/StringMethods:
PreferredMethods:
intern: to_sym
Enabled: true
-# Use %i or %I for arrays of symbols.
-Style/SymbolArray:
- Enabled: false
-
-# Use symbols as procs instead of blocks when possible.
-Style/SymbolProc:
- Enabled: false
-
# No hard tabs.
Style/Tab:
Enabled: true
@@ -654,40 +425,10 @@ Style/Tab:
Style/TrailingBlankLines:
Enabled: true
-# Checks for trailing comma in array and hash literals.
-Style/TrailingCommaInLiteral:
- Enabled: false
-
-# Checks for trailing comma in argument lists.
-Style/TrailingCommaInArguments:
- Enabled: false
-
-# Avoid trailing whitespace.
-Style/TrailingWhitespace:
- Enabled: false
-
-# Checks for the usage of unneeded trailing underscores at the end of
-# parallel variable assignment.
-Style/TrailingUnderscoreVariable:
- Enabled: false
-
-# Prefer attr_* methods to trivial readers/writers.
-Style/TrivialAccessors:
- Enabled: false
-
-# Do not use unless with else. Rewrite these with the positive case first.
-Style/UnlessElse:
- Enabled: false
-
# Checks for %W when interpolation is not needed.
Style/UnneededCapitalW:
Enabled: true
-# TODO: Enable UnneededInterpolation Cop.
-# Checks for strings that are just an interpolated expression.
-Style/UnneededInterpolation:
- Enabled: false
-
# Checks for %q/%Q when single quotes or double quotes would do.
Style/UnneededPercentQ:
Enabled: false
@@ -717,12 +458,6 @@ Style/WhileUntilModifier:
Style/WordArray:
Enabled: false
-# TODO: Enable ZeroLengthPredicate Cop.
-# Use #empty? when testing for objects of length 0.
-Style/ZeroLengthPredicate:
- Enabled: false
-
-
#################### Metrics ################################
# A calculated magnitude based on number of assignments,
@@ -776,15 +511,6 @@ Metrics/PerceivedComplexity:
Lint/AmbiguousOperator:
Enabled: true
-# Checks for ambiguous regexp literals in the first argument of a method
-# invocation without parentheses.
-Lint/AmbiguousRegexpLiteral:
- Enabled: false
-
-# Don't use assignment in conditions.
-Lint/AssignmentInCondition:
- Enabled: false
-
# Align block ends correctly.
Lint/BlockAlignment:
Enabled: true
@@ -810,14 +536,6 @@ Lint/DefEndAlignment:
Lint/DeprecatedClassMethods:
Enabled: true
-# Check for duplicate method definitions.
-Lint/DuplicateMethods:
- Enabled: false
-
-# Check for duplicate keys in hash literals.
-Lint/DuplicatedKey:
- Enabled: false
-
# Check for immutable argument given to each_with_object.
Lint/EachWithObjectArgument:
Enabled: true
@@ -830,10 +548,6 @@ Lint/ElseLayout:
Lint/EmptyEnsure:
Enabled: true
-# Checks for empty string interpolation.
-Lint/EmptyInterpolation:
- Enabled: false
-
# Align ends correctly.
Lint/EndAlignment:
Enabled: true
@@ -858,21 +572,11 @@ Lint/FloatOutOfRange:
Lint/FormatParameterMismatch:
Enabled: true
-# Don't suppress exception.
-Lint/HandleExceptions:
- Enabled: false
-
# Checks for adjacent string literals on the same line, which could better be
# represented as a single string literal.
Lint/ImplicitStringConcatenation:
Enabled: true
-# TODO: Enable IneffectiveAccessModifier Cop.
-# Checks for attempts to use `private` or `protected` to set the visibility
-# of a class method, which does not work.
-Lint/IneffectiveAccessModifier:
- Enabled: false
-
# Checks for invalid character literals with a non-escaped whitespace
# character.
Lint/InvalidCharacterLiteral:
@@ -886,11 +590,6 @@ Lint/LiteralInCondition:
Lint/LiteralInInterpolation:
Enabled: true
-# Use Kernel#loop with break rather than begin/end/until or begin/end/while
-# for post-loop tests.
-Lint/Loop:
- Enabled: false
-
# Do not use nested method definitions.
Lint/NestedMethodDefinition:
Enabled: true
@@ -916,13 +615,8 @@ Lint/RequireParentheses:
Lint/RescueException:
Enabled: true
-# Do not use the same name as outer local variable for block arguments
-# or block local variables.
-Lint/ShadowingOuterLocalVariable:
- Enabled: false
-
-# 'Checks for Object#to_s usage in string interpolation.
-Lint/StringConversionInInterpolation:
+# Checks for the order which exceptions are rescued to avoid rescueing a less specific exception before a more specific exception.
+Lint/ShadowedException:
Enabled: false
# Do not use prefix `_` for a variable that is used.
@@ -935,22 +629,10 @@ Lint/UnderscorePrefixedVariableName:
Lint/UnneededDisable:
Enabled: false
-# Checks for unused block arguments.
-Lint/UnusedBlockArgument:
- Enabled: false
-
-# Checks for unused method arguments.
-Lint/UnusedMethodArgument:
- Enabled: false
-
# Unreachable code.
Lint/UnreachableCode:
Enabled: true
-# Checks for useless access modifiers.
-Lint/UselessAccessModifier:
- Enabled: false
-
# Checks for useless assignment to a local variable.
Lint/UselessAssignment:
Enabled: true
@@ -983,11 +665,6 @@ Performance/Casecmp:
Performance/DoubleStartEndWith:
Enabled: true
-# TODO: Enable EndWith Cop.
-# Use `end_with?` instead of a regex match anchored to the end of a string.
-Performance/EndWith:
- Enabled: false
-
# Use `strip` instead of `lstrip.rstrip`.
Performance/LstripRstrip:
Enabled: true
@@ -996,24 +673,6 @@ Performance/LstripRstrip:
Performance/RangeInclude:
Enabled: true
-# TODO: Enable RedundantBlockCall Cop.
-# Use `yield` instead of `block.call`.
-Performance/RedundantBlockCall:
- Enabled: false
-
-# TODO: Enable RedundantMatch Cop.
-# Use `=~` instead of `String#match` or `Regexp#match` in a context where the
-# returned `MatchData` is not needed.
-Performance/RedundantMatch:
- Enabled: false
-
-# TODO: Enable RedundantMerge Cop.
-# Use `Hash#[]=`, rather than `Hash#merge!` with a single key-value pair.
-Performance/RedundantMerge:
- # Max number of key-value pairs to consider an offense
- MaxKeyValuePairs: 2
- Enabled: false
-
# Use `sort` instead of `sort_by { |x| x }`.
Performance/RedundantSortBy:
Enabled: true
@@ -1082,18 +741,6 @@ Rails/ReadWriteAttribute:
Rails/ScopeArgs:
Enabled: true
-# Checks the correct usage of time zone aware methods.
-# http://danilenko.org/2012/7/6/rails_timezones
-Rails/TimeZone:
- Enabled: false
-
-# Use validates :attribute, hash of validations.
-Rails/Validation:
- Enabled: false
-
-Rails/UniqBeforePluck:
- Enabled: false
-
##################### RSpec ##################################
# Check that instances are not being stubbed globally.
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
new file mode 100644
index 00000000000..9310e711889
--- /dev/null
+++ b/.rubocop_todo.yml
@@ -0,0 +1,462 @@
+# 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.
+# 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
+Lint/AmbiguousRegexpLiteral:
+ Enabled: false
+
+# Offense count: 43
+# Configuration parameters: AllowSafeAssignment.
+Lint/AssignmentInCondition:
+ Enabled: false
+
+# Offense count: 14
+Lint/HandleExceptions:
+ Enabled: false
+
+# Offense count: 21
+Lint/IneffectiveAccessModifier:
+ Enabled: false
+
+# Offense count: 2
+Lint/Loop:
+ Enabled: false
+
+# Offense count: 15
+Lint/ShadowingOuterLocalVariable:
+ Enabled: false
+
+# Offense count: 3
+# Cop supports --auto-correct.
+Lint/StringConversionInInterpolation:
+ Enabled: false
+
+# Offense count: 44
+# Cop supports --auto-correct.
+# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
+Lint/UnusedBlockArgument:
+ Enabled: false
+
+# Offense count: 129
+# Cop supports --auto-correct.
+# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods.
+Lint/UnusedMethodArgument:
+ Enabled: false
+
+# Offense count: 11
+Lint/UselessAccessModifier:
+ Enabled: false
+
+# Offense count: 12
+# Cop supports --auto-correct.
+Performance/PushSplat:
+ Enabled: false
+
+# Offense count: 2
+# Cop supports --auto-correct.
+Performance/RedundantBlockCall:
+ Enabled: false
+
+# Offense count: 4
+# Cop supports --auto-correct.
+Performance/RedundantMatch:
+ Enabled: false
+
+# Offense count: 24
+# Cop supports --auto-correct.
+# Configuration parameters: MaxKeyValuePairs.
+Performance/RedundantMerge:
+ Enabled: false
+
+# Offense count: 60
+Rails/OutputSafety:
+ Enabled: false
+
+# Offense count: 128
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: strict, flexible
+Rails/TimeZone:
+ Enabled: false
+
+# Offense count: 12
+# Cop supports --auto-correct.
+# Configuration parameters: Include.
+# Include: app/models/**/*.rb
+Rails/Validation:
+ Enabled: false
+
+# Offense count: 217
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
+# SupportedStyles: with_first_parameter, with_fixed_indentation
+Style/AlignParameters:
+ Enabled: false
+
+# Offense count: 32
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: always, conditionals
+Style/AndOr:
+ Enabled: false
+
+# Offense count: 47
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: percent_q, bare_percent
+Style/BarePercentLiterals:
+ Enabled: false
+
+# Offense count: 258
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: braces, no_braces, context_dependent
+Style/BracesAroundHashParameters:
+ Enabled: false
+
+# Offense count: 5
+Style/CaseEquality:
+ Enabled: false
+
+# Offense count: 19
+# Cop supports --auto-correct.
+Style/ColonMethodCall:
+ Enabled: false
+
+# Offense count: 3
+# Cop supports --auto-correct.
+# Configuration parameters: Keywords.
+# Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW
+Style/CommentAnnotation:
+ Enabled: false
+
+# Offense count: 34
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, SingleLineConditionsOnly.
+# SupportedStyles: assign_to_condition, assign_inside_condition
+Style/ConditionalAssignment:
+ Enabled: false
+
+# Offense count: 789
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: leading, trailing
+Style/DotPosition:
+ Enabled: false
+
+# Offense count: 13
+Style/DoubleNegation:
+ Enabled: false
+
+# Offense count: 3
+Style/EachWithObject:
+ Enabled: false
+
+# Offense count: 30
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: empty, nil, both
+Style/EmptyElse:
+ Enabled: false
+
+# Offense count: 3
+# Cop supports --auto-correct.
+Style/EmptyLiteral:
+ Enabled: false
+
+# Offense count: 123
+# Cop supports --auto-correct.
+# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment.
+Style/ExtraSpacing:
+ Enabled: false
+
+# Offense count: 7
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: format, sprintf, percent
+Style/FormatString:
+ Enabled: false
+
+# Offense count: 48
+# Configuration parameters: MinBodyLength.
+Style/GuardClause:
+ Enabled: false
+
+# Offense count: 11
+Style/IfInsideElse:
+ Enabled: false
+
+# Offense count: 177
+# Cop supports --auto-correct.
+# Configuration parameters: MaxLineLength.
+Style/IfUnlessModifier:
+ Enabled: false
+
+# Offense count: 52
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
+# SupportedStyles: special_inside_parentheses, consistent, align_brackets
+Style/IndentArray:
+ Enabled: false
+
+# Offense count: 89
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
+# SupportedStyles: special_inside_parentheses, consistent, align_braces
+Style/IndentHash:
+ Enabled: false
+
+# Offense count: 12
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: line_count_dependent, lambda, literal
+Style/Lambda:
+ Enabled: false
+
+# Offense count: 6
+# Cop supports --auto-correct.
+Style/LineEndConcatenation:
+ Enabled: false
+
+# Offense count: 13
+# Cop supports --auto-correct.
+Style/MethodCallParentheses:
+ Enabled: false
+
+# Offense count: 3
+Style/MultilineTernaryOperator:
+ Enabled: false
+
+# Offense count: 62
+# Cop supports --auto-correct.
+Style/MutableConstant:
+ Enabled: false
+
+# Offense count: 10
+# Cop supports --auto-correct.
+Style/NestedParenthesizedCalls:
+ Enabled: false
+
+# Offense count: 12
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles.
+# SupportedStyles: skip_modifier_ifs, always
+Style/Next:
+ Enabled: false
+
+# Offense count: 8
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedOctalStyle, SupportedOctalStyles.
+# SupportedOctalStyles: zero_with_o, zero_only
+Style/NumericLiteralPrefix:
+ Enabled: false
+
+# Offense count: 29
+# Cop supports --auto-correct.
+Style/ParallelAssignment:
+ Enabled: false
+
+# Offense count: 208
+# Cop supports --auto-correct.
+# Configuration parameters: PreferredDelimiters.
+Style/PercentLiteralDelimiters:
+ Enabled: false
+
+# Offense count: 11
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: lower_case_q, upper_case_q
+Style/PercentQLiterals:
+ Enabled: false
+
+# Offense count: 13
+# Cop supports --auto-correct.
+Style/PerlBackrefs:
+ Enabled: false
+
+# Offense count: 32
+# Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist.
+# NamePrefix: is_, has_, have_
+# NamePrefixBlacklist: is_, has_, have_
+# NameWhitelist: is_a?
+Style/PredicateName:
+ Enabled: false
+
+# Offense count: 28
+# Cop supports --auto-correct.
+Style/PreferredHashMethods:
+ Enabled: false
+
+# Offense count: 6
+# Cop supports --auto-correct.
+Style/Proc:
+ Enabled: false
+
+# Offense count: 20
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: compact, exploded
+Style/RaiseArgs:
+ Enabled: false
+
+# Offense count: 3
+# Cop supports --auto-correct.
+Style/RedundantBegin:
+ Enabled: false
+
+# Offense count: 1
+# Cop supports --auto-correct.
+Style/RedundantException:
+ Enabled: false
+
+# Offense count: 23
+# Cop supports --auto-correct.
+Style/RedundantFreeze:
+ Enabled: false
+
+# Offense count: 377
+# Cop supports --auto-correct.
+Style/RedundantSelf:
+ Enabled: false
+
+# Offense count: 94
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes.
+# SupportedStyles: slashes, percent_r, mixed
+Style/RegexpLiteral:
+ Enabled: false
+
+# Offense count: 17
+# Cop supports --auto-correct.
+Style/RescueModifier:
+ Enabled: false
+
+# Offense count: 2
+# Cop supports --auto-correct.
+Style/SelfAssignment:
+ Enabled: false
+
+# Offense count: 2
+# Configuration parameters: Methods.
+# Methods: {"reduce"=>["a", "e"]}, {"inject"=>["a", "e"]}
+Style/SingleLineBlockParams:
+ Enabled: false
+
+# Offense count: 50
+# Cop supports --auto-correct.
+# Configuration parameters: AllowIfMethodIsEmpty.
+Style/SingleLineMethods:
+ Enabled: false
+
+# Offense count: 14
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: space, no_space
+Style/SpaceAroundEqualsInParameterDefault:
+ Enabled: false
+
+# Offense count: 119
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: space, no_space
+Style/SpaceBeforeBlockBraces:
+ Enabled: false
+
+# Offense count: 11
+# Cop supports --auto-correct.
+# Configuration parameters: AllowForAlignment.
+Style/SpaceBeforeFirstArg:
+ Enabled: false
+
+# Offense count: 130
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
+# SupportedStyles: space, no_space
+Style/SpaceInsideBlockBraces:
+ Enabled: false
+
+# Offense count: 98
+# Cop supports --auto-correct.
+Style/SpaceInsideBrackets:
+ Enabled: false
+
+# Offense count: 60
+# Cop supports --auto-correct.
+Style/SpaceInsideParens:
+ Enabled: false
+
+# Offense count: 5
+# Cop supports --auto-correct.
+Style/SpaceInsidePercentLiteralDelimiters:
+ Enabled: false
+
+# Offense count: 36
+# Cop supports --auto-correct.
+# Configuration parameters: SupportedStyles.
+# SupportedStyles: use_perl_names, use_english_names
+Style/SpecialGlobalVars:
+ EnforcedStyle: use_perl_names
+
+# Offense count: 30
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: single_quotes, double_quotes
+Style/StringLiteralsInInterpolation:
+ Enabled: false
+
+# Offense count: 24
+# Cop supports --auto-correct.
+# Configuration parameters: IgnoredMethods.
+# IgnoredMethods: respond_to, define_method
+Style/SymbolProc:
+ Enabled: false
+
+# Offense count: 23
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
+# SupportedStyles: comma, consistent_comma, no_comma
+Style/TrailingCommaInArguments:
+ Enabled: false
+
+# Offense count: 113
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
+# SupportedStyles: comma, consistent_comma, no_comma
+Style/TrailingCommaInLiteral:
+ Enabled: false
+
+# Offense count: 7
+# Cop supports --auto-correct.
+# Configuration parameters: AllowNamedUnderscoreVariables.
+Style/TrailingUnderscoreVariable:
+ Enabled: false
+
+# Offense count: 90
+# Cop supports --auto-correct.
+Style/TrailingWhitespace:
+ Enabled: false
+
+# Offense count: 2
+# Cop supports --auto-correct.
+# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, Whitelist.
+# Whitelist: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym
+Style/TrivialAccessors:
+ Enabled: false
+
+# Offense count: 3
+# Cop supports --auto-correct.
+Style/UnlessElse:
+ Enabled: false
+
+# Offense count: 13
+# Cop supports --auto-correct.
+Style/UnneededInterpolation:
+ Enabled: false
+
+# Offense count: 8
+# Cop supports --auto-correct.
+Style/ZeroLengthPredicate:
+ Enabled: false
diff --git a/CHANGELOG b/CHANGELOG
index 32f33131de1..cb17be985a8 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.10.0 (unreleased)
- Expose {should,force}_remove_source_branch (Ben Boeckel)
+ - Disable PostgreSQL statement timeout during migrations
- Fix projects dropdown loading performance with a simplified api cal. !5113 (tiagonbotelho)
- Fix commit builds API, return all builds for all pipelines for given commit. !4849
- Replace Haml with Hamlit to make view rendering faster. !3666
@@ -23,12 +24,15 @@ v 8.10.0 (unreleased)
- Add Sidekiq queue duration to transaction metrics.
- Add a new column `artifacts_size` to table `ci_builds` !4964
- Let Workhorse serve format-patch diffs
+ - Display tooltip for mentioned users and groups !5261 (winniehell)
- Added day name to contribution calendar tooltips
- Make images fit to the size of the viewport !4810
- Fix check for New Branch button on Issue page !4630 (winniehell)
- Fix MR-auto-close text added to description. !4836
+ - Support U2F devices in Firefox. !5177
- Fix issue, preventing users w/o push access to sort tags !5105 (redetection)
- Add Spring EmojiOne updates.
+ - Fix fetching LFS objects for private CI projects
- Add syntax for multiline blockquote using `>>>` fence !3954
- Fix viewing notification settings when a project is pending deletion
- Updated compare dropdown menus to use GL dropdown
@@ -53,12 +57,16 @@ v 8.10.0 (unreleased)
- Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
- Only show New Snippet button to users that can create snippets.
- PipelinesFinder uses git cache data
+ - Track a user who created a pipeline
- Actually render old and new sections of parallel diff next to each other
- Throttle the update of `project.pushes_since_gc` to 1 minute.
- Allow expanding and collapsing files in diff view (!4990)
- Collapse large diffs by default (!4990)
+ - Fix mentioned users list on diff notes
+ - Fix creation of deployment on build that is retried, redeployed or rollback
- Check for conflicts with existing Project's wiki path when creating a new project.
- Show last push widget in upstream after push to fork
+ - Fix stage status shown for pipelines
- Cache todos pending/done dashboard query counts.
- Don't instantiate a git tree on Projects show default view
- Bump Rinku to 2.0.0
@@ -79,7 +87,9 @@ v 8.10.0 (unreleased)
- Add basic system information like memory and disk usage to the admin panel
- Don't garbage collect commits that have related DB records like comments
- More descriptive message for git hooks and file locks
+ - Aliases of award emoji should be stored as original name. !5060 (dixpac)
- Handle custom Git hook result in GitLab UI
+ - Allow to access Container Registry for Public and Internal projects
- Allow '?', or '&' for label names
- Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests
- Add date when user joined the team on the member page
@@ -95,6 +105,12 @@ v 8.10.0 (unreleased)
- Redesign Builds and Pipelines pages
- Change status color and icon for running builds
- Fix markdown rendering for: consecutive labels references, label references that begin with a digit or contains `.`
+ - Project export filename now includes the project and namespace path
+ - Fix last update timestamp on issues not preserved on gitlab.com and project imports
+ - Fix issues importing projects from EE to CE
+ - Fix creating group with space in group path
+ - Create Todos for Issue author when assign or mention himself (Katarzyna Kobierska)
+ - Limit the number of retries on error to 3 for exporting projects
v 8.9.6
- Fix importing of events under notes for GitLab projects. !5154
diff --git a/Gemfile b/Gemfile
index ee23f712f05..81e8ff60ad5 100644
--- a/Gemfile
+++ b/Gemfile
@@ -61,7 +61,7 @@ gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: 'omniauth-ldap'
# Git Wiki
# Required manually in config/initializers/gollum.rb to control load order
-gem 'gollum-lib', '~> 4.1.0', require: false
+gem 'gollum-lib', '~> 4.2', require: false
gem 'gollum-rugged_adapter', '~> 0.4.2', require: false
# Language detection
@@ -105,7 +105,7 @@ gem 'seed-fu', '~> 2.3.5'
# Markdown and HTML processing
gem 'html-pipeline', '~> 1.11.0'
gem 'task_list', '~> 1.0.2', require: 'task_list/railtie'
-gem 'github-markup', '~> 1.3.1'
+gem 'github-markup', '~> 1.4'
gem 'redcarpet', '~> 3.3.3'
gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~>3.6'
@@ -113,7 +113,7 @@ gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2'
-gem 'rouge', '~> 1.11'
+gem 'rouge', '~> 2.0'
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
# and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
@@ -299,7 +299,7 @@ group :development, :test do
gem 'spring-commands-spinach', '~> 1.1.0'
gem 'spring-commands-teaspoon', '~> 0.0.2'
- gem 'rubocop', '~> 0.40.0', require: false
+ gem 'rubocop', '~> 0.41.2', require: false
gem 'rubocop-rspec', '~> 1.5.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false
gem 'simplecov', '~> 0.11.0', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index 67c0645c3d9..0987fd5665a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -58,7 +58,7 @@ GEM
faraday_middleware-multi_json (~> 0.0)
oauth2 (~> 1.0)
asciidoctor (1.5.3)
- ast (2.2.0)
+ ast (2.3.0)
attr_encrypted (3.0.1)
encryptor (~> 3.0.0)
attr_required (1.0.0)
@@ -264,7 +264,7 @@ GEM
escape_utils (~> 1.1.0)
mime-types (>= 1.19)
rugged (>= 0.23.0b)
- github-markup (1.3.3)
+ github-markup (1.4.0)
gitlab-flowdock-git-hook (1.0.1)
flowdock (~> 0.7)
gitlab-grit (>= 2.4.1)
@@ -287,13 +287,13 @@ GEM
rubyntlm (~> 0.3)
globalid (0.3.6)
activesupport (>= 4.1.0)
- gollum-grit_adapter (1.0.0)
+ gollum-grit_adapter (1.0.1)
gitlab-grit (~> 2.7, >= 2.7.1)
- gollum-lib (4.1.0)
- github-markup (~> 1.3.3)
+ gollum-lib (4.2.1)
+ github-markup (~> 1.4.0)
gollum-grit_adapter (~> 1.0)
nokogiri (~> 1.6.4)
- rouge (~> 1.9)
+ rouge (~> 2.0)
sanitize (~> 2.1.0)
stringex (~> 2.5.1)
gollum-rugged_adapter (0.4.2)
@@ -473,7 +473,7 @@ GEM
orm_adapter (0.5.0)
paranoia (2.1.4)
activerecord (~> 4.0)
- parser (2.3.1.0)
+ parser (2.3.1.2)
ast (~> 2.2)
pg (0.18.4)
pkg-config (1.1.7)
@@ -578,7 +578,7 @@ GEM
railties (>= 4.2.0, < 5.1)
rinku (2.0.0)
rotp (2.1.2)
- rouge (1.11.0)
+ rouge (2.0.3)
rqrcode (0.7.0)
chunky_png
rqrcode-rails3 (0.1.7)
@@ -606,8 +606,8 @@ GEM
rspec-retry (0.4.5)
rspec-core
rspec-support (3.5.0)
- rubocop (0.40.0)
- parser (>= 2.3.1.0, < 3.0)
+ rubocop (0.41.2)
+ parser (>= 2.3.1.1, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7)
@@ -758,7 +758,7 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.7.2)
- unicode-display_width (1.0.5)
+ unicode-display_width (1.1.0)
unicorn (4.9.0)
kgio (~> 2.6)
rack
@@ -859,12 +859,12 @@ DEPENDENCIES
gemnasium-gitlab-service (~> 0.2)
gemojione (~> 2.6)
github-linguist (~> 4.7.0)
- github-markup (~> 1.3.1)
+ github-markup (~> 1.4)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab_git (~> 10.2)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
- gollum-lib (~> 4.1.0)
+ gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.2)
gon (~> 6.0.1)
grape (~> 0.13.0)
@@ -933,11 +933,11 @@ DEPENDENCIES
request_store (~> 1.3.0)
rerun (~> 0.11.0)
responders (~> 2.0)
- rouge (~> 1.11)
+ rouge (~> 2.0)
rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.5.0)
rspec-retry (~> 0.4.5)
- rubocop (~> 0.40.0)
+ rubocop (~> 0.41.2)
rubocop-rspec (~> 1.5.0)
ruby-fogbugz (~> 0.2.1)
sanitize (~> 2.0)
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index c98763d6271..eceff6d91d5 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -53,7 +53,6 @@
#= require_directory ./u2f
#= require_directory .
#= require fuzzaldrin-plus
-#= require u2f
window.slugify = (text) ->
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index b5da15e9e49..afaa6407b05 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -39,8 +39,6 @@ class Dispatcher
shortcut_handler = new ShortcutsNavigation()
new GLForm($('.issue-form'))
new IssuableForm($('.issue-form'))
- new LabelsSelect()
- new MilestoneSelect()
when 'projects:merge_requests:new', 'projects:merge_requests:edit'
new Diff()
shortcut_handler = new ShortcutsNavigation()
diff --git a/app/assets/javascripts/labels_select.js.coffee b/app/assets/javascripts/labels_select.js.coffee
index 1a802b81452..7688609b301 100644
--- a/app/assets/javascripts/labels_select.js.coffee
+++ b/app/assets/javascripts/labels_select.js.coffee
@@ -184,22 +184,20 @@ class @LabelsSelect
.value()
if $dropdown.hasClass 'js-extra-options'
- extraData = []
- if showAny
- extraData.push(
- isAny: true
- title: 'Any Label'
- )
-
if showNo
- extraData.push(
+ data.unshift(
id: 0
title: 'No Label'
)
- if extraData.length
- extraData.push 'divider'
- data = extraData.concat(data)
+ if showAny
+ data.unshift(
+ isAny: true
+ title: 'Any Label'
+ )
+
+ if data.length > 2
+ data.splice 2, 0, 'divider'
callback data
@@ -289,12 +287,6 @@ class @LabelsSelect
defaultLabel
fieldName: $dropdown.data('field-name')
id: (label) ->
- if $dropdown.hasClass('js-issuable-form-dropdown')
- if label.id is 0
- return
- else
- return label.id
-
if $dropdown.hasClass("js-filter-submit") and not label.isAny?
label.title
else
@@ -308,9 +300,6 @@ class @LabelsSelect
$selectbox.hide()
# display:block overrides the hide-collapse rule
$value.removeAttr('style')
-
- return if $dropdown.hasClass('js-issuable-form-dropdown')
-
if $dropdown.hasClass 'js-multiselect'
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
selectedLabels = $dropdown
@@ -332,7 +321,7 @@ class @LabelsSelect
clicked: (label) ->
_this.enableBulkLabelDropdown()
- if $dropdown.hasClass('js-filter-bulk-update') or $dropdown.hasClass('js-issuable-form-dropdown')
+ if $dropdown.hasClass('js-filter-bulk-update')
return
page = $('body').data 'page'
diff --git a/app/assets/javascripts/milestone_select.js.coffee b/app/assets/javascripts/milestone_select.js.coffee
index 3a036569317..8ab03ed93ee 100644
--- a/app/assets/javascripts/milestone_select.js.coffee
+++ b/app/assets/javascripts/milestone_select.js.coffee
@@ -62,7 +62,7 @@ class @MilestoneSelect
title: 'Upcoming'
)
- if extraOptions.length > 0
+ if extraOptions.length > 2
extraOptions.push 'divider'
callback(extraOptions.concat(data))
diff --git a/app/assets/javascripts/u2f/authenticate.js.coffee b/app/assets/javascripts/u2f/authenticate.js.coffee
index 6deb902c8de..918c0a560fd 100644
--- a/app/assets/javascripts/u2f/authenticate.js.coffee
+++ b/app/assets/javascripts/u2f/authenticate.js.coffee
@@ -6,8 +6,20 @@
class @U2FAuthenticate
constructor: (@container, u2fParams) ->
@appId = u2fParams.app_id
- @challenges = u2fParams.challenges
- @signRequests = u2fParams.sign_requests
+ @challenge = u2fParams.challenge
+
+ # 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
+ @signRequests = u2fParams.sign_requests.map (request) -> _(request).omit('challenge')
start: () =>
if U2FUtil.isU2FSupported()
@@ -16,7 +28,7 @@ class @U2FAuthenticate
@renderNotSupported()
authenticate: () =>
- u2f.sign(@appId, @challenges, @signRequests, (response) =>
+ u2f.sign(@appId, @challenge, @signRequests, (response) =>
if response.errorCode
error = new U2FError(response.errorCode)
@renderError(error);
diff --git a/app/assets/javascripts/u2f/util.js.coffee b/app/assets/javascripts/u2f/util.js.coffee
new file mode 100644
index 00000000000..5ef324f609d
--- /dev/null
+++ b/app/assets/javascripts/u2f/util.js.coffee
@@ -0,0 +1,3 @@
+class @U2FUtil
+ @isU2FSupported: ->
+ window.u2f
diff --git a/app/assets/javascripts/u2f/util.js.coffee.erb b/app/assets/javascripts/u2f/util.js.coffee.erb
deleted file mode 100644
index d59341c38b9..00000000000
--- a/app/assets/javascripts/u2f/util.js.coffee.erb
+++ /dev/null
@@ -1,15 +0,0 @@
-# Helper class for U2F (universal 2nd factor) device registration and authentication.
-
-class @U2FUtil
- @isU2FSupported: ->
- if @testMode
- true
- else
- gon.u2f.browser_supports_u2f
-
- @enableTestMode: ->
- @testMode = true
-
-<% if Rails.env.test? %>
-U2FUtil.enableTestMode();
-<% end %>
diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee
index e061f042ca9..344be811e0d 100644
--- a/app/assets/javascripts/users_select.js.coffee
+++ b/app/assets/javascripts/users_select.js.coffee
@@ -155,13 +155,11 @@ class @UsersSelect
# display:block overrides the hide-collapse rule
$value.css('display', '')
- clicked: (user, $el, e) ->
+ clicked: (user) ->
page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index'
isMRIndex = page is page is 'projects:merge_requests:index'
- if $dropdown.hasClass('js-filter-bulk-update') or $dropdown.hasClass('js-issuable-form-dropdown')
- e.preventDefault()
- selectedId = user.id
+ if $dropdown.hasClass('js-filter-bulk-update')
return
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
@@ -174,8 +172,7 @@ class @UsersSelect
.closest('.selectbox')
.find("input[name='#{$dropdown.data('field-name')}']").val()
assignTo(selected)
- id: (user) ->
- user.id
+
renderRow: (user) ->
username = if user.username then "@#{user.username}" else ""
avatar = if user.avatar_url then user.avatar_url else false
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 3fd0e12568d..d4e900f80ef 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -56,7 +56,7 @@
position: absolute;
top: 50%;
right: 6px;
- margin-top: -6px;
+ margin-top: -4px;
color: $dropdown-toggle-icon-color;
font-size: 10px;
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index dba9a7ab3ee..15c6c9f231a 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -324,10 +324,6 @@
.issuable-form-select-holder {
display: inline-block;
width: 250px;
-
- .dropdown-menu-toggle {
- width: 100%;
- }
}
.table-holder {
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 08da4e290dc..a0334207c68 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -129,6 +129,8 @@
}
.cancel-retry-btns {
+ vertical-align: middle;
+
.btn:not(:first-child) {
margin-left: 8px;
}
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 9cc31620d9f..a1004d9bcea 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -344,10 +344,6 @@ class ApplicationController < ActionController::Base
session[:skip_tfa] && session[:skip_tfa] > Time.current
end
- def browser_supports_u2f?
- browser.chrome? && browser.version.to_i >= 41 && !browser.device.mobile?
- end
-
def redirect_to_home_page_url?
# If user is not signed-in and tries to access root_path - redirect him to landing page
# Don't redirect to the default URL to prevent endless redirections
diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb
index 998b8adc411..ba07cea569c 100644
--- a/app/controllers/concerns/authenticates_with_two_factor.rb
+++ b/app/controllers/concerns/authenticates_with_two_factor.rb
@@ -57,7 +57,7 @@ module AuthenticatesWithTwoFactor
# Authenticate using the response from a U2F (universal 2nd factor) device
def authenticate_with_two_factor_via_u2f(user)
- if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenges])
+ if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenge])
# Remove any lingering user data from login
session.delete(:otp_user_id)
session.delete(:challenges)
@@ -77,11 +77,9 @@ module AuthenticatesWithTwoFactor
if key_handles.present?
sign_requests = u2f.authentication_requests(key_handles)
- challenges = sign_requests.map(&:challenge)
- session[:challenges] = challenges
- gon.push(u2f: { challenges: challenges, app_id: u2f_app_id,
- sign_requests: sign_requests,
- browser_supports_u2f: browser_supports_u2f? })
+ session[:challenge] ||= u2f.challenge
+ gon.push(u2f: { challenge: session[:challenge], app_id: u2f_app_id,
+ sign_requests: sign_requests })
end
end
end
diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb
index 6a358fdcc05..e37e9e136db 100644
--- a/app/controllers/profiles/two_factor_auths_controller.rb
+++ b/app/controllers/profiles/two_factor_auths_controller.rb
@@ -100,7 +100,6 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
gon.push(u2f: { challenges: session[:challenges], app_id: u2f_app_id,
register_requests: registration_requests,
- sign_requests: sign_requests,
- browser_supports_u2f: browser_supports_u2f? })
+ sign_requests: sign_requests })
end
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index f7ada5cfee4..b6e80762e3c 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -119,10 +119,6 @@ class Projects::IssuesController < Projects::ApplicationController
render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } })
end
end
-
- rescue ActiveRecord::StaleObjectError
- @conflict = true
- render :edit
end
def referenced_merge_requests
@@ -220,7 +216,7 @@ class Projects::IssuesController < Projects::ApplicationController
def issue_params
params.require(:issue).permit(
:title, :assignee_id, :position, :description, :confidential,
- :milestone_id, :due_date, :state_event, :task_num, :lock_version, label_ids: []
+ :milestone_id, :due_date, :state_event, :task_num, label_ids: []
)
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 2deb7959700..df659bb8c3b 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -196,9 +196,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
else
render "edit"
end
- rescue ActiveRecord::StaleObjectError
- @conflict = true
- render :edit
end
def remove_wip
@@ -427,7 +424,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
:title, :assignee_id, :source_project_id, :source_branch,
:target_project_id, :target_branch, :milestone_id,
:state_event, :description, :task_num, :force_remove_source_branch,
- :lock_version, label_ids: []
+ label_ids: []
)
end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 428a42266d0..abe115d8c68 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -1,10 +1,7 @@
module BlobHelper
- def highlighter(blob_name, blob_content, repository: nil, nowrap: false)
- Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap, repository: repository)
- end
-
- def highlight(blob_name, blob_content, repository: nil, nowrap: false, plain: false)
- Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain, repository: repository)
+ def highlight(blob_name, blob_content, repository: nil, plain: false)
+ highlighted = Gitlab::Highlight.highlight(blob_name, blob_content, plain: plain, repository: repository)
+ raw %(<pre class="code highlight"><code>#{highlighted}</code></pre>)
end
def no_highlight_files
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index a3a8c7d5ff9..47d174361db 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -9,7 +9,7 @@ module IssuablesHelper
def multi_label_name(current_labels, default_label)
# current_labels may be a string from before
- if current_labels.is_a?(Array) && current_labels.any?
+ if current_labels.is_a?(Array)
if current_labels.count > 1
"#{current_labels[0]} +#{current_labels.count - 1} more"
else
diff --git a/app/helpers/u2f_helper.rb b/app/helpers/u2f_helper.rb
new file mode 100644
index 00000000000..143b4ca6b51
--- /dev/null
+++ b/app/helpers/u2f_helper.rb
@@ -0,0 +1,5 @@
+module U2fHelper
+ def inject_u2f_api?
+ browser.chrome? && browser.version.to_i >= 41 && !browser.device.mobile?
+ end
+end
diff --git a/app/models/ability.rb b/app/models/ability.rb
index eeb0ceba081..6fd18f2ee24 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -204,7 +204,8 @@ class Ability
:download_code,
:fork_project,
:read_commit_status,
- :read_pipeline
+ :read_pipeline,
+ :read_container_image
]
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 8c18f7ceb12..ffac3a22efc 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -56,6 +56,7 @@ module Ci
new_build.yaml_variables = build.yaml_variables
new_build.when = build.when
new_build.user = user
+ new_build.environment = build.environment
new_build.save
MergeRequests::AddTodoWhenBuildFailsService.new(build.project, nil).close(new_build)
new_build
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index fa4071e2482..b468434866b 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -6,6 +6,8 @@ module Ci
self.table_name = 'ci_commits'
belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
+ belongs_to :user
+
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id
has_many :builds, class_name: 'Ci::Build', foreign_key: :commit_id
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest', foreign_key: :commit_id
@@ -215,6 +217,8 @@ module Ci
end
def keep_around_commits
+ return unless project
+
project.repository.keep_around(self.sha)
project.repository.keep_around(self.before_sha)
end
diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb
index 06beff177b1..800a16ab246 100644
--- a/app/models/concerns/awardable.rb
+++ b/app/models/concerns/awardable.rb
@@ -65,8 +65,7 @@ module Awardable
def create_award_emoji(name, current_user)
return unless emoji_awardable?
-
- award_emoji.create(name: name, user: current_user)
+ award_emoji.create(name: normalize_name(name), user: current_user)
end
def remove_award_emoji(name, current_user)
@@ -80,4 +79,10 @@ module Awardable
create_award_emoji(emoji_name, current_user)
end
end
+
+ private
+
+ def normalize_name(name)
+ Gitlab::AwardEmoji.normalize_emoji_name(name)
+ end
end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index fb49bd7dd64..acb6f5a2998 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -87,12 +87,6 @@ module Issuable
User.find(assignee_id_was).update_cache_counts if assignee_id_was
assignee.update_cache_counts if assignee
end
-
- # We want to use optimistic lock for cases when only title or description are involved
- # http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html
- def locking_enabled?
- title_changed? || description_changed?
- end
end
module ClassMethods
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index 8cac47246db..ec9e0f1b1d0 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -14,14 +14,14 @@ module Mentionable
attr = attr.to_s
mentionable_attrs << [attr, options]
end
+ end
+ included do
# Accessor for attributes marked mentionable.
- def mentionable_attrs
- @mentionable_attrs ||= []
+ cattr_accessor :mentionable_attrs, instance_accessor: false do
+ []
end
- end
- included do
if self < Participable
participant -> (user, ext) { all_references(user, extractor: ext) }
end
diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb
index 9822844357d..70740c76e43 100644
--- a/app/models/concerns/participable.rb
+++ b/app/models/concerns/participable.rb
@@ -41,9 +41,12 @@ module Participable
def participant(attr)
participant_attrs << attr
end
+ end
- def participant_attrs
- @participant_attrs ||= []
+ included do
+ # Accessor for participant attributes.
+ cattr_accessor :participant_attrs, instance_accessor: false do
+ []
end
end
diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb
index 790dfd4d480..04a651d50ab 100644
--- a/app/models/legacy_diff_note.rb
+++ b/app/models/legacy_diff_note.rb
@@ -38,7 +38,7 @@ class LegacyDiffNote < Note
end
def diff_line
- @diff_line ||= diff_file.line_for_line_code(self.line_code)
+ @diff_line ||= diff_file.line_for_line_code(self.line_code) if diff_file
end
def for_line?(line)
@@ -55,7 +55,7 @@ class LegacyDiffNote < Note
def active?
return @active if defined?(@active)
return true if for_commit?
- return true unless self.diff
+ return true unless diff_line
return false unless noteable
noteable_diff = find_noteable_diff
diff --git a/app/models/note.rb b/app/models/note.rb
index 8dca2ef09a8..0ce10c77de9 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -229,8 +229,7 @@ class Note < ActiveRecord::Base
end
def award_emoji_name
- original_name = note.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1]
- Gitlab::AwardEmoji.normalize_emoji_name(original_name)
+ note.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1]
end
private
diff --git a/app/models/project.rb b/app/models/project.rb
index e7b9835692d..1cdf22f6a51 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -162,7 +162,7 @@ class Project < ActiveRecord::Base
validates :namespace, presence: true
validates_uniqueness_of :name, scope: :namespace_id
validates_uniqueness_of :path, scope: :namespace_id
- validates :import_url, addressable_url: true, if: :import_url
+ validates :import_url, addressable_url: true, if: :external_import?
validates :star_count, numericality: { greater_than_or_equal_to: 0 }
validate :check_limit, on: :create
validate :avatar_type,
@@ -482,7 +482,7 @@ class Project < ActiveRecord::Base
end
def create_or_update_import_data(data: nil, credentials: nil)
- return unless valid_import_url?
+ return unless import_url.present? && valid_import_url?
project_import_data = import_data || build_import_data
if data
@@ -1038,8 +1038,8 @@ class Project < ActiveRecord::Base
pipelines.order(id: :desc).find_by(sha: sha, ref: ref)
end
- def ensure_pipeline(sha, ref)
- pipeline(sha, ref) || pipelines.create(sha: sha, ref: ref)
+ def ensure_pipeline(sha, ref, current_user = nil)
+ pipeline(sha, ref) || pipelines.create(sha: sha, ref: ref, user: current_user)
end
def enable_ci
diff --git a/app/models/user.rb b/app/models/user.rb
index 7a72c202150..3d0a033785c 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -85,6 +85,7 @@ class User < ActiveRecord::Base
has_one :abuse_report, dependent: :destroy
has_many :spam_logs, dependent: :destroy
has_many :builds, dependent: :nullify, class_name: 'Ci::Build'
+ has_many :pipelines, dependent: :nullify, class_name: 'Ci::Pipeline'
has_many :todos, dependent: :destroy
has_many :notification_settings, dependent: :destroy
has_many :award_emoji, dependent: :destroy
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index b1ee6874190..be91bf0db85 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -2,6 +2,7 @@ module Ci
class CreatePipelineService < BaseService
def execute
pipeline = project.pipelines.new(params)
+ pipeline.user = current_user
unless ref_names.include?(params[:ref])
pipeline.errors.add(:base, 'Reference not found')
diff --git a/app/services/create_commit_builds_service.rb b/app/services/create_commit_builds_service.rb
index f947e8f452e..0b66b854dea 100644
--- a/app/services/create_commit_builds_service.rb
+++ b/app/services/create_commit_builds_service.rb
@@ -14,7 +14,13 @@ class CreateCommitBuildsService
return false
end
- @pipeline = Ci::Pipeline.new(project: project, sha: sha, ref: ref, before_sha: before_sha, tag: tag)
+ @pipeline = Ci::Pipeline.new(
+ project: project,
+ sha: sha,
+ ref: ref,
+ before_sha: before_sha,
+ tag: tag,
+ user: user)
##
# Skip creating pipeline if no gitlab-ci.yml is found
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index 02fca5c0ea3..18971bd0be3 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -8,7 +8,6 @@ module Notes
if note.award_emoji?
noteable = note.noteable
todo_service.new_award_emoji(noteable, current_user)
-
return noteable.create_award_emoji(note.award_emoji_name, current_user)
end
diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb
index 6afc048576d..998789d64d2 100644
--- a/app/services/projects/import_export/export_service.rb
+++ b/app/services/projects/import_export/export_service.rb
@@ -10,7 +10,7 @@ module Projects
def save_all
if [version_saver, project_tree_saver, uploads_saver, repo_saver, wiki_repo_saver].all?(&:save)
- Gitlab::ImportExport::Saver.save(shared: @shared)
+ Gitlab::ImportExport::Saver.save(project: project, shared: @shared)
notify_success
else
cleanup_and_notify
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index 6bb0a72d30e..6b48d68cccb 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -194,7 +194,7 @@ class TodoService
end
def create_assignment_todo(issuable, author)
- if issuable.assignee && issuable.assignee != author
+ if issuable.assignee
attributes = attributes_for_todo(issuable.project, issuable, author, Todo::ASSIGNED)
create_todos(issuable.assignee, attributes)
end
@@ -239,7 +239,6 @@ class TodoService
def filter_mentioned_users(project, target, author)
mentioned_users = target.mentioned_users(author)
mentioned_users = reject_users_without_access(mentioned_users, project, target)
- mentioned_users.delete(author)
mentioned_users.uniq
end
diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml
index a373f61bd3c..4debd3d608f 100644
--- a/app/views/devise/sessions/two_factor.html.haml
+++ b/app/views/devise/sessions/two_factor.html.haml
@@ -1,3 +1,7 @@
+- if inject_u2f_api?
+ - content_for :page_specific_javascripts do
+ = page_specific_javascript_tag('u2f.js')
+
%div
.login-box
.login-heading
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index 245b9c3b4d4..f7580f00159 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -44,7 +44,7 @@
name: "#{j(@project.name)}"
};
- - if @group and @group.path
+ - if @group && @group.persisted? && @group.path
:javascript
gl.groupOptions = gl.groupOptions || {};
gl.groupOptions["#{j(@group.path)}"] = {
diff --git a/app/views/notify/note_merge_request_email.html.haml b/app/views/notify/note_merge_request_email.html.haml
index 35c4b862bb7..ea7e3d199fd 100644
--- a/app/views/notify/note_merge_request_email.html.haml
+++ b/app/views/notify/note_merge_request_email.html.haml
@@ -1,4 +1,4 @@
-- if @note.diff_note?
+- if @note.diff_note? && @note.diff_file
%p.details
New comment on diff for
= link_to @note.diff_file.file_path, @target_url
diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml
index 8780da1dec4..366f1fed35b 100644
--- a/app/views/profiles/two_factor_auths/show.html.haml
+++ b/app/views/profiles/two_factor_auths/show.html.haml
@@ -2,6 +2,10 @@
- header_title "Two-Factor Authentication", profile_two_factor_auth_path
= render 'profiles/head'
+- if inject_u2f_api?
+ - content_for :page_specific_javascripts do
+ = page_specific_javascript_tag('u2f.js')
+
.row.prepend-top-default
.col-lg-3
%h4.prepend-top-0
diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml
index b53a8633937..0557d384e33 100644
--- a/app/views/projects/ci/pipelines/_pipeline.html.haml
+++ b/app/views/projects/ci/pipelines/_pipeline.html.haml
@@ -32,7 +32,7 @@
Cant find HEAD commit for this branch
- - stages_status = pipeline.statuses.stages_status
+ - stages_status = pipeline.statuses.latest.stages_status
- stages.each do |stage|
%td
- status = stages_status[stage]
@@ -58,16 +58,7 @@
.controls.hidden-xs.pull-right
- artifacts = pipeline.builds.latest.select { |b| b.artifacts? }
- if artifacts.present?
- .btn-group.inline
- .btn-group
- %a.dropdown-toggle.btn.btn-default{type: 'button', 'data-toggle' => 'dropdown'}
- = icon("play")
- %b.caret
- %ul.dropdown-menu.dropdown-menu-align-right
- %li
- = link_to '#' do
- = icon("play")
- %span Deploy to production
+ .inline
.btn-group
%a.dropdown-toggle.btn.btn-default.build-artifacts{type: 'button', 'data-toggle' => 'dropdown'}
= icon("download")
@@ -80,7 +71,7 @@
%span Download '#{build.name}' artifacts
- if can?(current_user, :update_pipeline, @project)
- .cancel-retry-btns
+ .cancel-retry-btns.inline
- if pipeline.retryable?
= link_to retry_namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: 'btn has-tooltip', title: "Retry", method: :post do
= icon("repeat")
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index d5199bd86dd..094d6636c66 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -21,10 +21,10 @@
placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (@project.id if @project), selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } })
.filter-item.inline.milestone-filter
- = render "shared/issuable/milestone_dropdown", selected: params[:milestone_title], name: :milestone_title, show_any: true, show_upcoming: true
+ = render "shared/issuable/milestone_dropdown"
.filter-item.inline.labels-filter
- = render "shared/issuable/label_dropdown", selected: params[:label_name], data_options: { field_name: "label_name[]" }
+ = render "shared/issuable/label_dropdown"
.pull-right
= render 'shared/sort_dropdown'
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index 07b39c233b1..c30bdb0ae91 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -1,12 +1,5 @@
= form_errors(issuable)
-- if @conflict
- .alert.alert-danger
- Someone edited the #{issuable.class.model_name.human.downcase} the same time you did.
- Please check out
- = link_to "the #{issuable.class.model_name.human.downcase}", polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), target: "_blank"
- and make sure your changes will not unintentionally remove theirs
-
.form-group
= f.label :title, class: 'control-label'
.col-sm-10
@@ -59,24 +52,38 @@
= f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) }
.issuable-form-select-holder
- - project = @target_project || @project
- - if issuable.assignee_id
- = hidden_field_tag("#{issuable.class.model_name.param_key}[assignee_id]", issuable.assignee_id)
- = dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-user-search js-issuable-form-dropdown js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
- placeholder: "Search assignee", data: { first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (project.id if project), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee" } })
+ = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
+ placeholder: 'Select assignee', class: 'custom-form-control', null_user: true,
+ selected: issuable.assignee_id, project: @target_project || @project,
+ first_user: true, current_user: true, include_blank: true)
+ %div
+ = link_to 'Assign to me', '#', class: 'assign-to-me-link prepend-top-5 inline'
.form-group.issue-milestone
= f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) }
- .issuable-form-select-holder
- = render "shared/issuable/milestone_dropdown", selected: issuable.milestone_id, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false
+ - if milestone_options(issuable).present?
+ .issuable-form-select-holder
+ = f.select(:milestone_id, milestone_options(issuable),
+ { include_blank: true }, { class: 'select2', data: { placeholder: 'Select milestone' } })
+ - else
+ .prepend-top-10
+ %span.light No open milestones available.
+ - if can? current_user, :admin_milestone, issuable.project
+ %div
+ = link_to 'Create new milestone', new_namespace_project_milestone_path(issuable.project.namespace, issuable.project), target: :blank, class: "prepend-top-5 inline"
.form-group
- has_labels = issuable.project.labels.any?
- - selected_labels = issuable.label_ids.any? ? issuable.label_ids : nil
- - label_dropdown_toggle = issuable.labels.map { |label| label.title }
= f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
- .issuable-form-select-holder
- = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: selected_labels, selected_toggle: label_dropdown_toggle, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: "false" }
+ - if has_labels
+ .issuable-form-select-holder
+ = f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
+ { selected: issuable.label_ids }, multiple: true, class: 'select2', data: { placeholder: "Select labels" }
+ - else
+ %span.light No labels yet.
+ - if can? current_user, :admin_label, issuable.project
+ %div
+ = link_to 'Create new label', new_namespace_project_label_path(issuable.project.namespace, issuable.project), target: :blank, class: "prepend-top-5 inline"
- if has_due_date
.col-lg-6
.form-group
@@ -142,5 +149,3 @@
= link_to 'Delete', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), data: { confirm: "#{issuable.class.name.titleize} will be removed! Are you sure?" },
method: :delete, class: 'btn btn-danger btn-grouped'
= link_to 'Cancel', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), class: 'btn btn-grouped btn-cancel'
-
-= f.hidden_field :lock_version
diff --git a/app/views/shared/issuable/_label_dropdown.html.haml b/app/views/shared/issuable/_label_dropdown.html.haml
index bcbe133ce62..d34d28f6736 100644
--- a/app/views/shared/issuable/_label_dropdown.html.haml
+++ b/app/views/shared/issuable/_label_dropdown.html.haml
@@ -4,21 +4,19 @@
- show_footer = local_assigns.fetch(:show_footer, true)
- data_options = local_assigns.fetch(:data_options, {})
- classes = local_assigns.fetch(:classes, [])
-- selected = local_assigns.fetch(:selected, nil)
-- selected_toggle = local_assigns.fetch(:selected_toggle, nil)
-- dropdown_data = {toggle: 'dropdown', field_name: "label_name[]", show_no: "true", show_any: "true", selected: selected, project_id: @project.try(:id), labels: labels_filter_path, default_label: "Label"}
+- dropdown_data = {toggle: 'dropdown', field_name: 'label_name[]', show_no: "true", show_any: "true", selected: params[:label_name], project_id: @project.try(:id), labels: labels_filter_path, default_label: "Label"}
- dropdown_data.merge!(data_options)
- classes << 'js-extra-options' if extra_options
- classes << 'js-filter-submit' if filter_submit
-- if selected.present?
- - if selected.respond_to?('any?')
- - selected.each do |label|
- = hidden_field_tag data_options[:field_name], label, id: nil
+- if params[:label_name].present?
+ - if params[:label_name].respond_to?('any?')
+ - params[:label_name].each do |label|
+ = hidden_field_tag "label_name[]", label, id: nil
.dropdown
%button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data}
%span.dropdown-toggle-text
- = h(multi_label_name(selected_toggle || selected, "Label"))
+ = h(multi_label_name(params[:label_name], "Label"))
= icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
= render partial: "shared/issuable/label_page_default", locals: { title: "Filter by label", show_footer: show_footer, show_create: show_create }
diff --git a/app/views/shared/issuable/_milestone_dropdown.html.haml b/app/views/shared/issuable/_milestone_dropdown.html.haml
index 9188ef72d52..2fcf40ece99 100644
--- a/app/views/shared/issuable/_milestone_dropdown.html.haml
+++ b/app/views/shared/issuable/_milestone_dropdown.html.haml
@@ -1,7 +1,7 @@
-- if selected.present?
- = hidden_field_tag(name, selected)
-= dropdown_tag(milestone_dropdown_label(selected), options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit', filter: true, dropdown_class: "dropdown-menu-selectable",
- placeholder: "Search milestones", footer_content: @project.present?, data: { show_no: true, show_any: show_any, show_upcoming: show_upcoming, field_name: name, selected: selected, project_id: @project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
+- if params[:milestone_title].present?
+ = hidden_field_tag(:milestone_title, params[:milestone_title])
+= dropdown_tag(milestone_dropdown_label(params[:milestone_title]), options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit', filter: true, dropdown_class: "dropdown-menu-selectable",
+ placeholder: "Search milestones", footer_content: @project.present?, data: { show_no: true, show_any: true, show_upcoming: true, field_name: "milestone_title", selected: params[:milestone_title], project_id: @project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
- if @project
%ul.dropdown-footer-list
- if can? current_user, :admin_milestone, @project
diff --git a/app/workers/project_export_worker.rb b/app/workers/project_export_worker.rb
index 39f6037e077..615311e63f5 100644
--- a/app/workers/project_export_worker.rb
+++ b/app/workers/project_export_worker.rb
@@ -1,7 +1,7 @@
class ProjectExportWorker
include Sidekiq::Worker
- sidekiq_options queue: :gitlab_shell, retry: true
+ sidekiq_options queue: :gitlab_shell, retry: 3
def perform(current_user_id, project_id)
current_user = User.find(current_user_id)
diff --git a/config/application.rb b/config/application.rb
index 21e7cc7b6e8..5f7b6a3c049 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -87,6 +87,7 @@ module Gitlab
config.assets.precompile << "profile/application.js"
config.assets.precompile << "lib/utils/*.js"
config.assets.precompile << "lib/*.js"
+ config.assets.precompile << "u2f.js"
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'
diff --git a/db/migrate/20160707104333_add_lock_to_issuables.rb b/db/migrate/20160707104333_add_lock_to_issuables.rb
deleted file mode 100644
index cb516672800..00000000000
--- a/db/migrate/20160707104333_add_lock_to_issuables.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# See http://doc.gitlab.com/ce/development/migration_style_guide.html
-# for more information on how to write migrations for GitLab.
-
-class AddLockToIssuables < ActiveRecord::Migration
- include Gitlab::Database::MigrationHelpers
- disable_ddl_transaction!
-
- def up
- add_column_with_default :issues, :lock_version, :integer, default: 0
- add_column_with_default :merge_requests, :lock_version, :integer, default: 0
- end
-
- def down
- remove_column :issues, :lock_version
- remove_column :merge_requests, :lock_version
- end
-end
diff --git a/db/migrate/20160715132507_add_user_id_to_pipeline.rb b/db/migrate/20160715132507_add_user_id_to_pipeline.rb
new file mode 100644
index 00000000000..af0461c4daf
--- /dev/null
+++ b/db/migrate/20160715132507_add_user_id_to_pipeline.rb
@@ -0,0 +1,7 @@
+class AddUserIdToPipeline < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ def change
+ add_column :ci_commits, :user_id, :integer
+ end
+end
diff --git a/db/migrate/20160715134306_add_index_for_pipeline_user_id.rb b/db/migrate/20160715134306_add_index_for_pipeline_user_id.rb
new file mode 100644
index 00000000000..e09caa0e6d7
--- /dev/null
+++ b/db/migrate/20160715134306_add_index_for_pipeline_user_id.rb
@@ -0,0 +1,7 @@
+class AddIndexForPipelineUserId < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ def change
+ add_concurrent_index :ci_commits, :user_id
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 4a0df65aafb..d250d65fe21 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -70,11 +70,11 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.string "recaptcha_site_key"
t.string "recaptcha_private_key"
t.integer "metrics_port", default: 8089
+ t.boolean "akismet_enabled", default: false
+ t.string "akismet_api_key"
t.integer "metrics_sample_interval", default: 15
t.boolean "sentry_enabled", default: false
t.string "sentry_dsn"
- t.boolean "akismet_enabled", default: false
- t.string "akismet_api_key"
t.boolean "email_author_in_body", default: false
t.integer "default_group_visibility"
t.boolean "repository_checks_enabled", default: false
@@ -84,10 +84,10 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.string "health_check_access_token"
t.boolean "send_user_confirmation_email", default: false
t.integer "container_registry_token_expire_delay", default: 5
+ t.boolean "user_default_external", default: false, null: false
t.text "after_sign_up_text"
t.string "repository_storage", default: "default"
t.string "enabled_git_access_protocol"
- t.boolean "user_default_external", default: false, null: false
end
create_table "audit_events", force: :cascade do |t|
@@ -165,8 +165,8 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.text "artifacts_metadata"
t.integer "erased_by_id"
t.datetime "erased_at"
- t.datetime "artifacts_expire_at"
t.string "environment"
+ t.datetime "artifacts_expire_at"
t.integer "artifacts_size"
t.string "when"
t.text "yaml_variables"
@@ -201,6 +201,7 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.datetime "started_at"
t.datetime "finished_at"
t.integer "duration"
+ t.integer "user_id"
end
add_index "ci_commits", ["gl_project_id", "sha"], name: "index_ci_commits_on_gl_project_id_and_sha", using: :btree
@@ -212,6 +213,7 @@ ActiveRecord::Schema.define(version: 20160716115710) do
add_index "ci_commits", ["project_id"], name: "index_ci_commits_on_project_id", using: :btree
add_index "ci_commits", ["sha"], name: "index_ci_commits_on_sha", using: :btree
add_index "ci_commits", ["status"], name: "index_ci_commits_on_status", using: :btree
+ add_index "ci_commits", ["user_id"], name: "index_ci_commits_on_user_id", using: :btree
create_table "ci_events", force: :cascade do |t|
t.integer "project_id"
@@ -483,11 +485,10 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.string "state"
t.integer "iid"
t.integer "updated_by_id"
- t.integer "moved_to_id"
t.boolean "confidential", default: false
t.datetime "deleted_at"
t.date "due_date"
- t.integer "lock_version", default: 0, null: false
+ t.integer "moved_to_id"
end
add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree
@@ -627,7 +628,6 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.integer "merge_user_id"
t.string "merge_commit_sha"
t.datetime "deleted_at"
- t.integer "lock_version", default: 0, null: false
end
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
@@ -777,10 +777,10 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.integer "user_id", null: false
t.string "token", null: false
t.string "name", null: false
- t.boolean "revoked", default: false
- t.datetime "expires_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.boolean "revoked", default: false
+ t.datetime "expires_at"
end
add_index "personal_access_tokens", ["token"], name: "index_personal_access_tokens_on_token", unique: true, using: :btree
@@ -900,9 +900,9 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.string "type"
t.string "title"
t.integer "project_id"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
- t.boolean "active", null: false
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.boolean "active", default: false, null: false
t.text "properties"
t.boolean "template", default: false
t.boolean "push_events", default: true
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 16a1461a7e4..50fa263f693 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -985,11 +985,11 @@ directive defined in `.postgres_services` and `.mysql_services` respectively:
- ruby
test:postgres:
- << *job_definition
+ <<: *job_definition
services: *postgres_definition
test:mysql:
- << *job_definition
+ <<: *job_definition
services: *mysql_definition
```
diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md
index 975bb82c37d..fac35ec964d 100644
--- a/doc/development/doc_styleguide.md
+++ b/doc/development/doc_styleguide.md
@@ -44,7 +44,7 @@ it organized and easy to find.
- When introducing a new document, be careful for the headings to be
grammatically and syntactically correct. It is advised to mention one or all
of the following GitLab members for a review: `@axil`, `@rspeicher`,
- `@dblessing`, `@ashleys`, `@nearlythere`. This is to ensure that no document
+ `@dblessing`, `@ashleys`. This is to ensure that no document
with wrong heading is going live without an audit, thus preventing dead links
and redirection issues when corrected
- Leave exactly one newline after a heading
diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature
index 80670063ea0..358e622b736 100644
--- a/features/project/issues/issues.feature
+++ b/features/project/issues/issues.feature
@@ -37,7 +37,6 @@ Feature: Project Issues
And I submit new issue "500 error on profile"
Then I should see issue "500 error on profile"
- @javascript
Scenario: I submit new unassigned issue with labels
Given project "Shop" has labels: "bug", "feature", "enhancement"
And I click link "New Issue"
diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature
index 8176ec5ab45..21768c15c17 100644
--- a/features/project/merge_requests.feature
+++ b/features/project/merge_requests.feature
@@ -89,7 +89,7 @@ Feature: Project Merge Requests
Then The list should be sorted by "Oldest updated"
@javascript
- Scenario: Visiting Merge Requests from a different Project after sorting
+ Scenario: Visiting Merge Requests from a differente Project after sorting
Given I visit project "Shop" merge requests page
And I sort the list by "Oldest updated"
And I visit dashboard merge requests page
diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb
index 8f71dfdd899..6b56a77b832 100644
--- a/features/steps/project/forked_merge_requests.rb
+++ b/features/steps/project/forked_merge_requests.rb
@@ -135,17 +135,19 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
end
step 'I click "Assign to" dropdown"' do
- click_button 'Assignee'
+ first('.ajax-users-select').click
end
step 'I should see the target project ID in the input selector' do
- expect(find('.js-assignee-search')["data-project-id"]).to eq "#{@project.id}"
+ expect(page).to have_selector("input[data-project-id=\"#{@project.id}\"]")
end
step 'I should see the users from the target project ID' do
- expect(page).to have_content 'Unassigned'
- expect(page).to have_content current_user.name
- expect(page).to have_content @project.users.first.name
+ expect(page).to have_selector('.user-result', visible: true, count: 3)
+ users = page.all('.user-name')
+ expect(users[0].text).to eq 'Unassigned'
+ expect(users[1].text).to eq current_user.name
+ expect(users[2].text).to eq @project.users.first.name
end
# Verify a link is generated against the correct project
diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb
index b785e15f70e..35f166c7c08 100644
--- a/features/steps/project/issues/issues.rb
+++ b/features/steps/project/issues/issues.rb
@@ -82,8 +82,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
step 'I submit new issue "500 error on profile" with label \'bug\'' do
fill_in "issue_title", with: "500 error on profile"
- click_button "Label"
- click_link "bug"
+ select 'bug', from: "Labels"
click_button "Submit issue"
end
diff --git a/lib/api/award_emoji.rb b/lib/api/award_emoji.rb
index c4fa1838b5a..2efe7e3adf3 100644
--- a/lib/api/award_emoji.rb
+++ b/lib/api/award_emoji.rb
@@ -56,9 +56,9 @@ module API
not_found!('Award Emoji') unless can_read_awardable?
- award = awardable.award_emoji.new(name: params[:name], user: current_user)
+ award = awardable.create_award_emoji(params[:name], current_user)
- if award.save
+ if award.persisted?
present award, with: Entities::AwardEmoji
else
not_found!("Award Emoji #{award.errors.messages}")
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index 323a7086890..acb4812b5cf 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -64,7 +64,7 @@ module API
ref = branches.first
end
- pipeline = @project.ensure_pipeline(commit.sha, ref)
+ pipeline = @project.ensure_pipeline(commit.sha, ref, current_user)
name = params[:name] || params[:context]
status = GenericCommitStatus.running_or_pending.find_by(pipeline: pipeline, name: name, ref: params[:ref])
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index d5dfba5e0cc..959b700de78 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -63,7 +63,12 @@ module API
if access_status.status
# Return the repository full path so that gitlab-shell has it when
# handling ssh commands
- response[:repository_path] = project.repository.path_to_repo
+ response[:repository_path] =
+ if wiki?
+ project.wiki.repository.path_to_repo
+ else
+ project.repository.path_to_repo
+ end
end
response
diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb
index 536b478979f..91f0159f9a1 100644
--- a/lib/banzai/filter/syntax_highlight_filter.rb
+++ b/lib/banzai/filter/syntax_highlight_filter.rb
@@ -19,14 +19,22 @@ module Banzai
language = node.attr('class')
code = node.text
+ css_classes = "code highlight"
+
+ lexer = Rouge::Lexer.find_fancy(language) || Rouge::Lexers::PlainText
+ formatter = Rouge::Formatters::HTML.new
+
begin
- highlighted = block_code(code, language)
+ code = formatter.format(lexer.lex(code))
+
+ css_classes << " js-syntax-highlight #{lexer.tag}"
rescue
# Gracefully handle syntax highlighter bugs/errors to ensure
# users can still access an issue/comment/etc.
- highlighted = "<pre>#{code}</pre>"
end
+ highlighted = %(<pre class="#{css_classes}"><code>#{code}</code></pre>)
+
# Extracted to a method to measure it
replace_parent_pre_element(node, highlighted)
end
@@ -40,8 +48,7 @@ module Banzai
# Override Rouge::Plugins::Redcarpet#rouge_formatter
def rouge_formatter(lexer)
- Rouge::Formatters::HTMLGitlab.new(
- cssclass: "code highlight js-syntax-highlight #{lexer.tag}")
+ Rouge::Formatters::HTML.new
end
end
end
diff --git a/lib/banzai/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb
index 5b0a6d8541b..e1ca7f4d24b 100644
--- a/lib/banzai/filter/user_reference_filter.rb
+++ b/lib/banzai/filter/user_reference_filter.rb
@@ -112,7 +112,7 @@ module Banzai
data = data_attribute(project: project.id, author: author.try(:id))
text = link_text || User.reference_prefix + 'all'
- link_tag(url, data, text)
+ link_tag(url, data, text, 'All Project and Group Members')
end
def link_to_namespace(namespace, link_text: nil)
@@ -128,7 +128,7 @@ module Banzai
data = data_attribute(group: namespace.id)
text = link_text || Group.reference_prefix + group
- link_tag(url, data, text)
+ link_tag(url, data, text, namespace.name)
end
def link_to_user(user, namespace, link_text: nil)
@@ -136,11 +136,11 @@ module Banzai
data = data_attribute(user: namespace.owner_id)
text = link_text || User.reference_prefix + user
- link_tag(url, data, text)
+ link_tag(url, data, text, namespace.owner_name)
end
- def link_tag(url, data, text)
- %(<a href="#{url}" #{data} class="#{link_class}">#{escape_once(text)}</a>)
+ def link_tag(url, data, text, title)
+ %(<a href="#{url}" #{data} class="#{link_class}" title="#{escape_once(title)}">#{escape_once(text)}</a>)
end
end
end
diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb
index 478f145bfed..ab94abeda77 100644
--- a/lib/gitlab/backend/grack_auth.rb
+++ b/lib/gitlab/backend/grack_auth.rb
@@ -63,7 +63,7 @@ module Grack
def ci_request?(login, password)
matched_login = /(?<s>^[a-zA-Z]*-ci)-token$/.match(login)
- if project && matched_login.present? && git_cmd == 'git-upload-pack'
+ if project && matched_login.present?
underscored_service = matched_login['s'].underscore
if underscored_service == 'gitlab_ci'
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index dec20d8659b..927f9dad20b 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -20,11 +20,19 @@ module Gitlab
if Database.postgresql?
options = options.merge({ algorithm: :concurrently })
+ disable_statement_timeout
end
add_index(table_name, column_name, options)
end
+ # Long-running migrations may take more than the timeout allowed by
+ # the database. Disable the session's statement timeout to ensure
+ # migrations don't get killed prematurely. (PostgreSQL only)
+ def disable_statement_timeout
+ ActiveRecord::Base.connection.execute('SET statement_timeout TO 0') if Database.postgresql?
+ end
+
# Updates the value of a column in batches.
#
# This method updates the table in batches of 5% of the total row count.
@@ -133,6 +141,8 @@ module Gitlab
'in the body of your migration class'
end
+ disable_statement_timeout
+
transaction do
add_column(table, column, type, default: nil)
diff --git a/lib/gitlab/gitlab_import/importer.rb b/lib/gitlab/gitlab_import/importer.rb
index e6d31ea04c0..46d40f75be6 100644
--- a/lib/gitlab/gitlab_import/importer.rb
+++ b/lib/gitlab/gitlab_import/importer.rb
@@ -15,32 +15,35 @@ module Gitlab
end
def execute
- project_identifier = CGI.escape(project.import_source)
-
- # Issues && Comments
- issues = client.issues(project_identifier)
-
- issues.each do |issue|
- body = @formatter.author_line(issue["author"]["name"])
- body += issue["description"]
-
- comments = client.issue_comments(project_identifier, issue["id"])
-
- if comments.any?
- body += @formatter.comments_header
+ ActiveRecord::Base.no_touching do
+ project_identifier = CGI.escape(project.import_source)
+
+ # Issues && Comments
+ issues = client.issues(project_identifier)
+
+ issues.each do |issue|
+ body = @formatter.author_line(issue["author"]["name"])
+ body += issue["description"]
+
+ comments = client.issue_comments(project_identifier, issue["id"])
+
+ if comments.any?
+ body += @formatter.comments_header
+ end
+
+ comments.each do |comment|
+ body += @formatter.comment(comment["author"]["name"], comment["created_at"], comment["body"])
+ end
+
+ project.issues.create!(
+ iid: issue["iid"],
+ description: body,
+ title: issue["title"],
+ state: issue["state"],
+ updated_at: issue["updated_at"],
+ author_id: gl_user_id(project, issue["author"]["id"])
+ )
end
-
- comments.each do |comment|
- body += @formatter.comment(comment["author"]["name"], comment["created_at"], comment["body"])
- end
-
- project.issues.create!(
- iid: issue["iid"],
- description: body,
- title: issue["title"],
- state: issue["state"],
- author_id: gl_user_id(project, issue["author"]["id"])
- )
end
true
diff --git a/lib/gitlab/highlight.rb b/lib/gitlab/highlight.rb
index 41296415e35..9360afedfcb 100644
--- a/lib/gitlab/highlight.rb
+++ b/lib/gitlab/highlight.rb
@@ -1,7 +1,7 @@
module Gitlab
class Highlight
- def self.highlight(blob_name, blob_content, repository: nil, nowrap: true, plain: false)
- new(blob_name, blob_content, nowrap: nowrap, repository: repository).
+ def self.highlight(blob_name, blob_content, repository: nil, plain: false)
+ new(blob_name, blob_content, repository: repository).
highlight(blob_content, continue: false, plain: plain)
end
@@ -13,30 +13,34 @@ module Gitlab
highlight(file_name, blob.data, repository: repository).lines.map!(&:html_safe)
end
- attr_reader :lexer
- def initialize(blob_name, blob_content, repository: nil, nowrap: true)
+ def initialize(blob_name, blob_content, repository: nil)
+ @formatter = Rouge::Formatters::HTMLGitlab.new
+ @repository = repository
@blob_name = blob_name
@blob_content = blob_content
- @repository = repository
- @formatter = rouge_formatter(nowrap: nowrap)
-
- @lexer = custom_language || begin
- Rouge::Lexer.guess(filename: blob_name, source: blob_content).new
- rescue Rouge::Lexer::AmbiguousGuess => e
- e.alternatives.sort_by(&:tag).first
- end
end
def highlight(text, continue: true, plain: false)
if plain
- @formatter.format(Rouge::Lexers::PlainText.lex(text)).html_safe
+ hl_lexer = Rouge::Lexers::PlainText
+ continue = false
else
- @formatter.format(@lexer.lex(text, continue: continue)).html_safe
+ hl_lexer = self.lexer
end
+
+ @formatter.format(hl_lexer.lex(text, continue: continue)).html_safe
rescue
@formatter.format(Rouge::Lexers::PlainText.lex(text)).html_safe
end
+ def lexer
+ @lexer ||= custom_language || begin
+ Rouge::Lexer.guess(filename: @blob_name, source: @blob_content).new
+ rescue Rouge::Guesser::Ambiguous => e
+ e.alternatives.sort_by(&:tag).first
+ end
+ end
+
private
def custom_language
@@ -46,16 +50,5 @@ module Gitlab
Rouge::Lexer.find_fancy(language_name)
end
-
- def rouge_formatter(options = {})
- options = options.reverse_merge(
- nowrap: true,
- cssclass: 'code highlight',
- lineanchors: true,
- lineanchorsid: 'LC'
- )
-
- Rouge::Formatters::HTMLGitlab.new(options)
- end
end
end
diff --git a/lib/gitlab/import_export.rb b/lib/gitlab/import_export.rb
index 588647e5adb..bab2ea73c4f 100644
--- a/lib/gitlab/import_export.rb
+++ b/lib/gitlab/import_export.rb
@@ -3,6 +3,7 @@ module Gitlab
extend self
VERSION = '0.1.1'
+ FILENAME_LIMIT = 50
def export_path(relative_path:)
File.join(storage_path, relative_path)
@@ -28,6 +29,12 @@ module Gitlab
'VERSION'
end
+ def export_filename(project:)
+ basename = "#{Time.now.strftime('%Y-%m-%d_%H-%M-%3N')}_#{project.namespace.path}_#{project.path}"
+
+ "#{basename[0..FILENAME_LIMIT]}_export.tar.gz"
+ end
+
def version
VERSION
end
diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb
index 025ecc12f9f..051110c23cf 100644
--- a/lib/gitlab/import_export/project_tree_restorer.rb
+++ b/lib/gitlab/import_export/project_tree_restorer.rb
@@ -12,7 +12,10 @@ module Gitlab
json = IO.read(@path)
@tree_hash = ActiveSupport::JSON.decode(json)
@project_members = @tree_hash.delete('project_members')
- create_relations
+
+ ActiveRecord::Base.no_touching do
+ create_relations
+ end
rescue => e
@shared.error(e)
false
diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb
index 9824df3f274..6ba25a31641 100644
--- a/lib/gitlab/import_export/relation_factory.rb
+++ b/lib/gitlab/import_export/relation_factory.rb
@@ -87,7 +87,7 @@ module Gitlab
project_id = @relation_hash.delete('project_id')
# project_id may not be part of the export, but we always need to populate it if required.
- @relation_hash['project_id'] = project_id if relation_class.column_names.include?('project_id')
+ @relation_hash['project_id'] = project_id
@relation_hash['gl_project_id'] = project_id if @relation_hash['gl_project_id']
@relation_hash['target_project_id'] = project_id if @relation_hash['target_project_id']
@relation_hash['source_project_id'] = -1 if @relation_hash['source_project_id']
@@ -111,7 +111,7 @@ module Gitlab
end
def imported_object
- imported_object = relation_class.new(@relation_hash)
+ imported_object = relation_class.new(parsed_relation_hash)
yield(imported_object) if block_given?
imported_object.importing = true if imported_object.respond_to?(:importing)
imported_object
@@ -125,6 +125,10 @@ module Gitlab
def admin_user?
@user.is_admin?
end
+
+ def parsed_relation_hash
+ @relation_hash.reject { |k, _v| !relation_class.attribute_method?(k) }
+ end
end
end
end
diff --git a/lib/gitlab/import_export/saver.rb b/lib/gitlab/import_export/saver.rb
index 6a60b65071f..6130c124dd1 100644
--- a/lib/gitlab/import_export/saver.rb
+++ b/lib/gitlab/import_export/saver.rb
@@ -7,7 +7,8 @@ module Gitlab
new(*args).save
end
- def initialize(shared:)
+ def initialize(project:, shared:)
+ @project = project
@shared = shared
end
@@ -36,7 +37,7 @@ module Gitlab
end
def archive_file
- @archive_file ||= File.join(@shared.export_path, '..', "#{Time.now.strftime('%Y-%m-%d_%H-%M-%3N')}_project_export.tar.gz")
+ @archive_file ||= File.join(@shared.export_path, '..', Gitlab::ImportExport.export_filename(project: @project))
end
end
end
diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb
index 811363405a8..a1ee1aa81ff 100644
--- a/lib/gitlab/lfs/response.rb
+++ b/lib/gitlab/lfs/response.rb
@@ -47,6 +47,8 @@ module Gitlab
end
def render_storage_upload_store_response(oid, size, tmp_file_name)
+ return render_forbidden unless tmp_file_name
+
render_response_to_push do
render_lfs_upload_ok(oid, size, tmp_file_name)
end
diff --git a/lib/gitlab/lfs/router.rb b/lib/gitlab/lfs/router.rb
index 69bd5e62305..f2a76a56b8f 100644
--- a/lib/gitlab/lfs/router.rb
+++ b/lib/gitlab/lfs/router.rb
@@ -74,8 +74,6 @@ module Gitlab
lfs.render_storage_upload_authorize_response(oid, size)
else
tmp_file_name = sanitize_tmp_filename(@request.env['HTTP_X_GITLAB_LFS_TMP'])
- return nil unless tmp_file_name
-
lfs.render_storage_upload_store_response(oid, size, tmp_file_name)
end
end
diff --git a/lib/rouge/formatters/html_gitlab.rb b/lib/rouge/formatters/html_gitlab.rb
index 3358ed6773e..f818dc78d34 100644
--- a/lib/rouge/formatters/html_gitlab.rb
+++ b/lib/rouge/formatters/html_gitlab.rb
@@ -1,171 +1,27 @@
-require 'cgi'
-
module Rouge
module Formatters
- class HTMLGitlab < Rouge::Formatter
+ class HTMLGitlab < Rouge::Formatters::HTML
tag 'html_gitlab'
# Creates a new <tt>Rouge::Formatter::HTMLGitlab</tt> instance.
#
- # [+nowrap+] If set to True, don't wrap the output at all, not
- # even inside a <tt><pre></tt> tag (default: false).
- # [+cssclass+] CSS class for the wrapping <tt><div></tt> tag
- # (default: 'highlight').
- # [+linenos+] If set to 'table', output line numbers as a table
- # with two cells, one containing the line numbers,
- # the other the whole code. This is copy paste friendly,
- # but may cause alignment problems with some browsers
- # or fonts. If set to 'inline', the line numbers will
- # be integrated in the <tt><pre></tt> tag that contains
- # the code (default: nil).
# [+linenostart+] The line number for the first line (default: 1).
- # [+lineanchors+] If set to true the formatter will wrap each output
- # line in an anchor tag with a name of L-linenumber.
- # This allows easy linking to certain lines
- # (default: false).
- # [+lineanchorsid+] If lineanchors is true the name of the anchors can
- # be changed with lineanchorsid to e.g. foo-linenumber
- # (default: 'L').
- # [+anchorlinenos+] If set to true, will wrap line numbers in <tt><a></tt>
- # tags. Used in combination with linenos and lineanchors
- # (default: false).
- # [+inline_theme+] Inline CSS styles for the <pre> tag (default: false).
- def initialize(
- nowrap: false,
- cssclass: 'highlight',
- linenos: nil,
- linenostart: 1,
- lineanchors: false,
- lineanchorsid: 'L',
- anchorlinenos: false,
- inline_theme: nil
- )
- @nowrap = nowrap
- @cssclass = cssclass
- @linenos = linenos
+ def initialize(linenostart: 1)
@linenostart = linenostart
- @lineanchors = lineanchors
- @lineanchorsid = lineanchorsid
- @anchorlinenos = anchorlinenos
- @inline_theme = Theme.find(inline_theme).new if inline_theme.is_a?(String)
- end
-
- def render(tokens)
- case @linenos
- when 'table'
- render_tableized(tokens)
- when 'inline'
- render_untableized(tokens)
- else
- render_untableized(tokens)
- end
- end
-
- alias_method :format, :render
-
- private
-
- def render_untableized(tokens)
- data = process_tokens(tokens)
-
- html = ''
- html << "<pre class=\"#{@cssclass}\"><code>" unless @nowrap
- html << wrap_lines(data[:code])
- html << "</code></pre>\n" unless @nowrap
- html
+ @line_number = linenostart
end
- def render_tableized(tokens)
- data = process_tokens(tokens)
-
- html = ''
- html << "<div class=\"#{@cssclass}\">" unless @nowrap
- html << '<table><tbody>'
- html << "<td class=\"linenos\"><pre>"
- html << wrap_linenos(data[:numbers])
- html << '</pre></td>'
- html << "<td class=\"lines\"><pre><code>"
- html << wrap_lines(data[:code])
- html << '</code></pre></td>'
- html << '</tbody></table>'
- html << '</div>' unless @nowrap
- html
- end
-
- def process_tokens(tokens)
- rendered = []
- current_line = ''
-
- tokens.each do |tok, val|
- # In the case of multi-line values (e.g. comments), we need to apply
- # styling to each line since span elements are inline.
- val.lines.each do |line|
- stripped = line.chomp
- current_line << span(tok, stripped)
-
- if line.end_with?("\n")
- rendered << current_line
- current_line = ''
- end
- end
- end
-
- # Add leftover text
- rendered << current_line if current_line.present?
-
- num_lines = rendered.size
- numbers = (@linenostart..num_lines + @linenostart - 1).to_a
-
- { numbers: numbers, code: rendered }
- end
-
- def wrap_linenos(numbers)
- if @anchorlinenos
- numbers.map! do |number|
- "<a href=\"##{@lineanchorsid}#{number}\">#{number}</a>"
- end
- end
- numbers.join("\n")
- end
-
- def wrap_lines(lines)
- if @lineanchors
- lines = lines.each_with_index.map do |line, index|
- number = index + @linenostart
-
- if @linenos == 'inline'
- "<a name=\"L#{number}\"></a>" \
- "<span class=\"linenos\">#{number}</span>" \
- "<span id=\"#{@lineanchorsid}#{number}\" class=\"line\">#{line}" \
- '</span>'
- else
- "<span id=\"#{@lineanchorsid}#{number}\" class=\"line\">#{line}" \
- '</span>'
- end
- end
- elsif @linenos == 'inline'
- lines = lines.each_with_index.map do |line, index|
- number = index + @linenostart
- "<span class=\"linenos\">#{number}</span>#{line}"
- end
- end
-
- lines.join("\n")
- end
+ def stream(tokens, &b)
+ is_first = true
+ token_lines(tokens) do |line|
+ yield "\n" unless is_first
+ is_first = false
- def span(tok, val)
- # http://stackoverflow.com/a/1600584/2587286
- val = CGI.escapeHTML(val)
+ yield %(<span id="LC#{@line_number}" class="line">)
+ line.each { |token, value| yield span(token, value) }
+ yield %(</span>)
- if tok.shortname.empty?
- val
- else
- if @inline_theme
- rules = @inline_theme.style_for(tok).rendered_rules
- "<span style=\"#{rules.to_a.join(';')}\"#{val}</span>"
- else
- "<span class=\"#{tok.shortname}\">#{val}</span>"
- end
+ @line_number += 1
end
end
end
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index 891df65216d..2d8b59472e8 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -1,14 +1,26 @@
require 'spec_helper'
feature 'Group', feature: true do
+ before do
+ login_as(:admin)
+ end
+
+ describe 'creating a group with space in group path' do
+ it 'renders new group form with validation errors' do
+ visit new_group_path
+ fill_in 'Group path', with: 'space group'
+
+ click_button 'Create group'
+
+ expect(current_path).to eq(groups_path)
+ expect(page).to have_content("Path can contain only letters, digits, '_', '-' and '.'. Cannot start with '-' or end in '.'.")
+ end
+ end
+
describe 'description' do
let(:group) { create(:group) }
let(:path) { group_path(group) }
- before do
- login_as(:admin)
- end
-
it 'parses Markdown' do
group.update_attribute(:description, 'This is **my** group')
visit path
diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb
index 055210399a7..7773c486b4e 100644
--- a/spec/features/issues/move_spec.rb
+++ b/spec/features/issues/move_spec.rb
@@ -55,7 +55,7 @@ feature 'issue move to another project' do
first('.select2-choice').click
end
- fill_in('s2id_autogen1_search', with: new_project_search.name)
+ fill_in('s2id_autogen2_search', with: new_project_search.name)
page.within '.select2-drop' do
expect(page).to have_content(new_project_search.name)
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index 3fec75a07df..d51c9abea19 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -50,8 +50,9 @@ describe 'Issues', feature: true do
expect(page).to have_content "Assignee #{@user.name}"
- first('.js-user-search').click
- click_link 'Unassigned'
+ first('#s2id_issue_assignee_id').click
+ sleep 2 # wait for ajax stuff to complete
+ first('.user-result').click
click_button 'Save changes'
@@ -120,17 +121,6 @@ describe 'Issues', feature: true do
expect(page).to have_content date.to_s(:medium)
end
end
-
- it 'warns about version conflict' do
- issue.update(title: "New title")
-
- fill_in 'issue_title', with: 'bug 345'
- fill_in 'issue_description', with: 'bug description'
-
- click_button 'Save changes'
-
- expect(page).to have_content 'Someone edited the issue the same time you did'
- end
end
end
diff --git a/spec/features/merge_requests/edit_mr_spec.rb b/spec/features/merge_requests/edit_mr_spec.rb
index 8ad884492d1..9e007ab7635 100644
--- a/spec/features/merge_requests/edit_mr_spec.rb
+++ b/spec/features/merge_requests/edit_mr_spec.rb
@@ -17,16 +17,5 @@ feature 'Edit Merge Request', feature: true do
it 'form should have class js-quick-submit' do
expect(page).to have_selector('.js-quick-submit')
end
-
- it 'warns about version conflict' do
- merge_request.update(title: "New title")
-
- fill_in 'merge_request_title', with: 'bug 345'
- fill_in 'merge_request_description', with: 'bug description'
-
- click_button 'Save changes'
-
- expect(page).to have_content 'Someone edited the merge request the same time you did'
- end
end
end
diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb
index 13d980a326f..b6acc509342 100644
--- a/spec/features/security/project/internal_access_spec.rb
+++ b/spec/features/security/project/internal_access_spec.rb
@@ -426,4 +426,23 @@ describe "Internal Project Access", feature: true do
it { is_expected.to be_denied_for :external }
it { is_expected.to be_denied_for :visitor }
end
+
+ describe "GET /:project_path/container_registry" do
+ before do
+ stub_container_registry_tags('latest')
+ stub_container_registry_config(enabled: true)
+ end
+
+ subject { namespace_project_container_registry_index_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for owner }
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for developer }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_allowed_for guest }
+ it { is_expected.to be_allowed_for :user }
+ it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_denied_for :visitor }
+ end
end
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index ac9690cc127..ccb5c06dab0 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -362,4 +362,23 @@ describe "Private Project Access", feature: true do
it { is_expected.to be_denied_for :external }
it { is_expected.to be_denied_for :visitor }
end
+
+ describe "GET /:project_path/container_registry" do
+ before do
+ stub_container_registry_tags('latest')
+ stub_container_registry_config(enabled: true)
+ end
+
+ subject { namespace_project_container_registry_index_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for owner }
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for developer }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_denied_for guest }
+ it { is_expected.to be_denied_for :user }
+ it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_denied_for :visitor }
+ end
end
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index 737897de52b..985663e7c98 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -426,4 +426,23 @@ describe "Public Project Access", feature: true do
it { is_expected.to be_denied_for :external }
it { is_expected.to be_denied_for :visitor }
end
+
+ describe "GET /:project_path/container_registry" do
+ before do
+ stub_container_registry_tags('latest')
+ stub_container_registry_config(enabled: true)
+ end
+
+ subject { namespace_project_container_registry_index_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for owner }
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for developer }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_allowed_for guest }
+ it { is_expected.to be_allowed_for :user }
+ it { is_expected.to be_allowed_for :external }
+ it { is_expected.to be_allowed_for :visitor }
+ end
end
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
index 14613754f74..9335f5bf120 100644
--- a/spec/features/u2f_spec.rb
+++ b/spec/features/u2f_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: true, js: true do
+ before { allow_any_instance_of(U2fHelper).to receive(:inject_u2f_api?).and_return(true) }
+
def register_u2f_device(u2f_device = nil)
u2f_device ||= FakeU2fDevice.new(page)
u2f_device.respond_to_u2f_registration
@@ -208,21 +210,52 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
expect(page.body).to match('Authentication via U2F device failed')
end
end
- end
- describe "when two-factor authentication is disabled" do
- let(:user) { create(:user) }
+ describe "when more than one device has been registered by the same user" do
+ it "allows logging in with either device" do
+ # Register first device
+ user = login_as(:user)
+ user.update_attribute(:otp_required_for_login, true)
+ visit profile_two_factor_auth_path
+ expect(page).to have_content("Your U2F device needs to be set up.")
+ first_device = register_u2f_device
+
+ # Register second device
+ visit profile_two_factor_auth_path
+ expect(page).to have_content("Your U2F device needs to be set up.")
+ second_device = register_u2f_device
+ logout
+
+ # Authenticate as both devices
+ [first_device, second_device].each do |device|
+ login_as(user)
+ device.respond_to_u2f_authentication
+ click_on "Login Via U2F Device"
+ expect(page.body).to match('We heard back from your U2F device')
+ click_on "Authenticate via U2F Device"
- before do
- login_as(user)
- user.update_attribute(:otp_required_for_login, true)
- visit profile_account_path
- click_on 'Manage Two-Factor Authentication'
- register_u2f_device
+ expect(page.body).to match('Signed in successfully')
+
+ logout
+ end
+ end
end
- it "deletes u2f registrations" do
- expect { click_on "Disable" }.to change { U2fRegistration.count }.from(1).to(0)
+ describe "when two-factor authentication is disabled" do
+ let(:user) { create(:user) }
+
+ before do
+ user = login_as(:user)
+ user.update_attribute(:otp_required_for_login, true)
+ visit profile_account_path
+ click_on 'Manage Two-Factor Authentication'
+ expect(page).to have_content("Your U2F device needs to be set up.")
+ register_u2f_device
+ end
+
+ it "deletes u2f registrations" do
+ expect { click_on "Disable" }.to change { U2fRegistration.count }.by(-1)
+ end
end
end
end
diff --git a/spec/fixtures/parallel_diff_result.yml b/spec/fixtures/parallel_diff_result.yml
index 333eda1191a..37066c8e930 100644
--- a/spec/fixtures/parallel_diff_result.yml
+++ b/spec/fixtures/parallel_diff_result.yml
@@ -121,7 +121,7 @@
:type: old
:number: 9
:text: |
- -<span id="LC9" class="line"> <span class="k">raise</span> <span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span>
+ -<span id="LC9" class="line"> <span class="k">raise</span> <span class="s2">"System commands must be given as an array of strings"</span></span>
:line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9
:position: !ruby/object:Gitlab::Diff::Position
attributes:
@@ -136,7 +136,7 @@
:type: new
:number: 9
:text: |
- +<span id="LC9" class="line"> <span class="k">raise</span> <span class="no"><span class='idiff left'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff right'> </span><span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span>
+ +<span id="LC9" class="line"> <span class="k">raise</span> <span class="no"><span class='idiff left'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff right'> </span><span class="s2">"System commands must be given as an array of strings"</span></span>
:line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9
:position: !ruby/object:Gitlab::Diff::Position
attributes:
@@ -241,7 +241,7 @@
:type: old
:number: 13
:text: |
- -<span id="LC13" class="line"> <span class="n">vars</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">&quot;PWD&quot;</span> <span class="o">=&gt;</span> <span class="n">path</span> <span class="p">}</span></span>
+ -<span id="LC13" class="line"> <span class="n">vars</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"PWD"</span> <span class="o">=&gt;</span> <span class="n">path</span> <span class="p">}</span></span>
:line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_13_13
:position: !ruby/object:Gitlab::Diff::Position
attributes:
@@ -315,7 +315,7 @@
:type: new
:number: 15
:text: |
- +<span id="LC15" class="line"> <span class="s2">&quot;PWD&quot;</span> <span class="o">=&gt;</span> <span class="n">path</span></span>
+ +<span id="LC15" class="line"> <span class="s2">"PWD"</span> <span class="o">=&gt;</span> <span class="n">path</span></span>
:line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15
:position: !ruby/object:Gitlab::Diff::Position
attributes:
@@ -623,7 +623,7 @@
:type:
:number: 20
:text: |2
- <span id="LC26" class="line"> <span class="vi">@cmd_output</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span></span>
+ <span id="LC26" class="line"> <span class="vi">@cmd_output</span> <span class="o">=</span> <span class="s2">""</span></span>
:line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26
:position: !ruby/object:Gitlab::Diff::Position
attributes:
@@ -638,7 +638,7 @@
:type:
:number: 26
:text: |2
- <span id="LC26" class="line"> <span class="vi">@cmd_output</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span></span>
+ <span id="LC26" class="line"> <span class="vi">@cmd_output</span> <span class="o">=</span> <span class="s2">""</span></span>
:line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26
:position: !ruby/object:Gitlab::Diff::Position
attributes:
diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb
index 6d1c02db297..bd0108f9938 100644
--- a/spec/helpers/blob_helper_spec.rb
+++ b/spec/helpers/blob_helper_spec.rb
@@ -16,19 +16,19 @@ describe BlobHelper do
describe '#highlight' do
it 'should return plaintext for unknown lexer context' do
- result = helper.highlight(blob_name, no_context_content, nowrap: true)
- expect(result).to eq('<span id="LC1" class="line">:type &quot;assem&quot;))</span>')
+ result = helper.highlight(blob_name, no_context_content)
+ expect(result).to eq(%[<pre class="code highlight"><code><span id="LC1" class="line">:type "assem"))</span></code></pre>])
end
it 'should highlight single block' do
- expected = %Q[<span id="LC1" class="line"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span>
-<span id="LC2" class="line"><span class="ss">:type</span> <span class="s">&quot;assem&quot;</span><span class="p">))</span></span>]
+ expected = %Q[<pre class="code highlight"><code><span id="LC1" class="line"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span>
+<span id="LC2" class="line"><span class="ss">:type</span> <span class="s">"assem"</span><span class="p">))</span></span></code></pre>]
- expect(helper.highlight(blob_name, blob_content, nowrap: true)).to eq(expected)
+ expect(helper.highlight(blob_name, blob_content)).to eq(expected)
end
it 'should highlight multi-line comments' do
- result = helper.highlight(blob_name, multiline_content, nowrap: true)
+ result = helper.highlight(blob_name, multiline_content)
html = Nokogiri::HTML(result)
lines = html.search('.s')
expect(lines.count).to eq(3)
@@ -41,33 +41,19 @@ describe BlobHelper do
let(:blob_name) { 'test.diff' }
let(:blob_content) { "+aaa\n+bbb\n- ccc\n ddd\n"}
let(:expected) do
- %q(<span id="LC1" class="line"><span class="gi">+aaa</span></span>
+ %q(<pre class="code highlight"><code><span id="LC1" class="line"><span class="gi">+aaa</span></span>
<span id="LC2" class="line"><span class="gi">+bbb</span></span>
<span id="LC3" class="line"><span class="gd">- ccc</span></span>
-<span id="LC4" class="line"> ddd</span>)
+<span id="LC4" class="line"> ddd</span></code></pre>)
end
it 'should highlight each line properly' do
- result = helper.highlight(blob_name, blob_content, nowrap: true)
+ result = helper.highlight(blob_name, blob_content)
expect(result).to eq(expected)
end
end
end
- describe "#highlighter" do
- it 'should highlight continued blocks' do
- # Both lines have LC1 as ID since formatter doesn't support continue at the moment
- expected = [
- '<span id="LC1" class="line"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span>',
- '<span id="LC1" class="line"><span class="ss">:type</span> <span class="s">&quot;assem&quot;</span><span class="p">))</span></span>'
- ]
-
- highlighter = helper.highlighter(blob_name, blob_content, nowrap: true)
- result = split_content.map{ |content| highlighter.highlight(content) }
- expect(result).to eq(expected)
- end
- end
-
describe "#sanitize_svg" do
let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') }
let(:data) { open(input_svg_path).read }
diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb
index c0d2be98e85..6b5e3d93d48 100644
--- a/spec/helpers/events_helper_spec.rb
+++ b/spec/helpers/events_helper_spec.rb
@@ -57,7 +57,7 @@ describe EventsHelper do
expected = '<pre class="code highlight js-syntax-highlight ruby">' \
"<code><span class=\"k\">def</span> <span class=\"nf\">test</span>\n" \
" <span class=\"s1\">\'hello world\'</span>\n" \
- "<span class=\"k\">end</span>" \
+ "<span class=\"k\">end</span>\n" \
'</code></pre>'
expect(helper.event_note(input)).to eq(expected)
end
diff --git a/spec/javascripts/u2f/authenticate_spec.coffee b/spec/javascripts/u2f/authenticate_spec.coffee
index e8a2892d678..8ffeda11704 100644
--- a/spec/javascripts/u2f/authenticate_spec.coffee
+++ b/spec/javascripts/u2f/authenticate_spec.coffee
@@ -5,13 +5,12 @@
#= require ./mock_u2f_device
describe 'U2FAuthenticate', ->
- U2FUtil.enableTestMode()
fixture.load('u2f/authenticate')
beforeEach ->
@u2fDevice = new MockU2FDevice
@container = $("#js-authenticate-u2f")
- @component = new U2FAuthenticate(@container, {}, "token")
+ @component = new U2FAuthenticate(@container, {sign_requests: []}, "token")
@component.start()
it 'allows authenticating via a U2F device', ->
diff --git a/spec/javascripts/u2f/register_spec.js.coffee b/spec/javascripts/u2f/register_spec.js.coffee
index 0858abeca1a..87dc769792b 100644
--- a/spec/javascripts/u2f/register_spec.js.coffee
+++ b/spec/javascripts/u2f/register_spec.js.coffee
@@ -5,7 +5,6 @@
#= require ./mock_u2f_device
describe 'U2FRegister', ->
- U2FUtil.enableTestMode()
fixture.load('u2f/register')
beforeEach ->
diff --git a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
index 407617f3307..b1370bca833 100644
--- a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
+++ b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
@@ -3,15 +3,35 @@ require 'spec_helper'
describe Banzai::Filter::SyntaxHighlightFilter, lib: true do
include FilterSpecHelper
- it 'highlights valid code blocks' do
- result = filter('<pre><code>def fun end</code>')
- expect(result.to_html).to eq("<pre class=\"code highlight js-syntax-highlight plaintext\"><code>def fun end</code></pre>\n")
+ context "when no language is specified" do
+ it "highlights as plaintext" do
+ result = filter('<pre><code>def fun end</code></pre>')
+ expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext"><code>def fun end</code></pre>')
+ end
end
- it 'passes through invalid code blocks' do
- allow_any_instance_of(described_class).to receive(:block_code).and_raise(StandardError)
+ context "when a valid language is specified" do
+ it "highlights as that language" do
+ result = filter('<pre><code class="ruby">def fun end</code></pre>')
+ expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight ruby"><code><span class="k">def</span> <span class="nf">fun</span> <span class="k">end</span></code></pre>')
+ end
+ end
+
+ context "when an invalid language is specified" do
+ it "highlights as plaintext" do
+ result = filter('<pre><code class="gnuplot">This is a test</code></pre>')
+ expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext"><code>This is a test</code></pre>')
+ end
+ end
+
+ context "when Rouge formatting fails" do
+ before do
+ allow_any_instance_of(Rouge::Formatter).to receive(:format).and_raise(StandardError)
+ end
- result = filter('<pre><code>This is a test</code></pre>')
- expect(result.to_html).to eq('<pre>This is a test</pre>')
+ it "highlights as plaintext" do
+ result = filter('<pre><code class="ruby">This is a test</code></pre>')
+ expect(result.to_html).to eq('<pre class="code highlight"><code>This is a test</code></pre>')
+ end
end
end
diff --git a/spec/lib/gitlab/bitbucket_import/client_spec.rb b/spec/lib/gitlab/bitbucket_import/client_spec.rb
index 760d66a1488..7543c29bcc4 100644
--- a/spec/lib/gitlab/bitbucket_import/client_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/client_spec.rb
@@ -54,12 +54,12 @@ describe Gitlab::BitbucketImport::Client, lib: true do
context 'project import' do
it 'calls .from_project with no errors' do
project = create(:empty_project)
+ project.import_url = "ssh://git@bitbucket.org/test/test.git"
project.create_or_update_import_data(credentials:
{ user: "git",
password: nil,
bb_session: { bitbucket_access_token: "test",
bitbucket_access_token_secret: "test" } })
- project.import_url = "ssh://git@bitbucket.org/test/test.git"
expect { described_class.from_project(project) }.not_to raise_error
end
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 9096ad101b0..4ec3f19e03f 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -13,6 +13,10 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
context 'outside a transaction' do
before do
expect(model).to receive(:transaction_open?).and_return(false)
+
+ unless Gitlab::Database.postgresql?
+ allow_any_instance_of(Gitlab::Database::MigrationHelpers).to receive(:disable_statement_timeout)
+ end
end
context 'using PostgreSQL' do
diff --git a/spec/lib/gitlab/diff/highlight_spec.rb b/spec/lib/gitlab/diff/highlight_spec.rb
index fb5d50a5c68..88e4115c453 100644
--- a/spec/lib/gitlab/diff/highlight_spec.rb
+++ b/spec/lib/gitlab/diff/highlight_spec.rb
@@ -28,13 +28,13 @@ describe Gitlab::Diff::Highlight, lib: true do
end
it 'highlights and marks removed lines' do
- code = %Q{-<span id="LC9" class="line"> <span class="k">raise</span> <span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span>\n}
+ code = %Q{-<span id="LC9" class="line"> <span class="k">raise</span> <span class="s2">"System commands must be given as an array of strings"</span></span>\n}
expect(subject[4].text).to eq(code)
end
it 'highlights and marks added lines' do
- code = %Q{+<span id="LC9" class="line"> <span class="k">raise</span> <span class="no"><span class='idiff left'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff right'> </span><span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span>\n}
+ code = %Q{+<span id="LC9" class="line"> <span class="k">raise</span> <span class="no"><span class='idiff left'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff right'> </span><span class="s2">"System commands must be given as an array of strings"</span></span>\n}
expect(subject[5].text).to eq(code)
end
diff --git a/spec/lib/gitlab/import_export/import_export_spec.rb b/spec/lib/gitlab/import_export/import_export_spec.rb
new file mode 100644
index 00000000000..d6409a29550
--- /dev/null
+++ b/spec/lib/gitlab/import_export/import_export_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport, services: true do
+ describe 'export filename' do
+ let(:project) { create(:project, :public, path: 'project-path') }
+
+ it 'contains the project path' do
+ expect(described_class.export_filename(project: project)).to include(project.path)
+ end
+
+ it 'contains the namespace path' do
+ expect(described_class.export_filename(project: project)).to include(project.namespace.path)
+ end
+
+ it 'does not go over a certain length' do
+ project.path = 'a' * 100
+
+ expect(described_class.export_filename(project: project).length).to be < 70
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index 7286b0c39c0..4113d829c3c 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -26,6 +26,7 @@
"deleted_at": null,
"due_date": null,
"moved_to_id": null,
+ "test_ee_field": "test",
"notes": [
{
"id": 351,
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index 05ffec8ea0a..877be300262 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -30,6 +30,14 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
expect(Event.where.not(data: nil).first.data[:ref]).not_to be_empty
end
+ it 'preserves updated_at on issues' do
+ restored_project_json
+
+ issue = Issue.where(description: 'Aliquam enim illo et possimus.').first
+
+ expect(issue.reload.updated_at.to_s).to eq('2016-06-14 15:02:47 UTC')
+ end
+
context 'event at forth level of the tree' do
let(:event) { Event.where(title: 'test levels').first }
diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb
deleted file mode 100644
index 659facd6c19..00000000000
--- a/spec/lib/gitlab/lfs/lfs_router_spec.rb
+++ /dev/null
@@ -1,730 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Lfs::Router, lib: true do
- let(:project) { create(:project) }
- let(:public_project) { create(:project, :public) }
- let(:forked_project) { fork_project(public_project, user) }
-
- let(:user) { create(:user) }
- let(:user_two) { create(:user) }
- let!(:lfs_object) { create(:lfs_object, :with_file) }
-
- let(:request) { Rack::Request.new(env) }
- let(:env) do
- {
- 'rack.input' => '',
- 'REQUEST_METHOD' => 'GET',
- }
- end
-
- let(:lfs_router_auth) { new_lfs_router(project, user: user) }
- let(:lfs_router_ci_auth) { new_lfs_router(project, ci: true) }
- let(:lfs_router_noauth) { new_lfs_router(project) }
- let(:lfs_router_public_auth) { new_lfs_router(public_project, user: user) }
- let(:lfs_router_public_ci_auth) { new_lfs_router(public_project, ci: true) }
- let(:lfs_router_public_noauth) { new_lfs_router(public_project) }
- let(:lfs_router_forked_noauth) { new_lfs_router(forked_project) }
- let(:lfs_router_forked_auth) { new_lfs_router(forked_project, user: user_two) }
- let(:lfs_router_forked_ci_auth) { new_lfs_router(forked_project, ci: true) }
-
- let(:sample_oid) { "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80" }
- let(:sample_size) { 499013 }
- let(:respond_with_deprecated) {[ 501, { "Content-Type" => "application/json; charset=utf-8" }, ["{\"message\":\"Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]}
- let(:respond_with_disabled) {[ 501, { "Content-Type" => "application/json; charset=utf-8" }, ["{\"message\":\"Git LFS is not enabled on this GitLab server, contact your admin.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]}
-
- describe 'when lfs is disabled' do
- before do
- allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
- env['REQUEST_METHOD'] = 'POST'
- body = {
- 'objects' => [
- { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
- 'size' => 1575078
- },
- { 'oid' => sample_oid,
- 'size' => sample_size
- }
- ],
- 'operation' => 'upload'
- }.to_json
- env['rack.input'] = StringIO.new(body)
- env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch"
- end
-
- it 'responds with 501' do
- expect(lfs_router_auth.try_call).to match_array(respond_with_disabled)
- end
- end
-
- describe 'when fetching lfs object using deprecated API' do
- before do
- enable_lfs
- env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}"
- end
-
- it 'responds with 501' do
- expect(lfs_router_auth.try_call).to match_array(respond_with_deprecated)
- end
- end
-
- describe 'when fetching lfs object' do
- before do
- enable_lfs
- env['HTTP_ACCEPT'] = "application/vnd.git-lfs+json; charset=utf-8"
- env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}"
- end
-
- describe 'and request comes from gitlab-workhorse' do
- context 'without user being authorized' do
- it "responds with status 401" do
- expect(lfs_router_noauth.try_call.first).to eq(401)
- end
- end
-
- context 'with required headers' do
- before do
- project.lfs_objects << lfs_object
- env['HTTP_X_SENDFILE_TYPE'] = "X-Sendfile"
- end
-
- context 'when user does not have project access' do
- it "responds with status 403" do
- expect(lfs_router_auth.try_call.first).to eq(403)
- end
- end
-
- context 'when user has project access' do
- before do
- project.team << [user, :master]
- end
-
- it "responds with status 200" do
- expect(lfs_router_auth.try_call.first).to eq(200)
- end
-
- it "responds with the file location" do
- expect(lfs_router_auth.try_call[1]['Content-Type']).to eq("application/octet-stream")
- expect(lfs_router_auth.try_call[1]['X-Sendfile']).to eq(lfs_object.file.path)
- end
- end
-
- context 'when CI is authorized' do
- it "responds with status 200" do
- expect(lfs_router_ci_auth.try_call.first).to eq(200)
- end
-
- it "responds with the file location" do
- expect(lfs_router_ci_auth.try_call[1]['Content-Type']).to eq("application/octet-stream")
- expect(lfs_router_ci_auth.try_call[1]['X-Sendfile']).to eq(lfs_object.file.path)
- end
- end
- end
-
- context 'without required headers' do
- it "responds with status 403" do
- expect(lfs_router_auth.try_call.first).to eq(403)
- end
- end
- end
- end
-
- describe 'when handling lfs request using deprecated API' do
- before do
- enable_lfs
- env['REQUEST_METHOD'] = 'POST'
- env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects"
- end
-
- it 'responds with 501' do
- expect(lfs_router_auth.try_call).to match_array(respond_with_deprecated)
- end
- end
-
- describe 'when handling lfs batch request' do
- before do
- enable_lfs
- env['REQUEST_METHOD'] = 'POST'
- env['PATH_INFO'] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch"
- end
-
- describe 'download' do
- before do
- body = { 'operation' => 'download',
- 'objects' => [
- { 'oid' => sample_oid,
- 'size' => sample_size
- }]
- }.to_json
- env['rack.input'] = StringIO.new(body)
- end
-
- shared_examples 'an authorized requests' do
- context 'when downloading an lfs object that is assigned to our project' do
- before do
- project.lfs_objects << lfs_object
- end
-
- it 'responds with status 200 and href to download' do
- response = router.try_call
- expect(response.first).to eq(200)
- response_body = ActiveSupport::JSON.decode(response.last.first)
-
- expect(response_body).to eq('objects' => [
- { 'oid' => sample_oid,
- 'size' => sample_size,
- 'actions' => {
- 'download' => {
- 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
- 'header' => { 'Authorization' => auth }
- }
- }
- }])
- end
- end
-
- context 'when downloading an lfs object that is assigned to other project' do
- before do
- public_project.lfs_objects << lfs_object
- end
-
- it 'responds with status 200 and error message' do
- response = router.try_call
- expect(response.first).to eq(200)
- response_body = ActiveSupport::JSON.decode(response.last.first)
-
- expect(response_body).to eq('objects' => [
- { 'oid' => sample_oid,
- 'size' => sample_size,
- 'error' => {
- 'code' => 404,
- 'message' => "Object does not exist on the server or you don't have permissions to access it",
- }
- }])
- end
- end
-
- context 'when downloading a lfs object that does not exist' do
- before do
- body = { 'operation' => 'download',
- 'objects' => [
- { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
- 'size' => 1575078
- }]
- }.to_json
- env['rack.input'] = StringIO.new(body)
- end
-
- it "responds with status 200 and error message" do
- response = router.try_call
- expect(response.first).to eq(200)
- response_body = ActiveSupport::JSON.decode(response.last.first)
-
- expect(response_body).to eq('objects' => [
- { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
- 'size' => 1575078,
- 'error' => {
- 'code' => 404,
- 'message' => "Object does not exist on the server or you don't have permissions to access it",
- }
- }])
- end
- end
-
- context 'when downloading one new and one existing lfs object' do
- before do
- body = { 'operation' => 'download',
- 'objects' => [
- { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
- 'size' => 1575078
- },
- { 'oid' => sample_oid,
- 'size' => sample_size
- }
- ]
- }.to_json
- env['rack.input'] = StringIO.new(body)
- project.lfs_objects << lfs_object
- end
-
- it "responds with status 200 with upload hypermedia link for the new object" do
- response = router.try_call
- expect(response.first).to eq(200)
- response_body = ActiveSupport::JSON.decode(response.last.first)
-
- expect(response_body).to eq('objects' => [
- { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
- 'size' => 1575078,
- 'error' => {
- 'code' => 404,
- 'message' => "Object does not exist on the server or you don't have permissions to access it",
- }
- },
- { 'oid' => sample_oid,
- 'size' => sample_size,
- 'actions' => {
- 'download' => {
- 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
- 'header' => { 'Authorization' => auth }
- }
- }
- }])
- end
- end
- end
-
- context 'when user is authenticated' do
- let(:auth) { authorize(user) }
-
- before do
- env["HTTP_AUTHORIZATION"] = auth
- project.team << [user, role]
- end
-
- it_behaves_like 'an authorized requests' do
- let(:role) { :reporter }
- let(:router) { lfs_router_auth }
- end
-
- context 'when user does is not member of the project' do
- let(:role) { :guest }
-
- it 'responds with 403' do
- expect(lfs_router_auth.try_call.first).to eq(403)
- end
- end
-
- context 'when user does not have download access' do
- let(:role) { :guest }
-
- it 'responds with 403' do
- expect(lfs_router_auth.try_call.first).to eq(403)
- end
- end
- end
-
- context 'when CI is authorized' do
- let(:auth) { 'gitlab-ci-token:password' }
-
- before do
- env["HTTP_AUTHORIZATION"] = auth
- end
-
- it_behaves_like 'an authorized requests' do
- let(:router) { lfs_router_ci_auth }
- end
- end
-
- context 'when user is not authenticated' do
- describe 'is accessing public project' do
- before do
- public_project.lfs_objects << lfs_object
- end
-
- it 'responds with status 200 and href to download' do
- response = lfs_router_public_noauth.try_call
- expect(response.first).to eq(200)
- response_body = ActiveSupport::JSON.decode(response.last.first)
-
- expect(response_body).to eq('objects' => [
- { 'oid' => sample_oid,
- 'size' => sample_size,
- 'actions' => {
- 'download' => {
- 'href' => "#{public_project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
- 'header' => {}
- }
- }
- }])
- end
- end
-
- describe 'is accessing non-public project' do
- before do
- project.lfs_objects << lfs_object
- end
-
- it 'responds with authorization required' do
- expect(lfs_router_noauth.try_call.first).to eq(401)
- end
- end
- end
- end
-
- describe 'upload' do
- before do
- body = { 'operation' => 'upload',
- 'objects' => [
- { 'oid' => sample_oid,
- 'size' => sample_size
- }]
- }.to_json
- env['rack.input'] = StringIO.new(body)
- end
-
- describe 'when request is authenticated' do
- describe 'when user has project push access' do
- before do
- @auth = authorize(user)
- env["HTTP_AUTHORIZATION"] = @auth
- project.team << [user, :developer]
- end
-
- context 'when pushing an lfs object that already exists' do
- before do
- public_project.lfs_objects << lfs_object
- end
-
- it "responds with status 200 and links the object to the project" do
- response_body = lfs_router_auth.try_call.last
- response = ActiveSupport::JSON.decode(response_body.first)
-
- expect(response['objects']).to be_kind_of(Array)
- expect(response['objects'].first['oid']).to eq(sample_oid)
- expect(response['objects'].first['size']).to eq(sample_size)
- expect(lfs_object.projects.pluck(:id)).not_to include(project.id)
- expect(lfs_object.projects.pluck(:id)).to include(public_project.id)
- expect(response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}")
- expect(response['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth)
- end
- end
-
- context 'when pushing a lfs object that does not exist' do
- before do
- body = { 'operation' => 'upload',
- 'objects' => [
- { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
- 'size' => 1575078
- }]
- }.to_json
- env['rack.input'] = StringIO.new(body)
- end
-
- it "responds with status 200 and upload hypermedia link" do
- response = lfs_router_auth.try_call
- expect(response.first).to eq(200)
-
- response_body = ActiveSupport::JSON.decode(response.last.first)
- expect(response_body['objects']).to be_kind_of(Array)
- expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
- expect(response_body['objects'].first['size']).to eq(1575078)
- expect(lfs_object.projects.pluck(:id)).not_to include(project.id)
- expect(response_body['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
- expect(response_body['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth)
- end
- end
-
- context 'when pushing one new and one existing lfs object' do
- before do
- body = { 'operation' => 'upload',
- 'objects' => [
- { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
- 'size' => 1575078
- },
- { 'oid' => sample_oid,
- 'size' => sample_size
- }
- ]
- }.to_json
- env['rack.input'] = StringIO.new(body)
- project.lfs_objects << lfs_object
- end
-
- it "responds with status 200 with upload hypermedia link for the new object" do
- response = lfs_router_auth.try_call
- expect(response.first).to eq(200)
-
- response_body = ActiveSupport::JSON.decode(response.last.first)
- expect(response_body['objects']).to be_kind_of(Array)
-
- expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
- expect(response_body['objects'].first['size']).to eq(1575078)
- expect(response_body['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
- expect(response_body['objects'].first['actions']['upload']['header']).to eq("Authorization" => @auth)
-
- expect(response_body['objects'].last['oid']).to eq(sample_oid)
- expect(response_body['objects'].last['size']).to eq(sample_size)
- expect(response_body['objects'].last).not_to have_key('actions')
- end
- end
- end
-
- context 'when user does not have push access' do
- it 'responds with 403' do
- expect(lfs_router_auth.try_call.first).to eq(403)
- end
- end
-
- context 'when CI is authorized' do
- it 'responds with 401' do
- expect(lfs_router_ci_auth.try_call.first).to eq(401)
- end
- end
- end
-
- context 'when user is not authenticated' do
- context 'when user has push access' do
- before do
- project.team << [user, :master]
- end
-
- it "responds with status 401" do
- expect(lfs_router_public_noauth.try_call.first).to eq(401)
- end
- end
-
- context 'when user does not have push access' do
- it "responds with status 401" do
- expect(lfs_router_public_noauth.try_call.first).to eq(401)
- end
- end
- end
-
- context 'when CI is authorized' do
- let(:auth) { 'gitlab-ci-token:password' }
-
- before do
- env["HTTP_AUTHORIZATION"] = auth
- end
-
- it "responds with status 403" do
- expect(lfs_router_public_ci_auth.try_call.first).to eq(401)
- end
- end
- end
-
- describe 'unsupported' do
- before do
- body = { 'operation' => 'other',
- 'objects' => [
- { 'oid' => sample_oid,
- 'size' => sample_size
- }]
- }.to_json
- env['rack.input'] = StringIO.new(body)
- end
-
- it 'responds with status 404' do
- expect(lfs_router_public_noauth.try_call.first).to eq(404)
- end
- end
- end
-
- describe 'when pushing a lfs object' do
- before do
- enable_lfs
- env['REQUEST_METHOD'] = 'PUT'
- end
-
- shared_examples 'unauthorized' do
- context 'and request is sent by gitlab-workhorse to authorize the request' do
- before do
- header_for_upload_authorize(router.project)
- end
-
- it 'responds with status 401' do
- expect(router.try_call.first).to eq(401)
- end
- end
-
- context 'and request is sent by gitlab-workhorse to finalize the upload' do
- before do
- headers_for_upload_finalize(router.project)
- end
-
- it 'responds with status 401' do
- expect(router.try_call.first).to eq(401)
- end
- end
-
- context 'and request is sent with a malformed headers' do
- before do
- env["PATH_INFO"] = "#{router.project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}"
- env["HTTP_X_GITLAB_LFS_TMP"] = "cat /etc/passwd"
- end
-
- it 'does not recognize it as a valid lfs command' do
- expect(router.try_call).to eq(nil)
- end
- end
- end
-
- shared_examples 'forbidden' do
- context 'and request is sent by gitlab-workhorse to authorize the request' do
- before do
- header_for_upload_authorize(router.project)
- end
-
- it 'responds with 403' do
- expect(router.try_call.first).to eq(403)
- end
- end
-
- context 'and request is sent by gitlab-workhorse to finalize the upload' do
- before do
- headers_for_upload_finalize(router.project)
- end
-
- it 'responds with 403' do
- expect(router.try_call.first).to eq(403)
- end
- end
- end
-
- describe 'to one project' do
- describe 'when user is authenticated' do
- describe 'when user has push access to the project' do
- before do
- project.team << [user, :developer]
- end
-
- context 'and request is sent by gitlab-workhorse to authorize the request' do
- before do
- header_for_upload_authorize(project)
- end
-
- it 'responds with status 200, location of lfs store and object details' do
- json_response = ActiveSupport::JSON.decode(lfs_router_auth.try_call.last.first)
-
- expect(lfs_router_auth.try_call.first).to eq(200)
- expect(json_response['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
- expect(json_response['LfsOid']).to eq(sample_oid)
- expect(json_response['LfsSize']).to eq(sample_size)
- end
- end
-
- context 'and request is sent by gitlab-workhorse to finalize the upload' do
- before do
- headers_for_upload_finalize(project)
- end
-
- it 'responds with status 200 and lfs object is linked to the project' do
- expect(lfs_router_auth.try_call.first).to eq(200)
- expect(lfs_object.projects.pluck(:id)).to include(project.id)
- end
- end
- end
-
- describe 'and user does not have push access' do
- let(:router) { lfs_router_auth }
-
- it_behaves_like 'forbidden'
- end
- end
-
- context 'when CI is authenticated' do
- let(:router) { lfs_router_ci_auth }
-
- it_behaves_like 'unauthorized'
- end
-
- context 'for unauthenticated' do
- let(:router) { new_lfs_router(project) }
-
- it_behaves_like 'unauthorized'
- end
- end
-
- describe 'to a forked project' do
- let(:forked_project) { fork_project(public_project, user) }
-
- describe 'when user is authenticated' do
- describe 'when user has push access to the project' do
- before do
- forked_project.team << [user_two, :developer]
- end
-
- context 'and request is sent by gitlab-workhorse to authorize the request' do
- before do
- header_for_upload_authorize(forked_project)
- end
-
- it 'responds with status 200, location of lfs store and object details' do
- json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first)
-
- expect(lfs_router_forked_auth.try_call.first).to eq(200)
- expect(json_response['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
- expect(json_response['LfsOid']).to eq(sample_oid)
- expect(json_response['LfsSize']).to eq(sample_size)
- end
- end
-
- context 'and request is sent by gitlab-workhorse to finalize the upload' do
- before do
- headers_for_upload_finalize(forked_project)
- end
-
- it 'responds with status 200 and lfs object is linked to the source project' do
- expect(lfs_router_forked_auth.try_call.first).to eq(200)
- expect(lfs_object.projects.pluck(:id)).to include(public_project.id)
- end
- end
- end
-
- describe 'and user does not have push access' do
- let(:router) { lfs_router_forked_auth }
-
- it_behaves_like 'forbidden'
- end
- end
-
- context 'when CI is authenticated' do
- let(:router) { lfs_router_forked_ci_auth }
-
- it_behaves_like 'unauthorized'
- end
-
- context 'for unauthenticated' do
- let(:router) { lfs_router_forked_noauth }
-
- it_behaves_like 'unauthorized'
- end
-
- describe 'and second project not related to fork or a source project' do
- let(:second_project) { create(:project) }
- let(:lfs_router_second_project) { new_lfs_router(second_project, user: user) }
-
- before do
- public_project.lfs_objects << lfs_object
- headers_for_upload_finalize(second_project)
- end
-
- context 'when pushing the same lfs object to the second project' do
- before do
- second_project.team << [user, :master]
- end
-
- it 'responds with 200 and links the lfs object to the project' do
- expect(lfs_router_second_project.try_call.first).to eq(200)
- expect(lfs_object.projects.pluck(:id)).to include(second_project.id, public_project.id)
- end
- end
- end
- end
- end
-
- def enable_lfs
- allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
- end
-
- def authorize(user)
- ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
- end
-
- def new_lfs_router(project, user: nil, ci: false)
- Gitlab::Lfs::Router.new(project, user, ci, request)
- end
-
- def header_for_upload_authorize(project)
- env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}/authorize"
- end
-
- def headers_for_upload_finalize(project)
- env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}"
- env["HTTP_X_GITLAB_LFS_TMP"] = "#{sample_oid}6e561c9d4"
- end
-
- def fork_project(project, user, object = nil)
- allow(RepositoryForkWorker).to receive(:perform_async).and_return(true)
- Projects::ForkService.new(project, user, {}).execute
- end
-end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 4e5481f9154..10db79bd15f 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -5,9 +5,12 @@ describe Ci::Pipeline, models: true do
let(:pipeline) { FactoryGirl.create :ci_pipeline, project: project }
it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:user) }
+
it { is_expected.to have_many(:statuses) }
it { is_expected.to have_many(:trigger_requests) }
it { is_expected.to have_many(:builds) }
+
it { is_expected.to validate_presence_of :sha }
it { is_expected.to validate_presence_of :status }
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index 05f22c7a9eb..ff6371ad685 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -177,10 +177,10 @@ describe CommitStatus, models: true do
describe '#stages' do
before do
- FactoryGirl.create :commit_status, pipeline: pipeline, stage: 'build', stage_idx: 0, status: 'success'
- FactoryGirl.create :commit_status, pipeline: pipeline, stage: 'build', stage_idx: 0, status: 'failed'
- FactoryGirl.create :commit_status, pipeline: pipeline, stage: 'deploy', stage_idx: 2, status: 'running'
- FactoryGirl.create :commit_status, pipeline: pipeline, stage: 'test', stage_idx: 1, status: 'success'
+ create :commit_status, pipeline: pipeline, stage: 'build', name: 'linux', stage_idx: 0, status: 'success'
+ create :commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'failed'
+ create :commit_status, pipeline: pipeline, stage: 'deploy', name: 'staging', stage_idx: 2, status: 'running'
+ create :commit_status, pipeline: pipeline, stage: 'test', name: 'rspec', stage_idx: 1, status: 'success'
end
context 'stages list' do
@@ -192,7 +192,7 @@ describe CommitStatus, models: true do
end
context 'stages with statuses' do
- subject { CommitStatus.where(pipeline: pipeline).stages_status }
+ subject { CommitStatus.where(pipeline: pipeline).latest.stages_status }
it 'return list of stages with statuses' do
is_expected.to eq({
@@ -201,6 +201,20 @@ describe CommitStatus, models: true do
'deploy' => 'running'
})
end
+
+ context 'when build is retried' do
+ before do
+ create :commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'success'
+ end
+
+ it 'ignores a previous state' do
+ is_expected.to eq({
+ 'build' => 'success',
+ 'test' => 'success',
+ 'deploy' => 'running'
+ })
+ end
+ end
end
end
diff --git a/spec/models/legacy_diff_note_spec.rb b/spec/models/legacy_diff_note_spec.rb
index d64d89edbd3..d23fc06c3ad 100644
--- a/spec/models/legacy_diff_note_spec.rb
+++ b/spec/models/legacy_diff_note_spec.rb
@@ -16,10 +16,10 @@ describe LegacyDiffNote, models: true do
end
describe '#active?' do
- it 'is always true when the note has no associated diff' do
+ it 'is always true when the note has no associated diff line' do
note = build(:legacy_diff_note_on_merge_request)
- expect(note).to receive(:diff).and_return(nil)
+ expect(note).to receive(:diff_line).and_return(nil)
expect(note).to be_active
end
@@ -27,7 +27,7 @@ describe LegacyDiffNote, models: true do
it 'is never true when the note has no noteable associated' do
note = build(:legacy_diff_note_on_merge_request)
- expect(note).to receive(:diff).and_return(double)
+ expect(note).to receive(:diff_line).and_return(double)
expect(note).to receive(:noteable).and_return(nil)
expect(note).not_to be_active
@@ -47,7 +47,7 @@ describe LegacyDiffNote, models: true do
merge = build_stubbed(:merge_request, :simple)
note = build(:legacy_diff_note_on_merge_request, noteable: merge)
- allow(note).to receive(:diff).and_return(double)
+ allow(note).to receive(:diff_line).and_return(double)
expect(note).to receive(:find_noteable_diff).and_return(nil)
expect(note).not_to be_active
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index e842c58dd82..9dc34276f18 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -142,10 +142,10 @@ describe Project, models: true do
expect(project2).to be_valid
end
- it 'does not allow to introduce an empty URI' do
+ it 'allows an empty URI' do
project2 = build(:project, import_url: '')
- expect(project2).not_to be_valid
+ expect(project2).to be_valid
end
it 'does not produce import data on an empty URI' do
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index ff39f187759..fc74488ac0e 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -31,6 +31,8 @@ describe User, models: true do
it { is_expected.to have_many(:spam_logs).dependent(:destroy) }
it { is_expected.to have_many(:todos).dependent(:destroy) }
it { is_expected.to have_many(:award_emoji).dependent(:destroy) }
+ it { is_expected.to have_many(:builds).dependent(:nullify) }
+ it { is_expected.to have_many(:pipelines).dependent(:nullify) }
describe '#group_members' do
it 'does not include group memberships for which user is a requester' do
diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb
index 72a6d45f47d..2b74dd4bbb0 100644
--- a/spec/requests/api/award_emoji_spec.rb
+++ b/spec/requests/api/award_emoji_spec.rb
@@ -135,6 +135,22 @@ describe API::API, api: true do
expect(response).to have_http_status(401)
end
+
+ it "normalizes +1 as thumbsup award" do
+ post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user), name: '+1'
+
+ expect(issue.award_emoji.last.name).to eq("thumbsup")
+ end
+
+ context 'when the emoji already has been awarded' do
+ it 'returns a 404 status code' do
+ post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user), name: 'thumbsup'
+ post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user), name: 'thumbsup'
+
+ expect(response).to have_http_status(404)
+ expect(json_response["message"]).to match("has already been taken")
+ end
+ end
end
end
@@ -147,6 +163,22 @@ describe API::API, api: true do
expect(response).to have_http_status(201)
expect(json_response['user']['username']).to eq(user.username)
end
+
+ it "normalizes +1 as thumbsup award" do
+ post api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user), name: '+1'
+
+ expect(note.award_emoji.last.name).to eq("thumbsup")
+ end
+
+ context 'when the emoji already has been awarded' do
+ it 'returns a 404 status code' do
+ post api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user), name: 'rocket'
+ post api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user), name: 'rocket'
+
+ expect(response).to have_http_status(404)
+ expect(json_response["message"]).to match("has already been taken")
+ end
+ end
end
describe 'DELETE /projects/:id/awardable/:awardable_id/award_emoji/:award_id' do
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index e567d36afa8..f6f85d6e95e 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -56,13 +56,21 @@ describe API::API, api: true do
context "git push with project.wiki" do
it 'responds with success' do
- project_wiki = create(:project, name: 'my.wiki', path: 'my.wiki')
- project_wiki.team << [user, :developer]
+ push(key, project.wiki)
- push(key, project_wiki)
+ expect(response).to have_http_status(200)
+ expect(json_response["status"]).to be_truthy
+ expect(json_response["repository_path"]).to eq(project.wiki.repository.path_to_repo)
+ end
+ end
+
+ context "git pull with project.wiki" do
+ it 'responds with success' do
+ pull(key, project.wiki)
expect(response).to have_http_status(200)
expect(json_response["status"]).to be_truthy
+ expect(json_response["repository_path"]).to eq(project.wiki.repository.path_to_repo)
end
end
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
new file mode 100644
index 00000000000..93d2bc160cc
--- /dev/null
+++ b/spec/requests/lfs_http_spec.rb
@@ -0,0 +1,768 @@
+require 'spec_helper'
+
+describe Gitlab::Lfs::Router do
+ let(:user) { create(:user) }
+ let!(:lfs_object) { create(:lfs_object, :with_file) }
+
+ let(:headers) do
+ {
+ 'Authorization' => authorization,
+ 'X-Sendfile-Type' => sendfile
+ }.compact
+ end
+ let(:authorization) { }
+ let(:sendfile) { }
+
+ let(:sample_oid) { lfs_object.oid }
+ let(:sample_size) { lfs_object.size }
+
+ describe 'when lfs is disabled' do
+ let(:project) { create(:empty_project) }
+ let(:body) do
+ {
+ 'objects' => [
+ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+ 'size' => 1575078
+ },
+ { 'oid' => sample_oid,
+ 'size' => sample_size
+ }
+ ],
+ 'operation' => 'upload'
+ }
+ end
+
+ before do
+ allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
+ post_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
+ end
+
+ it 'responds with 501' do
+ expect(response).to have_http_status(501)
+ expect(json_response).to include('message' => 'Git LFS is not enabled on this GitLab server, contact your admin.')
+ end
+ end
+
+ describe 'deprecated API' do
+ let(:project) { create(:empty_project) }
+
+ before do
+ enable_lfs
+ end
+
+ shared_examples 'a deprecated' do
+ it 'responds with 501' do
+ expect(response).to have_http_status(501)
+ end
+
+ it 'returns deprecated message' do
+ expect(json_response).to include('message' => 'Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.')
+ end
+ end
+
+ context 'when fetching lfs object using deprecated API' do
+ let(:authorization) { authorize_user }
+
+ before do
+ get "#{project.http_url_to_repo}/info/lfs/objects/#{sample_oid}", nil, headers
+ end
+
+ it_behaves_like 'a deprecated'
+ end
+
+ context 'when handling lfs request using deprecated API' do
+ before do
+ post_json "#{project.http_url_to_repo}/info/lfs/objects", nil, headers
+ end
+
+ it_behaves_like 'a deprecated'
+ end
+ end
+
+ describe 'when fetching lfs object' do
+ let(:project) { create(:empty_project) }
+ let(:update_permissions) { }
+
+ before do
+ enable_lfs
+ update_permissions
+ get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
+ end
+
+ context 'and request comes from gitlab-workhorse' do
+ context 'without user being authorized' do
+ it 'responds with status 401' do
+ expect(response).to have_http_status(401)
+ end
+ end
+
+ context 'with required headers' do
+ shared_examples 'responds with a file' do
+ let(:sendfile) { 'X-Sendfile' }
+
+ it 'responds with status 200' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'responds with the file location' do
+ expect(response.headers['Content-Type']).to eq('application/octet-stream')
+ expect(response.headers['X-Sendfile']).to eq(lfs_object.file.path)
+ end
+ end
+
+ context 'with user is authorized' do
+ let(:authorization) { authorize_user }
+
+ context 'and does not have project access' do
+ let(:update_permissions) do
+ project.lfs_objects << lfs_object
+ end
+
+ it 'responds with status 403' do
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'and does have project access' do
+ let(:update_permissions) do
+ project.team << [user, :master]
+ project.lfs_objects << lfs_object
+ end
+
+ it_behaves_like 'responds with a file'
+ end
+ end
+
+ context 'when CI is authorized' do
+ let(:authorization) { authorize_ci_project }
+
+ let(:update_permissions) do
+ project.lfs_objects << lfs_object
+ end
+
+ it_behaves_like 'responds with a file'
+ end
+ end
+
+ context 'without required headers' do
+ let(:authorization) { authorize_user }
+
+ it 'responds with status 403' do
+ expect(response).to have_http_status(403)
+ end
+ end
+ end
+ end
+
+ describe 'when handling lfs batch request' do
+ let(:update_lfs_permissions) { }
+ let(:update_user_permissions) { }
+
+ before do
+ enable_lfs
+ update_lfs_permissions
+ update_user_permissions
+ post_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
+ end
+
+ describe 'download' do
+ let(:project) { create(:empty_project) }
+ let(:body) do
+ { 'operation' => 'download',
+ 'objects' => [
+ { 'oid' => sample_oid,
+ 'size' => sample_size
+ }]
+ }
+ end
+
+ shared_examples 'an authorized requests' do
+ context 'when downloading an lfs object that is assigned to our project' do
+ let(:update_lfs_permissions) do
+ project.lfs_objects << lfs_object
+ end
+
+ it 'responds with status 200' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'with href to download' do
+ expect(json_response).to eq('objects' => [
+ { 'oid' => sample_oid,
+ 'size' => sample_size,
+ 'actions' => {
+ 'download' => {
+ 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
+ 'header' => { 'Authorization' => authorization }
+ }
+ }
+ }])
+ end
+ end
+
+ context 'when downloading an lfs object that is assigned to other project' do
+ let(:other_project) { create(:empty_project) }
+ let(:update_lfs_permissions) do
+ other_project.lfs_objects << lfs_object
+ end
+
+ it 'responds with status 200' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'with href to download' do
+ expect(json_response).to eq('objects' => [
+ { 'oid' => sample_oid,
+ 'size' => sample_size,
+ 'error' => {
+ 'code' => 404,
+ 'message' => "Object does not exist on the server or you don't have permissions to access it",
+ }
+ }])
+ end
+ end
+
+ context 'when downloading a lfs object that does not exist' do
+ let(:body) do
+ { 'operation' => 'download',
+ 'objects' => [
+ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+ 'size' => 1575078
+ }]
+ }
+ end
+
+ it 'responds with status 200' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'with an 404 for specific object' do
+ expect(json_response).to eq('objects' => [
+ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+ 'size' => 1575078,
+ 'error' => {
+ 'code' => 404,
+ 'message' => "Object does not exist on the server or you don't have permissions to access it",
+ }
+ }])
+ end
+ end
+
+ context 'when downloading one new and one existing lfs object' do
+ let(:body) do
+ { 'operation' => 'download',
+ 'objects' => [
+ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+ 'size' => 1575078
+ },
+ { 'oid' => sample_oid,
+ 'size' => sample_size
+ }
+ ]
+ }
+ end
+
+ let(:update_lfs_permissions) do
+ project.lfs_objects << lfs_object
+ end
+
+ it 'responds with status 200' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'responds with upload hypermedia link for the new object' do
+ expect(json_response).to eq('objects' => [
+ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+ 'size' => 1575078,
+ 'error' => {
+ 'code' => 404,
+ 'message' => "Object does not exist on the server or you don't have permissions to access it",
+ }
+ },
+ { 'oid' => sample_oid,
+ 'size' => sample_size,
+ 'actions' => {
+ 'download' => {
+ 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
+ 'header' => { 'Authorization' => authorization }
+ }
+ }
+ }])
+ end
+ end
+ end
+
+ context 'when user is authenticated' do
+ let(:authorization) { authorize_user }
+
+ let(:update_user_permissions) do
+ project.team << [user, role]
+ end
+
+ it_behaves_like 'an authorized requests' do
+ let(:role) { :reporter }
+ end
+
+ context 'when user does is not member of the project' do
+ let(:role) { :guest }
+
+ it 'responds with 403' do
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'when user does not have download access' do
+ let(:role) { :guest }
+
+ it 'responds with 403' do
+ expect(response).to have_http_status(403)
+ end
+ end
+ end
+
+ context 'when CI is authorized' do
+ let(:authorization) { authorize_ci_project }
+
+ it_behaves_like 'an authorized requests'
+ end
+
+ context 'when user is not authenticated' do
+ describe 'is accessing public project' do
+ let(:project) { create(:project, :public) }
+
+ let(:update_lfs_permissions) do
+ project.lfs_objects << lfs_object
+ end
+
+ it 'responds with status 200 and href to download' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'responds with status 200 and href to download' do
+ expect(json_response).to eq('objects' => [
+ { 'oid' => sample_oid,
+ 'size' => sample_size,
+ 'actions' => {
+ 'download' => {
+ 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
+ 'header' => {}
+ }
+ }
+ }])
+ end
+ end
+
+ describe 'is accessing non-public project' do
+ let(:update_lfs_permissions) do
+ project.lfs_objects << lfs_object
+ end
+
+ it 'responds with authorization required' do
+ expect(response).to have_http_status(401)
+ end
+ end
+ end
+ end
+
+ describe 'upload' do
+ let(:project) { create(:project, :public) }
+ let(:body) do
+ { 'operation' => 'upload',
+ 'objects' => [
+ { 'oid' => sample_oid,
+ 'size' => sample_size
+ }]
+ }
+ end
+
+ describe 'when request is authenticated' do
+ describe 'when user has project push access' do
+ let(:authorization) { authorize_user }
+
+ let(:update_user_permissions) do
+ project.team << [user, :developer]
+ end
+
+ context 'when pushing an lfs object that already exists' do
+ let(:other_project) { create(:empty_project) }
+ let(:update_lfs_permissions) do
+ other_project.lfs_objects << lfs_object
+ end
+
+ it 'responds with status 200' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'responds with links the object to the project' do
+ expect(json_response['objects']).to be_kind_of(Array)
+ expect(json_response['objects'].first['oid']).to eq(sample_oid)
+ expect(json_response['objects'].first['size']).to eq(sample_size)
+ expect(lfs_object.projects.pluck(:id)).not_to include(project.id)
+ expect(lfs_object.projects.pluck(:id)).to include(other_project.id)
+ expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}")
+ expect(json_response['objects'].first['actions']['upload']['header']).to eq('Authorization' => authorization)
+ end
+ end
+
+ context 'when pushing a lfs object that does not exist' do
+ let(:body) do
+ { 'operation' => 'upload',
+ 'objects' => [
+ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+ 'size' => 1575078
+ }]
+ }
+ end
+
+ it 'responds with status 200' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'responds with upload hypermedia link' do
+ expect(json_response['objects']).to be_kind_of(Array)
+ expect(json_response['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
+ expect(json_response['objects'].first['size']).to eq(1575078)
+ expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
+ expect(json_response['objects'].first['actions']['upload']['header']).to eq('Authorization' => authorization)
+ end
+ end
+
+ context 'when pushing one new and one existing lfs object' do
+ let(:body) do
+ { 'operation' => 'upload',
+ 'objects' => [
+ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+ 'size' => 1575078
+ },
+ { 'oid' => sample_oid,
+ 'size' => sample_size
+ }
+ ]
+ }
+ end
+
+ let(:update_lfs_permissions) do
+ project.lfs_objects << lfs_object
+ end
+
+ it 'responds with status 200' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'responds with upload hypermedia link for the new object' do
+ expect(json_response['objects']).to be_kind_of(Array)
+
+ expect(json_response['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
+ expect(json_response['objects'].first['size']).to eq(1575078)
+ expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{project.http_url_to_repo}/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
+ expect(json_response['objects'].first['actions']['upload']['header']).to eq("Authorization" => authorization)
+
+ expect(json_response['objects'].last['oid']).to eq(sample_oid)
+ expect(json_response['objects'].last['size']).to eq(sample_size)
+ expect(json_response['objects'].last).not_to have_key('actions')
+ end
+ end
+ end
+
+ context 'when user does not have push access' do
+ let(:authorization) { authorize_user }
+
+ it 'responds with 403' do
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'when CI is authorized' do
+ let(:authorization) { authorize_ci_project }
+
+ it 'responds with 401' do
+ expect(response).to have_http_status(401)
+ end
+ end
+ end
+
+ context 'when user is not authenticated' do
+ context 'when user has push access' do
+ let(:update_user_permissions) do
+ project.team << [user, :master]
+ end
+
+ it 'responds with status 401' do
+ expect(response).to have_http_status(401)
+ end
+ end
+
+ context 'when user does not have push access' do
+ it 'responds with status 401' do
+ expect(response).to have_http_status(401)
+ end
+ end
+ end
+
+ context 'when CI is authorized' do
+ let(:authorization) { authorize_ci_project }
+
+ it 'responds with status 403' do
+ expect(response).to have_http_status(401)
+ end
+ end
+ end
+
+ describe 'unsupported' do
+ let(:project) { create(:empty_project) }
+ let(:body) do
+ { 'operation' => 'other',
+ 'objects' => [
+ { 'oid' => sample_oid,
+ 'size' => sample_size
+ }]
+ }
+ end
+
+ it 'responds with status 404' do
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ describe 'when pushing a lfs object' do
+ before do
+ enable_lfs
+ end
+
+ shared_examples 'unauthorized' do
+ context 'and request is sent by gitlab-workhorse to authorize the request' do
+ before do
+ put_authorize
+ end
+
+ it 'responds with status 401' do
+ expect(response).to have_http_status(401)
+ end
+ end
+
+ context 'and request is sent by gitlab-workhorse to finalize the upload' do
+ before do
+ put_finalize
+ end
+
+ it 'responds with status 401' do
+ expect(response).to have_http_status(401)
+ end
+ end
+
+ context 'and request is sent with a malformed headers' do
+ before do
+ put_finalize('cat /etc/passwd')
+ end
+
+ it 'does not recognize it as a valid lfs command' do
+ expect(response).to have_http_status(403)
+ end
+ end
+ end
+
+ shared_examples 'forbidden' do
+ context 'and request is sent by gitlab-workhorse to authorize the request' do
+ before do
+ put_authorize
+ end
+
+ it 'responds with 403' do
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'and request is sent by gitlab-workhorse to finalize the upload' do
+ before do
+ put_finalize
+ end
+
+ it 'responds with 403' do
+ expect(response).to have_http_status(403)
+ end
+ end
+ end
+
+ describe 'to one project' do
+ let(:project) { create(:empty_project) }
+
+ describe 'when user is authenticated' do
+ let(:authorization) { authorize_user }
+
+ describe 'when user has push access to the project' do
+ before do
+ project.team << [user, :developer]
+ end
+
+ context 'and request is sent by gitlab-workhorse to authorize the request' do
+ before do
+ put_authorize
+ end
+
+ it 'responds with status 200' do
+ expect(response).to have_http_status(200)
+ 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)
+ expect(json_response['LfsSize']).to eq(sample_size)
+ end
+ end
+
+ context 'and request is sent by gitlab-workhorse to finalize the upload' do
+ before do
+ put_finalize
+ end
+
+ it 'responds with status 200' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'lfs object is linked to the project' do
+ expect(lfs_object.projects.pluck(:id)).to include(project.id)
+ end
+ end
+ end
+
+ describe 'and user does not have push access' do
+ it_behaves_like 'forbidden'
+ end
+ end
+
+ context 'when CI is authenticated' do
+ let(:authorization) { authorize_ci_project }
+
+ it_behaves_like 'unauthorized'
+ end
+
+ context 'for unauthenticated' do
+ it_behaves_like 'unauthorized'
+ end
+ end
+
+ describe 'to a forked project' do
+ let(:upstream_project) { create(:project, :public) }
+ let(:project_owner) { create(:user) }
+ let(:project) { fork_project(upstream_project, project_owner) }
+
+ describe 'when user is authenticated' do
+ let(:authorization) { authorize_user }
+
+ describe 'when user has push access to the project' do
+ before do
+ project.team << [user, :developer]
+ end
+
+ context 'and request is sent by gitlab-workhorse to authorize the request' do
+ before do
+ put_authorize
+ end
+
+ it 'responds with status 200' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'with 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)
+ expect(json_response['LfsSize']).to eq(sample_size)
+ end
+ end
+
+ context 'and request is sent by gitlab-workhorse to finalize the upload' do
+ before do
+ put_finalize
+ end
+
+ it 'responds with status 200' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'lfs object is linked to the source project' do
+ expect(lfs_object.projects.pluck(:id)).to include(upstream_project.id)
+ end
+ end
+ end
+
+ describe 'and user does not have push access' do
+ it_behaves_like 'forbidden'
+ end
+ end
+
+ context 'when CI is authenticated' do
+ let(:authorization) { authorize_ci_project }
+
+ it_behaves_like 'unauthorized'
+ end
+
+ context 'for unauthenticated' do
+ it_behaves_like 'unauthorized'
+ end
+
+ describe 'and second project not related to fork or a source project' do
+ let(:second_project) { create(:empty_project) }
+ let(:authorization) { authorize_user }
+
+ before do
+ second_project.team << [user, :master]
+ upstream_project.lfs_objects << lfs_object
+ end
+
+ context 'when pushing the same lfs object to the second project' do
+ before do
+ put "#{second_project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}", nil,
+ headers.merge('X-Gitlab-Lfs-Tmp' => lfs_tmp_file).compact
+ end
+
+ it 'responds with status 200' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'links the lfs object to the project' do
+ expect(lfs_object.projects.pluck(:id)).to include(second_project.id, upstream_project.id)
+ end
+ end
+ end
+ end
+
+ def put_authorize
+ put "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}/authorize", nil, headers
+ end
+
+ def put_finalize(lfs_tmp = lfs_tmp_file)
+ put "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}", nil,
+ headers.merge('X-Gitlab-Lfs-Tmp' => lfs_tmp).compact
+ end
+
+ def lfs_tmp_file
+ "#{sample_oid}012345678"
+ end
+ end
+
+ def enable_lfs
+ allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
+ end
+
+ def authorize_ci_project
+ ActionController::HttpAuthentication::Basic.encode_credentials('gitlab-ci-token', project.runners_token)
+ end
+
+ def authorize_user
+ ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
+ end
+
+ def fork_project(project, user, object = nil)
+ allow(RepositoryForkWorker).to receive(:perform_async).and_return(true)
+ Projects::ForkService.new(project, user, {}).execute
+ end
+
+ def post_json(url, body = nil, headers = nil)
+ post(url, body.try(:to_json), (headers || {}).merge('Content-Type' => 'application/json'))
+ end
+
+ def json_response
+ @json_response ||= JSON.parse(response.body)
+ end
+end
diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb
index 67777ad48bc..7cc71f706ce 100644
--- a/spec/services/auth/container_registry_authentication_service_spec.rb
+++ b/spec/services/auth/container_registry_authentication_service_spec.rb
@@ -87,51 +87,105 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
end
context 'user authorization' do
- let(:project) { create(:project) }
let(:current_user) { create(:user) }
- context 'allow to use scope-less authentication' do
- it_behaves_like 'a valid token'
- end
+ context 'for private project' do
+ let(:project) { create(:empty_project) }
- context 'allow developer to push images' do
- before { project.team << [current_user, :developer] }
+ context 'allow to use scope-less authentication' do
+ it_behaves_like 'a valid token'
+ end
- let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:push" }
+ context 'allow developer to push images' do
+ before { project.team << [current_user, :developer] }
+
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:push" }
+ end
+
+ it_behaves_like 'a pushable'
end
- it_behaves_like 'a pushable'
- end
+ context 'allow reporter to pull images' do
+ before { project.team << [current_user, :reporter] }
+
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:pull" }
+ end
- context 'allow reporter to pull images' do
- before { project.team << [current_user, :reporter] }
+ it_behaves_like 'a pullable'
+ end
- let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:pull" }
+ context 'return a least of privileges' do
+ before { project.team << [current_user, :reporter] }
+
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:push,pull" }
+ end
+
+ it_behaves_like 'a pullable'
end
- it_behaves_like 'a pullable'
+ context 'disallow guest to pull or push images' do
+ before { project.team << [current_user, :guest] }
+
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:pull,push" }
+ end
+
+ it_behaves_like 'an inaccessible'
+ end
end
- context 'return a least of privileges' do
- before { project.team << [current_user, :reporter] }
+ context 'for public project' do
+ let(:project) { create(:empty_project, :public) }
- let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:push,pull" }
+ context 'allow anyone to pull images' do
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:pull" }
+ end
+
+ it_behaves_like 'a pullable'
end
- it_behaves_like 'a pullable'
+ context 'disallow anyone to push images' do
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:push" }
+ end
+
+ it_behaves_like 'an inaccessible'
+ end
end
- context 'disallow guest to pull or push images' do
- before { project.team << [current_user, :guest] }
+ context 'for internal project' do
+ let(:project) { create(:empty_project, :internal) }
- let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:pull,push" }
+ context 'for internal user' do
+ context 'allow anyone to pull images' do
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:pull" }
+ end
+
+ it_behaves_like 'a pullable'
+ end
+
+ context 'disallow anyone to push images' do
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:push" }
+ end
+
+ it_behaves_like 'an inaccessible'
+ end
end
- it_behaves_like 'an inaccessible'
+ context 'for external user' do
+ let(:current_user) { create(:user, external: true) }
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:pull,push" }
+ end
+
+ it_behaves_like 'an inaccessible'
+ end
end
end
diff --git a/spec/services/create_commit_builds_service_spec.rb b/spec/services/create_commit_builds_service_spec.rb
index 4d09bc5fb12..d4c5e584421 100644
--- a/spec/services/create_commit_builds_service_spec.rb
+++ b/spec/services/create_commit_builds_service_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe CreateCommitBuildsService, services: true do
let(:service) { CreateCommitBuildsService.new }
let(:project) { FactoryGirl.create(:empty_project) }
- let(:user) { nil }
+ let(:user) { create(:user) }
before do
stub_ci_pipeline_to_return_yaml_file
@@ -24,6 +24,7 @@ describe CreateCommitBuildsService, services: true do
it { expect(pipeline).to be_valid }
it { expect(pipeline).to be_persisted }
it { expect(pipeline).to eq(project.pipelines.last) }
+ it { expect(pipeline).to have_attributes(user: user) }
it { expect(pipeline.builds.first).to be_kind_of(Ci::Build) }
end
diff --git a/spec/services/create_deployment_service_spec.rb b/spec/services/create_deployment_service_spec.rb
index 654e441f3cd..8da2a2b3c1b 100644
--- a/spec/services/create_deployment_service_spec.rb
+++ b/spec/services/create_deployment_service_spec.rb
@@ -89,6 +89,12 @@ describe CreateDeploymentService, services: true do
expect_any_instance_of(described_class).to receive(:execute)
subject
end
+
+ it 'is set as deployable' do
+ subject
+
+ expect(Deployment.last.deployable).to eq(deployable)
+ end
end
context 'without environment specified' do
@@ -105,6 +111,8 @@ describe CreateDeploymentService, services: true do
context 'when build succeeds' do
it_behaves_like 'does create environment and deployment' do
+ let(:deployable) { build }
+
subject { build.success }
end
end
@@ -114,6 +122,14 @@ describe CreateDeploymentService, services: true do
subject { build.drop }
end
end
+
+ context 'when build is retried' do
+ it_behaves_like 'does create environment and deployment' do
+ let(:deployable) { Ci::Build.retry(build) }
+
+ subject { deployable.success }
+ end
+ end
end
end
end
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index b4522536724..34d8ea9090e 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -35,8 +35,11 @@ describe TodoService, services: true do
should_not_create_any_todo { service.new_issue(unassigned_issue, author) }
end
- it 'does not create a todo if assignee is the current user' do
- should_not_create_any_todo { service.new_issue(unassigned_issue, john_doe) }
+ it 'creates a todo if assignee is the current user' do
+ unassigned_issue.update_attribute(:assignee, john_doe)
+ service.new_issue(unassigned_issue, john_doe)
+
+ should_create_todo(user: john_doe, target: unassigned_issue, author: john_doe, action: Todo::ASSIGNED)
end
it 'creates a todo for each valid mentioned user' do
@@ -44,7 +47,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: issue, action: Todo::MENTIONED)
should_create_todo(user: guest, target: issue, action: Todo::MENTIONED)
- should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED)
+ should_create_todo(user: author, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
end
@@ -57,7 +60,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
- should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
+ should_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
end
context 'when a private group is mentioned' do
@@ -87,7 +90,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: issue, action: Todo::MENTIONED)
should_create_todo(user: guest, target: issue, action: Todo::MENTIONED)
should_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED)
- should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED)
+ should_create_todo(user: author, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
end
@@ -105,7 +108,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
- should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
+ should_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
end
context 'issues with a task list' do
@@ -156,10 +159,11 @@ describe TodoService, services: true do
should_not_create_any_todo { service.reassigned_issue(issue, author) }
end
- it 'does not create a todo if new assignee is the current user' do
+ it 'creates a todo if new assignee is the current user' do
unassigned_issue.update_attribute(:assignee, john_doe)
+ service.reassigned_issue(unassigned_issue, john_doe)
- should_not_create_any_todo { service.reassigned_issue(unassigned_issue, john_doe) }
+ should_create_todo(user: john_doe, target: unassigned_issue, author: john_doe, action: Todo::ASSIGNED)
end
end
@@ -250,7 +254,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_create_todo(user: guest, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_create_todo(user: author, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
- should_not_create_todo(user: john_doe, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
+ should_create_todo(user: john_doe, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_not_create_todo(user: non_member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
end
@@ -262,7 +266,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
- should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
+ should_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
end
it 'creates a todo for each valid mentioned user when leaving a note on commit' do
@@ -270,7 +274,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
should_create_todo(user: author, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
- should_not_create_todo(user: john_doe, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
+ should_create_todo(user: john_doe, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
should_not_create_todo(user: non_member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
end
@@ -312,7 +316,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
should_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
- should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
+ should_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
end
@@ -325,7 +329,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
should_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
should_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
- should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
+ should_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
end
@@ -382,10 +386,11 @@ describe TodoService, services: true do
should_not_create_any_todo { service.reassigned_merge_request(mr_assigned, author) }
end
- it 'does not create a todo if new assignee is the current user' do
+ it 'creates a todo if new assignee is the current user' do
mr_assigned.update_attribute(:assignee, john_doe)
+ service.reassigned_merge_request(mr_assigned, john_doe)
- should_not_create_any_todo { service.reassigned_merge_request(mr_assigned, john_doe) }
+ should_create_todo(user: john_doe, target: mr_assigned, author: john_doe, action: Todo::ASSIGNED)
end
end
@@ -435,6 +440,24 @@ describe TodoService, services: true do
should_create_todo(user: author, target: mr_unassigned, action: Todo::MARKED)
end
end
+
+ describe '#new_note' do
+ let(:mention) { john_doe.to_reference }
+ let(:diff_note_on_merge_request) { create(:diff_note_on_merge_request, project: project, noteable: mr_unassigned, author: author, note: "Hey #{mention}") }
+ let(:legacy_diff_note_on_merge_request) { create(:legacy_diff_note_on_merge_request, project: project, noteable: mr_unassigned, author: author, note: "Hey #{mention}") }
+
+ it 'creates a todo for mentioned user on new diff note' do
+ service.new_note(diff_note_on_merge_request, author)
+
+ should_create_todo(user: john_doe, target: mr_unassigned, author: author, action: Todo::MENTIONED, note: diff_note_on_merge_request)
+ end
+
+ it 'creates a todo for mentioned user on legacy diff note' do
+ service.new_note(legacy_diff_note_on_merge_request, author)
+
+ should_create_todo(user: john_doe, target: mr_unassigned, author: author, action: Todo::MENTIONED, note: legacy_diff_note_on_merge_request)
+ end
+ end
end
it 'updates cached counts when a todo is created' do
diff --git a/spec/support/fake_u2f_device.rb b/spec/support/fake_u2f_device.rb
index 553fe9f1fbc..f550e9a0160 100644
--- a/spec/support/fake_u2f_device.rb
+++ b/spec/support/fake_u2f_device.rb
@@ -18,8 +18,8 @@ class FakeU2fDevice
def respond_to_u2f_authentication
app_id = @page.evaluate_script('gon.u2f.app_id')
- challenges = @page.evaluate_script('gon.u2f.challenges')
- json_response = u2f_device(app_id).sign_response(challenges[0])
+ challenge = @page.evaluate_script('gon.u2f.challenge')
+ json_response = u2f_device(app_id).sign_response(challenge)
@page.execute_script("
u2f.sign = function(appId, challenges, signRequests, callback) {