Commit ffea9c46 authored by Kamil Trzcinski's avatar Kamil Trzcinski

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into...

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into store-variables-and-when-in-builds-table

# Conflicts:
#	db/schema.rb
parents 22c2814b f1083ba1
...@@ -2,6 +2,8 @@ require: ...@@ -2,6 +2,8 @@ require:
- rubocop-rspec - rubocop-rspec
- ./rubocop/rubocop - ./rubocop/rubocop
inherit_from: .rubocop_todo.yml
AllCops: AllCops:
TargetRubyVersion: 2.1 TargetRubyVersion: 2.1
# Cop names are not displayed in offense messages by default. Change behavior # Cop names are not displayed in offense messages by default. Change behavior
...@@ -52,14 +54,6 @@ Style/AlignArray: ...@@ -52,14 +54,6 @@ Style/AlignArray:
Style/AlignHash: Style/AlignHash:
Enabled: true 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#*`. # Use `Array#join` instead of `Array#*`.
Style/ArrayJoin: Style/ArrayJoin:
Enabled: true Enabled: true
...@@ -80,10 +74,6 @@ Style/Attr: ...@@ -80,10 +74,6 @@ Style/Attr:
Style/BeginBlock: Style/BeginBlock:
Enabled: true Enabled: true
# Checks if usage of %() or %Q() matches configuration.
Style/BarePercentLiterals:
Enabled: false
# Do not use block comments. # Do not use block comments.
Style/BlockComments: Style/BlockComments:
Enabled: true Enabled: true
...@@ -97,14 +87,6 @@ Style/BlockEndNewline: ...@@ -97,14 +87,6 @@ Style/BlockEndNewline:
Style/BlockDelimiters: Style/BlockDelimiters:
Enabled: true 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. # Indentation of when in a case/when/[else/]end.
Style/CaseIndentation: Style/CaseIndentation:
Enabled: true Enabled: true
...@@ -133,24 +115,10 @@ Style/ClassMethods: ...@@ -133,24 +115,10 @@ Style/ClassMethods:
Style/ClassVars: Style/ClassVars:
Enabled: true 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. # Indentation of comments.
Style/CommentIndentation: Style/CommentIndentation:
Enabled: true 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. # Constants should use SCREAMING_SNAKE_CASE.
Style/ConstantName: Style/ConstantName:
Enabled: true Enabled: true
...@@ -159,34 +127,14 @@ Style/ConstantName: ...@@ -159,34 +127,14 @@ Style/ConstantName:
Style/DefWithParentheses: Style/DefWithParentheses:
Enabled: true Enabled: true
# Checks for use of deprecated Hash methods.
Style/DeprecatedHashMethods:
Enabled: false
# Document classes and non-namespace modules. # Document classes and non-namespace modules.
Style/Documentation: Style/Documentation:
Enabled: false 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. # Align elses and elsifs correctly.
Style/ElseAlignment: Style/ElseAlignment:
Enabled: true Enabled: true
# Avoid empty else-clauses.
Style/EmptyElse:
Enabled: false
# Use empty lines between defs. # Use empty lines between defs.
Style/EmptyLineBetweenDefs: Style/EmptyLineBetweenDefs:
Enabled: false Enabled: false
...@@ -215,10 +163,6 @@ Style/EmptyLinesAroundModuleBody: ...@@ -215,10 +163,6 @@ Style/EmptyLinesAroundModuleBody:
Style/EmptyLinesAroundMethodBody: Style/EmptyLinesAroundMethodBody:
Enabled: false Enabled: false
# Prefer literals to Array.new/Hash.new/String.new.
Style/EmptyLiteral:
Enabled: false
# Avoid the use of END blocks. # Avoid the use of END blocks.
Style/EndBlock: Style/EndBlock:
Enabled: true Enabled: true
...@@ -231,10 +175,6 @@ Style/EndOfLine: ...@@ -231,10 +175,6 @@ Style/EndOfLine:
Style/EvenOdd: Style/EvenOdd:
Enabled: true Enabled: true
# Do not use unnecessary spacing.
Style/ExtraSpacing:
Enabled: false
# Use snake_case for source file names. # Use snake_case for source file names.
Style/FileName: Style/FileName:
Enabled: true Enabled: true
...@@ -252,31 +192,15 @@ Style/FlipFlop: ...@@ -252,31 +192,15 @@ Style/FlipFlop:
Style/For: Style/For:
Enabled: true Enabled: true
# Enforce the use of Kernel#sprintf, Kernel#format or String#%.
Style/FormatString:
Enabled: false
# Do not introduce global variables. # Do not introduce global variables.
Style/GlobalVars: Style/GlobalVars:
Enabled: true 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 }` # Prefer Ruby 1.9 hash syntax `{ a: 1, b: 2 }`
# over 1.8 syntax `{ :a => 1, :b => 2 }`. # over 1.8 syntax `{ :a => 1, :b => 2 }`.
Style/HashSyntax: Style/HashSyntax:
Enabled: true 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. # Do not use if x; .... Use the ternary operator instead.
Style/IfWithSemicolon: Style/IfWithSemicolon:
Enabled: true Enabled: true
...@@ -299,22 +223,10 @@ Style/IndentationConsistency: ...@@ -299,22 +223,10 @@ Style/IndentationConsistency:
Style/IndentationWidth: Style/IndentationWidth:
Enabled: true 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. # Use Kernel#loop for infinite loops.
Style/InfiniteLoop: Style/InfiniteLoop:
Enabled: true Enabled: true
# Use the new lambda literal syntax for single-line blocks.
Style/Lambda:
Enabled: false
# Use lambda.call(...) instead of lambda.(...). # Use lambda.call(...) instead of lambda.(...).
Style/LambdaCall: Style/LambdaCall:
Enabled: true Enabled: true
...@@ -323,14 +235,6 @@ Style/LambdaCall: ...@@ -323,14 +235,6 @@ Style/LambdaCall:
Style/LeadingCommentSpace: Style/LeadingCommentSpace:
Enabled: true 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. # Checks if the method definitions have or don't have parentheses.
Style/MethodDefParentheses: Style/MethodDefParentheses:
Enabled: true Enabled: true
...@@ -387,39 +291,18 @@ Style/MultilineMethodDefinitionBraceLayout: ...@@ -387,39 +291,18 @@ Style/MultilineMethodDefinitionBraceLayout:
Style/MultilineOperationIndentation: Style/MultilineOperationIndentation:
Enabled: false 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). # Favor unless over if for negative conditions (or control flow or).
Style/NegatedIf: Style/NegatedIf:
Enabled: true Enabled: true
# Favor until over while for negative conditions.
Style/NegatedWhile:
Enabled: false
# Avoid using nested modifiers. # Avoid using nested modifiers.
Style/NestedModifier: Style/NestedModifier:
Enabled: true 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. # Use one expression per branch in a ternary operator.
Style/NestedTernaryOperator: Style/NestedTernaryOperator:
Enabled: true Enabled: true
# Use `next` to skip iteration instead of a condition at the end.
Style/Next:
Enabled: false
# Prefer x.nil? to x == nil. # Prefer x.nil? to x == nil.
Style/NilComparison: Style/NilComparison:
Enabled: true Enabled: true
...@@ -444,51 +327,10 @@ Style/OneLineConditional: ...@@ -444,51 +327,10 @@ Style/OneLineConditional:
Style/OpMethod: Style/OpMethod:
Enabled: true 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. # Don't use parentheses around the condition of an if/unless/while.
Style/ParenthesesAroundCondition: Style/ParenthesesAroundCondition:
Enabled: true 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. # Checks for parentheses that seem not to serve any purpose.
Style/RedundantParentheses: Style/RedundantParentheses:
Enabled: true Enabled: true
...@@ -497,24 +339,6 @@ Style/RedundantParentheses: ...@@ -497,24 +339,6 @@ Style/RedundantParentheses:
Style/RedundantReturn: Style/RedundantReturn:
Enabled: true 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. # Don't use semicolons to terminate expressions.
Style/Semicolon: Style/Semicolon:
Enabled: true Enabled: true
...@@ -524,14 +348,6 @@ Style/SignalException: ...@@ -524,14 +348,6 @@ Style/SignalException:
EnforcedStyle: only_raise EnforcedStyle: only_raise
Enabled: true 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. # Use spaces after colons.
Style/SpaceAfterColon: Style/SpaceAfterColon:
Enabled: true Enabled: true
...@@ -553,11 +369,6 @@ Style/SpaceAfterNot: ...@@ -553,11 +369,6 @@ Style/SpaceAfterNot:
Style/SpaceAfterSemicolon: Style/SpaceAfterSemicolon:
Enabled: true 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. # Use a space around keywords if appropriate.
Style/SpaceAroundKeyword: Style/SpaceAroundKeyword:
Enabled: true Enabled: true
...@@ -566,10 +377,6 @@ Style/SpaceAroundKeyword: ...@@ -566,10 +377,6 @@ Style/SpaceAroundKeyword:
Style/SpaceAroundOperators: Style/SpaceAroundOperators:
Enabled: true Enabled: true
# Checks that the left block brace has or doesn't have space before it.
Style/SpaceBeforeBlockBraces:
Enabled: false
# No spaces before commas. # No spaces before commas.
Style/SpaceBeforeComma: Style/SpaceBeforeComma:
Enabled: true Enabled: true
...@@ -578,33 +385,14 @@ Style/SpaceBeforeComma: ...@@ -578,33 +385,14 @@ Style/SpaceBeforeComma:
Style/SpaceBeforeComment: Style/SpaceBeforeComment:
Enabled: true 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. # No spaces before semicolons.
Style/SpaceBeforeSemicolon: Style/SpaceBeforeSemicolon:
Enabled: true 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. # Use spaces inside hash literal braces - or don't.
Style/SpaceInsideHashLiteralBraces: Style/SpaceInsideHashLiteralBraces:
Enabled: true Enabled: true
# No spaces after ( or before ).
Style/SpaceInsideParens:
Enabled: false
# No spaces inside range literals. # No spaces inside range literals.
Style/SpaceInsideRangeLiteral: Style/SpaceInsideRangeLiteral:
Enabled: true Enabled: true
...@@ -614,10 +402,6 @@ Style/SpaceInsideStringInterpolation: ...@@ -614,10 +402,6 @@ Style/SpaceInsideStringInterpolation:
EnforcedStyle: no_space EnforcedStyle: no_space
Enabled: true Enabled: true
# Avoid Perl-style global variables.
Style/SpecialGlobalVars:
Enabled: false
# Check for the usage of parentheses around stabby lambda arguments. # Check for the usage of parentheses around stabby lambda arguments.
Style/StabbyLambdaParentheses: Style/StabbyLambdaParentheses:
EnforcedStyle: require_parentheses EnforcedStyle: require_parentheses
...@@ -627,25 +411,12 @@ Style/StabbyLambdaParentheses: ...@@ -627,25 +411,12 @@ Style/StabbyLambdaParentheses:
Style/StringLiterals: Style/StringLiterals:
Enabled: false 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. # Checks if configured preferred methods are used over non-preferred.
Style/StringMethods: Style/StringMethods:
PreferredMethods: PreferredMethods:
intern: to_sym intern: to_sym
Enabled: true 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. # No hard tabs.
Style/Tab: Style/Tab:
Enabled: true Enabled: true
...@@ -654,40 +425,10 @@ Style/Tab: ...@@ -654,40 +425,10 @@ Style/Tab:
Style/TrailingBlankLines: Style/TrailingBlankLines:
Enabled: true 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. # Checks for %W when interpolation is not needed.
Style/UnneededCapitalW: Style/UnneededCapitalW:
Enabled: true 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. # Checks for %q/%Q when single quotes or double quotes would do.
Style/UnneededPercentQ: Style/UnneededPercentQ:
Enabled: false Enabled: false
...@@ -717,12 +458,6 @@ Style/WhileUntilModifier: ...@@ -717,12 +458,6 @@ Style/WhileUntilModifier:
Style/WordArray: Style/WordArray:
Enabled: false Enabled: false
# TODO: Enable ZeroLengthPredicate Cop.
# Use #empty? when testing for objects of length 0.
Style/ZeroLengthPredicate:
Enabled: false
#################### Metrics ################################ #################### Metrics ################################
# A calculated magnitude based on number of assignments, # A calculated magnitude based on number of assignments,
...@@ -776,15 +511,6 @@ Metrics/PerceivedComplexity: ...@@ -776,15 +511,6 @@ Metrics/PerceivedComplexity:
Lint/AmbiguousOperator: Lint/AmbiguousOperator:
Enabled: true 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. # Align block ends correctly.
Lint/BlockAlignment: Lint/BlockAlignment:
Enabled: true Enabled: true
...@@ -810,14 +536,6 @@ Lint/DefEndAlignment: ...@@ -810,14 +536,6 @@ Lint/DefEndAlignment:
Lint/DeprecatedClassMethods: Lint/DeprecatedClassMethods:
Enabled: true 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. # Check for immutable argument given to each_with_object.
Lint/EachWithObjectArgument: Lint/EachWithObjectArgument:
Enabled: true Enabled: true
...@@ -830,10 +548,6 @@ Lint/ElseLayout: ...@@ -830,10 +548,6 @@ Lint/ElseLayout:
Lint/EmptyEnsure: Lint/EmptyEnsure:
Enabled: true Enabled: true
# Checks for empty string interpolation.
Lint/EmptyInterpolation:
Enabled: false
# Align ends correctly. # Align ends correctly.
Lint/EndAlignment: Lint/EndAlignment:
Enabled: true Enabled: true
...@@ -858,21 +572,11 @@ Lint/FloatOutOfRange: ...@@ -858,21 +572,11 @@ Lint/FloatOutOfRange:
Lint/FormatParameterMismatch: Lint/FormatParameterMismatch:
Enabled: true Enabled: true
# Don't suppress exception.
Lint/HandleExceptions:
Enabled: false
# Checks for adjacent string literals on the same line, which could better be # Checks for adjacent string literals on the same line, which could better be
# represented as a single string literal. # represented as a single string literal.
Lint/ImplicitStringConcatenation: Lint/ImplicitStringConcatenation:
Enabled: true 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 # Checks for invalid character literals with a non-escaped whitespace
# character. # character.
Lint/InvalidCharacterLiteral: Lint/InvalidCharacterLiteral:
...@@ -886,11 +590,6 @@ Lint/LiteralInCondition: ...@@ -886,11 +590,6 @@ Lint/LiteralInCondition:
Lint/LiteralInInterpolation: Lint/LiteralInInterpolation:
Enabled: true 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. # Do not use nested method definitions.
Lint/NestedMethodDefinition: Lint/NestedMethodDefinition:
Enabled: true Enabled: true
...@@ -916,13 +615,8 @@ Lint/RequireParentheses: ...@@ -916,13 +615,8 @@ Lint/RequireParentheses:
Lint/RescueException: Lint/RescueException:
Enabled: true Enabled: true
# Do not use the same name as outer local variable for block arguments # Checks for the order which exceptions are rescued to avoid rescueing a less specific exception before a more specific exception.
# or block local variables. Lint/ShadowedException:
Lint/ShadowingOuterLocalVariable:
Enabled: false
# 'Checks for Object#to_s usage in string interpolation.
Lint/StringConversionInInterpolation:
Enabled: false Enabled: false
# Do not use prefix `_` for a variable that is used. # Do not use prefix `_` for a variable that is used.
...@@ -935,22 +629,10 @@ Lint/UnderscorePrefixedVariableName: ...@@ -935,22 +629,10 @@ Lint/UnderscorePrefixedVariableName:
Lint/UnneededDisable: Lint/UnneededDisable:
Enabled: false Enabled: false
# Checks for unused block arguments.
Lint/UnusedBlockArgument:
Enabled: false
# Checks for unused method arguments.
Lint/UnusedMethodArgument:
Enabled: false
# Unreachable code. # Unreachable code.
Lint/UnreachableCode: Lint/UnreachableCode:
Enabled: true Enabled: true
# Checks for useless access modifiers.
Lint/UselessAccessModifier:
Enabled: false
# Checks for useless assignment to a local variable. # Checks for useless assignment to a local variable.
Lint/UselessAssignment: Lint/UselessAssignment:
Enabled: true Enabled: true
...@@ -983,11 +665,6 @@ Performance/Casecmp: ...@@ -983,11 +665,6 @@ Performance/Casecmp:
Performance/DoubleStartEndWith: Performance/DoubleStartEndWith:
Enabled: true 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`. # Use `strip` instead of `lstrip.rstrip`.
Performance/LstripRstrip: Performance/LstripRstrip:
Enabled: true Enabled: true
...@@ -996,24 +673,6 @@ Performance/LstripRstrip: ...@@ -996,24 +673,6 @@ Performance/LstripRstrip:
Performance/RangeInclude: Performance/RangeInclude:
Enabled: true 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 }`. # Use `sort` instead of `sort_by { |x| x }`.
Performance/RedundantSortBy: Performance/RedundantSortBy:
Enabled: true Enabled: true
...@@ -1082,18 +741,6 @@ Rails/ReadWriteAttribute: ...@@ -1082,18 +741,6 @@ Rails/ReadWriteAttribute:
Rails/ScopeArgs: Rails/ScopeArgs:
Enabled: true 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 ################################## ##################### RSpec ##################################
# Check that instances are not being stubbed globally. # Check that instances are not being stubbed globally.
......
# 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
...@@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -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) v 8.10.0 (unreleased)
- Expose {should,force}_remove_source_branch (Ben Boeckel) - 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 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 - Fix commit builds API, return all builds for all pipelines for given commit. !4849
- Replace Haml with Hamlit to make view rendering faster. !3666 - Replace Haml with Hamlit to make view rendering faster. !3666
...@@ -23,12 +24,15 @@ v 8.10.0 (unreleased) ...@@ -23,12 +24,15 @@ v 8.10.0 (unreleased)
- Add Sidekiq queue duration to transaction metrics. - Add Sidekiq queue duration to transaction metrics.
- Add a new column `artifacts_size` to table `ci_builds` !4964 - Add a new column `artifacts_size` to table `ci_builds` !4964
- Let Workhorse serve format-patch diffs - Let Workhorse serve format-patch diffs
- Display tooltip for mentioned users and groups !5261 (winniehell)
- Added day name to contribution calendar tooltips - Added day name to contribution calendar tooltips
- Make images fit to the size of the viewport !4810 - Make images fit to the size of the viewport !4810
- Fix check for New Branch button on Issue page !4630 (winniehell) - Fix check for New Branch button on Issue page !4630 (winniehell)
- Fix MR-auto-close text added to description. !4836 - 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) - Fix issue, preventing users w/o push access to sort tags !5105 (redetection)
- Add Spring EmojiOne updates. - Add Spring EmojiOne updates.
- Fix fetching LFS objects for private CI projects
- Add syntax for multiline blockquote using `>>>` fence !3954 - Add syntax for multiline blockquote using `>>>` fence !3954
- Fix viewing notification settings when a project is pending deletion - Fix viewing notification settings when a project is pending deletion
- Updated compare dropdown menus to use GL dropdown - Updated compare dropdown menus to use GL dropdown
...@@ -53,12 +57,16 @@ v 8.10.0 (unreleased) ...@@ -53,12 +57,16 @@ v 8.10.0 (unreleased)
- Fix user creation with stronger minimum password requirements !4054 (nathan-pmt) - Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
- Only show New Snippet button to users that can create snippets. - Only show New Snippet button to users that can create snippets.
- PipelinesFinder uses git cache data - 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 - Actually render old and new sections of parallel diff next to each other
- Throttle the update of `project.pushes_since_gc` to 1 minute. - Throttle the update of `project.pushes_since_gc` to 1 minute.
- Allow expanding and collapsing files in diff view (!4990) - Allow expanding and collapsing files in diff view (!4990)
- Collapse large diffs by default (!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. - Check for conflicts with existing Project's wiki path when creating a new project.
- Show last push widget in upstream after push to fork - Show last push widget in upstream after push to fork
- Fix stage status shown for pipelines
- Cache todos pending/done dashboard query counts. - Cache todos pending/done dashboard query counts.
- Don't instantiate a git tree on Projects show default view - Don't instantiate a git tree on Projects show default view
- Bump Rinku to 2.0.0 - Bump Rinku to 2.0.0
...@@ -79,7 +87,9 @@ v 8.10.0 (unreleased) ...@@ -79,7 +87,9 @@ v 8.10.0 (unreleased)
- Add basic system information like memory and disk usage to the admin panel - 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 - Don't garbage collect commits that have related DB records like comments
- More descriptive message for git hooks and file locks - 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 - Handle custom Git hook result in GitLab UI
- Allow to access Container Registry for Public and Internal projects
- Allow '?', or '&' for label names - Allow '?', or '&' for label names
- Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests - 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 - Add date when user joined the team on the member page
...@@ -95,6 +105,12 @@ v 8.10.0 (unreleased) ...@@ -95,6 +105,12 @@ v 8.10.0 (unreleased)
- Redesign Builds and Pipelines pages - Redesign Builds and Pipelines pages
- Change status color and icon for running builds - Change status color and icon for running builds
- Fix markdown rendering for: consecutive labels references, label references that begin with a digit or contains `.` - 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 v 8.9.6
- Fix importing of events under notes for GitLab projects. !5154 - Fix importing of events under notes for GitLab projects. !5154
......
...@@ -61,7 +61,7 @@ gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: 'omniauth-ldap' ...@@ -61,7 +61,7 @@ gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: 'omniauth-ldap'
# Git Wiki # Git Wiki
# Required manually in config/initializers/gollum.rb to control load order # 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 gem 'gollum-rugged_adapter', '~> 0.4.2', require: false
# Language detection # Language detection
...@@ -105,7 +105,7 @@ gem 'seed-fu', '~> 2.3.5' ...@@ -105,7 +105,7 @@ gem 'seed-fu', '~> 2.3.5'
# Markdown and HTML processing # Markdown and HTML processing
gem 'html-pipeline', '~> 1.11.0' gem 'html-pipeline', '~> 1.11.0'
gem 'task_list', '~> 1.0.2', require: 'task_list/railtie' 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 'redcarpet', '~> 3.3.3'
gem 'RedCloth', '~> 4.3.2' gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~>3.6' gem 'rdoc', '~>3.6'
...@@ -113,7 +113,7 @@ gem 'org-ruby', '~> 0.9.12' ...@@ -113,7 +113,7 @@ gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0' gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1' gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2' gem 'asciidoctor', '~> 1.5.2'
gem 'rouge', '~> 1.11' gem 'rouge', '~> 2.0'
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s # See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
# and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM # and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
...@@ -299,7 +299,7 @@ group :development, :test do ...@@ -299,7 +299,7 @@ group :development, :test do
gem 'spring-commands-spinach', '~> 1.1.0' gem 'spring-commands-spinach', '~> 1.1.0'
gem 'spring-commands-teaspoon', '~> 0.0.2' 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 'rubocop-rspec', '~> 1.5.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false gem 'scss_lint', '~> 0.47.0', require: false
gem 'simplecov', '~> 0.11.0', require: false gem 'simplecov', '~> 0.11.0', require: false
......
...@@ -58,7 +58,7 @@ GEM ...@@ -58,7 +58,7 @@ GEM
faraday_middleware-multi_json (~> 0.0) faraday_middleware-multi_json (~> 0.0)
oauth2 (~> 1.0) oauth2 (~> 1.0)
asciidoctor (1.5.3) asciidoctor (1.5.3)
ast (2.2.0) ast (2.3.0)
attr_encrypted (3.0.1) attr_encrypted (3.0.1)
encryptor (~> 3.0.0) encryptor (~> 3.0.0)
attr_required (1.0.0) attr_required (1.0.0)
...@@ -264,7 +264,7 @@ GEM ...@@ -264,7 +264,7 @@ GEM
escape_utils (~> 1.1.0) escape_utils (~> 1.1.0)
mime-types (>= 1.19) mime-types (>= 1.19)
rugged (>= 0.23.0b) rugged (>= 0.23.0b)
github-markup (1.3.3) github-markup (1.4.0)
gitlab-flowdock-git-hook (1.0.1) gitlab-flowdock-git-hook (1.0.1)
flowdock (~> 0.7) flowdock (~> 0.7)
gitlab-grit (>= 2.4.1) gitlab-grit (>= 2.4.1)
...@@ -287,13 +287,13 @@ GEM ...@@ -287,13 +287,13 @@ GEM
rubyntlm (~> 0.3) rubyntlm (~> 0.3)
globalid (0.3.6) globalid (0.3.6)
activesupport (>= 4.1.0) activesupport (>= 4.1.0)
gollum-grit_adapter (1.0.0) gollum-grit_adapter (1.0.1)
gitlab-grit (~> 2.7, >= 2.7.1) gitlab-grit (~> 2.7, >= 2.7.1)
gollum-lib (4.1.0) gollum-lib (4.2.1)
github-markup (~> 1.3.3) github-markup (~> 1.4.0)
gollum-grit_adapter (~> 1.0) gollum-grit_adapter (~> 1.0)
nokogiri (~> 1.6.4) nokogiri (~> 1.6.4)
rouge (~> 1.9) rouge (~> 2.0)
sanitize (~> 2.1.0) sanitize (~> 2.1.0)
stringex (~> 2.5.1) stringex (~> 2.5.1)
gollum-rugged_adapter (0.4.2) gollum-rugged_adapter (0.4.2)
...@@ -473,7 +473,7 @@ GEM ...@@ -473,7 +473,7 @@ GEM
orm_adapter (0.5.0) orm_adapter (0.5.0)
paranoia (2.1.4) paranoia (2.1.4)
activerecord (~> 4.0) activerecord (~> 4.0)
parser (2.3.1.0) parser (2.3.1.2)
ast (~> 2.2) ast (~> 2.2)
pg (0.18.4) pg (0.18.4)
pkg-config (1.1.7) pkg-config (1.1.7)
...@@ -578,7 +578,7 @@ GEM ...@@ -578,7 +578,7 @@ GEM
railties (>= 4.2.0, < 5.1) railties (>= 4.2.0, < 5.1)
rinku (2.0.0) rinku (2.0.0)
rotp (2.1.2) rotp (2.1.2)
rouge (1.11.0) rouge (2.0.3)
rqrcode (0.7.0) rqrcode (0.7.0)
chunky_png chunky_png
rqrcode-rails3 (0.1.7) rqrcode-rails3 (0.1.7)
...@@ -606,8 +606,8 @@ GEM ...@@ -606,8 +606,8 @@ GEM
rspec-retry (0.4.5) rspec-retry (0.4.5)
rspec-core rspec-core
rspec-support (3.5.0) rspec-support (3.5.0)
rubocop (0.40.0) rubocop (0.41.2)
parser (>= 2.3.1.0, < 3.0) parser (>= 2.3.1.1, < 3.0)
powerpack (~> 0.1) powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0) rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
...@@ -758,7 +758,7 @@ GEM ...@@ -758,7 +758,7 @@ GEM
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.2) unf_ext (0.0.7.2)
unicode-display_width (1.0.5) unicode-display_width (1.1.0)
unicorn (4.9.0) unicorn (4.9.0)
kgio (~> 2.6) kgio (~> 2.6)
rack rack
...@@ -859,12 +859,12 @@ DEPENDENCIES ...@@ -859,12 +859,12 @@ DEPENDENCIES
gemnasium-gitlab-service (~> 0.2) gemnasium-gitlab-service (~> 0.2)
gemojione (~> 2.6) gemojione (~> 2.6)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
github-markup (~> 1.3.1) github-markup (~> 1.4)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab_git (~> 10.2) gitlab_git (~> 10.2)
gitlab_meta (= 7.0) gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1) gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.1.0) gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.2) gollum-rugged_adapter (~> 0.4.2)
gon (~> 6.0.1) gon (~> 6.0.1)
grape (~> 0.13.0) grape (~> 0.13.0)
...@@ -933,11 +933,11 @@ DEPENDENCIES ...@@ -933,11 +933,11 @@ DEPENDENCIES
request_store (~> 1.3.0) request_store (~> 1.3.0)
rerun (~> 0.11.0) rerun (~> 0.11.0)
responders (~> 2.0) responders (~> 2.0)
rouge (~> 1.11) rouge (~> 2.0)
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.5.0) rspec-rails (~> 3.5.0)
rspec-retry (~> 0.4.5) rspec-retry (~> 0.4.5)
rubocop (~> 0.40.0) rubocop (~> 0.41.2)
rubocop-rspec (~> 1.5.0) rubocop-rspec (~> 1.5.0)
ruby-fogbugz (~> 0.2.1) ruby-fogbugz (~> 0.2.1)
sanitize (~> 2.0) sanitize (~> 2.0)
......
...@@ -53,7 +53,6 @@ ...@@ -53,7 +53,6 @@
#= require_directory ./u2f #= require_directory ./u2f
#= require_directory . #= require_directory .
#= require fuzzaldrin-plus #= require fuzzaldrin-plus
#= require u2f
window.slugify = (text) -> window.slugify = (text) ->
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase() text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
......
...@@ -39,8 +39,6 @@ class Dispatcher ...@@ -39,8 +39,6 @@ class Dispatcher
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new GLForm($('.issue-form')) new GLForm($('.issue-form'))
new IssuableForm($('.issue-form')) new IssuableForm($('.issue-form'))
new LabelsSelect()
new MilestoneSelect()
when 'projects:merge_requests:new', 'projects:merge_requests:edit' when 'projects:merge_requests:new', 'projects:merge_requests:edit'
new Diff() new Diff()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
......
...@@ -184,22 +184,20 @@ class @LabelsSelect ...@@ -184,22 +184,20 @@ class @LabelsSelect
.value() .value()
if $dropdown.hasClass 'js-extra-options' if $dropdown.hasClass 'js-extra-options'
extraData = []
if showAny
extraData.push(
isAny: true
title: 'Any Label'
)
if showNo if showNo
extraData.push( data.unshift(
id: 0 id: 0
title: 'No Label' title: 'No Label'
) )
if extraData.length if showAny
extraData.push 'divider' data.unshift(
data = extraData.concat(data) isAny: true
title: 'Any Label'
)
if data.length > 2
data.splice 2, 0, 'divider'
callback data callback data
...@@ -289,12 +287,6 @@ class @LabelsSelect ...@@ -289,12 +287,6 @@ class @LabelsSelect
defaultLabel defaultLabel
fieldName: $dropdown.data('field-name') fieldName: $dropdown.data('field-name')
id: (label) -> 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? if $dropdown.hasClass("js-filter-submit") and not label.isAny?
label.title label.title
else else
...@@ -308,9 +300,6 @@ class @LabelsSelect ...@@ -308,9 +300,6 @@ class @LabelsSelect
$selectbox.hide() $selectbox.hide()
# display:block overrides the hide-collapse rule # display:block overrides the hide-collapse rule
$value.removeAttr('style') $value.removeAttr('style')
return if $dropdown.hasClass('js-issuable-form-dropdown')
if $dropdown.hasClass 'js-multiselect' if $dropdown.hasClass 'js-multiselect'
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
selectedLabels = $dropdown selectedLabels = $dropdown
...@@ -332,7 +321,7 @@ class @LabelsSelect ...@@ -332,7 +321,7 @@ class @LabelsSelect
clicked: (label) -> clicked: (label) ->
_this.enableBulkLabelDropdown() _this.enableBulkLabelDropdown()
if $dropdown.hasClass('js-filter-bulk-update') or $dropdown.hasClass('js-issuable-form-dropdown') if $dropdown.hasClass('js-filter-bulk-update')
return return
page = $('body').data 'page' page = $('body').data 'page'
......
...@@ -62,7 +62,7 @@ class @MilestoneSelect ...@@ -62,7 +62,7 @@ class @MilestoneSelect
title: 'Upcoming' title: 'Upcoming'
) )
if extraOptions.length > 0 if extraOptions.length > 2
extraOptions.push 'divider' extraOptions.push 'divider'
callback(extraOptions.concat(data)) callback(extraOptions.concat(data))
......
...@@ -6,8 +6,20 @@ ...@@ -6,8 +6,20 @@
class @U2FAuthenticate class @U2FAuthenticate
constructor: (@container, u2fParams) -> constructor: (@container, u2fParams) ->
@appId = u2fParams.app_id @appId = u2fParams.app_id
@challenges = u2fParams.challenges @challenge = u2fParams.challenge
@signRequests = u2fParams.sign_requests
# 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: () => start: () =>
if U2FUtil.isU2FSupported() if U2FUtil.isU2FSupported()
...@@ -16,7 +28,7 @@ class @U2FAuthenticate ...@@ -16,7 +28,7 @@ class @U2FAuthenticate
@renderNotSupported() @renderNotSupported()
authenticate: () => authenticate: () =>
u2f.sign(@appId, @challenges, @signRequests, (response) => u2f.sign(@appId, @challenge, @signRequests, (response) =>
if response.errorCode if response.errorCode
error = new U2FError(response.errorCode) error = new U2FError(response.errorCode)
@renderError(error); @renderError(error);
......
class @U2FUtil
@isU2FSupported: ->
window.u2f
# 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 %>
...@@ -155,13 +155,11 @@ class @UsersSelect ...@@ -155,13 +155,11 @@ class @UsersSelect
# display:block overrides the hide-collapse rule # display:block overrides the hide-collapse rule
$value.css('display', '') $value.css('display', '')
clicked: (user, $el, e) -> clicked: (user) ->
page = $('body').data 'page' page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index' isIssueIndex = page is 'projects:issues:index'
isMRIndex = page is page is 'projects:merge_requests:index' isMRIndex = page is page is 'projects:merge_requests:index'
if $dropdown.hasClass('js-filter-bulk-update') or $dropdown.hasClass('js-issuable-form-dropdown') if $dropdown.hasClass('js-filter-bulk-update')
e.preventDefault()
selectedId = user.id
return return
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
...@@ -174,8 +172,7 @@ class @UsersSelect ...@@ -174,8 +172,7 @@ class @UsersSelect
.closest('.selectbox') .closest('.selectbox')
.find("input[name='#{$dropdown.data('field-name')}']").val() .find("input[name='#{$dropdown.data('field-name')}']").val()
assignTo(selected) assignTo(selected)
id: (user) ->
user.id
renderRow: (user) -> renderRow: (user) ->
username = if user.username then "@#{user.username}" else "" username = if user.username then "@#{user.username}" else ""
avatar = if user.avatar_url then user.avatar_url else false avatar = if user.avatar_url then user.avatar_url else false
......
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
position: absolute; position: absolute;
top: 50%; top: 50%;
right: 6px; right: 6px;
margin-top: -6px; margin-top: -4px;
color: $dropdown-toggle-icon-color; color: $dropdown-toggle-icon-color;
font-size: 10px; font-size: 10px;
} }
......
...@@ -324,10 +324,6 @@ ...@@ -324,10 +324,6 @@
.issuable-form-select-holder { .issuable-form-select-holder {
display: inline-block; display: inline-block;
width: 250px; width: 250px;
.dropdown-menu-toggle {
width: 100%;
}
} }
.table-holder { .table-holder {
......
...@@ -129,6 +129,8 @@ ...@@ -129,6 +129,8 @@
} }
.cancel-retry-btns { .cancel-retry-btns {
vertical-align: middle;
.btn:not(:first-child) { .btn:not(:first-child) {
margin-left: 8px; margin-left: 8px;
} }
......
...@@ -344,10 +344,6 @@ class ApplicationController < ActionController::Base ...@@ -344,10 +344,6 @@ class ApplicationController < ActionController::Base
session[:skip_tfa] && session[:skip_tfa] > Time.current session[:skip_tfa] && session[:skip_tfa] > Time.current
end end
def browser_supports_u2f?
browser.chrome? && browser.version.to_i >= 41 && !browser.device.mobile?
end
def redirect_to_home_page_url? def redirect_to_home_page_url?
# If user is not signed-in and tries to access root_path - redirect him to landing page # 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 # Don't redirect to the default URL to prevent endless redirections
......
...@@ -57,7 +57,7 @@ module AuthenticatesWithTwoFactor ...@@ -57,7 +57,7 @@ module AuthenticatesWithTwoFactor
# Authenticate using the response from a U2F (universal 2nd factor) device # Authenticate using the response from a U2F (universal 2nd factor) device
def authenticate_with_two_factor_via_u2f(user) 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 # Remove any lingering user data from login
session.delete(:otp_user_id) session.delete(:otp_user_id)
session.delete(:challenges) session.delete(:challenges)
...@@ -77,11 +77,9 @@ module AuthenticatesWithTwoFactor ...@@ -77,11 +77,9 @@ module AuthenticatesWithTwoFactor
if key_handles.present? if key_handles.present?
sign_requests = u2f.authentication_requests(key_handles) sign_requests = u2f.authentication_requests(key_handles)
challenges = sign_requests.map(&:challenge) session[:challenge] ||= u2f.challenge
session[:challenges] = challenges gon.push(u2f: { challenge: session[:challenge], app_id: u2f_app_id,
gon.push(u2f: { challenges: challenges, app_id: u2f_app_id, sign_requests: sign_requests })
sign_requests: sign_requests,
browser_supports_u2f: browser_supports_u2f? })
end end
end end
end end
...@@ -100,7 +100,6 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController ...@@ -100,7 +100,6 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
gon.push(u2f: { challenges: session[:challenges], app_id: u2f_app_id, gon.push(u2f: { challenges: session[:challenges], app_id: u2f_app_id,
register_requests: registration_requests, register_requests: registration_requests,
sign_requests: sign_requests, sign_requests: sign_requests })
browser_supports_u2f: browser_supports_u2f? })
end end
end end
...@@ -119,10 +119,6 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -119,10 +119,6 @@ class Projects::IssuesController < Projects::ApplicationController
render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }) render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } })
end end
end end
rescue ActiveRecord::StaleObjectError
@conflict = true
render :edit
end end
def referenced_merge_requests def referenced_merge_requests
...@@ -220,7 +216,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -220,7 +216,7 @@ class Projects::IssuesController < Projects::ApplicationController
def issue_params def issue_params
params.require(:issue).permit( params.require(:issue).permit(
:title, :assignee_id, :position, :description, :confidential, :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 end
......
...@@ -196,9 +196,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -196,9 +196,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
else else
render "edit" render "edit"
end end
rescue ActiveRecord::StaleObjectError
@conflict = true
render :edit
end end
def remove_wip def remove_wip
...@@ -427,7 +424,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -427,7 +424,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
:title, :assignee_id, :source_project_id, :source_branch, :title, :assignee_id, :source_project_id, :source_branch,
:target_project_id, :target_branch, :milestone_id, :target_project_id, :target_branch, :milestone_id,
:state_event, :description, :task_num, :force_remove_source_branch, :state_event, :description, :task_num, :force_remove_source_branch,
:lock_version, label_ids: [] label_ids: []
) )
end end
......
module BlobHelper module BlobHelper
def highlighter(blob_name, blob_content, repository: nil, nowrap: false) def highlight(blob_name, blob_content, repository: nil, plain: false)
Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap, repository: repository) highlighted = Gitlab::Highlight.highlight(blob_name, blob_content, plain: plain, repository: repository)
end raw %(<pre class="code highlight"><code>#{highlighted}</code></pre>)
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)
end end
def no_highlight_files def no_highlight_files
......
...@@ -9,7 +9,7 @@ module IssuablesHelper ...@@ -9,7 +9,7 @@ module IssuablesHelper
def multi_label_name(current_labels, default_label) def multi_label_name(current_labels, default_label)
# current_labels may be a string from before # 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 if current_labels.count > 1
"#{current_labels[0]} +#{current_labels.count - 1} more" "#{current_labels[0]} +#{current_labels.count - 1} more"
else else
......
module U2fHelper
def inject_u2f_api?
browser.chrome? && browser.version.to_i >= 41 && !browser.device.mobile?
end
end
...@@ -204,7 +204,8 @@ class Ability ...@@ -204,7 +204,8 @@ class Ability
:download_code, :download_code,
:fork_project, :fork_project,
:read_commit_status, :read_commit_status,
:read_pipeline :read_pipeline,
:read_container_image
] ]
end end
......
...@@ -56,6 +56,7 @@ module Ci ...@@ -56,6 +56,7 @@ module Ci
new_build.yaml_variables = build.yaml_variables new_build.yaml_variables = build.yaml_variables
new_build.when = build.when new_build.when = build.when
new_build.user = user new_build.user = user
new_build.environment = build.environment
new_build.save new_build.save
MergeRequests::AddTodoWhenBuildFailsService.new(build.project, nil).close(new_build) MergeRequests::AddTodoWhenBuildFailsService.new(build.project, nil).close(new_build)
new_build new_build
......
...@@ -6,6 +6,8 @@ module Ci ...@@ -6,6 +6,8 @@ module Ci
self.table_name = 'ci_commits' self.table_name = 'ci_commits'
belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id 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 :statuses, class_name: 'CommitStatus', foreign_key: :commit_id
has_many :builds, class_name: 'Ci::Build', 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 has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest', foreign_key: :commit_id
...@@ -215,6 +217,8 @@ module Ci ...@@ -215,6 +217,8 @@ module Ci
end end
def keep_around_commits def keep_around_commits
return unless project
project.repository.keep_around(self.sha) project.repository.keep_around(self.sha)
project.repository.keep_around(self.before_sha) project.repository.keep_around(self.before_sha)
end end
......
...@@ -65,8 +65,7 @@ module Awardable ...@@ -65,8 +65,7 @@ module Awardable
def create_award_emoji(name, current_user) def create_award_emoji(name, current_user)
return unless emoji_awardable? return unless emoji_awardable?
award_emoji.create(name: normalize_name(name), user: current_user)
award_emoji.create(name: name, user: current_user)
end end
def remove_award_emoji(name, current_user) def remove_award_emoji(name, current_user)
...@@ -80,4 +79,10 @@ module Awardable ...@@ -80,4 +79,10 @@ module Awardable
create_award_emoji(emoji_name, current_user) create_award_emoji(emoji_name, current_user)
end end
end end
private
def normalize_name(name)
Gitlab::AwardEmoji.normalize_emoji_name(name)
end
end end
...@@ -87,12 +87,6 @@ module Issuable ...@@ -87,12 +87,6 @@ module Issuable
User.find(assignee_id_was).update_cache_counts if assignee_id_was User.find(assignee_id_was).update_cache_counts if assignee_id_was
assignee.update_cache_counts if assignee assignee.update_cache_counts if assignee
end 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 end
module ClassMethods module ClassMethods
......
...@@ -14,14 +14,14 @@ module Mentionable ...@@ -14,14 +14,14 @@ module Mentionable
attr = attr.to_s attr = attr.to_s
mentionable_attrs << [attr, options] mentionable_attrs << [attr, options]
end end
end
included do
# Accessor for attributes marked mentionable. # Accessor for attributes marked mentionable.
def mentionable_attrs cattr_accessor :mentionable_attrs, instance_accessor: false do
@mentionable_attrs ||= [] []
end end
end
included do
if self < Participable if self < Participable
participant -> (user, ext) { all_references(user, extractor: ext) } participant -> (user, ext) { all_references(user, extractor: ext) }
end end
......
...@@ -41,9 +41,12 @@ module Participable ...@@ -41,9 +41,12 @@ module Participable
def participant(attr) def participant(attr)
participant_attrs << attr participant_attrs << attr
end end
end
def participant_attrs included do
@participant_attrs ||= [] # Accessor for participant attributes.
cattr_accessor :participant_attrs, instance_accessor: false do
[]
end end
end end
......
...@@ -38,7 +38,7 @@ class LegacyDiffNote < Note ...@@ -38,7 +38,7 @@ class LegacyDiffNote < Note
end end
def diff_line 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 end
def for_line?(line) def for_line?(line)
...@@ -55,7 +55,7 @@ class LegacyDiffNote < Note ...@@ -55,7 +55,7 @@ class LegacyDiffNote < Note
def active? def active?
return @active if defined?(@active) return @active if defined?(@active)
return true if for_commit? return true if for_commit?
return true unless self.diff return true unless diff_line
return false unless noteable return false unless noteable
noteable_diff = find_noteable_diff noteable_diff = find_noteable_diff
......
...@@ -229,8 +229,7 @@ class Note < ActiveRecord::Base ...@@ -229,8 +229,7 @@ class Note < ActiveRecord::Base
end end
def award_emoji_name def award_emoji_name
original_name = note.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1] note.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1]
Gitlab::AwardEmoji.normalize_emoji_name(original_name)
end end
private private
......
...@@ -162,7 +162,7 @@ class Project < ActiveRecord::Base ...@@ -162,7 +162,7 @@ class Project < ActiveRecord::Base
validates :namespace, presence: true validates :namespace, presence: true
validates_uniqueness_of :name, scope: :namespace_id validates_uniqueness_of :name, scope: :namespace_id
validates_uniqueness_of :path, 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 } validates :star_count, numericality: { greater_than_or_equal_to: 0 }
validate :check_limit, on: :create validate :check_limit, on: :create
validate :avatar_type, validate :avatar_type,
...@@ -482,7 +482,7 @@ class Project < ActiveRecord::Base ...@@ -482,7 +482,7 @@ class Project < ActiveRecord::Base
end end
def create_or_update_import_data(data: nil, credentials: nil) 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 project_import_data = import_data || build_import_data
if data if data
...@@ -1038,8 +1038,8 @@ class Project < ActiveRecord::Base ...@@ -1038,8 +1038,8 @@ class Project < ActiveRecord::Base
pipelines.order(id: :desc).find_by(sha: sha, ref: ref) pipelines.order(id: :desc).find_by(sha: sha, ref: ref)
end end
def ensure_pipeline(sha, ref) def ensure_pipeline(sha, ref, current_user = nil)
pipeline(sha, ref) || pipelines.create(sha: sha, ref: ref) pipeline(sha, ref) || pipelines.create(sha: sha, ref: ref, user: current_user)
end end
def enable_ci def enable_ci
......
...@@ -85,6 +85,7 @@ class User < ActiveRecord::Base ...@@ -85,6 +85,7 @@ class User < ActiveRecord::Base
has_one :abuse_report, dependent: :destroy has_one :abuse_report, dependent: :destroy
has_many :spam_logs, dependent: :destroy has_many :spam_logs, dependent: :destroy
has_many :builds, dependent: :nullify, class_name: 'Ci::Build' 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 :todos, dependent: :destroy
has_many :notification_settings, dependent: :destroy has_many :notification_settings, dependent: :destroy
has_many :award_emoji, dependent: :destroy has_many :award_emoji, dependent: :destroy
......
...@@ -2,6 +2,7 @@ module Ci ...@@ -2,6 +2,7 @@ module Ci
class CreatePipelineService < BaseService class CreatePipelineService < BaseService
def execute def execute
pipeline = project.pipelines.new(params) pipeline = project.pipelines.new(params)
pipeline.user = current_user
unless ref_names.include?(params[:ref]) unless ref_names.include?(params[:ref])
pipeline.errors.add(:base, 'Reference not found') pipeline.errors.add(:base, 'Reference not found')
......
...@@ -14,7 +14,13 @@ class CreateCommitBuildsService ...@@ -14,7 +14,13 @@ class CreateCommitBuildsService
return false return false
end 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 # Skip creating pipeline if no gitlab-ci.yml is found
......
...@@ -8,7 +8,6 @@ module Notes ...@@ -8,7 +8,6 @@ module Notes
if note.award_emoji? if note.award_emoji?
noteable = note.noteable noteable = note.noteable
todo_service.new_award_emoji(noteable, current_user) todo_service.new_award_emoji(noteable, current_user)
return noteable.create_award_emoji(note.award_emoji_name, current_user) return noteable.create_award_emoji(note.award_emoji_name, current_user)
end end
......
...@@ -10,7 +10,7 @@ module Projects ...@@ -10,7 +10,7 @@ module Projects
def save_all def save_all
if [version_saver, project_tree_saver, uploads_saver, repo_saver, wiki_repo_saver].all?(&:save) 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 notify_success
else else
cleanup_and_notify cleanup_and_notify
......
...@@ -194,7 +194,7 @@ class TodoService ...@@ -194,7 +194,7 @@ class TodoService
end end
def create_assignment_todo(issuable, author) 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) attributes = attributes_for_todo(issuable.project, issuable, author, Todo::ASSIGNED)
create_todos(issuable.assignee, attributes) create_todos(issuable.assignee, attributes)
end end
...@@ -239,7 +239,6 @@ class TodoService ...@@ -239,7 +239,6 @@ class TodoService
def filter_mentioned_users(project, target, author) def filter_mentioned_users(project, target, author)
mentioned_users = target.mentioned_users(author) mentioned_users = target.mentioned_users(author)
mentioned_users = reject_users_without_access(mentioned_users, project, target) mentioned_users = reject_users_without_access(mentioned_users, project, target)
mentioned_users.delete(author)
mentioned_users.uniq mentioned_users.uniq
end end
......
- if inject_u2f_api?
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('u2f.js')
%div %div
.login-box .login-box
.login-heading .login-heading
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
name: "#{j(@project.name)}" name: "#{j(@project.name)}"
}; };
- if @group and @group.path - if @group && @group.persisted? && @group.path
:javascript :javascript
gl.groupOptions = gl.groupOptions || {}; gl.groupOptions = gl.groupOptions || {};
gl.groupOptions["#{j(@group.path)}"] = { gl.groupOptions["#{j(@group.path)}"] = {
......
- if @note.diff_note? - if @note.diff_note? && @note.diff_file
%p.details %p.details
New comment on diff for New comment on diff for
= link_to @note.diff_file.file_path, @target_url = link_to @note.diff_file.file_path, @target_url
......
...@@ -2,6 +2,10 @@ ...@@ -2,6 +2,10 @@
- header_title "Two-Factor Authentication", profile_two_factor_auth_path - header_title "Two-Factor Authentication", profile_two_factor_auth_path
= render 'profiles/head' = render 'profiles/head'
- if inject_u2f_api?
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('u2f.js')
.row.prepend-top-default .row.prepend-top-default
.col-lg-3 .col-lg-3
%h4.prepend-top-0 %h4.prepend-top-0
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
Cant find HEAD commit for this branch Cant find HEAD commit for this branch
- stages_status = pipeline.statuses.stages_status - stages_status = pipeline.statuses.latest.stages_status
- stages.each do |stage| - stages.each do |stage|
%td %td
- status = stages_status[stage] - status = stages_status[stage]
...@@ -58,16 +58,7 @@ ...@@ -58,16 +58,7 @@
.controls.hidden-xs.pull-right .controls.hidden-xs.pull-right
- artifacts = pipeline.builds.latest.select { |b| b.artifacts? } - artifacts = pipeline.builds.latest.select { |b| b.artifacts? }
- if artifacts.present? - if artifacts.present?
.btn-group.inline .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
.btn-group .btn-group
%a.dropdown-toggle.btn.btn-default.build-artifacts{type: 'button', 'data-toggle' => 'dropdown'} %a.dropdown-toggle.btn.btn-default.build-artifacts{type: 'button', 'data-toggle' => 'dropdown'}
= icon("download") = icon("download")
...@@ -80,7 +71,7 @@ ...@@ -80,7 +71,7 @@
%span Download '#{build.name}' artifacts %span Download '#{build.name}' artifacts
- if can?(current_user, :update_pipeline, @project) - if can?(current_user, :update_pipeline, @project)
.cancel-retry-btns .cancel-retry-btns.inline
- if pipeline.retryable? - if pipeline.retryable?
= link_to retry_namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: 'btn has-tooltip', title: "Retry", method: :post do = link_to retry_namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: 'btn has-tooltip', title: "Retry", method: :post do
= icon("repeat") = icon("repeat")
......
...@@ -21,10 +21,10 @@ ...@@ -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" } }) 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 .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 .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 .pull-right
= render 'shared/sort_dropdown' = render 'shared/sort_dropdown'
......
= form_errors(issuable) = 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 .form-group
= f.label :title, class: 'control-label' = f.label :title, class: 'control-label'
.col-sm-10 .col-sm-10
...@@ -59,24 +52,38 @@ ...@@ -59,24 +52,38 @@
= f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}" = 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) } .col-sm-10{ class: ("col-lg-8" if has_due_date) }
.issuable-form-select-holder .issuable-form-select-holder
- project = @target_project || @project = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
- if issuable.assignee_id placeholder: 'Select assignee', class: 'custom-form-control', null_user: true,
= hidden_field_tag("#{issuable.class.model_name.param_key}[assignee_id]", issuable.assignee_id) selected: issuable.assignee_id, project: @target_project || @project,
= 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", first_user: true, current_user: true, include_blank: true)
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" } }) %div
= link_to 'Assign to me', '#', class: 'assign-to-me-link prepend-top-5 inline'
.form-group.issue-milestone .form-group.issue-milestone
= f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}" = 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) } .col-sm-10{ class: ("col-lg-8" if has_due_date) }
.issuable-form-select-holder - if milestone_options(issuable).present?
= render "shared/issuable/milestone_dropdown", selected: issuable.milestone_id, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false .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 .form-group
- has_labels = issuable.project.labels.any? - 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}" = 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}" } .col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
.issuable-form-select-holder - if has_labels
= 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" } .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 - if has_due_date
.col-lg-6 .col-lg-6
.form-group .form-group
...@@ -142,5 +149,3 @@ ...@@ -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?" }, = 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' 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' = link_to 'Cancel', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), class: 'btn btn-grouped btn-cancel'
= f.hidden_field :lock_version
...@@ -4,21 +4,19 @@ ...@@ -4,21 +4,19 @@
- show_footer = local_assigns.fetch(:show_footer, true) - show_footer = local_assigns.fetch(:show_footer, true)
- data_options = local_assigns.fetch(:data_options, {}) - data_options = local_assigns.fetch(:data_options, {})
- classes = local_assigns.fetch(:classes, []) - classes = local_assigns.fetch(:classes, [])
- selected = local_assigns.fetch(:selected, nil) - 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"}
- 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.merge!(data_options) - dropdown_data.merge!(data_options)
- classes << 'js-extra-options' if extra_options - classes << 'js-extra-options' if extra_options
- classes << 'js-filter-submit' if filter_submit - classes << 'js-filter-submit' if filter_submit
- if selected.present? - if params[:label_name].present?
- if selected.respond_to?('any?') - if params[:label_name].respond_to?('any?')
- selected.each do |label| - params[:label_name].each do |label|
= hidden_field_tag data_options[:field_name], label, id: nil = hidden_field_tag "label_name[]", label, id: nil
.dropdown .dropdown
%button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data} %button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data}
%span.dropdown-toggle-text %span.dropdown-toggle-text
= h(multi_label_name(selected_toggle || selected, "Label")) = h(multi_label_name(params[:label_name], "Label"))
= icon('chevron-down') = icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable .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 } = render partial: "shared/issuable/label_page_default", locals: { title: "Filter by label", show_footer: show_footer, show_create: show_create }
......
- if selected.present? - if params[:milestone_title].present?
= hidden_field_tag(name, selected) = hidden_field_tag(:milestone_title, params[:milestone_title])
= 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", = 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: 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 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 - if @project
%ul.dropdown-footer-list %ul.dropdown-footer-list
- if can? current_user, :admin_milestone, @project - if can? current_user, :admin_milestone, @project
......
class ProjectExportWorker class ProjectExportWorker
include Sidekiq::Worker include Sidekiq::Worker
sidekiq_options queue: :gitlab_shell, retry: true sidekiq_options queue: :gitlab_shell, retry: 3
def perform(current_user_id, project_id) def perform(current_user_id, project_id)
current_user = User.find(current_user_id) current_user = User.find(current_user_id)
......
...@@ -87,6 +87,7 @@ module Gitlab ...@@ -87,6 +87,7 @@ module Gitlab
config.assets.precompile << "profile/application.js" config.assets.precompile << "profile/application.js"
config.assets.precompile << "lib/utils/*.js" config.assets.precompile << "lib/utils/*.js"
config.assets.precompile << "lib/*.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 # Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0' config.assets.version = '1.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
class AddUserIdToPipeline < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
def change
add_column :ci_commits, :user_id, :integer
end
end
class AddIndexForPipelineUserId < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
def change
add_concurrent_index :ci_commits, :user_id
end
end
...@@ -70,11 +70,11 @@ ActiveRecord::Schema.define(version: 20160716115710) do ...@@ -70,11 +70,11 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.string "recaptcha_site_key" t.string "recaptcha_site_key"
t.string "recaptcha_private_key" t.string "recaptcha_private_key"
t.integer "metrics_port", default: 8089 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.integer "metrics_sample_interval", default: 15
t.boolean "sentry_enabled", default: false t.boolean "sentry_enabled", default: false
t.string "sentry_dsn" t.string "sentry_dsn"
t.boolean "akismet_enabled", default: false
t.string "akismet_api_key"
t.boolean "email_author_in_body", default: false t.boolean "email_author_in_body", default: false
t.integer "default_group_visibility" t.integer "default_group_visibility"
t.boolean "repository_checks_enabled", default: false t.boolean "repository_checks_enabled", default: false
...@@ -84,10 +84,10 @@ ActiveRecord::Schema.define(version: 20160716115710) do ...@@ -84,10 +84,10 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.string "health_check_access_token" t.string "health_check_access_token"
t.boolean "send_user_confirmation_email", default: false t.boolean "send_user_confirmation_email", default: false
t.integer "container_registry_token_expire_delay", default: 5 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.text "after_sign_up_text"
t.string "repository_storage", default: "default" t.string "repository_storage", default: "default"
t.string "enabled_git_access_protocol" t.string "enabled_git_access_protocol"
t.boolean "user_default_external", default: false, null: false
end end
create_table "audit_events", force: :cascade do |t| create_table "audit_events", force: :cascade do |t|
...@@ -165,8 +165,8 @@ ActiveRecord::Schema.define(version: 20160716115710) do ...@@ -165,8 +165,8 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.text "artifacts_metadata" t.text "artifacts_metadata"
t.integer "erased_by_id" t.integer "erased_by_id"
t.datetime "erased_at" t.datetime "erased_at"
t.datetime "artifacts_expire_at"
t.string "environment" t.string "environment"
t.datetime "artifacts_expire_at"
t.integer "artifacts_size" t.integer "artifacts_size"
t.string "when" t.string "when"
t.text "yaml_variables" t.text "yaml_variables"
...@@ -201,6 +201,7 @@ ActiveRecord::Schema.define(version: 20160716115710) do ...@@ -201,6 +201,7 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.datetime "started_at" t.datetime "started_at"
t.datetime "finished_at" t.datetime "finished_at"
t.integer "duration" t.integer "duration"
t.integer "user_id"
end end
add_index "ci_commits", ["gl_project_id", "sha"], name: "index_ci_commits_on_gl_project_id_and_sha", using: :btree 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 ...@@ -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", ["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", ["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", ["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| create_table "ci_events", force: :cascade do |t|
t.integer "project_id" t.integer "project_id"
...@@ -483,11 +485,10 @@ ActiveRecord::Schema.define(version: 20160716115710) do ...@@ -483,11 +485,10 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.string "state" t.string "state"
t.integer "iid" t.integer "iid"
t.integer "updated_by_id" t.integer "updated_by_id"
t.integer "moved_to_id"
t.boolean "confidential", default: false t.boolean "confidential", default: false
t.datetime "deleted_at" t.datetime "deleted_at"
t.date "due_date" t.date "due_date"
t.integer "lock_version", default: 0, null: false t.integer "moved_to_id"
end end
add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree
...@@ -627,7 +628,6 @@ ActiveRecord::Schema.define(version: 20160716115710) do ...@@ -627,7 +628,6 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.integer "merge_user_id" t.integer "merge_user_id"
t.string "merge_commit_sha" t.string "merge_commit_sha"
t.datetime "deleted_at" t.datetime "deleted_at"
t.integer "lock_version", default: 0, null: false
end end
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree 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 ...@@ -777,10 +777,10 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.integer "user_id", null: false t.integer "user_id", null: false
t.string "token", null: false t.string "token", null: false
t.string "name", null: false t.string "name", null: false
t.boolean "revoked", default: false
t.datetime "expires_at"
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.boolean "revoked", default: false
t.datetime "expires_at"
end end
add_index "personal_access_tokens", ["token"], name: "index_personal_access_tokens_on_token", unique: true, using: :btree 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 ...@@ -900,9 +900,9 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.string "type" t.string "type"
t.string "title" t.string "title"
t.integer "project_id" t.integer "project_id"
t.datetime "created_at", null: false t.datetime "created_at"
t.datetime "updated_at", null: false t.datetime "updated_at"
t.boolean "active", null: false t.boolean "active", default: false, null: false
t.text "properties" t.text "properties"
t.boolean "template", default: false t.boolean "template", default: false
t.boolean "push_events", default: true t.boolean "push_events", default: true
......
...@@ -985,11 +985,11 @@ directive defined in `.postgres_services` and `.mysql_services` respectively: ...@@ -985,11 +985,11 @@ directive defined in `.postgres_services` and `.mysql_services` respectively:
- ruby - ruby
test:postgres: test:postgres:
<< *job_definition <<: *job_definition
services: *postgres_definition services: *postgres_definition
test:mysql: test:mysql:
<< *job_definition <<: *job_definition
services: *mysql_definition services: *mysql_definition
``` ```
......
...@@ -44,7 +44,7 @@ it organized and easy to find. ...@@ -44,7 +44,7 @@ it organized and easy to find.
- When introducing a new document, be careful for the headings to be - When introducing a new document, be careful for the headings to be
grammatically and syntactically correct. It is advised to mention one or all grammatically and syntactically correct. It is advised to mention one or all
of the following GitLab members for a review: `@axil`, `@rspeicher`, 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 with wrong heading is going live without an audit, thus preventing dead links
and redirection issues when corrected and redirection issues when corrected
- Leave exactly one newline after a heading - Leave exactly one newline after a heading
......
...@@ -37,7 +37,6 @@ Feature: Project Issues ...@@ -37,7 +37,6 @@ Feature: Project Issues
And I submit new issue "500 error on profile" And I submit new issue "500 error on profile"
Then I should see issue "500 error on profile" Then I should see issue "500 error on profile"
@javascript
Scenario: I submit new unassigned issue with labels Scenario: I submit new unassigned issue with labels
Given project "Shop" has labels: "bug", "feature", "enhancement" Given project "Shop" has labels: "bug", "feature", "enhancement"
And I click link "New Issue" And I click link "New Issue"
......
...@@ -89,7 +89,7 @@ Feature: Project Merge Requests ...@@ -89,7 +89,7 @@ Feature: Project Merge Requests
Then The list should be sorted by "Oldest updated" Then The list should be sorted by "Oldest updated"
@javascript @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 Given I visit project "Shop" merge requests page
And I sort the list by "Oldest updated" And I sort the list by "Oldest updated"
And I visit dashboard merge requests page And I visit dashboard merge requests page
......
...@@ -135,17 +135,19 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps ...@@ -135,17 +135,19 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
end end
step 'I click "Assign to" dropdown"' do step 'I click "Assign to" dropdown"' do
click_button 'Assignee' first('.ajax-users-select').click
end end
step 'I should see the target project ID in the input selector' do 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 end
step 'I should see the users from the target project ID' do step 'I should see the users from the target project ID' do
expect(page).to have_content 'Unassigned' expect(page).to have_selector('.user-result', visible: true, count: 3)
expect(page).to have_content current_user.name users = page.all('.user-name')
expect(page).to have_content @project.users.first.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 end
# Verify a link is generated against the correct project # Verify a link is generated against the correct project
......
...@@ -82,8 +82,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps ...@@ -82,8 +82,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
step 'I submit new issue "500 error on profile" with label \'bug\'' do step 'I submit new issue "500 error on profile" with label \'bug\'' do
fill_in "issue_title", with: "500 error on profile" fill_in "issue_title", with: "500 error on profile"
click_button "Label" select 'bug', from: "Labels"
click_link "bug"
click_button "Submit issue" click_button "Submit issue"
end end
......
...@@ -56,9 +56,9 @@ module API ...@@ -56,9 +56,9 @@ module API
not_found!('Award Emoji') unless can_read_awardable? 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 present award, with: Entities::AwardEmoji
else else
not_found!("Award Emoji #{award.errors.messages}") not_found!("Award Emoji #{award.errors.messages}")
......
...@@ -64,7 +64,7 @@ module API ...@@ -64,7 +64,7 @@ module API
ref = branches.first ref = branches.first
end end
pipeline = @project.ensure_pipeline(commit.sha, ref) pipeline = @project.ensure_pipeline(commit.sha, ref, current_user)
name = params[:name] || params[:context] name = params[:name] || params[:context]
status = GenericCommitStatus.running_or_pending.find_by(pipeline: pipeline, name: name, ref: params[:ref]) status = GenericCommitStatus.running_or_pending.find_by(pipeline: pipeline, name: name, ref: params[:ref])
......
...@@ -63,7 +63,12 @@ module API ...@@ -63,7 +63,12 @@ module API
if access_status.status if access_status.status
# Return the repository full path so that gitlab-shell has it when # Return the repository full path so that gitlab-shell has it when
# handling ssh commands # 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 end
response response
......
...@@ -19,14 +19,22 @@ module Banzai ...@@ -19,14 +19,22 @@ module Banzai
language = node.attr('class') language = node.attr('class')
code = node.text code = node.text
css_classes = "code highlight"
lexer = Rouge::Lexer.find_fancy(language) || Rouge::Lexers::PlainText
formatter = Rouge::Formatters::HTML.new
begin begin
highlighted = block_code(code, language) code = formatter.format(lexer.lex(code))
css_classes << " js-syntax-highlight #{lexer.tag}"
rescue rescue
# Gracefully handle syntax highlighter bugs/errors to ensure # Gracefully handle syntax highlighter bugs/errors to ensure
# users can still access an issue/comment/etc. # users can still access an issue/comment/etc.
highlighted = "<pre>#{code}</pre>"
end end
highlighted = %(<pre class="#{css_classes}"><code>#{code}</code></pre>)
# Extracted to a method to measure it # Extracted to a method to measure it
replace_parent_pre_element(node, highlighted) replace_parent_pre_element(node, highlighted)
end end
...@@ -40,8 +48,7 @@ module Banzai ...@@ -40,8 +48,7 @@ module Banzai
# Override Rouge::Plugins::Redcarpet#rouge_formatter # Override Rouge::Plugins::Redcarpet#rouge_formatter
def rouge_formatter(lexer) def rouge_formatter(lexer)
Rouge::Formatters::HTMLGitlab.new( Rouge::Formatters::HTML.new
cssclass: "code highlight js-syntax-highlight #{lexer.tag}")
end end
end end
end end
......
...@@ -112,7 +112,7 @@ module Banzai ...@@ -112,7 +112,7 @@ module Banzai
data = data_attribute(project: project.id, author: author.try(:id)) data = data_attribute(project: project.id, author: author.try(:id))
text = link_text || User.reference_prefix + 'all' text = link_text || User.reference_prefix + 'all'
link_tag(url, data, text) link_tag(url, data, text, 'All Project and Group Members')
end end
def link_to_namespace(namespace, link_text: nil) def link_to_namespace(namespace, link_text: nil)
...@@ -128,7 +128,7 @@ module Banzai ...@@ -128,7 +128,7 @@ module Banzai
data = data_attribute(group: namespace.id) data = data_attribute(group: namespace.id)
text = link_text || Group.reference_prefix + group text = link_text || Group.reference_prefix + group
link_tag(url, data, text) link_tag(url, data, text, namespace.name)
end end
def link_to_user(user, namespace, link_text: nil) def link_to_user(user, namespace, link_text: nil)
...@@ -136,11 +136,11 @@ module Banzai ...@@ -136,11 +136,11 @@ module Banzai
data = data_attribute(user: namespace.owner_id) data = data_attribute(user: namespace.owner_id)
text = link_text || User.reference_prefix + user text = link_text || User.reference_prefix + user
link_tag(url, data, text) link_tag(url, data, text, namespace.owner_name)
end end
def link_tag(url, data, text) def link_tag(url, data, text, title)
%(<a href="#{url}" #{data} class="#{link_class}">#{escape_once(text)}</a>) %(<a href="#{url}" #{data} class="#{link_class}" title="#{escape_once(title)}">#{escape_once(text)}</a>)
end end
end end
end end
......
...@@ -63,7 +63,7 @@ module Grack ...@@ -63,7 +63,7 @@ module Grack
def ci_request?(login, password) def ci_request?(login, password)
matched_login = /(?<s>^[a-zA-Z]*-ci)-token$/.match(login) 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 underscored_service = matched_login['s'].underscore
if underscored_service == 'gitlab_ci' if underscored_service == 'gitlab_ci'
......
...@@ -20,11 +20,19 @@ module Gitlab ...@@ -20,11 +20,19 @@ module Gitlab
if Database.postgresql? if Database.postgresql?
options = options.merge({ algorithm: :concurrently }) options = options.merge({ algorithm: :concurrently })
disable_statement_timeout
end end
add_index(table_name, column_name, options) add_index(table_name, column_name, options)
end 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. # Updates the value of a column in batches.
# #
# This method updates the table in batches of 5% of the total row count. # This method updates the table in batches of 5% of the total row count.
...@@ -133,6 +141,8 @@ module Gitlab ...@@ -133,6 +141,8 @@ module Gitlab
'in the body of your migration class' 'in the body of your migration class'
end end
disable_statement_timeout
transaction do transaction do
add_column(table, column, type, default: nil) add_column(table, column, type, default: nil)
......
...@@ -15,32 +15,35 @@ module Gitlab ...@@ -15,32 +15,35 @@ module Gitlab
end end
def execute def execute
project_identifier = CGI.escape(project.import_source) ActiveRecord::Base.no_touching do
project_identifier = CGI.escape(project.import_source)
# Issues && Comments
issues = client.issues(project_identifier) # Issues && Comments
issues = client.issues(project_identifier)
issues.each do |issue|
body = @formatter.author_line(issue["author"]["name"]) issues.each do |issue|
body += issue["description"] body = @formatter.author_line(issue["author"]["name"])
body += issue["description"]
comments = client.issue_comments(project_identifier, issue["id"])
comments = client.issue_comments(project_identifier, issue["id"])
if comments.any?
body += @formatter.comments_header 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 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 end
true true
......
module Gitlab module Gitlab
class Highlight class Highlight
def self.highlight(blob_name, blob_content, repository: nil, nowrap: true, plain: false) def self.highlight(blob_name, blob_content, repository: nil, plain: false)
new(blob_name, blob_content, nowrap: nowrap, repository: repository). new(blob_name, blob_content, repository: repository).
highlight(blob_content, continue: false, plain: plain) highlight(blob_content, continue: false, plain: plain)
end end
...@@ -13,30 +13,34 @@ module Gitlab ...@@ -13,30 +13,34 @@ module Gitlab
highlight(file_name, blob.data, repository: repository).lines.map!(&:html_safe) highlight(file_name, blob.data, repository: repository).lines.map!(&:html_safe)
end end
attr_reader :lexer def initialize(blob_name, blob_content, repository: nil)
def initialize(blob_name, blob_content, repository: nil, nowrap: true) @formatter = Rouge::Formatters::HTMLGitlab.new
@repository = repository
@blob_name = blob_name @blob_name = blob_name
@blob_content = blob_content @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 end
def highlight(text, continue: true, plain: false) def highlight(text, continue: true, plain: false)
if plain if plain
@formatter.format(Rouge::Lexers::PlainText.lex(text)).html_safe hl_lexer = Rouge::Lexers::PlainText
continue = false
else else
@formatter.format(@lexer.lex(text, continue: continue)).html_safe hl_lexer = self.lexer
end end
@formatter.format(hl_lexer.lex(text, continue: continue)).html_safe
rescue rescue
@formatter.format(Rouge::Lexers::PlainText.lex(text)).html_safe @formatter.format(Rouge::Lexers::PlainText.lex(text)).html_safe
end 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 private
def custom_language def custom_language
...@@ -46,16 +50,5 @@ module Gitlab ...@@ -46,16 +50,5 @@ module Gitlab
Rouge::Lexer.find_fancy(language_name) Rouge::Lexer.find_fancy(language_name)
end 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
end end
...@@ -3,6 +3,7 @@ module Gitlab ...@@ -3,6 +3,7 @@ module Gitlab
extend self extend self
VERSION = '0.1.1' VERSION = '0.1.1'
FILENAME_LIMIT = 50
def export_path(relative_path:) def export_path(relative_path:)
File.join(storage_path, relative_path) File.join(storage_path, relative_path)
...@@ -28,6 +29,12 @@ module Gitlab ...@@ -28,6 +29,12 @@ module Gitlab
'VERSION' 'VERSION'
end 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 def version
VERSION VERSION
end end
......
...@@ -12,7 +12,10 @@ module Gitlab ...@@ -12,7 +12,10 @@ module Gitlab
json = IO.read(@path) json = IO.read(@path)
@tree_hash = ActiveSupport::JSON.decode(json) @tree_hash = ActiveSupport::JSON.decode(json)
@project_members = @tree_hash.delete('project_members') @project_members = @tree_hash.delete('project_members')
create_relations
ActiveRecord::Base.no_touching do
create_relations
end
rescue => e rescue => e
@shared.error(e) @shared.error(e)
false false
......
...@@ -87,7 +87,7 @@ module Gitlab ...@@ -87,7 +87,7 @@ module Gitlab
project_id = @relation_hash.delete('project_id') 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. # 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['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['target_project_id'] = project_id if @relation_hash['target_project_id']
@relation_hash['source_project_id'] = -1 if @relation_hash['source_project_id'] @relation_hash['source_project_id'] = -1 if @relation_hash['source_project_id']
...@@ -111,7 +111,7 @@ module Gitlab ...@@ -111,7 +111,7 @@ module Gitlab
end end
def imported_object def imported_object
imported_object = relation_class.new(@relation_hash) imported_object = relation_class.new(parsed_relation_hash)
yield(imported_object) if block_given? yield(imported_object) if block_given?
imported_object.importing = true if imported_object.respond_to?(:importing) imported_object.importing = true if imported_object.respond_to?(:importing)
imported_object imported_object
...@@ -125,6 +125,10 @@ module Gitlab ...@@ -125,6 +125,10 @@ module Gitlab
def admin_user? def admin_user?
@user.is_admin? @user.is_admin?
end end
def parsed_relation_hash
@relation_hash.reject { |k, _v| !relation_class.attribute_method?(k) }
end
end end
end end
end end
...@@ -7,7 +7,8 @@ module Gitlab ...@@ -7,7 +7,8 @@ module Gitlab
new(*args).save new(*args).save
end end
def initialize(shared:) def initialize(project:, shared:)
@project = project
@shared = shared @shared = shared
end end
...@@ -36,7 +37,7 @@ module Gitlab ...@@ -36,7 +37,7 @@ module Gitlab
end end
def archive_file 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 end
end end
......
...@@ -47,6 +47,8 @@ module Gitlab ...@@ -47,6 +47,8 @@ module Gitlab
end end
def render_storage_upload_store_response(oid, size, tmp_file_name) def render_storage_upload_store_response(oid, size, tmp_file_name)
return render_forbidden unless tmp_file_name
render_response_to_push do render_response_to_push do
render_lfs_upload_ok(oid, size, tmp_file_name) render_lfs_upload_ok(oid, size, tmp_file_name)
end end
......
...@@ -74,8 +74,6 @@ module Gitlab ...@@ -74,8 +74,6 @@ module Gitlab
lfs.render_storage_upload_authorize_response(oid, size) lfs.render_storage_upload_authorize_response(oid, size)
else else
tmp_file_name = sanitize_tmp_filename(@request.env['HTTP_X_GITLAB_LFS_TMP']) 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) lfs.render_storage_upload_store_response(oid, size, tmp_file_name)
end end
end end
......
require 'cgi'
module Rouge module Rouge
module Formatters module Formatters
class HTMLGitlab < Rouge::Formatter class HTMLGitlab < Rouge::Formatters::HTML
tag 'html_gitlab' tag 'html_gitlab'
# Creates a new <tt>Rouge::Formatter::HTMLGitlab</tt> instance. # 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). # [+linenostart+] The line number for the first line (default: 1).
# [+lineanchors+] If set to true the formatter will wrap each output def initialize(linenostart: 1)
# 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
@linenostart = linenostart @linenostart = linenostart
@lineanchors = lineanchors @line_number = linenostart
@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
end end
def render_tableized(tokens) def stream(tokens, &b)
data = process_tokens(tokens) is_first = true
token_lines(tokens) do |line|
html = '' yield "\n" unless is_first
html << "<div class=\"#{@cssclass}\">" unless @nowrap is_first = false
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 span(tok, val) yield %(<span id="LC#{@line_number}" class="line">)
# http://stackoverflow.com/a/1600584/2587286 line.each { |token, value| yield span(token, value) }
val = CGI.escapeHTML(val) yield %(</span>)
if tok.shortname.empty? @line_number += 1
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
end end
end end
end end
......
require 'spec_helper' require 'spec_helper'
feature 'Group', feature: true do 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 describe 'description' do
let(:group) { create(:group) } let(:group) { create(:group) }
let(:path) { group_path(group) } let(:path) { group_path(group) }
before do
login_as(:admin)
end
it 'parses Markdown' do it 'parses Markdown' do
group.update_attribute(:description, 'This is **my** group') group.update_attribute(:description, 'This is **my** group')
visit path visit path
......
...@@ -55,7 +55,7 @@ feature 'issue move to another project' do ...@@ -55,7 +55,7 @@ feature 'issue move to another project' do
first('.select2-choice').click first('.select2-choice').click
end 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 page.within '.select2-drop' do
expect(page).to have_content(new_project_search.name) expect(page).to have_content(new_project_search.name)
......
...@@ -50,8 +50,9 @@ describe 'Issues', feature: true do ...@@ -50,8 +50,9 @@ describe 'Issues', feature: true do
expect(page).to have_content "Assignee #{@user.name}" expect(page).to have_content "Assignee #{@user.name}"
first('.js-user-search').click first('#s2id_issue_assignee_id').click
click_link 'Unassigned' sleep 2 # wait for ajax stuff to complete
first('.user-result').click
click_button 'Save changes' click_button 'Save changes'
...@@ -120,17 +121,6 @@ describe 'Issues', feature: true do ...@@ -120,17 +121,6 @@ describe 'Issues', feature: true do
expect(page).to have_content date.to_s(:medium) expect(page).to have_content date.to_s(:medium)
end end
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
end end
......
...@@ -17,16 +17,5 @@ feature 'Edit Merge Request', feature: true do ...@@ -17,16 +17,5 @@ feature 'Edit Merge Request', feature: true do
it 'form should have class js-quick-submit' do it 'form should have class js-quick-submit' do
expect(page).to have_selector('.js-quick-submit') expect(page).to have_selector('.js-quick-submit')
end 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
end end
...@@ -426,4 +426,23 @@ describe "Internal Project Access", feature: true do ...@@ -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 :external }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end 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 end
...@@ -362,4 +362,23 @@ describe "Private Project Access", feature: true do ...@@ -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 :external }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end 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 end
...@@ -426,4 +426,23 @@ describe "Public Project Access", feature: true do ...@@ -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 :external }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end 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 end
require 'spec_helper' require 'spec_helper'
feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: true, js: true do 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) def register_u2f_device(u2f_device = nil)
u2f_device ||= FakeU2fDevice.new(page) u2f_device ||= FakeU2fDevice.new(page)
u2f_device.respond_to_u2f_registration u2f_device.respond_to_u2f_registration
...@@ -208,21 +210,52 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: ...@@ -208,21 +210,52 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
expect(page.body).to match('Authentication via U2F device failed') expect(page.body).to match('Authentication via U2F device failed')
end end
end end
end
describe "when two-factor authentication is disabled" do describe "when more than one device has been registered by the same user" do
let(:user) { create(:user) } 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 expect(page.body).to match('Signed in successfully')
login_as(user)
user.update_attribute(:otp_required_for_login, true) logout
visit profile_account_path end
click_on 'Manage Two-Factor Authentication' end
register_u2f_device
end end
it "deletes u2f registrations" do describe "when two-factor authentication is disabled" do
expect { click_on "Disable" }.to change { U2fRegistration.count }.from(1).to(0) 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 end
end end
...@@ -121,7 +121,7 @@ ...@@ -121,7 +121,7 @@
:type: old :type: old
:number: 9 :number: 9
:text: | :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 :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9
:position: !ruby/object:Gitlab::Diff::Position :position: !ruby/object:Gitlab::Diff::Position
attributes: attributes:
...@@ -136,7 +136,7 @@ ...@@ -136,7 +136,7 @@
:type: new :type: new
:number: 9 :number: 9
:text: | :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 :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9
:position: !ruby/object:Gitlab::Diff::Position :position: !ruby/object:Gitlab::Diff::Position
attributes: attributes:
...@@ -241,7 +241,7 @@ ...@@ -241,7 +241,7 @@
:type: old :type: old
:number: 13 :number: 13
:text: | :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 :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_13_13
:position: !ruby/object:Gitlab::Diff::Position :position: !ruby/object:Gitlab::Diff::Position
attributes: attributes:
...@@ -315,7 +315,7 @@ ...@@ -315,7 +315,7 @@
:type: new :type: new
:number: 15 :number: 15
:text: | :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 :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15
:position: !ruby/object:Gitlab::Diff::Position :position: !ruby/object:Gitlab::Diff::Position
attributes: attributes:
...@@ -623,7 +623,7 @@ ...@@ -623,7 +623,7 @@
:type: :type:
:number: 20 :number: 20
:text: |2 :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 :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26
:position: !ruby/object:Gitlab::Diff::Position :position: !ruby/object:Gitlab::Diff::Position
attributes: attributes:
...@@ -638,7 +638,7 @@ ...@@ -638,7 +638,7 @@
:type: :type:
:number: 26 :number: 26
:text: |2 :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 :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26
:position: !ruby/object:Gitlab::Diff::Position :position: !ruby/object:Gitlab::Diff::Position
attributes: attributes:
......
...@@ -16,19 +16,19 @@ describe BlobHelper do ...@@ -16,19 +16,19 @@ describe BlobHelper do
describe '#highlight' do describe '#highlight' do
it 'should return plaintext for unknown lexer context' do it 'should return plaintext for unknown lexer context' do
result = helper.highlight(blob_name, no_context_content, nowrap: true) result = helper.highlight(blob_name, no_context_content)
expect(result).to eq('<span id="LC1" class="line">:type &quot;assem&quot;))</span>') expect(result).to eq(%[<pre class="code highlight"><code><span id="LC1" class="line">:type "assem"))</span></code></pre>])
end end
it 'should highlight single block' do 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> 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">&quot;assem&quot;</span><span class="p">))</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 end
it 'should highlight multi-line comments' do 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) html = Nokogiri::HTML(result)
lines = html.search('.s') lines = html.search('.s')
expect(lines.count).to eq(3) expect(lines.count).to eq(3)
...@@ -41,33 +41,19 @@ describe BlobHelper do ...@@ -41,33 +41,19 @@ describe BlobHelper do
let(:blob_name) { 'test.diff' } let(:blob_name) { 'test.diff' }
let(:blob_content) { "+aaa\n+bbb\n- ccc\n ddd\n"} let(:blob_content) { "+aaa\n+bbb\n- ccc\n ddd\n"}
let(:expected) do 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="LC2" class="line"><span class="gi">+bbb</span></span>
<span id="LC3" class="line"><span class="gd">- ccc</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 end
it 'should highlight each line properly' do 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) expect(result).to eq(expected)
end end
end 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 describe "#sanitize_svg" do
let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') } let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') }
let(:data) { open(input_svg_path).read } let(:data) { open(input_svg_path).read }
......
...@@ -57,7 +57,7 @@ describe EventsHelper do ...@@ -57,7 +57,7 @@ describe EventsHelper do
expected = '<pre class="code highlight js-syntax-highlight ruby">' \ expected = '<pre class="code highlight js-syntax-highlight ruby">' \
"<code><span class=\"k\">def</span> <span class=\"nf\">test</span>\n" \ "<code><span class=\"k\">def</span> <span class=\"nf\">test</span>\n" \
" <span class=\"s1\">\'hello world\'</span>\n" \ " <span class=\"s1\">\'hello world\'</span>\n" \
"<span class=\"k\">end</span>" \ "<span class=\"k\">end</span>\n" \
'</code></pre>' '</code></pre>'
expect(helper.event_note(input)).to eq(expected) expect(helper.event_note(input)).to eq(expected)
end end
......
...@@ -5,13 +5,12 @@ ...@@ -5,13 +5,12 @@
#= require ./mock_u2f_device #= require ./mock_u2f_device
describe 'U2FAuthenticate', -> describe 'U2FAuthenticate', ->
U2FUtil.enableTestMode()
fixture.load('u2f/authenticate') fixture.load('u2f/authenticate')
beforeEach -> beforeEach ->
@u2fDevice = new MockU2FDevice @u2fDevice = new MockU2FDevice
@container = $("#js-authenticate-u2f") @container = $("#js-authenticate-u2f")
@component = new U2FAuthenticate(@container, {}, "token") @component = new U2FAuthenticate(@container, {sign_requests: []}, "token")
@component.start() @component.start()
it 'allows authenticating via a U2F device', -> it 'allows authenticating via a U2F device', ->
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
#= require ./mock_u2f_device #= require ./mock_u2f_device
describe 'U2FRegister', -> describe 'U2FRegister', ->
U2FUtil.enableTestMode()
fixture.load('u2f/register') fixture.load('u2f/register')
beforeEach -> beforeEach ->
......
...@@ -3,15 +3,35 @@ require 'spec_helper' ...@@ -3,15 +3,35 @@ require 'spec_helper'
describe Banzai::Filter::SyntaxHighlightFilter, lib: true do describe Banzai::Filter::SyntaxHighlightFilter, lib: true do
include FilterSpecHelper include FilterSpecHelper
it 'highlights valid code blocks' do context "when no language is specified" do
result = filter('<pre><code>def fun end</code>') it "highlights as plaintext" do
expect(result.to_html).to eq("<pre class=\"code highlight js-syntax-highlight plaintext\"><code>def fun end</code></pre>\n") 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 end
it 'passes through invalid code blocks' do context "when a valid language is specified" do
allow_any_instance_of(described_class).to receive(:block_code).and_raise(StandardError) 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>') it "highlights as plaintext" do
expect(result.to_html).to eq('<pre>This is a test</pre>') 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
end end
...@@ -54,12 +54,12 @@ describe Gitlab::BitbucketImport::Client, lib: true do ...@@ -54,12 +54,12 @@ describe Gitlab::BitbucketImport::Client, lib: true do
context 'project import' do context 'project import' do
it 'calls .from_project with no errors' do it 'calls .from_project with no errors' do
project = create(:empty_project) project = create(:empty_project)
project.import_url = "ssh://git@bitbucket.org/test/test.git"
project.create_or_update_import_data(credentials: project.create_or_update_import_data(credentials:
{ user: "git", { user: "git",
password: nil, password: nil,
bb_session: { bitbucket_access_token: "test", bb_session: { bitbucket_access_token: "test",
bitbucket_access_token_secret: "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 expect { described_class.from_project(project) }.not_to raise_error
end end
......
...@@ -13,6 +13,10 @@ describe Gitlab::Database::MigrationHelpers, lib: true do ...@@ -13,6 +13,10 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
context 'outside a transaction' do context 'outside a transaction' do
before do before do
expect(model).to receive(:transaction_open?).and_return(false) 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 end
context 'using PostgreSQL' do context 'using PostgreSQL' do
......
...@@ -28,13 +28,13 @@ describe Gitlab::Diff::Highlight, lib: true do ...@@ -28,13 +28,13 @@ describe Gitlab::Diff::Highlight, lib: true do
end end
it 'highlights and marks removed lines' do 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) expect(subject[4].text).to eq(code)
end end
it 'highlights and marks added lines' do 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) expect(subject[5].text).to eq(code)
end end
......
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
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
"deleted_at": null, "deleted_at": null,
"due_date": null, "due_date": null,
"moved_to_id": null, "moved_to_id": null,
"test_ee_field": "test",
"notes": [ "notes": [
{ {
"id": 351, "id": 351,
......
...@@ -30,6 +30,14 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do ...@@ -30,6 +30,14 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
expect(Event.where.not(data: nil).first.data[:ref]).not_to be_empty expect(Event.where.not(data: nil).first.data[:ref]).not_to be_empty
end 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 context 'event at forth level of the tree' do
let(:event) { Event.where(title: 'test levels').first } let(:event) { Event.where(title: 'test levels').first }
......
...@@ -5,9 +5,12 @@ describe Ci::Pipeline, models: true do ...@@ -5,9 +5,12 @@ describe Ci::Pipeline, models: true do
let(:pipeline) { FactoryGirl.create :ci_pipeline, project: project } let(:pipeline) { FactoryGirl.create :ci_pipeline, project: project }
it { is_expected.to belong_to(: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(:statuses) }
it { is_expected.to have_many(:trigger_requests) } it { is_expected.to have_many(:trigger_requests) }
it { is_expected.to have_many(:builds) } it { is_expected.to have_many(:builds) }
it { is_expected.to validate_presence_of :sha } it { is_expected.to validate_presence_of :sha }
it { is_expected.to validate_presence_of :status } it { is_expected.to validate_presence_of :status }
......
...@@ -177,10 +177,10 @@ describe CommitStatus, models: true do ...@@ -177,10 +177,10 @@ describe CommitStatus, models: true do
describe '#stages' do describe '#stages' do
before do before do
FactoryGirl.create :commit_status, pipeline: pipeline, stage: 'build', stage_idx: 0, status: 'success' create :commit_status, pipeline: pipeline, stage: 'build', name: 'linux', stage_idx: 0, status: 'success'
FactoryGirl.create :commit_status, pipeline: pipeline, stage: 'build', stage_idx: 0, status: 'failed' create :commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'failed'
FactoryGirl.create :commit_status, pipeline: pipeline, stage: 'deploy', stage_idx: 2, status: 'running' create :commit_status, pipeline: pipeline, stage: 'deploy', name: 'staging', stage_idx: 2, status: 'running'
FactoryGirl.create :commit_status, pipeline: pipeline, stage: 'test', stage_idx: 1, status: 'success' create :commit_status, pipeline: pipeline, stage: 'test', name: 'rspec', stage_idx: 1, status: 'success'
end end
context 'stages list' do context 'stages list' do
...@@ -192,7 +192,7 @@ describe CommitStatus, models: true do ...@@ -192,7 +192,7 @@ describe CommitStatus, models: true do
end end
context 'stages with statuses' do 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 it 'return list of stages with statuses' do
is_expected.to eq({ is_expected.to eq({
...@@ -201,6 +201,20 @@ describe CommitStatus, models: true do ...@@ -201,6 +201,20 @@ describe CommitStatus, models: true do
'deploy' => 'running' 'deploy' => 'running'
}) })
end 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
end end
......
...@@ -16,10 +16,10 @@ describe LegacyDiffNote, models: true do ...@@ -16,10 +16,10 @@ describe LegacyDiffNote, models: true do
end end
describe '#active?' do 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) 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 expect(note).to be_active
end end
...@@ -27,7 +27,7 @@ describe LegacyDiffNote, models: true do ...@@ -27,7 +27,7 @@ describe LegacyDiffNote, models: true do
it 'is never true when the note has no noteable associated' do it 'is never true when the note has no noteable associated' do
note = build(:legacy_diff_note_on_merge_request) 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).to receive(:noteable).and_return(nil)
expect(note).not_to be_active expect(note).not_to be_active
...@@ -47,7 +47,7 @@ describe LegacyDiffNote, models: true do ...@@ -47,7 +47,7 @@ describe LegacyDiffNote, models: true do
merge = build_stubbed(:merge_request, :simple) merge = build_stubbed(:merge_request, :simple)
note = build(:legacy_diff_note_on_merge_request, noteable: merge) 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).to receive(:find_noteable_diff).and_return(nil)
expect(note).not_to be_active expect(note).not_to be_active
......
...@@ -142,10 +142,10 @@ describe Project, models: true do ...@@ -142,10 +142,10 @@ describe Project, models: true do
expect(project2).to be_valid expect(project2).to be_valid
end end
it 'does not allow to introduce an empty URI' do it 'allows an empty URI' do
project2 = build(:project, import_url: '') project2 = build(:project, import_url: '')
expect(project2).not_to be_valid expect(project2).to be_valid
end end
it 'does not produce import data on an empty URI' do it 'does not produce import data on an empty URI' do
......
...@@ -31,6 +31,8 @@ describe User, models: true do ...@@ -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(:spam_logs).dependent(:destroy) }
it { is_expected.to have_many(:todos).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(: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 describe '#group_members' do
it 'does not include group memberships for which user is a requester' do it 'does not include group memberships for which user is a requester' do
......
...@@ -135,6 +135,22 @@ describe API::API, api: true do ...@@ -135,6 +135,22 @@ describe API::API, api: true do
expect(response).to have_http_status(401) expect(response).to have_http_status(401)
end 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
end end
...@@ -147,6 +163,22 @@ describe API::API, api: true do ...@@ -147,6 +163,22 @@ describe API::API, api: true do
expect(response).to have_http_status(201) expect(response).to have_http_status(201)
expect(json_response['user']['username']).to eq(user.username) expect(json_response['user']['username']).to eq(user.username)
end 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 end
describe 'DELETE /projects/:id/awardable/:awardable_id/award_emoji/:award_id' do describe 'DELETE /projects/:id/awardable/:awardable_id/award_emoji/:award_id' do
......
...@@ -56,13 +56,21 @@ describe API::API, api: true do ...@@ -56,13 +56,21 @@ describe API::API, api: true do
context "git push with project.wiki" do context "git push with project.wiki" do
it 'responds with success' do it 'responds with success' do
project_wiki = create(:project, name: 'my.wiki', path: 'my.wiki') push(key, project.wiki)
project_wiki.team << [user, :developer]
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(response).to have_http_status(200)
expect(json_response["status"]).to be_truthy expect(json_response["status"]).to be_truthy
expect(json_response["repository_path"]).to eq(project.wiki.repository.path_to_repo)
end end
end end
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::Lfs::Router, lib: true do describe Gitlab::Lfs::Router 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) { create(:user) }
let(:user_two) { create(:user) }
let!(:lfs_object) { create(:lfs_object, :with_file) } let!(:lfs_object) { create(:lfs_object, :with_file) }
let(:request) { Rack::Request.new(env) } let(:headers) do
let(:env) do
{ {
'rack.input' => '', 'Authorization' => authorization,
'REQUEST_METHOD' => 'GET', 'X-Sendfile-Type' => sendfile
} }.compact
end end
let(:authorization) { }
let(:sendfile) { }
let(:lfs_router_auth) { new_lfs_router(project, user: user) } let(:sample_oid) { lfs_object.oid }
let(:lfs_router_ci_auth) { new_lfs_router(project, ci: true) } let(:sample_size) { lfs_object.size }
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 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 before do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(false) allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
env['REQUEST_METHOD'] = 'POST' post_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
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 end
it 'responds with 501' do it 'responds with 501' do
expect(lfs_router_auth.try_call).to match_array(respond_with_disabled) 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
end end
describe 'when fetching lfs object using deprecated API' do describe 'deprecated API' do
let(:project) { create(:empty_project) }
before do before do
enable_lfs enable_lfs
env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}"
end end
it 'responds with 501' do shared_examples 'a deprecated' do
expect(lfs_router_auth.try_call).to match_array(respond_with_deprecated) 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
end end
describe 'when fetching lfs object' do describe 'when fetching lfs object' do
let(:project) { create(:empty_project) }
let(:update_permissions) { }
before do before do
enable_lfs enable_lfs
env['HTTP_ACCEPT'] = "application/vnd.git-lfs+json; charset=utf-8" update_permissions
env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}" get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
end end
describe 'and request comes from gitlab-workhorse' do context 'and request comes from gitlab-workhorse' do
context 'without user being authorized' do context 'without user being authorized' do
it "responds with status 401" do it 'responds with status 401' do
expect(lfs_router_noauth.try_call.first).to eq(401) expect(response).to have_http_status(401)
end end
end end
context 'with required headers' do context 'with required headers' do
before do shared_examples 'responds with a file' do
project.lfs_objects << lfs_object let(:sendfile) { 'X-Sendfile' }
env['HTTP_X_SENDFILE_TYPE'] = "X-Sendfile"
end
context 'when user does not have project access' do it 'responds with status 200' do
it "responds with status 403" do expect(response).to have_http_status(200)
expect(lfs_router_auth.try_call.first).to eq(403)
end end
end
context 'when user has project access' do it 'responds with the file location' do
before do expect(response.headers['Content-Type']).to eq('application/octet-stream')
project.team << [user, :master] expect(response.headers['X-Sendfile']).to eq(lfs_object.file.path)
end 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 200" do it 'responds with status 403' do
expect(lfs_router_auth.try_call.first).to eq(200) expect(response).to have_http_status(403)
end
end end
it "responds with the file location" do context 'and does have project access' do
expect(lfs_router_auth.try_call[1]['Content-Type']).to eq("application/octet-stream") let(:update_permissions) do
expect(lfs_router_auth.try_call[1]['X-Sendfile']).to eq(lfs_object.file.path) project.team << [user, :master]
project.lfs_objects << lfs_object
end
it_behaves_like 'responds with a file'
end end
end end
context 'when CI is authorized' do context 'when CI is authorized' do
it "responds with status 200" do let(:authorization) { authorize_ci_project }
expect(lfs_router_ci_auth.try_call.first).to eq(200)
end
it "responds with the file location" do let(:update_permissions) do
expect(lfs_router_ci_auth.try_call[1]['Content-Type']).to eq("application/octet-stream") project.lfs_objects << lfs_object
expect(lfs_router_ci_auth.try_call[1]['X-Sendfile']).to eq(lfs_object.file.path)
end end
it_behaves_like 'responds with a file'
end end
end end
context 'without required headers' do context 'without required headers' do
it "responds with status 403" do let(:authorization) { authorize_user }
expect(lfs_router_auth.try_call.first).to eq(403)
it 'responds with status 403' do
expect(response).to have_http_status(403)
end end
end end
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 describe 'when handling lfs batch request' do
let(:update_lfs_permissions) { }
let(:update_user_permissions) { }
before do before do
enable_lfs enable_lfs
env['REQUEST_METHOD'] = 'POST' update_lfs_permissions
env['PATH_INFO'] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch" update_user_permissions
post_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
end end
describe 'download' do describe 'download' do
before do let(:project) { create(:empty_project) }
body = { 'operation' => 'download', let(:body) do
'objects' => [ { 'operation' => 'download',
{ 'oid' => sample_oid, 'objects' => [
'size' => sample_size { 'oid' => sample_oid,
}] 'size' => sample_size
}.to_json }]
env['rack.input'] = StringIO.new(body) }
end end
shared_examples 'an authorized requests' do shared_examples 'an authorized requests' do
context 'when downloading an lfs object that is assigned to our project' do context 'when downloading an lfs object that is assigned to our project' do
before do let(:update_lfs_permissions) do
project.lfs_objects << lfs_object project.lfs_objects << lfs_object
end end
it 'responds with status 200 and href to download' do it 'responds with status 200' do
response = router.try_call expect(response).to have_http_status(200)
expect(response.first).to eq(200) end
response_body = ActiveSupport::JSON.decode(response.last.first)
expect(response_body).to eq('objects' => [ it 'with href to download' do
expect(json_response).to eq('objects' => [
{ 'oid' => sample_oid, { 'oid' => sample_oid,
'size' => sample_size, 'size' => sample_size,
'actions' => { 'actions' => {
'download' => { 'download' => {
'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
'header' => { 'Authorization' => auth } 'header' => { 'Authorization' => authorization }
} }
} }
}]) }])
...@@ -183,16 +201,17 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -183,16 +201,17 @@ describe Gitlab::Lfs::Router, lib: true do
end end
context 'when downloading an lfs object that is assigned to other project' do context 'when downloading an lfs object that is assigned to other project' do
before do let(:other_project) { create(:empty_project) }
public_project.lfs_objects << lfs_object let(:update_lfs_permissions) do
other_project.lfs_objects << lfs_object
end end
it 'responds with status 200 and error message' do it 'responds with status 200' do
response = router.try_call expect(response).to have_http_status(200)
expect(response.first).to eq(200) end
response_body = ActiveSupport::JSON.decode(response.last.first)
expect(response_body).to eq('objects' => [ it 'with href to download' do
expect(json_response).to eq('objects' => [
{ 'oid' => sample_oid, { 'oid' => sample_oid,
'size' => sample_size, 'size' => sample_size,
'error' => { 'error' => {
...@@ -204,22 +223,21 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -204,22 +223,21 @@ describe Gitlab::Lfs::Router, lib: true do
end end
context 'when downloading a lfs object that does not exist' do context 'when downloading a lfs object that does not exist' do
before do let(:body) do
body = { 'operation' => 'download', { 'operation' => 'download',
'objects' => [ 'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078 'size' => 1575078
}] }]
}.to_json }
env['rack.input'] = StringIO.new(body)
end end
it "responds with status 200 and error message" do it 'responds with status 200' do
response = router.try_call expect(response).to have_http_status(200)
expect(response.first).to eq(200) end
response_body = ActiveSupport::JSON.decode(response.last.first)
expect(response_body).to eq('objects' => [ it 'with an 404 for specific object' do
expect(json_response).to eq('objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078, 'size' => 1575078,
'error' => { 'error' => {
...@@ -231,27 +249,29 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -231,27 +249,29 @@ describe Gitlab::Lfs::Router, lib: true do
end end
context 'when downloading one new and one existing lfs object' do context 'when downloading one new and one existing lfs object' do
before do let(:body) do
body = { 'operation' => 'download', { 'operation' => 'download',
'objects' => [ 'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078 'size' => 1575078
}, },
{ 'oid' => sample_oid, { 'oid' => sample_oid,
'size' => sample_size 'size' => sample_size
} }
] ]
}.to_json }
env['rack.input'] = StringIO.new(body) end
let(:update_lfs_permissions) do
project.lfs_objects << lfs_object project.lfs_objects << lfs_object
end end
it "responds with status 200 with upload hypermedia link for the new object" do it 'responds with status 200' do
response = router.try_call expect(response).to have_http_status(200)
expect(response.first).to eq(200) end
response_body = ActiveSupport::JSON.decode(response.last.first)
expect(response_body).to eq('objects' => [ it 'responds with upload hypermedia link for the new object' do
expect(json_response).to eq('objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078, 'size' => 1575078,
'error' => { 'error' => {
...@@ -264,7 +284,7 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -264,7 +284,7 @@ describe Gitlab::Lfs::Router, lib: true do
'actions' => { 'actions' => {
'download' => { 'download' => {
'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
'header' => { 'Authorization' => auth } 'header' => { 'Authorization' => authorization }
} }
} }
}]) }])
...@@ -273,23 +293,21 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -273,23 +293,21 @@ describe Gitlab::Lfs::Router, lib: true do
end end
context 'when user is authenticated' do context 'when user is authenticated' do
let(:auth) { authorize(user) } let(:authorization) { authorize_user }
before do let(:update_user_permissions) do
env["HTTP_AUTHORIZATION"] = auth
project.team << [user, role] project.team << [user, role]
end end
it_behaves_like 'an authorized requests' do it_behaves_like 'an authorized requests' do
let(:role) { :reporter } let(:role) { :reporter }
let(:router) { lfs_router_auth }
end end
context 'when user does is not member of the project' do context 'when user does is not member of the project' do
let(:role) { :guest } let(:role) { :guest }
it 'responds with 403' do it 'responds with 403' do
expect(lfs_router_auth.try_call.first).to eq(403) expect(response).to have_http_status(403)
end end
end end
...@@ -297,40 +315,36 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -297,40 +315,36 @@ describe Gitlab::Lfs::Router, lib: true do
let(:role) { :guest } let(:role) { :guest }
it 'responds with 403' do it 'responds with 403' do
expect(lfs_router_auth.try_call.first).to eq(403) expect(response).to have_http_status(403)
end end
end end
end end
context 'when CI is authorized' do context 'when CI is authorized' do
let(:auth) { 'gitlab-ci-token:password' } let(:authorization) { authorize_ci_project }
before do
env["HTTP_AUTHORIZATION"] = auth
end
it_behaves_like 'an authorized requests' do it_behaves_like 'an authorized requests'
let(:router) { lfs_router_ci_auth }
end
end end
context 'when user is not authenticated' do context 'when user is not authenticated' do
describe 'is accessing public project' do describe 'is accessing public project' do
before do let(:project) { create(:project, :public) }
public_project.lfs_objects << lfs_object
let(:update_lfs_permissions) do
project.lfs_objects << lfs_object
end end
it 'responds with status 200 and href to download' do it 'responds with status 200 and href to download' do
response = lfs_router_public_noauth.try_call expect(response).to have_http_status(200)
expect(response.first).to eq(200) end
response_body = ActiveSupport::JSON.decode(response.last.first)
expect(response_body).to eq('objects' => [ it 'responds with status 200 and href to download' do
expect(json_response).to eq('objects' => [
{ 'oid' => sample_oid, { 'oid' => sample_oid,
'size' => sample_size, 'size' => sample_size,
'actions' => { 'actions' => {
'download' => { 'download' => {
'href' => "#{public_project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
'header' => {} 'header' => {}
} }
} }
...@@ -339,172 +353,174 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -339,172 +353,174 @@ describe Gitlab::Lfs::Router, lib: true do
end end
describe 'is accessing non-public project' do describe 'is accessing non-public project' do
before do let(:update_lfs_permissions) do
project.lfs_objects << lfs_object project.lfs_objects << lfs_object
end end
it 'responds with authorization required' do it 'responds with authorization required' do
expect(lfs_router_noauth.try_call.first).to eq(401) expect(response).to have_http_status(401)
end end
end end
end end
end end
describe 'upload' do describe 'upload' do
before do let(:project) { create(:project, :public) }
body = { 'operation' => 'upload', let(:body) do
'objects' => [ { 'operation' => 'upload',
{ 'oid' => sample_oid, 'objects' => [
'size' => sample_size { 'oid' => sample_oid,
}] 'size' => sample_size
}.to_json }]
env['rack.input'] = StringIO.new(body) }
end end
describe 'when request is authenticated' do describe 'when request is authenticated' do
describe 'when user has project push access' do describe 'when user has project push access' do
before do let(:authorization) { authorize_user }
@auth = authorize(user)
env["HTTP_AUTHORIZATION"] = @auth let(:update_user_permissions) do
project.team << [user, :developer] project.team << [user, :developer]
end end
context 'when pushing an lfs object that already exists' do context 'when pushing an lfs object that already exists' do
before do let(:other_project) { create(:empty_project) }
public_project.lfs_objects << lfs_object let(:update_lfs_permissions) do
other_project.lfs_objects << lfs_object
end end
it "responds with status 200 and links the object to the project" do it 'responds with status 200' do
response_body = lfs_router_auth.try_call.last expect(response).to have_http_status(200)
response = ActiveSupport::JSON.decode(response_body.first) end
expect(response['objects']).to be_kind_of(Array) it 'responds with links the object to the project' do
expect(response['objects'].first['oid']).to eq(sample_oid) expect(json_response['objects']).to be_kind_of(Array)
expect(response['objects'].first['size']).to eq(sample_size) 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)).not_to include(project.id)
expect(lfs_object.projects.pluck(:id)).to include(public_project.id) expect(lfs_object.projects.pluck(:id)).to include(other_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(json_response['objects'].first['actions']['upload']['href']).to eq("#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}")
expect(response['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth) expect(json_response['objects'].first['actions']['upload']['header']).to eq('Authorization' => authorization)
end end
end end
context 'when pushing a lfs object that does not exist' do context 'when pushing a lfs object that does not exist' do
before do let(:body) do
body = { 'operation' => 'upload', { 'operation' => 'upload',
'objects' => [ 'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078 'size' => 1575078
}] }]
}.to_json }
env['rack.input'] = StringIO.new(body)
end end
it "responds with status 200 and upload hypermedia link" do it 'responds with status 200' do
response = lfs_router_auth.try_call expect(response).to have_http_status(200)
expect(response.first).to eq(200) end
response_body = ActiveSupport::JSON.decode(response.last.first) it 'responds with upload hypermedia link' do
expect(response_body['objects']).to be_kind_of(Array) expect(json_response['objects']).to be_kind_of(Array)
expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") expect(json_response['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
expect(response_body['objects'].first['size']).to eq(1575078) expect(json_response['objects'].first['size']).to eq(1575078)
expect(lfs_object.projects.pluck(:id)).not_to include(project.id) 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(response_body['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)
expect(response_body['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth)
end end
end end
context 'when pushing one new and one existing lfs object' do context 'when pushing one new and one existing lfs object' do
before do let(:body) do
body = { 'operation' => 'upload', { 'operation' => 'upload',
'objects' => [ 'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078 'size' => 1575078
}, },
{ 'oid' => sample_oid, { 'oid' => sample_oid,
'size' => sample_size 'size' => sample_size
} }
] ]
}.to_json }
env['rack.input'] = StringIO.new(body) end
let(:update_lfs_permissions) do
project.lfs_objects << lfs_object project.lfs_objects << lfs_object
end end
it "responds with status 200 with upload hypermedia link for the new object" do it 'responds with status 200' do
response = lfs_router_auth.try_call expect(response).to have_http_status(200)
expect(response.first).to eq(200) end
response_body = ActiveSupport::JSON.decode(response.last.first) it 'responds with upload hypermedia link for the new object' do
expect(response_body['objects']).to be_kind_of(Array) expect(json_response['objects']).to be_kind_of(Array)
expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") expect(json_response['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
expect(response_body['objects'].first['size']).to eq(1575078) expect(json_response['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(json_response['objects'].first['actions']['upload']['href']).to eq("#{project.http_url_to_repo}/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
expect(response_body['objects'].first['actions']['upload']['header']).to eq("Authorization" => @auth) expect(json_response['objects'].first['actions']['upload']['header']).to eq("Authorization" => authorization)
expect(response_body['objects'].last['oid']).to eq(sample_oid) expect(json_response['objects'].last['oid']).to eq(sample_oid)
expect(response_body['objects'].last['size']).to eq(sample_size) expect(json_response['objects'].last['size']).to eq(sample_size)
expect(response_body['objects'].last).not_to have_key('actions') expect(json_response['objects'].last).not_to have_key('actions')
end end
end end
end end
context 'when user does not have push access' do context 'when user does not have push access' do
let(:authorization) { authorize_user }
it 'responds with 403' do it 'responds with 403' do
expect(lfs_router_auth.try_call.first).to eq(403) expect(response).to have_http_status(403)
end end
end end
context 'when CI is authorized' do context 'when CI is authorized' do
let(:authorization) { authorize_ci_project }
it 'responds with 401' do it 'responds with 401' do
expect(lfs_router_ci_auth.try_call.first).to eq(401) expect(response).to have_http_status(401)
end end
end end
end end
context 'when user is not authenticated' do context 'when user is not authenticated' do
context 'when user has push access' do context 'when user has push access' do
before do let(:update_user_permissions) do
project.team << [user, :master] project.team << [user, :master]
end end
it "responds with status 401" do it 'responds with status 401' do
expect(lfs_router_public_noauth.try_call.first).to eq(401) expect(response).to have_http_status(401)
end end
end end
context 'when user does not have push access' do context 'when user does not have push access' do
it "responds with status 401" do it 'responds with status 401' do
expect(lfs_router_public_noauth.try_call.first).to eq(401) expect(response).to have_http_status(401)
end end
end end
end end
context 'when CI is authorized' do context 'when CI is authorized' do
let(:auth) { 'gitlab-ci-token:password' } let(:authorization) { authorize_ci_project }
before do it 'responds with status 403' do
env["HTTP_AUTHORIZATION"] = auth expect(response).to have_http_status(401)
end
it "responds with status 403" do
expect(lfs_router_public_ci_auth.try_call.first).to eq(401)
end end
end end
end end
describe 'unsupported' do describe 'unsupported' do
before do let(:project) { create(:empty_project) }
body = { 'operation' => 'other', let(:body) do
'objects' => [ { 'operation' => 'other',
{ 'oid' => sample_oid, 'objects' => [
'size' => sample_size { 'oid' => sample_oid,
}] 'size' => sample_size
}.to_json }]
env['rack.input'] = StringIO.new(body) }
end end
it 'responds with status 404' do it 'responds with status 404' do
expect(lfs_router_public_noauth.try_call.first).to eq(404) expect(response).to have_http_status(404)
end end
end end
end end
...@@ -512,38 +528,36 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -512,38 +528,36 @@ describe Gitlab::Lfs::Router, lib: true do
describe 'when pushing a lfs object' do describe 'when pushing a lfs object' do
before do before do
enable_lfs enable_lfs
env['REQUEST_METHOD'] = 'PUT'
end end
shared_examples 'unauthorized' do shared_examples 'unauthorized' do
context 'and request is sent by gitlab-workhorse to authorize the request' do context 'and request is sent by gitlab-workhorse to authorize the request' do
before do before do
header_for_upload_authorize(router.project) put_authorize
end end
it 'responds with status 401' do it 'responds with status 401' do
expect(router.try_call.first).to eq(401) expect(response).to have_http_status(401)
end end
end end
context 'and request is sent by gitlab-workhorse to finalize the upload' do context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do before do
headers_for_upload_finalize(router.project) put_finalize
end end
it 'responds with status 401' do it 'responds with status 401' do
expect(router.try_call.first).to eq(401) expect(response).to have_http_status(401)
end end
end end
context 'and request is sent with a malformed headers' do context 'and request is sent with a malformed headers' do
before do before do
env["PATH_INFO"] = "#{router.project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}" put_finalize('cat /etc/passwd')
env["HTTP_X_GITLAB_LFS_TMP"] = "cat /etc/passwd"
end end
it 'does not recognize it as a valid lfs command' do it 'does not recognize it as a valid lfs command' do
expect(router.try_call).to eq(nil) expect(response).to have_http_status(403)
end end
end end
end end
...@@ -551,27 +565,31 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -551,27 +565,31 @@ describe Gitlab::Lfs::Router, lib: true do
shared_examples 'forbidden' do shared_examples 'forbidden' do
context 'and request is sent by gitlab-workhorse to authorize the request' do context 'and request is sent by gitlab-workhorse to authorize the request' do
before do before do
header_for_upload_authorize(router.project) put_authorize
end end
it 'responds with 403' do it 'responds with 403' do
expect(router.try_call.first).to eq(403) expect(response).to have_http_status(403)
end end
end end
context 'and request is sent by gitlab-workhorse to finalize the upload' do context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do before do
headers_for_upload_finalize(router.project) put_finalize
end end
it 'responds with 403' do it 'responds with 403' do
expect(router.try_call.first).to eq(403) expect(response).to have_http_status(403)
end end
end end
end end
describe 'to one project' do describe 'to one project' do
let(:project) { create(:empty_project) }
describe 'when user is authenticated' do describe 'when user is authenticated' do
let(:authorization) { authorize_user }
describe 'when user has push access to the project' do describe 'when user has push access to the project' do
before do before do
project.team << [user, :developer] project.team << [user, :developer]
...@@ -579,13 +597,14 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -579,13 +597,14 @@ describe Gitlab::Lfs::Router, lib: true do
context 'and request is sent by gitlab-workhorse to authorize the request' do context 'and request is sent by gitlab-workhorse to authorize the request' do
before do before do
header_for_upload_authorize(project) put_authorize
end end
it 'responds with status 200, location of lfs store and object details' do it 'responds with status 200' do
json_response = ActiveSupport::JSON.decode(lfs_router_auth.try_call.last.first) expect(response).to have_http_status(200)
end
expect(lfs_router_auth.try_call.first).to eq(200) 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['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
expect(json_response['LfsOid']).to eq(sample_oid) expect(json_response['LfsOid']).to eq(sample_oid)
expect(json_response['LfsSize']).to eq(sample_size) expect(json_response['LfsSize']).to eq(sample_size)
...@@ -594,54 +613,58 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -594,54 +613,58 @@ describe Gitlab::Lfs::Router, lib: true do
context 'and request is sent by gitlab-workhorse to finalize the upload' do context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do before do
headers_for_upload_finalize(project) put_finalize
end
it 'responds with status 200' do
expect(response).to have_http_status(200)
end end
it 'responds with status 200 and lfs object is linked to the project' do it '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) expect(lfs_object.projects.pluck(:id)).to include(project.id)
end end
end end
end end
describe 'and user does not have push access' do describe 'and user does not have push access' do
let(:router) { lfs_router_auth }
it_behaves_like 'forbidden' it_behaves_like 'forbidden'
end end
end end
context 'when CI is authenticated' do context 'when CI is authenticated' do
let(:router) { lfs_router_ci_auth } let(:authorization) { authorize_ci_project }
it_behaves_like 'unauthorized' it_behaves_like 'unauthorized'
end end
context 'for unauthenticated' do context 'for unauthenticated' do
let(:router) { new_lfs_router(project) }
it_behaves_like 'unauthorized' it_behaves_like 'unauthorized'
end end
end end
describe 'to a forked project' do describe 'to a forked project' do
let(:forked_project) { fork_project(public_project, user) } 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 describe 'when user is authenticated' do
let(:authorization) { authorize_user }
describe 'when user has push access to the project' do describe 'when user has push access to the project' do
before do before do
forked_project.team << [user_two, :developer] project.team << [user, :developer]
end end
context 'and request is sent by gitlab-workhorse to authorize the request' do context 'and request is sent by gitlab-workhorse to authorize the request' do
before do before do
header_for_upload_authorize(forked_project) put_authorize
end end
it 'responds with status 200, location of lfs store and object details' do it 'responds with status 200' do
json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first) expect(response).to have_http_status(200)
end
expect(lfs_router_forked_auth.try_call.first).to eq(200) 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['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
expect(json_response['LfsOid']).to eq(sample_oid) expect(json_response['LfsOid']).to eq(sample_oid)
expect(json_response['LfsSize']).to eq(sample_size) expect(json_response['LfsSize']).to eq(sample_size)
...@@ -650,81 +673,96 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -650,81 +673,96 @@ describe Gitlab::Lfs::Router, lib: true do
context 'and request is sent by gitlab-workhorse to finalize the upload' do context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do before do
headers_for_upload_finalize(forked_project) put_finalize
end
it 'responds with status 200' do
expect(response).to have_http_status(200)
end end
it 'responds with status 200 and lfs object is linked to the source project' do it '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(upstream_project.id)
expect(lfs_object.projects.pluck(:id)).to include(public_project.id)
end end
end end
end end
describe 'and user does not have push access' do describe 'and user does not have push access' do
let(:router) { lfs_router_forked_auth }
it_behaves_like 'forbidden' it_behaves_like 'forbidden'
end end
end end
context 'when CI is authenticated' do context 'when CI is authenticated' do
let(:router) { lfs_router_forked_ci_auth } let(:authorization) { authorize_ci_project }
it_behaves_like 'unauthorized' it_behaves_like 'unauthorized'
end end
context 'for unauthenticated' do context 'for unauthenticated' do
let(:router) { lfs_router_forked_noauth }
it_behaves_like 'unauthorized' it_behaves_like 'unauthorized'
end end
describe 'and second project not related to fork or a source project' do describe 'and second project not related to fork or a source project' do
let(:second_project) { create(:project) } let(:second_project) { create(:empty_project) }
let(:lfs_router_second_project) { new_lfs_router(second_project, user: user) } let(:authorization) { authorize_user }
before do before do
public_project.lfs_objects << lfs_object second_project.team << [user, :master]
headers_for_upload_finalize(second_project) upstream_project.lfs_objects << lfs_object
end end
context 'when pushing the same lfs object to the second project' do context 'when pushing the same lfs object to the second project' do
before do before do
second_project.team << [user, :master] 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 end
it 'responds with 200 and links the lfs object to the project' do it '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, upstream_project.id)
expect(lfs_object.projects.pluck(:id)).to include(second_project.id, public_project.id)
end end
end end
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 end
def enable_lfs def enable_lfs
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
end end
def authorize(user) def authorize_ci_project
ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password) ActionController::HttpAuthentication::Basic.encode_credentials('gitlab-ci-token', project.runners_token)
end end
def new_lfs_router(project, user: nil, ci: false) def authorize_user
Gitlab::Lfs::Router.new(project, user, ci, request) ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
end end
def header_for_upload_authorize(project) def fork_project(project, user, object = nil)
env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}/authorize" allow(RepositoryForkWorker).to receive(:perform_async).and_return(true)
Projects::ForkService.new(project, user, {}).execute
end end
def headers_for_upload_finalize(project) def post_json(url, body = nil, headers = nil)
env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}" post(url, body.try(:to_json), (headers || {}).merge('Content-Type' => 'application/json'))
env["HTTP_X_GITLAB_LFS_TMP"] = "#{sample_oid}6e561c9d4"
end end
def fork_project(project, user, object = nil) def json_response
allow(RepositoryForkWorker).to receive(:perform_async).and_return(true) @json_response ||= JSON.parse(response.body)
Projects::ForkService.new(project, user, {}).execute
end end
end end
...@@ -87,51 +87,105 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do ...@@ -87,51 +87,105 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
end end
context 'user authorization' do context 'user authorization' do
let(:project) { create(:project) }
let(:current_user) { create(:user) } let(:current_user) { create(:user) }
context 'allow to use scope-less authentication' do context 'for private project' do
it_behaves_like 'a valid token' let(:project) { create(:empty_project) }
end
context 'allow developer to push images' do context 'allow to use scope-less authentication' do
before { project.team << [current_user, :developer] } it_behaves_like 'a valid token'
end
let(:current_params) do context 'allow developer to push images' do
{ scope: "repository:#{project.path_with_namespace}:push" } before { project.team << [current_user, :developer] }
let(:current_params) do
{ scope: "repository:#{project.path_with_namespace}:push" }
end
it_behaves_like 'a pushable'
end end
it_behaves_like 'a pushable' context 'allow reporter to pull images' do
end 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 it_behaves_like 'a pullable'
before { project.team << [current_user, :reporter] } end
let(:current_params) do context 'return a least of privileges' do
{ scope: "repository:#{project.path_with_namespace}:pull" } 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 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 end
context 'return a least of privileges' do context 'for public project' do
before { project.team << [current_user, :reporter] } let(:project) { create(:empty_project, :public) }
let(:current_params) do context 'allow anyone to pull images' do
{ scope: "repository:#{project.path_with_namespace}:push,pull" } let(:current_params) do
{ scope: "repository:#{project.path_with_namespace}:pull" }
end
it_behaves_like 'a pullable'
end 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 end
context 'disallow guest to pull or push images' do context 'for internal project' do
before { project.team << [current_user, :guest] } let(:project) { create(:empty_project, :internal) }
let(:current_params) do context 'for internal user' do
{ scope: "repository:#{project.path_with_namespace}:pull,push" } 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 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
end end
......
...@@ -3,7 +3,7 @@ require 'spec_helper' ...@@ -3,7 +3,7 @@ require 'spec_helper'
describe CreateCommitBuildsService, services: true do describe CreateCommitBuildsService, services: true do
let(:service) { CreateCommitBuildsService.new } let(:service) { CreateCommitBuildsService.new }
let(:project) { FactoryGirl.create(:empty_project) } let(:project) { FactoryGirl.create(:empty_project) }
let(:user) { nil } let(:user) { create(:user) }
before do before do
stub_ci_pipeline_to_return_yaml_file stub_ci_pipeline_to_return_yaml_file
...@@ -24,6 +24,7 @@ describe CreateCommitBuildsService, services: true do ...@@ -24,6 +24,7 @@ describe CreateCommitBuildsService, services: true do
it { expect(pipeline).to be_valid } it { expect(pipeline).to be_valid }
it { expect(pipeline).to be_persisted } it { expect(pipeline).to be_persisted }
it { expect(pipeline).to eq(project.pipelines.last) } 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) } it { expect(pipeline.builds.first).to be_kind_of(Ci::Build) }
end end
......
...@@ -89,6 +89,12 @@ describe CreateDeploymentService, services: true do ...@@ -89,6 +89,12 @@ describe CreateDeploymentService, services: true do
expect_any_instance_of(described_class).to receive(:execute) expect_any_instance_of(described_class).to receive(:execute)
subject subject
end end
it 'is set as deployable' do
subject
expect(Deployment.last.deployable).to eq(deployable)
end
end end
context 'without environment specified' do context 'without environment specified' do
...@@ -105,6 +111,8 @@ describe CreateDeploymentService, services: true do ...@@ -105,6 +111,8 @@ describe CreateDeploymentService, services: true do
context 'when build succeeds' do context 'when build succeeds' do
it_behaves_like 'does create environment and deployment' do it_behaves_like 'does create environment and deployment' do
let(:deployable) { build }
subject { build.success } subject { build.success }
end end
end end
...@@ -114,6 +122,14 @@ describe CreateDeploymentService, services: true do ...@@ -114,6 +122,14 @@ describe CreateDeploymentService, services: true do
subject { build.drop } subject { build.drop }
end end
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 end
end end
...@@ -35,8 +35,11 @@ describe TodoService, services: true do ...@@ -35,8 +35,11 @@ describe TodoService, services: true do
should_not_create_any_todo { service.new_issue(unassigned_issue, author) } should_not_create_any_todo { service.new_issue(unassigned_issue, author) }
end end
it 'does not create a todo if assignee is the current user' do it 'creates a todo if assignee is the current user' do
should_not_create_any_todo { service.new_issue(unassigned_issue, john_doe) } 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 end
it 'creates a todo for each valid mentioned user' do it 'creates a todo for each valid mentioned user' do
...@@ -44,7 +47,7 @@ describe TodoService, services: true do ...@@ -44,7 +47,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: issue, action: Todo::MENTIONED) should_create_todo(user: member, target: issue, action: Todo::MENTIONED)
should_create_todo(user: guest, 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: john_doe, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED) should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
end end
...@@ -57,7 +60,7 @@ describe TodoService, services: true do ...@@ -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: 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_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: 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 end
context 'when a private group is mentioned' do context 'when a private group is mentioned' do
...@@ -87,7 +90,7 @@ describe TodoService, services: true do ...@@ -87,7 +90,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: issue, action: Todo::MENTIONED) should_create_todo(user: member, target: issue, action: Todo::MENTIONED)
should_create_todo(user: guest, 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_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) should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
end end
...@@ -105,7 +108,7 @@ describe TodoService, services: true do ...@@ -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: 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_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: 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 end
context 'issues with a task list' do context 'issues with a task list' do
...@@ -156,10 +159,11 @@ describe TodoService, services: true do ...@@ -156,10 +159,11 @@ describe TodoService, services: true do
should_not_create_any_todo { service.reassigned_issue(issue, author) } should_not_create_any_todo { service.reassigned_issue(issue, author) }
end 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) 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
end end
...@@ -250,7 +254,7 @@ describe TodoService, services: true do ...@@ -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: 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: 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_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) should_not_create_todo(user: non_member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
end end
...@@ -262,7 +266,7 @@ describe TodoService, services: true do ...@@ -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: 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_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: 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 end
it 'creates a todo for each valid mentioned user when leaving a note on commit' do 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 ...@@ -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: 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_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) 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 end
...@@ -312,7 +316,7 @@ describe TodoService, services: true do ...@@ -312,7 +316,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED) 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: 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: john_doe, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
end end
...@@ -325,7 +329,7 @@ describe TodoService, services: true do ...@@ -325,7 +329,7 @@ describe TodoService, services: true do
should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED) 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: guest, target: mr_assigned, action: Todo::MENTIONED)
should_create_todo(user: john_doe, 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) should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
end end
...@@ -382,10 +386,11 @@ describe TodoService, services: true do ...@@ -382,10 +386,11 @@ describe TodoService, services: true do
should_not_create_any_todo { service.reassigned_merge_request(mr_assigned, author) } should_not_create_any_todo { service.reassigned_merge_request(mr_assigned, author) }
end 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) 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
end end
...@@ -435,6 +440,24 @@ describe TodoService, services: true do ...@@ -435,6 +440,24 @@ describe TodoService, services: true do
should_create_todo(user: author, target: mr_unassigned, action: Todo::MARKED) should_create_todo(user: author, target: mr_unassigned, action: Todo::MARKED)
end end
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 end
it 'updates cached counts when a todo is created' do it 'updates cached counts when a todo is created' do
......
...@@ -18,8 +18,8 @@ class FakeU2fDevice ...@@ -18,8 +18,8 @@ class FakeU2fDevice
def respond_to_u2f_authentication def respond_to_u2f_authentication
app_id = @page.evaluate_script('gon.u2f.app_id') app_id = @page.evaluate_script('gon.u2f.app_id')
challenges = @page.evaluate_script('gon.u2f.challenges') challenge = @page.evaluate_script('gon.u2f.challenge')
json_response = u2f_device(app_id).sign_response(challenges[0]) json_response = u2f_device(app_id).sign_response(challenge)
@page.execute_script(" @page.execute_script("
u2f.sign = function(appId, challenges, signRequests, callback) { u2f.sign = function(appId, challenges, signRequests, callback) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment