diff options
author | Filipa Lacerda <filipa@gitlab.com> | 2017-02-27 12:30:26 +0000 |
---|---|---|
committer | Filipa Lacerda <filipa@gitlab.com> | 2017-02-27 12:30:26 +0000 |
commit | 729d2ea04d24c068519515a4df6d4c38f25cd229 (patch) | |
tree | 57048bfd961acd44b0f278daf215b6e141fbd021 | |
parent | f95d46c9d22603445fc7b8247df1120eaed67cd1 (diff) | |
parent | c425f366bfa84efab92b5d5e1d0721f16a2890bc (diff) | |
download | gitlab-ce-729d2ea04d24c068519515a4df6d4c38f25cd229.tar.gz |
Merge branch 'master' into ci-tables-ui-improvementsci-tables-ui-improvements
* master: (196 commits)
Add quotes to coverage pattern
Update CHANGELOG.md
Bump omniauth to 1.4.2
Bump Hashie to 3.5.5 to eliminate warning noise
use single backticks for inline code
Add performance query regression fix for !9088 affecting #27267
Fix spec
API: Return 400 for all validation erros in the mebers API
Adds confirmation for cancel button
Add newline
Corrected indentation on the template string
Adds backoff algo from EE to CE
We don't need these checks anymore
Raise error when no content is provided
Address review
Update API v3 in line with v4
Fix new offenses
Fix spec
Fix specs
Rename commit_file, commit_dir and remove_file and update specs
...
599 files changed, 6228 insertions, 3773 deletions
diff --git a/.gitignore b/.gitignore index 0b602d613c7..680651986e8 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,4 @@ eslint-report.html /builds/* /shared/* /.gitlab_workhorse_secret +/webpack-report/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 20f410d0b4c..e7a279c828b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -240,6 +240,25 @@ rake db:seed_fu: paths: - log/development.log +rake gitlab:assets:compile: + stage: test + <<: *dedicated-runner + dependencies: [] + variables: + NODE_ENV: "production" + RAILS_ENV: "production" + SETUP_DB: "false" + USE_DB: "false" + SKIP_STORAGE_VALIDATION: "true" + WEBPACK_REPORT: "true" + script: + - bundle exec rake yarn:install gitlab:assets:compile + artifacts: + name: webpack-report + expire_in: 31d + paths: + - webpack-report/ + rake karma: cache: paths: @@ -388,6 +407,7 @@ pages: dependencies: - coverage - rake karma + - rake gitlab:assets:compile - lint:javascript:report script: - mv public/ .public/ @@ -395,6 +415,7 @@ pages: - mv coverage/ public/coverage-ruby/ || true - mv coverage-javascript/ public/coverage-javascript/ || true - mv eslint-report.html public/ || true + - mv webpack-report/ public/webpack-report/ || true artifacts: paths: - public diff --git a/.rubocop.yml b/.rubocop.yml index a836b469cc7..38b71d74fea 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -22,15 +22,12 @@ AllCops: - 'db/fixtures/**/*' - 'tmp/**/*' - 'bin/**/*' - - 'lib/backup/**/*' - - 'lib/ci/backup/**/*' - - 'lib/tasks/**/*' - - 'lib/ci/migrate/**/*' - - 'lib/email_validator.rb' - - 'lib/gitlab/upgrader.rb' - - 'lib/gitlab/seeder.rb' - 'generator_templates/**/*' +# Gems in consecutive lines should be alphabetically sorted +Bundler/OrderedGems: + Enabled: false + # Style ####################################################################### # Check indentation of private/protected visibility modifiers. @@ -54,6 +51,11 @@ Style/AlignArray: Style/AlignHash: Enabled: true +# Here we check if the parameters on a multi-line method call or +# definition are aligned. +Style/AlignParameters: + Enabled: false + # Whether `and` and `or` are banned only in conditionals (conditionals) # or completely (always). Style/AndOr: @@ -83,15 +85,24 @@ Style/BeginBlock: Style/BlockComments: Enabled: true -# Put end statement of multiline block on its own line. -Style/BlockEndNewline: - Enabled: true - # Avoid using {...} for multi-line blocks (multiline chaining is # always # ugly). Prefer {...} over do...end for single-line blocks. Style/BlockDelimiters: Enabled: true +# Put end statement of multiline block on its own line. +Style/BlockEndNewline: + Enabled: true + + # This cop checks for braces around the last parameter in a method call +# if the last parameter is a hash. +Style/BracesAroundHashParameters: + Enabled: false + +# This cop checks for uses of the case equality operator(===). +Style/CaseEquality: + Enabled: false + # Indentation of when in a case/when/[else/]end. Style/CaseIndentation: Enabled: true @@ -110,7 +121,7 @@ Style/ClassAndModuleChildren: # Enforces consistent use of `Object#is_a?` or `Object#kind_of?`. Style/ClassCheck: - Enabled: false + Enabled: true # Use self when defining module/class methods. Style/ClassMethods: @@ -120,10 +131,26 @@ Style/ClassMethods: Style/ClassVars: Enabled: true +# This cop checks for methods invoked via the :: operator instead +# of the . operator (like FileUtils::rmdir instead of FileUtils.rmdir). +Style/ColonMethodCall: + Enabled: true + +# This cop checks that comment annotation keywords are written according +# to guidelines. +Style/CommentAnnotation: + Enabled: false + # Indentation of comments. Style/CommentIndentation: Enabled: true +# Check for `if` and `case` statements where each branch is used for +# assignment to the same variable when using the return of the +# condition can be used instead. +Style/ConditionalAssignment: + Enabled: true + # Constants should use SCREAMING_SNAKE_CASE. Style/ConstantName: Enabled: true @@ -136,13 +163,19 @@ Style/DefWithParentheses: Style/Documentation: Enabled: false +# This cop checks for uses of double negation (!!) to convert something +# to a boolean value. As this is both cryptic and usually redundant, it +# should be avoided. +Style/DoubleNegation: + Enabled: false + # Align elses and elsifs correctly. Style/ElseAlignment: Enabled: true # Use empty lines between defs. Style/EmptyLineBetweenDefs: - Enabled: false + Enabled: true # Don't use several empty lines in a row. Style/EmptyLines: @@ -160,14 +193,14 @@ Style/EmptyLinesAroundBlockBody: Style/EmptyLinesAroundClassBody: Enabled: true -# Keeps track of empty lines around module bodies. -Style/EmptyLinesAroundModuleBody: - Enabled: true - # Keeps track of empty lines around method bodies. Style/EmptyLinesAroundMethodBody: Enabled: true +# Keeps track of empty lines around module bodies. +Style/EmptyLinesAroundModuleBody: + Enabled: true + # Avoid the use of END blocks. Style/EndBlock: Enabled: true @@ -200,24 +233,28 @@ Style/For: # Checks if there is a magic comment to enforce string literals Style/FrozenStringLiteralComment: Enabled: false + # Do not introduce global variables. Style/GlobalVars: Enabled: true + Exclude: + - 'lib/backup/**/*' + - 'lib/tasks/**/*' # Prefer Ruby 1.9 hash syntax `{ a: 1, b: 2 }` # over 1.8 syntax `{ :a => 1, :b => 2 }`. Style/HashSyntax: Enabled: true -# Do not use if x; .... Use the ternary operator instead. -Style/IfWithSemicolon: - Enabled: true - # Checks that conditional statements do not have an identical line at the # end of each branch, which can validly be moved out of the conditional. Style/IdenticalConditionalBranches: Enabled: true +# Do not use if x; .... Use the ternary operator instead. +Style/IfWithSemicolon: + Enabled: true + # Checks the indentation of the first line of the right-hand-side of a # multi-line assignment. Style/IndentAssignment: @@ -258,7 +295,7 @@ Style/ModuleFunction: # Checks that the closing brace in an array literal is either on the same line # as the last array element, or a new line. Style/MultilineArrayBraceLayout: - Enabled: false + Enabled: true EnforcedStyle: symmetrical # Avoid multi-line chains of blocks. @@ -272,7 +309,7 @@ Style/MultilineBlockLayout: # Checks that the closing brace in a hash literal is either on the same line as # the last hash element, or a new line. Style/MultilineHashBraceLayout: - Enabled: false + Enabled: true EnforcedStyle: symmetrical # Do not use then for multi-line if/unless. @@ -304,6 +341,14 @@ Style/MultilineOperationIndentation: Style/MultilineTernaryOperator: Enabled: true +# This cop checks whether some constant value isn't a +# mutable literal (e.g. array or hash). +Style/MutableConstant: + Enabled: true + Exclude: + - 'db/migrate/**/*' + - 'db/post_migrate/**/*' + # Favor unless over if for negative conditions (or control flow or). Style/NegatedIf: Enabled: true @@ -406,6 +451,10 @@ Style/SpaceBeforeComment: Style/SpaceBeforeSemicolon: Enabled: true +# Checks for spaces inside square brackets. +Style/SpaceInsideBrackets: + Enabled: true + # Use spaces inside hash literal braces - or don't. Style/SpaceInsideHashLiteralBraces: Enabled: true @@ -442,6 +491,10 @@ Style/Tab: Style/TrailingBlankLines: Enabled: true +# This cop checks for trailing comma in array and hash literals. +Style/TrailingCommaInLiteral: + Enabled: false + # Checks for %W when interpolation is not needed. Style/UnneededCapitalW: Enabled: true @@ -477,7 +530,7 @@ Style/WhileUntilModifier: # Use %w or %W for arrays of words. Style/WordArray: - Enabled: false + Enabled: true # Metrics ##################################################################### @@ -487,6 +540,10 @@ Metrics/AbcSize: Enabled: true Max: 60 +# This cop checks if the length of a block exceeds some maximum value. +Metrics/BlockLength: + Enabled: false + # Avoid excessive block nesting. Metrics/BlockNesting: Enabled: true @@ -526,20 +583,21 @@ Metrics/PerceivedComplexity: # Lint ######################################################################## -# Checks for useless access modifiers. -Lint/UselessAccessModifier: - Enabled: true - -# 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 ambiguous operators in the first argument of a method invocation # without parentheses. Lint/AmbiguousOperator: Enabled: true +# This cop checks for ambiguous regexp literals in the first argument of +# a method invocation without parentheses. +Lint/AmbiguousRegexpLiteral: + Enabled: false + +# This cop checks for assignments in the conditions of +# if/while/until. +Lint/AssignmentInCondition: + Enabled: false + # Align block ends correctly. Lint/BlockAlignment: Enabled: true @@ -593,10 +651,6 @@ Lint/EndInMethod: Lint/EnsureReturn: Enabled: true -# The use of eval represents a serious security risk. -Lint/Eval: - Enabled: true - # Catches floating-point literals too large or small for Ruby to represent. Lint/FloatOutOfRange: Enabled: true @@ -605,11 +659,20 @@ Lint/FloatOutOfRange: Lint/FormatParameterMismatch: Enabled: true +# This cop checks for *rescue* blocks with no body. +Lint/HandleExceptions: + Enabled: false + # Checks for adjacent string literals on the same line, which could better be # represented as a single string literal. Lint/ImplicitStringConcatenation: Enabled: true +# Checks for attempts to use `private` or `protected` to set the visibility +# of a class method, which does not work. +Lint/IneffectiveAccessModifier: + Enabled: false + # Checks for invalid character literals with a non-escaped whitespace # character. Lint/InvalidCharacterLiteral: @@ -623,6 +686,10 @@ Lint/LiteralInCondition: Lint/LiteralInInterpolation: Enabled: true +# This cop checks for uses of *begin...end while/until something*. +Lint/Loop: + Enabled: false + # Do not use nested method definitions. Lint/NestedMethodDefinition: Enabled: true @@ -652,6 +719,11 @@ Lint/RescueException: Lint/ShadowedException: Enabled: false +# This cop looks for use of the same name as outer local variables +# for block arguments or block local variables. +Lint/ShadowingOuterLocalVariable: + Enabled: false + # Checks for Object#to_s usage in string interpolation. Lint/StringConversionInInterpolation: Enabled: true @@ -660,16 +732,36 @@ Lint/StringConversionInInterpolation: Lint/UnderscorePrefixedVariableName: Enabled: true +# This cop checks for using Fixnum or Bignum constant +Lint/UnifiedInteger: + Enabled: true + # Checks for rubocop:disable comments that can be removed. # Note: this cop is not disabled when disabling all cops. # It must be explicitly disabled. Lint/UnneededDisable: Enabled: false +# This cop checks for unneeded usages of splat expansion +Lint/UnneededSplatExpansion: + Enabled: false + # Unreachable code. Lint/UnreachableCode: Enabled: true +# This cop checks for unused block arguments. +Lint/UnusedBlockArgument: + Enabled: false + +# This cop checks for unused method arguments. +Lint/UnusedMethodArgument: + Enabled: false + +# Checks for useless access modifiers. +Lint/UselessAccessModifier: + Enabled: true + # Checks for useless assignment to a local variable. Lint/UselessAssignment: Enabled: true @@ -709,6 +801,22 @@ Performance/LstripRstrip: Performance/RangeInclude: Enabled: true +# This cop identifies the use of a `&block` parameter and `block.call` +# where `yield` would do just as well. +Performance/RedundantBlockCall: + Enabled: true + +# This cop identifies use of `Regexp#match` or `String#match in a context +# where the integral return value of `=~` would do just as well. +Performance/RedundantMatch: + Enabled: true + +# This cop identifies places where `Hash#merge!` can be replaced by +# `Hash#[]=`. +Performance/RedundantMerge: + Enabled: true + MaxKeyValuePairs: 1 + # Use `sort` instead of `sort_by { |x| x }`. Performance/RedundantSortBy: Enabled: true @@ -728,6 +836,17 @@ Performance/StringReplacement: Performance/TimesMap: Enabled: true +# Security #################################################################### + +# This cop checks for the use of JSON class methods which have potential +# security issues. +Security/JSONLoad: + Enabled: true + +# This cop checks for the use of *Kernel#eval*. +Security/Eval: + Enabled: true + # Rails ####################################################################### # Enables Rails cops. @@ -746,8 +865,19 @@ Rails/Date: # Prefer delegate method for delegations. Rails/Delegate: + Enabled: true + +# This cop checks dynamic `find_by_*` methods. +Rails/DynamicFindBy: Enabled: false +# This cop enforces that 'exit' calls are not used within a rails app. +Rails/Exit: + Enabled: true + Exclude: + - lib/gitlab/upgrader.rb + - 'lib/backup/**/*' + # Prefer `find_by` over `where.first`. Rails/FindBy: Enabled: true @@ -760,9 +890,25 @@ Rails/FindEach: Rails/HasAndBelongsToMany: Enabled: true +# This cop is used to identify usages of http methods like `get`, `post`, +# `put`, `patch` without the usage of keyword arguments in your tests and +# change them to use keyword args. +Rails/HttpPositionalArguments: + Enabled: false + # Checks for calls to puts, print, etc. Rails/Output: Enabled: true + Exclude: + - lib/gitlab/seeder.rb + - lib/gitlab/upgrader.rb + - 'lib/backup/**/*' + - 'lib/tasks/**/*' + +# This cop checks for the use of output safety calls like html_safe and +# raw. +Rails/OutputSafety: + Enabled: false # Checks for incorrect grammar when using methods like `3.day.ago`. Rails/PluralizationGrammar: @@ -776,6 +922,14 @@ Rails/ReadWriteAttribute: Rails/ScopeArgs: Enabled: true +# This cop checks for the use of Time methods without zone. +Rails/TimeZone: + Enabled: false + +# This cop checks for the use of old-style attribute validation macros. +Rails/Validation: + Enabled: true + # RSpec ####################################################################### # Check that instances are not being stubbed globally. @@ -784,7 +938,7 @@ RSpec/AnyInstance: # Check for expectations where `be(...)` can replace `eql(...)`. RSpec/BeEql: - Enabled: false + Enabled: true # Check that the first argument to the top level describe is the tested class or # module. @@ -833,21 +987,51 @@ RSpec/Focus: RSpec/InstanceVariable: Enabled: false +# Checks for `subject` definitions that come after `let` definitions. +RSpec/LeadingSubject: + Enabled: false + +# Checks unreferenced `let!` calls being used for test setup. +RSpec/LetSetup: + Enabled: false + +# Check that chains of messages are not being stubbed. +RSpec/MessageChain: + Enabled: false + +# Checks that message expectations are set using spies. +RSpec/MessageSpies: + Enabled: false + # Checks for multiple top-level describes. RSpec/MultipleDescribes: Enabled: false +# Checks if examples contain too many `expect` calls. +RSpec/MultipleExpectations: + Enabled: false + +# Checks for explicitly referenced test subjects. +RSpec/NamedSubject: + Enabled: false + +# Checks for nested example groups. +RSpec/NestedGroups: + Enabled: false + # Enforces the usage of the same method on all negative message expectations. RSpec/NotToNot: EnforcedStyle: not_to Enabled: true -# Prefer using verifying doubles over normal doubles. -RSpec/VerifiedDoubles: +# Check for repeated description strings in example groups. +RSpec/RepeatedDescription: Enabled: false -# Custom ###################################################################### +# Checks for stubbed test subjects. +RSpec/SubjectStub: + Enabled: false -# Disallow the `git` and `github` arguments in the Gemfile. -GemFetcher: - Enabled: true +# Prefer using verifying doubles over normal doubles. +RSpec/VerifiedDoubles: + Enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index a5b4d2f5b02..c24142c0a11 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,79 +1,13 @@ # This configuration was generated by # `rubocop --auto-gen-config --exclude-limit 0` -# on 2017-01-11 09:38:25 +0000 using RuboCop version 0.46.0. +# on 2017-02-22 13:02:35 -0600 using RuboCop version 0.47.1. # 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: 27 -# Configuration parameters: Include. -# Include: **/Gemfile, **/gems.rb -Bundler/OrderedGems: - Enabled: false - -# Offense count: 175 -Lint/AmbiguousRegexpLiteral: - Enabled: false - -# Offense count: 53 -# Configuration parameters: AllowSafeAssignment. -Lint/AssignmentInCondition: - Enabled: false - -# Offense count: 20 -Lint/HandleExceptions: - Enabled: false - -# Offense count: 1 -Lint/Loop: - Enabled: false - -# Offense count: 27 -Lint/ShadowingOuterLocalVariable: - Enabled: false - -# Offense count: 10 -# Cop supports --auto-correct. -Lint/UnifiedInteger: - Enabled: false - -# Offense count: 21 -# Cop supports --auto-correct. -Lint/UnneededSplatExpansion: - Enabled: false - -# Offense count: 82 -# Cop supports --auto-correct. -# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. -Lint/UnusedBlockArgument: - Enabled: false - -# Offense count: 173 -# Cop supports --auto-correct. -# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods. -Lint/UnusedMethodArgument: - Enabled: false - -# Offense count: 93 -# Configuration parameters: CountComments. -Metrics/BlockLength: - Enabled: false - -# Offense count: 3 -# Cop supports --auto-correct. -Performance/RedundantBlockCall: - Enabled: false - -# Offense count: 5 -# Cop supports --auto-correct. -Performance/RedundantMatch: - Enabled: false - -# Offense count: 32 -# Cop supports --auto-correct. -# Configuration parameters: MaxKeyValuePairs. -Performance/RedundantMerge: +# Offense count: 51 +RSpec/BeforeAfterAll: Enabled: false # Offense count: 15 @@ -81,7 +15,11 @@ Performance/RedundantMerge: RSpec/EmptyExampleGroup: Enabled: false -# Offense count: 58 +# Offense count: 1 +RSpec/ExpectOutput: + Enabled: false + +# Offense count: 63 # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: implicit, each, example RSpec/HookArgument: @@ -93,147 +31,59 @@ RSpec/HookArgument: RSpec/ImplicitExpect: Enabled: false -# Offense count: 237 -RSpec/LeadingSubject: - Enabled: false - -# Offense count: 253 -RSpec/LetSetup: - Enabled: false - -# Offense count: 13 -RSpec/MessageChain: - Enabled: false - -# Offense count: 479 -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: have_received, receive -RSpec/MessageSpies: - Enabled: false - -# Offense count: 3036 -RSpec/MultipleExpectations: - Enabled: false - -# Offense count: 2133 -RSpec/NamedSubject: - Enabled: false - -# Offense count: 1974 -# Configuration parameters: MaxNesting. -RSpec/NestedGroups: +# Offense count: 36 +RSpec/RepeatedExample: Enabled: false -# Offense count: 32 -RSpec/RepeatedDescription: +# Offense count: 34 +RSpec/ScatteredSetup: Enabled: false # Offense count: 1 RSpec/SingleArgumentMessageChain: Enabled: false -# Offense count: 133 -RSpec/SubjectStub: - Enabled: false - -# Offense count: 104 -# Cop supports --auto-correct. -# Configuration parameters: Whitelist. -# Whitelist: find_by_sql -Rails/DynamicFindBy: - Enabled: false - -# Offense count: 932 -# Cop supports --auto-correct. -# Configuration parameters: Include. -# Include: spec/**/*, test/**/* -Rails/HttpPositionalArguments: - Enabled: false - -# Offense count: 55 -Rails/OutputSafety: +# Offense count: 163 +Rails/FilePath: Enabled: false -# Offense count: 182 -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: strict, flexible -Rails/TimeZone: - Enabled: false - -# Offense count: 15 -# Cop supports --auto-correct. +# Offense count: 2 # Configuration parameters: Include. -# Include: app/models/**/*.rb -Rails/Validation: +# Include: db/migrate/*.rb +Rails/ReversibleMigration: Enabled: false -# Offense count: 8 -# Cop supports --auto-correct. -# Configuration parameters: AutoCorrect. -Security/JSONLoad: +# Offense count: 278 +# Configuration parameters: Blacklist. +# Blacklist: decrement!, decrement_counter, increment!, increment_counter, toggle!, touch, update_all, update_attribute, update_column, update_columns, update_counters +Rails/SkipsModelValidations: Enabled: false -# Offense count: 346 +# Offense count: 7 # Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. -# SupportedStyles: with_first_parameter, with_fixed_indentation -Style/AlignParameters: +Security/YAMLLoad: Enabled: false -# Offense count: 54 +# Offense count: 55 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: percent_q, bare_percent Style/BarePercentLiterals: Enabled: false -# Offense count: 358 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: braces, no_braces, context_dependent -Style/BracesAroundHashParameters: - Enabled: false - -# Offense count: 6 -Style/CaseEquality: - Enabled: false - -# Offense count: 37 -# Cop supports --auto-correct. -Style/ColonMethodCall: - Enabled: false - -# Offense count: 4 -# Cop supports --auto-correct. -# Configuration parameters: Keywords. -# Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW -Style/CommentAnnotation: - Enabled: false - -# Offense count: 29 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, SingleLineConditionsOnly. -# SupportedStyles: assign_to_condition, assign_inside_condition -Style/ConditionalAssignment: - Enabled: false - -# Offense count: 1210 +# Offense count: 1304 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: leading, trailing Style/DotPosition: Enabled: false -# Offense count: 18 -Style/DoubleNegation: - Enabled: false - -# Offense count: 7 +# Offense count: 6 # Cop supports --auto-correct. Style/EachWithObject: Enabled: false -# Offense count: 24 +# Offense count: 25 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: empty, nil, both @@ -245,14 +95,14 @@ Style/EmptyElse: Style/EmptyLiteral: Enabled: false -# Offense count: 57 +# Offense count: 56 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: compact, expanded Style/EmptyMethod: Enabled: false -# Offense count: 147 +# Offense count: 184 # Cop supports --auto-correct. # Configuration parameters: AllowForAlignment, ForceEqualSignAlignment. Style/ExtraSpacing: @@ -264,50 +114,50 @@ Style/ExtraSpacing: Style/FormatString: Enabled: false -# Offense count: 238 +# Offense count: 268 # Configuration parameters: MinBodyLength. Style/GuardClause: Enabled: false -# Offense count: 11 +# Offense count: 14 Style/IfInsideElse: Enabled: false -# Offense count: 173 +# Offense count: 179 # Cop supports --auto-correct. # Configuration parameters: MaxLineLength. Style/IfUnlessModifier: Enabled: false -# Offense count: 55 +# Offense count: 57 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. # SupportedStyles: special_inside_parentheses, consistent, align_brackets Style/IndentArray: Enabled: false -# Offense count: 101 +# Offense count: 120 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. # SupportedStyles: special_inside_parentheses, consistent, align_braces Style/IndentHash: Enabled: false -# Offense count: 41 +# Offense count: 45 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: line_count_dependent, lambda, literal Style/Lambda: Enabled: false -# Offense count: 5 +# Offense count: 7 # Cop supports --auto-correct. Style/LineEndConcatenation: Enabled: false -# Offense count: 19 +# Offense count: 22 # Cop supports --auto-correct. -Style/MethodCallParentheses: +Style/MethodCallWithoutArgsParentheses: Enabled: false # Offense count: 9 @@ -319,61 +169,49 @@ Style/MethodMissing: Style/MultilineIfModifier: Enabled: false -# Offense count: 179 -# Cop supports --auto-correct. -Style/MutableConstant: - Enabled: false - -# Offense count: 8 +# Offense count: 22 # Cop supports --auto-correct. Style/NestedParenthesizedCalls: Enabled: false -# Offense count: 13 +# Offense count: 17 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles. # SupportedStyles: skip_modifier_ifs, always Style/Next: Enabled: false -# Offense count: 19 +# Offense count: 31 # Cop supports --auto-correct. # Configuration parameters: EnforcedOctalStyle, SupportedOctalStyles. # SupportedOctalStyles: zero_with_o, zero_only Style/NumericLiteralPrefix: Enabled: false -# Offense count: 19 +# Offense count: 77 # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles. # SupportedStyles: predicate, comparison Style/NumericPredicate: Enabled: false -# Offense count: 34 +# Offense count: 36 # Cop supports --auto-correct. Style/ParallelAssignment: Enabled: false -# Offense count: 417 +# Offense count: 477 # Cop supports --auto-correct. # Configuration parameters: PreferredDelimiters. Style/PercentLiteralDelimiters: Enabled: false -# Offense count: 10 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: lower_case_q, upper_case_q -Style/PercentQLiterals: - Enabled: false - -# Offense count: 13 +# Offense count: 14 # Cop supports --auto-correct. Style/PerlBackrefs: Enabled: false -# Offense count: 64 +# Offense count: 72 # Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist. # NamePrefix: is_, has_, have_ # NamePrefixBlacklist: is_, has_, have_ @@ -381,7 +219,7 @@ Style/PerlBackrefs: Style/PredicateName: Enabled: false -# Offense count: 33 +# Offense count: 39 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: short, verbose @@ -393,7 +231,7 @@ Style/PreferredHashMethods: Style/Proc: Enabled: false -# Offense count: 50 +# Offense count: 62 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: compact, exploded @@ -405,30 +243,30 @@ Style/RaiseArgs: Style/RedundantBegin: Enabled: false -# Offense count: 29 +# Offense count: 32 # Cop supports --auto-correct. Style/RedundantFreeze: Enabled: false -# Offense count: 11 +# Offense count: 15 # Cop supports --auto-correct. # Configuration parameters: AllowMultipleReturnValues. Style/RedundantReturn: Enabled: false -# Offense count: 359 +# Offense count: 365 # Cop supports --auto-correct. Style/RedundantSelf: Enabled: false -# Offense count: 105 +# Offense count: 108 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes. # SupportedStyles: slashes, percent_r, mixed Style/RegexpLiteral: Enabled: false -# Offense count: 19 +# Offense count: 22 # Cop supports --auto-correct. Style/RescueModifier: Enabled: false @@ -438,19 +276,13 @@ Style/RescueModifier: Style/SelfAssignment: Enabled: false -# Offense count: 2 -# Configuration parameters: Methods. -# Methods: {"reduce"=>["acc", "elem"]}, {"inject"=>["acc", "elem"]} -Style/SingleLineBlockParams: - Enabled: false - # Offense count: 50 # Cop supports --auto-correct. # Configuration parameters: AllowIfMethodIsEmpty. Style/SingleLineMethods: Enabled: false -# Offense count: 138 +# Offense count: 155 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: space, no_space @@ -463,26 +295,22 @@ Style/SpaceBeforeBlockBraces: Style/SpaceBeforeFirstArg: Enabled: false -# Offense count: 37 +# Offense count: 38 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: require_no_space, require_space Style/SpaceInLambdaLiteral: Enabled: false -# Offense count: 174 +# Offense count: 203 # Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters. +# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters. # SupportedStyles: space, no_space +# SupportedStylesForEmptyBraces: space, no_space Style/SpaceInsideBlockBraces: Enabled: false -# Offense count: 115 -# Cop supports --auto-correct. -Style/SpaceInsideBrackets: - Enabled: false - -# Offense count: 77 +# Offense count: 91 # Cop supports --auto-correct. Style/SpaceInsideParens: Enabled: false @@ -492,21 +320,21 @@ Style/SpaceInsideParens: Style/SpaceInsidePercentLiteralDelimiters: Enabled: false -# Offense count: 53 +# Offense count: 55 # Cop supports --auto-correct. # Configuration parameters: SupportedStyles. # SupportedStyles: use_perl_names, use_english_names Style/SpecialGlobalVars: EnforcedStyle: use_perl_names -# Offense count: 25 +# Offense count: 40 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: single_quotes, double_quotes Style/StringLiteralsInInterpolation: Enabled: false -# Offense count: 54 +# Offense count: 57 # Cop supports --auto-correct. # Configuration parameters: IgnoredMethods. # IgnoredMethods: respond_to, define_method @@ -520,27 +348,20 @@ Style/SymbolProc: Style/TernaryParentheses: Enabled: false -# Offense count: 36 +# Offense count: 43 # Cop supports --auto-correct. -# Configuration parameters: EnforcedStyleForMultiline, SupportedStyles. -# SupportedStyles: comma, consistent_comma, no_comma +# Configuration parameters: EnforcedStyleForMultiline, SupportedStylesForMultiline. +# SupportedStylesForMultiline: comma, consistent_comma, no_comma Style/TrailingCommaInArguments: Enabled: false -# Offense count: 150 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyleForMultiline, SupportedStyles. -# SupportedStyles: comma, consistent_comma, no_comma -Style/TrailingCommaInLiteral: - Enabled: false - -# Offense count: 7 +# Offense count: 13 # Cop supports --auto-correct. # Configuration parameters: AllowNamedUnderscoreVariables. Style/TrailingUnderscoreVariable: Enabled: false -# Offense count: 67 +# Offense count: 70 # Cop supports --auto-correct. Style/TrailingWhitespace: Enabled: false @@ -552,12 +373,12 @@ Style/TrailingWhitespace: Style/TrivialAccessors: Enabled: false -# Offense count: 2 +# Offense count: 6 # Cop supports --auto-correct. Style/UnlessElse: Enabled: false -# Offense count: 17 +# Offense count: 22 # Cop supports --auto-correct. Style/UnneededInterpolation: Enabled: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index de32a953f63..eed63127d9f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -426,7 +426,7 @@ merge request: 1. [Ruby](https://github.com/bbatsov/ruby-style-guide). Important sections include [Source Code Layout][rss-source] and [Naming][rss-naming]. Use: - - multi-line method chaining style **Option B**: dot `.` on previous line + - multi-line method chaining style **Option A**: dot `.` on the second line - string literal quoting style **Option A**: single quoted by default 1. [Rails](https://github.com/bbatsov/rails-style-guide) 1. [Newlines styleguide][newlines-styleguide] diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION index 9e11b32fcaa..d15723fbe8d 100644 --- a/GITLAB_PAGES_VERSION +++ b/GITLAB_PAGES_VERSION @@ -1 +1 @@ -0.3.1 +0.3.2 @@ -20,7 +20,7 @@ gem 'rugged', '~> 0.24.0' # Authentication libraries gem 'devise', '~> 4.2' gem 'doorkeeper', '~> 4.2.0' -gem 'omniauth', '~> 1.3.2' +gem 'omniauth', '~> 1.4.2' gem 'omniauth-auth0', '~> 1.4.1' gem 'omniauth-azure-oauth2', '~> 0.0.6' gem 'omniauth-cas3', '~> 1.1.2' @@ -201,7 +201,7 @@ gem 'babosa', '~> 1.0.2' gem 'loofah', '~> 2.0.3' # Working with license -gem 'licensee', '~> 8.0.0' +gem 'licensee', '~> 8.7.0' # Protect against bruteforcing gem 'rack-attack', '~> 4.4.1' @@ -301,10 +301,10 @@ group :development, :test do gem 'spring-commands-rspec', '~> 1.0.4' gem 'spring-commands-spinach', '~> 1.1.0' - gem 'rubocop', '~> 0.46.0', require: false - gem 'rubocop-rspec', '~> 1.9.1', require: false + gem 'rubocop', '~> 0.47.1', require: false + gem 'rubocop-rspec', '~> 1.12.0', require: false gem 'scss_lint', '~> 0.47.0', require: false - gem 'haml_lint', '~> 0.18.2', require: false + gem 'haml_lint', '~> 0.21.0', require: false gem 'simplecov', '0.12.0', require: false gem 'flay', '~> 2.6.1', require: false gem 'bundler-audit', '~> 0.5.0', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 2a3be763753..5ff18442c4f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -319,16 +319,16 @@ GEM multi_json (>= 1.3.2) haml (4.0.7) tilt - haml_lint (0.18.2) + haml_lint (0.21.0) haml (~> 4.0) - rake (>= 10, < 12) - rubocop (>= 0.36.0) + rake (>= 10, < 13) + rubocop (>= 0.47.0) sysexits (~> 1.1) hamlit (2.6.1) temple (~> 0.7.6) thor tilt - hashie (3.4.4) + hashie (3.5.5) health_check (2.2.1) rails (>= 4.0) hipchat (1.5.2) @@ -398,8 +398,8 @@ GEM rubyzip thor xml-simple - licensee (8.0.0) - rugged (>= 0.24b) + licensee (8.7.0) + rugged (~> 0.24) little-plugger (1.1.4) logging (2.1.0) little-plugger (~> 1.1) @@ -441,7 +441,7 @@ GEM octokit (4.6.2) sawyer (~> 0.8.0, >= 0.5.3) oj (2.17.4) - omniauth (1.3.2) + omniauth (1.4.2) hashie (>= 1.2, < 4) rack (>= 1.0, < 3) omniauth-auth0 (1.4.1) @@ -501,7 +501,7 @@ GEM os (0.9.6) paranoia (2.2.0) activerecord (>= 4.0, < 5.1) - parser (2.3.1.4) + parser (2.4.0.0) ast (~> 2.2) pg (0.18.4) poltergeist (1.9.0) @@ -642,13 +642,13 @@ GEM pg rails sqlite3 - rubocop (0.46.0) - parser (>= 2.3.1.1, < 3.0) + rubocop (0.47.1) + parser (>= 2.3.3.1, < 3.0) powerpack (~> 0.1) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) - rubocop-rspec (1.9.1) + rubocop-rspec (1.12.0) rubocop (>= 0.42.0) ruby-fogbugz (0.2.1) crack (~> 0.4) @@ -759,7 +759,7 @@ GEM rack (>= 1, < 3) thor (0.19.4) thread_safe (0.3.5) - tilt (2.0.5) + tilt (2.0.6) timecop (0.8.1) timfel-krb5-auth (0.8.3) tool (0.2.3) @@ -776,7 +776,7 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.7.2) - unicode-display_width (1.1.1) + unicode-display_width (1.1.3) unicorn (5.1.0) kgio (~> 2.6) raindrops (~> 0.7) @@ -888,7 +888,7 @@ DEPENDENCIES google-api-client (~> 0.8.6) grape (~> 0.18.0) grape-entity (~> 0.6.0) - haml_lint (~> 0.18.2) + haml_lint (~> 0.21.0) hamlit (~> 2.6.1) health_check (~> 2.2.0) hipchat (~> 1.5.0) @@ -907,7 +907,7 @@ DEPENDENCIES kubeclient (~> 2.2.0) letter_opener_web (~> 1.3.0) license_finder (~> 2.1.0) - licensee (~> 8.0.0) + licensee (~> 8.7.0) loofah (~> 2.0.3) mail_room (~> 0.9.1) method_source (~> 0.8) @@ -920,7 +920,7 @@ DEPENDENCIES oauth2 (~> 1.2.0) octokit (~> 4.6.2) oj (~> 2.17.4) - omniauth (~> 1.3.2) + omniauth (~> 1.4.2) omniauth-auth0 (~> 1.4.1) omniauth-authentiq (~> 0.3.0) omniauth-azure-oauth2 (~> 0.0.6) @@ -963,8 +963,8 @@ DEPENDENCIES rspec-rails (~> 3.5.0) rspec-retry (~> 0.4.5) rspec_profiling (~> 0.0.5) - rubocop (~> 0.46.0) - rubocop-rspec (~> 1.9.1) + rubocop (~> 0.47.1) + rubocop-rspec (~> 1.12.0) ruby-fogbugz (~> 0.2.1) ruby-prof (~> 0.16.2) rugged (~> 0.24.0) diff --git a/app/assets/javascripts/ajax_loading_spinner.js b/app/assets/javascripts/ajax_loading_spinner.js new file mode 100644 index 00000000000..38a8317dbd7 --- /dev/null +++ b/app/assets/javascripts/ajax_loading_spinner.js @@ -0,0 +1,35 @@ +class AjaxLoadingSpinner { + static init() { + const $elements = $('.js-ajax-loading-spinner'); + + $elements.on('ajax:beforeSend', AjaxLoadingSpinner.ajaxBeforeSend); + $elements.on('ajax:complete', AjaxLoadingSpinner.ajaxComplete); + } + + static ajaxBeforeSend(e) { + e.target.setAttribute('disabled', ''); + const iconElement = e.target.querySelector('i'); + // get first fa- icon + const originalIcon = iconElement.className.match(/(fa-)([^\s]+)/g).first(); + iconElement.dataset.icon = originalIcon; + AjaxLoadingSpinner.toggleLoadingIcon(iconElement); + $(e.target).off('ajax:beforeSend', AjaxLoadingSpinner.ajaxBeforeSend); + } + + static ajaxComplete(e) { + e.target.removeAttribute('disabled'); + const iconElement = e.target.querySelector('i'); + AjaxLoadingSpinner.toggleLoadingIcon(iconElement); + $(e.target).off('ajax:complete', AjaxLoadingSpinner.ajaxComplete); + } + + static toggleLoadingIcon(iconElement) { + const classList = iconElement.classList; + classList.toggle(iconElement.dataset.icon); + classList.toggle('fa-spinner'); + classList.toggle('fa-spin'); + } +} + +window.gl = window.gl || {}; +gl.AjaxLoadingSpinner = AjaxLoadingSpinner; diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 8e468faedbf..53d8d313e39 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -6,12 +6,8 @@ /* global AwardsHandler */ /* global Aside */ -function requireAll(context) { return context.keys().map(context); } - window.$ = window.jQuery = require('jquery'); -require('jquery-ui/ui/autocomplete'); require('jquery-ui/ui/draggable'); -require('jquery-ui/ui/effect-highlight'); require('jquery-ui/ui/sortable'); require('jquery-ujs'); require('vendor/jquery.endless-scroll'); @@ -46,15 +42,176 @@ require('./shortcuts_dashboard_navigation'); require('./shortcuts_issuable'); require('./shortcuts_network'); require('vendor/jquery.nicescroll'); -requireAll(require.context('./behaviors', false, /^\.\/.*\.(js|es6)$/)); -requireAll(require.context('./blob', false, /^\.\/.*\.(js|es6)$/)); -requireAll(require.context('./templates', false, /^\.\/.*\.(js|es6)$/)); -requireAll(require.context('./commit', false, /^\.\/.*\.(js|es6)$/)); -requireAll(require.context('./extensions', false, /^\.\/.*\.(js|es6)$/)); -requireAll(require.context('./lib/utils', false, /^\.\/.*\.(js|es6)$/)); -requireAll(require.context('./u2f', false, /^\.\/.*\.(js|es6)$/)); -requireAll(require.context('./droplab', false, /^\.\/.*\.(js|es6)$/)); -requireAll(require.context('.', false, /^\.\/(?!application\.js).*\.(js|es6)$/)); + +// behaviors +require('./behaviors/autosize'); +require('./behaviors/details_behavior'); +require('./behaviors/quick_submit'); +require('./behaviors/requires_input'); +require('./behaviors/toggler_behavior'); + +// blob +require('./blob/blob_ci_yaml'); +require('./blob/blob_dockerfile_selector'); +require('./blob/blob_dockerfile_selectors'); +require('./blob/blob_file_dropzone'); +require('./blob/blob_gitignore_selector'); +require('./blob/blob_gitignore_selectors'); +require('./blob/blob_license_selector'); +require('./blob/blob_license_selectors'); +require('./blob/template_selector'); + +// templates +require('./templates/issuable_template_selector'); +require('./templates/issuable_template_selectors'); + +// commit +require('./commit/file.js'); +require('./commit/image_file.js'); + +// extensions +require('./extensions/array'); +require('./extensions/custom_event'); +require('./extensions/element'); +require('./extensions/jquery'); +require('./extensions/object'); + +// lib/utils +require('./lib/utils/animate'); +require('./lib/utils/bootstrap_linked_tabs'); +require('./lib/utils/common_utils'); +require('./lib/utils/datetime_utility'); +require('./lib/utils/notify'); +require('./lib/utils/pretty_time'); +require('./lib/utils/text_utility'); +require('./lib/utils/type_utility'); +require('./lib/utils/url_utility'); + +// u2f +require('./u2f/authenticate'); +require('./u2f/error'); +require('./u2f/register'); +require('./u2f/util'); + +// droplab +require('./droplab/droplab'); +require('./droplab/droplab_ajax'); +require('./droplab/droplab_ajax_filter'); +require('./droplab/droplab_filter'); + +// everything else +require('./abuse_reports'); +require('./activities'); +require('./admin'); +require('./ajax_loading_spinner'); +require('./api'); +require('./aside'); +require('./autosave'); +require('./awards_handler'); +require('./breakpoints'); +require('./broadcast_message'); +require('./build'); +require('./build_artifacts'); +require('./build_variables'); +require('./ci_lint_editor'); +require('./commit'); +require('./commits'); +require('./compare'); +require('./compare_autocomplete'); +require('./confirm_danger_modal'); +require('./copy_as_gfm'); +require('./copy_to_clipboard'); +require('./create_label'); +require('./diff'); +require('./dispatcher'); +require('./dropzone_input'); +require('./due_date_select'); +require('./files_comment_button'); +require('./flash'); +require('./gfm_auto_complete'); +require('./gl_dropdown'); +require('./gl_field_error'); +require('./gl_field_errors'); +require('./gl_form'); +require('./group_avatar'); +require('./group_label_subscription'); +require('./groups_select'); +require('./header'); +require('./importer_status'); +require('./issuable'); +require('./issuable_context'); +require('./issuable_form'); +require('./issue'); +require('./issue_status_select'); +require('./issues_bulk_assignment'); +require('./label_manager'); +require('./labels'); +require('./labels_select'); +require('./layout_nav'); +require('./line_highlighter'); +require('./logo'); +require('./member_expiration_date'); +require('./members'); +require('./merge_request'); +require('./merge_request_tabs'); +require('./merge_request_widget'); +require('./merged_buttons'); +require('./milestone'); +require('./milestone_select'); +require('./mini_pipeline_graph_dropdown'); +require('./namespace_select'); +require('./new_branch_form'); +require('./new_commit_form'); +require('./notes'); +require('./notifications_dropdown'); +require('./notifications_form'); +require('./pager'); +require('./pipelines'); +require('./preview_markdown'); +require('./project'); +require('./project_avatar'); +require('./project_find_file'); +require('./project_fork'); +require('./project_import'); +require('./project_label_subscription'); +require('./project_new'); +require('./project_select'); +require('./project_show'); +require('./project_variables'); +require('./projects_list'); +require('./render_gfm'); +require('./render_math'); +require('./right_sidebar'); +require('./search'); +require('./search_autocomplete'); +require('./shortcuts'); +require('./shortcuts_blob'); +require('./shortcuts_dashboard_navigation'); +require('./shortcuts_find_file'); +require('./shortcuts_issuable'); +require('./shortcuts_navigation'); +require('./shortcuts_network'); +require('./signin_tabs_memoizer'); +require('./single_file_diff'); +require('./smart_interval'); +require('./snippets_list'); +require('./star'); +require('./subbable_resource'); +require('./subscription'); +require('./subscription_select'); +require('./syntax_highlight'); +require('./task_list'); +require('./todos'); +require('./tree'); +require('./user'); +require('./user_tabs'); +require('./username_validator'); +require('./users_select'); +require('./version_check_image'); +require('./visibility_select'); +require('./wikis'); +require('./zen_mode'); + require('vendor/fuzzaldrin-plus'); require('es6-promise').polyfill(); diff --git a/app/assets/javascripts/boards/components/board_card.js b/app/assets/javascripts/boards/components/board_card.js new file mode 100644 index 00000000000..52f61d84517 --- /dev/null +++ b/app/assets/javascripts/boards/components/board_card.js @@ -0,0 +1,69 @@ +/* global Vue */ +require('./issue_card_inner'); + +const Store = gl.issueBoards.BoardsStore; + +module.exports = { + name: 'BoardsIssueCard', + template: ` + <li class="card" + :class="{ 'user-can-drag': !disabled && issue.id, 'is-disabled': disabled || !issue.id, 'is-active': issueDetailVisible }" + :index="index" + :data-issue-id="issue.id" + @mousedown="mouseDown" + @mousemove="mouseMove" + @mouseup="showIssue($event)"> + <issue-card-inner + :list="list" + :issue="issue" + :issue-link-base="issueLinkBase" + :root-path="rootPath" /> + </li> + `, + components: { + 'issue-card-inner': gl.issueBoards.IssueCardInner, + }, + props: { + list: Object, + issue: Object, + issueLinkBase: String, + disabled: Boolean, + index: Number, + rootPath: String, + }, + data() { + return { + showDetail: false, + detailIssue: Store.detail, + }; + }, + computed: { + issueDetailVisible() { + return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id; + }, + }, + methods: { + mouseDown() { + this.showDetail = true; + }, + mouseMove() { + this.showDetail = false; + }, + showIssue(e) { + const targetTagName = e.target.tagName.toLowerCase(); + + if (targetTagName === 'a' || targetTagName === 'button') return; + + if (this.showDetail) { + this.showDetail = false; + + if (Store.detail.issue && Store.detail.issue.id === this.issue.id) { + Store.detail.issue = {}; + } else { + Store.detail.issue = this.issue; + Store.detail.list = this.list; + } + } + }, + }, +}; diff --git a/app/assets/javascripts/boards/components/board_card.js.es6 b/app/assets/javascripts/boards/components/board_card.js.es6 deleted file mode 100644 index 0ea66bd027c..00000000000 --- a/app/assets/javascripts/boards/components/board_card.js.es6 +++ /dev/null @@ -1,61 +0,0 @@ -/* eslint-disable comma-dangle, space-before-function-paren, dot-notation */ -/* global Vue */ - -require('./issue_card_inner'); - -(() => { - const Store = gl.issueBoards.BoardsStore; - - window.gl = window.gl || {}; - window.gl.issueBoards = window.gl.issueBoards || {}; - - gl.issueBoards.BoardCard = Vue.extend({ - template: '#js-board-list-card', - components: { - 'issue-card-inner': gl.issueBoards.IssueCardInner, - }, - props: { - list: Object, - issue: Object, - issueLinkBase: String, - disabled: Boolean, - index: Number, - rootPath: String, - }, - data () { - return { - showDetail: false, - detailIssue: Store.detail - }; - }, - computed: { - issueDetailVisible () { - return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id; - } - }, - methods: { - mouseDown () { - this.showDetail = true; - }, - mouseMove() { - this.showDetail = false; - }, - showIssue (e) { - const targetTagName = e.target.tagName.toLowerCase(); - - if (targetTagName === 'a' || targetTagName === 'button') return; - - if (this.showDetail) { - this.showDetail = false; - - if (Store.detail.issue && Store.detail.issue.id === this.issue.id) { - Store.detail.issue = {}; - } else { - Store.detail.issue = this.issue; - Store.detail.list = this.list; - } - } - } - } - }); -})(); diff --git a/app/assets/javascripts/boards/components/board_list.js.es6 b/app/assets/javascripts/boards/components/board_list.js.es6 index 60b0a30af3f..d92047cc0f8 100644 --- a/app/assets/javascripts/boards/components/board_list.js.es6 +++ b/app/assets/javascripts/boards/components/board_list.js.es6 @@ -2,7 +2,7 @@ /* global Vue */ /* global Sortable */ -require('./board_card'); +const boardCard = require('./board_card'); require('./board_new_issue'); (() => { @@ -14,7 +14,7 @@ require('./board_new_issue'); gl.issueBoards.BoardList = Vue.extend({ template: '#js-board-list-template', components: { - 'board-card': gl.issueBoards.BoardCard, + boardCard, 'board-new-issue': gl.issueBoards.BoardNewIssue }, props: { diff --git a/app/assets/javascripts/boards/models/list.js.es6 b/app/assets/javascripts/boards/models/list.js.es6 index 5152be56b66..8158ed4ec2c 100644 --- a/app/assets/javascripts/boards/models/list.js.es6 +++ b/app/assets/javascripts/boards/models/list.js.es6 @@ -123,14 +123,18 @@ class List { if (listFrom) { this.issuesSize += 1; - gl.boardService.moveIssue(issue.id, listFrom.id, this.id) - .then(() => { - listFrom.getIssues(false); - }); + this.updateIssueLabel(issue, listFrom); } } } + updateIssueLabel(issue, listFrom) { + gl.boardService.moveIssue(issue.id, listFrom.id, this.id) + .then(() => { + listFrom.getIssues(false); + }); + } + findIssue (id) { return this.issues.filter(issue => issue.id === id)[0]; } diff --git a/app/assets/javascripts/boards/stores/boards_store.js.es6 b/app/assets/javascripts/boards/stores/boards_store.js.es6 index 50842ecbaaa..56436c8fdc7 100644 --- a/app/assets/javascripts/boards/stores/boards_store.js.es6 +++ b/app/assets/javascripts/boards/stores/boards_store.js.es6 @@ -92,9 +92,12 @@ const issueLists = issue.getLists(); const listLabels = issueLists.map(listIssue => listIssue.label); - // Add to new lists issues if it doesn't already exist if (!issueTo) { + // Add to new lists issues if it doesn't already exist listTo.addIssue(issue, listFrom, newIndex); + } else { + listTo.updateIssueLabel(issue, listFrom); + issueTo.removeLabel(listFrom.label); } if (listTo.type === 'done') { diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6 index f55db02f0fd..0f678492d4c 100644 --- a/app/assets/javascripts/dispatcher.js.es6 +++ b/app/assets/javascripts/dispatcher.js.es6 @@ -36,6 +36,7 @@ /* global Shortcuts */ const ShortcutsBlob = require('./shortcuts_blob'); +const UserCallout = require('./user_callout'); (function() { var Dispatcher; @@ -108,6 +109,9 @@ const ShortcutsBlob = require('./shortcuts_blob'); case 'projects:compare:show': new gl.Diff(); break; + case 'projects:branches:index': + gl.AjaxLoadingSpinner.init(); + break; case 'projects:issues:new': case 'projects:issues:edit': shortcut_handler = new ShortcutsNavigation(); @@ -274,6 +278,9 @@ const ShortcutsBlob = require('./shortcuts_blob'); case 'ci:lints:show': new gl.CILintEditor(); break; + case 'users:show': + new UserCallout(); + break; } switch (path.first()) { case 'sessions': @@ -310,6 +317,7 @@ const ShortcutsBlob = require('./shortcuts_blob'); case 'dashboard': case 'root': shortcut_handler = new ShortcutsDashboardNavigation(); + new UserCallout(); break; case 'profiles': new NotificationsForm(); diff --git a/app/assets/javascripts/graphs/graphs_bundle.js b/app/assets/javascripts/graphs/graphs_bundle.js index 086dcb34571..ea5afbd9d29 100644 --- a/app/assets/javascripts/graphs/graphs_bundle.js +++ b/app/assets/javascripts/graphs/graphs_bundle.js @@ -1,4 +1,4 @@ -require('./stat_graph_contributors_graph'); -require('./stat_graph_contributors_util'); -require('./stat_graph_contributors'); -require('./stat_graph'); +import ContributorsStatGraph from './stat_graph_contributors'; + +// export to global scope +window.ContributorsStatGraph = ContributorsStatGraph; diff --git a/app/assets/javascripts/graphs/stat_graph.js b/app/assets/javascripts/graphs/stat_graph.js deleted file mode 100644 index 75a53aae33c..00000000000 --- a/app/assets/javascripts/graphs/stat_graph.js +++ /dev/null @@ -1,18 +0,0 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-return-assign, max-len */ -(function() { - this.StatGraph = (function() { - function StatGraph() {} - - StatGraph.log = {}; - - StatGraph.get_log = function() { - return this.log; - }; - - StatGraph.set_log = function(data) { - return this.log = data; - }; - - return StatGraph; - })(); -}).call(window); diff --git a/app/assets/javascripts/graphs/stat_graph_contributors.js b/app/assets/javascripts/graphs/stat_graph_contributors.js index bbfb467ad50..c6be4c9e8fe 100644 --- a/app/assets/javascripts/graphs/stat_graph_contributors.js +++ b/app/assets/javascripts/graphs/stat_graph_contributors.js @@ -1,116 +1,111 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign */ -/* global ContributorsGraph */ -/* global ContributorsAuthorGraph */ -/* global ContributorsMasterGraph */ -/* global ContributorsStatGraphUtil */ -/* global d3 */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign, no-shadow */ -window.d3 = require('d3'); +import d3 from 'd3'; +import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph'; +import ContributorsStatGraphUtil from './stat_graph_contributors_util'; -(function() { - this.ContributorsStatGraph = (function() { - function ContributorsStatGraph() {} +export default (function() { + function ContributorsStatGraph() {} - ContributorsStatGraph.prototype.init = function(log) { - var author_commits, total_commits; - this.parsed_log = ContributorsStatGraphUtil.parse_log(log); - this.set_current_field("commits"); - total_commits = ContributorsStatGraphUtil.get_total_data(this.parsed_log, this.field); - author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field); - this.add_master_graph(total_commits); - this.add_authors_graph(author_commits); - return this.change_date_header(); - }; + ContributorsStatGraph.prototype.init = function(log) { + var author_commits, total_commits; + this.parsed_log = ContributorsStatGraphUtil.parse_log(log); + this.set_current_field("commits"); + total_commits = ContributorsStatGraphUtil.get_total_data(this.parsed_log, this.field); + author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field); + this.add_master_graph(total_commits); + this.add_authors_graph(author_commits); + return this.change_date_header(); + }; - ContributorsStatGraph.prototype.add_master_graph = function(total_data) { - this.master_graph = new ContributorsMasterGraph(total_data); - return this.master_graph.draw(); - }; + ContributorsStatGraph.prototype.add_master_graph = function(total_data) { + this.master_graph = new ContributorsMasterGraph(total_data); + return this.master_graph.draw(); + }; - ContributorsStatGraph.prototype.add_authors_graph = function(author_data) { - var limited_author_data; - this.authors = []; - limited_author_data = author_data.slice(0, 100); - return _.each(limited_author_data, (function(_this) { - return function(d) { - var author_graph, author_header; - author_header = _this.create_author_header(d); - $(".contributors-list").append(author_header); - _this.authors[d.author_name] = author_graph = new ContributorsAuthorGraph(d.dates); - return author_graph.draw(); - }; - })(this)); - }; + ContributorsStatGraph.prototype.add_authors_graph = function(author_data) { + var limited_author_data; + this.authors = []; + limited_author_data = author_data.slice(0, 100); + return _.each(limited_author_data, (function(_this) { + return function(d) { + var author_graph, author_header; + author_header = _this.create_author_header(d); + $(".contributors-list").append(author_header); + _this.authors[d.author_name] = author_graph = new ContributorsAuthorGraph(d.dates); + return author_graph.draw(); + }; + })(this)); + }; - ContributorsStatGraph.prototype.format_author_commit_info = function(author) { - var commits; - commits = $('<span/>', { - "class": 'graph-author-commits-count' - }); - commits.text(author.commits + " commits"); - return $('<span/>').append(commits); - }; + ContributorsStatGraph.prototype.format_author_commit_info = function(author) { + var commits; + commits = $('<span/>', { + "class": 'graph-author-commits-count' + }); + commits.text(author.commits + " commits"); + return $('<span/>').append(commits); + }; - ContributorsStatGraph.prototype.create_author_header = function(author) { - var author_commit_info, author_commit_info_span, author_email, author_name, list_item; - list_item = $('<li/>', { - "class": 'person', - style: 'display: block;' - }); - author_name = $('<h4>' + author.author_name + '</h4>'); - author_email = $('<p class="graph-author-email">' + author.author_email + '</p>'); - author_commit_info_span = $('<span/>', { - "class": 'commits' - }); - author_commit_info = this.format_author_commit_info(author); - author_commit_info_span.html(author_commit_info); - list_item.append(author_name); - list_item.append(author_email); - list_item.append(author_commit_info_span); - return list_item; - }; + ContributorsStatGraph.prototype.create_author_header = function(author) { + var author_commit_info, author_commit_info_span, author_email, author_name, list_item; + list_item = $('<li/>', { + "class": 'person', + style: 'display: block;' + }); + author_name = $('<h4>' + author.author_name + '</h4>'); + author_email = $('<p class="graph-author-email">' + author.author_email + '</p>'); + author_commit_info_span = $('<span/>', { + "class": 'commits' + }); + author_commit_info = this.format_author_commit_info(author); + author_commit_info_span.html(author_commit_info); + list_item.append(author_name); + list_item.append(author_email); + list_item.append(author_commit_info_span); + return list_item; + }; - ContributorsStatGraph.prototype.redraw_master = function() { - var total_data; - total_data = ContributorsStatGraphUtil.get_total_data(this.parsed_log, this.field); - this.master_graph.set_data(total_data); - return this.master_graph.redraw(); - }; + ContributorsStatGraph.prototype.redraw_master = function() { + var total_data; + total_data = ContributorsStatGraphUtil.get_total_data(this.parsed_log, this.field); + this.master_graph.set_data(total_data); + return this.master_graph.redraw(); + }; - ContributorsStatGraph.prototype.redraw_authors = function() { - var author_commits, x_domain; - $("ol").html(""); - x_domain = ContributorsGraph.prototype.x_domain; - author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field, x_domain); - return _.each(author_commits, (function(_this) { - return function(d) { - _this.redraw_author_commit_info(d); - $(_this.authors[d.author_name].list_item).appendTo("ol"); - _this.authors[d.author_name].set_data(d.dates); - return _this.authors[d.author_name].redraw(); - }; - })(this)); - }; + ContributorsStatGraph.prototype.redraw_authors = function() { + var author_commits, x_domain; + $("ol").html(""); + x_domain = ContributorsGraph.prototype.x_domain; + author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field, x_domain); + return _.each(author_commits, (function(_this) { + return function(d) { + _this.redraw_author_commit_info(d); + $(_this.authors[d.author_name].list_item).appendTo("ol"); + _this.authors[d.author_name].set_data(d.dates); + return _this.authors[d.author_name].redraw(); + }; + })(this)); + }; - ContributorsStatGraph.prototype.set_current_field = function(field) { - return this.field = field; - }; + ContributorsStatGraph.prototype.set_current_field = function(field) { + return this.field = field; + }; - ContributorsStatGraph.prototype.change_date_header = function() { - var print, print_date_format, x_domain; - x_domain = ContributorsGraph.prototype.x_domain; - print_date_format = d3.time.format("%B %e %Y"); - print = print_date_format(x_domain[0]) + " - " + print_date_format(x_domain[1]); - return $("#date_header").text(print); - }; + ContributorsStatGraph.prototype.change_date_header = function() { + var print, print_date_format, x_domain; + x_domain = ContributorsGraph.prototype.x_domain; + print_date_format = d3.time.format("%B %e %Y"); + print = print_date_format(x_domain[0]) + " - " + print_date_format(x_domain[1]); + return $("#date_header").text(print); + }; - ContributorsStatGraph.prototype.redraw_author_commit_info = function(author) { - var author_commit_info, author_list_item; - author_list_item = $(this.authors[author.author_name].list_item); - author_commit_info = this.format_author_commit_info(author); - return author_list_item.find("span").html(author_commit_info); - }; + ContributorsStatGraph.prototype.redraw_author_commit_info = function(author) { + var author_commit_info, author_list_item; + author_list_item = $(this.authors[author.author_name].list_item); + author_commit_info = this.format_author_commit_info(author); + return author_list_item.find("span").html(author_commit_info); + }; - return ContributorsStatGraph; - })(); -}).call(window); + return ContributorsStatGraph; +})(); diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js index 228771da4ee..521bc77db66 100644 --- a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js +++ b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js @@ -1,276 +1,272 @@ -/* eslint-disable func-names, space-before-function-paren, one-var, no-var, prefer-rest-params, max-len, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, comma-dangle, no-return-assign, prefer-arrow-callback, quotes, prefer-template, newline-per-chained-call, no-else-return */ -/* global d3 */ -/* global ContributorsGraph */ - -window.d3 = require('d3'); - -(function() { - var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; }, - extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - hasProp = {}.hasOwnProperty; - - this.ContributorsGraph = (function() { - function ContributorsGraph() {} - - ContributorsGraph.prototype.MARGIN = { - top: 20, - right: 20, - bottom: 30, - left: 50 - }; - - ContributorsGraph.prototype.x_domain = null; - - ContributorsGraph.prototype.y_domain = null; - - ContributorsGraph.prototype.dates = []; - - ContributorsGraph.set_x_domain = function(data) { - return ContributorsGraph.prototype.x_domain = data; - }; - - ContributorsGraph.set_y_domain = function(data) { - return ContributorsGraph.prototype.y_domain = [ - 0, d3.max(data, function(d) { - return d.commits = d.commits || d.additions || d.deletions; - }) - ]; - }; - - ContributorsGraph.init_x_domain = function(data) { - return ContributorsGraph.prototype.x_domain = d3.extent(data, function(d) { - return d.date; - }); - }; - - ContributorsGraph.init_y_domain = function(data) { - return ContributorsGraph.prototype.y_domain = [ - 0, d3.max(data, function(d) { - return d.commits = d.commits || d.additions || d.deletions; - }) - ]; - }; - - ContributorsGraph.init_domain = function(data) { - ContributorsGraph.init_x_domain(data); - return ContributorsGraph.init_y_domain(data); - }; - - ContributorsGraph.set_dates = function(data) { - return ContributorsGraph.prototype.dates = data; - }; - - ContributorsGraph.prototype.set_x_domain = function() { - return this.x.domain(this.x_domain); - }; - - ContributorsGraph.prototype.set_y_domain = function() { - return this.y.domain(this.y_domain); - }; - - ContributorsGraph.prototype.set_domain = function() { - this.set_x_domain(); - return this.set_y_domain(); - }; - - ContributorsGraph.prototype.create_scale = function(width, height) { - this.x = d3.time.scale().range([0, width]).clamp(true); - return this.y = d3.scale.linear().range([height, 0]).nice(); - }; - - ContributorsGraph.prototype.draw_x_axis = function() { - return this.svg.append("g").attr("class", "x axis").attr("transform", "translate(0, " + this.height + ")").call(this.x_axis); - }; - - ContributorsGraph.prototype.draw_y_axis = function() { - return this.svg.append("g").attr("class", "y axis").call(this.y_axis); - }; - - ContributorsGraph.prototype.set_data = function(data) { - return this.data = data; - }; - - return ContributorsGraph; - })(); - - this.ContributorsMasterGraph = (function(superClass) { - extend(ContributorsMasterGraph, superClass); - - function ContributorsMasterGraph(data1) { - this.data = data1; - this.update_content = bind(this.update_content, this); - this.width = $('.content').width() - 70; - this.height = 200; - this.x = null; - this.y = null; - this.x_axis = null; - this.y_axis = null; - this.area = null; - this.svg = null; - this.brush = null; - this.x_max_domain = null; +/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, max-len, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, comma-dangle, no-return-assign, prefer-arrow-callback, quotes, prefer-template, newline-per-chained-call, no-else-return, no-shadow */ + +import d3 from 'd3'; + +const bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; }; +const extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; +const hasProp = {}.hasOwnProperty; + +export const ContributorsGraph = (function() { + function ContributorsGraph() {} + + ContributorsGraph.prototype.MARGIN = { + top: 20, + right: 20, + bottom: 30, + left: 50 + }; + + ContributorsGraph.prototype.x_domain = null; + + ContributorsGraph.prototype.y_domain = null; + + ContributorsGraph.prototype.dates = []; + + ContributorsGraph.set_x_domain = function(data) { + return ContributorsGraph.prototype.x_domain = data; + }; + + ContributorsGraph.set_y_domain = function(data) { + return ContributorsGraph.prototype.y_domain = [ + 0, d3.max(data, function(d) { + return d.commits = d.commits || d.additions || d.deletions; + }) + ]; + }; + + ContributorsGraph.init_x_domain = function(data) { + return ContributorsGraph.prototype.x_domain = d3.extent(data, function(d) { + return d.date; + }); + }; + + ContributorsGraph.init_y_domain = function(data) { + return ContributorsGraph.prototype.y_domain = [ + 0, d3.max(data, function(d) { + return d.commits = d.commits || d.additions || d.deletions; + }) + ]; + }; + + ContributorsGraph.init_domain = function(data) { + ContributorsGraph.init_x_domain(data); + return ContributorsGraph.init_y_domain(data); + }; + + ContributorsGraph.set_dates = function(data) { + return ContributorsGraph.prototype.dates = data; + }; + + ContributorsGraph.prototype.set_x_domain = function() { + return this.x.domain(this.x_domain); + }; + + ContributorsGraph.prototype.set_y_domain = function() { + return this.y.domain(this.y_domain); + }; + + ContributorsGraph.prototype.set_domain = function() { + this.set_x_domain(); + return this.set_y_domain(); + }; + + ContributorsGraph.prototype.create_scale = function(width, height) { + this.x = d3.time.scale().range([0, width]).clamp(true); + return this.y = d3.scale.linear().range([height, 0]).nice(); + }; + + ContributorsGraph.prototype.draw_x_axis = function() { + return this.svg.append("g").attr("class", "x axis").attr("transform", "translate(0, " + this.height + ")").call(this.x_axis); + }; + + ContributorsGraph.prototype.draw_y_axis = function() { + return this.svg.append("g").attr("class", "y axis").call(this.y_axis); + }; + + ContributorsGraph.prototype.set_data = function(data) { + return this.data = data; + }; + + return ContributorsGraph; +})(); + +export const ContributorsMasterGraph = (function(superClass) { + extend(ContributorsMasterGraph, superClass); + + function ContributorsMasterGraph(data1) { + this.data = data1; + this.update_content = bind(this.update_content, this); + this.width = $('.content').width() - 70; + this.height = 200; + this.x = null; + this.y = null; + this.x_axis = null; + this.y_axis = null; + this.area = null; + this.svg = null; + this.brush = null; + this.x_max_domain = null; + } + + ContributorsMasterGraph.prototype.process_dates = function(data) { + var dates; + dates = this.get_dates(data); + this.parse_dates(data); + return ContributorsGraph.set_dates(dates); + }; + + ContributorsMasterGraph.prototype.get_dates = function(data) { + return _.pluck(data, 'date'); + }; + + ContributorsMasterGraph.prototype.parse_dates = function(data) { + var parseDate; + parseDate = d3.time.format("%Y-%m-%d").parse; + return data.forEach(function(d) { + return d.date = parseDate(d.date); + }); + }; + + ContributorsMasterGraph.prototype.create_scale = function() { + return ContributorsMasterGraph.__super__.create_scale.call(this, this.width, this.height); + }; + + ContributorsMasterGraph.prototype.create_axes = function() { + this.x_axis = d3.svg.axis().scale(this.x).orient("bottom"); + return this.y_axis = d3.svg.axis().scale(this.y).orient("left").ticks(5); + }; + + ContributorsMasterGraph.prototype.create_svg = function() { + return this.svg = d3.select("#contributors-master").append("svg").attr("width", this.width + this.MARGIN.left + this.MARGIN.right).attr("height", this.height + this.MARGIN.top + this.MARGIN.bottom).attr("class", "tint-box").append("g").attr("transform", "translate(" + this.MARGIN.left + "," + this.MARGIN.top + ")"); + }; + + ContributorsMasterGraph.prototype.create_area = function(x, y) { + return this.area = d3.svg.area().x(function(d) { + return x(d.date); + }).y0(this.height).y1(function(d) { + d.commits = d.commits || d.additions || d.deletions; + return y(d.commits); + }).interpolate("basis"); + }; + + ContributorsMasterGraph.prototype.create_brush = function() { + return this.brush = d3.svg.brush().x(this.x).on("brushend", this.update_content); + }; + + ContributorsMasterGraph.prototype.draw_path = function(data) { + return this.svg.append("path").datum(data).attr("class", "area").attr("d", this.area); + }; + + ContributorsMasterGraph.prototype.add_brush = function() { + return this.svg.append("g").attr("class", "selection").call(this.brush).selectAll("rect").attr("height", this.height); + }; + + ContributorsMasterGraph.prototype.update_content = function() { + ContributorsGraph.set_x_domain(this.brush.empty() ? this.x_max_domain : this.brush.extent()); + return $("#brush_change").trigger('change'); + }; + + ContributorsMasterGraph.prototype.draw = function() { + this.process_dates(this.data); + this.create_scale(); + this.create_axes(); + ContributorsGraph.init_domain(this.data); + this.x_max_domain = this.x_domain; + this.set_domain(); + this.create_area(this.x, this.y); + this.create_svg(); + this.create_brush(); + this.draw_path(this.data); + this.draw_x_axis(); + this.draw_y_axis(); + return this.add_brush(); + }; + + ContributorsMasterGraph.prototype.redraw = function() { + this.process_dates(this.data); + ContributorsGraph.set_y_domain(this.data); + this.set_y_domain(); + this.svg.select("path").datum(this.data); + this.svg.select("path").attr("d", this.area); + return this.svg.select(".y.axis").call(this.y_axis); + }; + + return ContributorsMasterGraph; +})(ContributorsGraph); + +export const ContributorsAuthorGraph = (function(superClass) { + extend(ContributorsAuthorGraph, superClass); + + function ContributorsAuthorGraph(data1) { + this.data = data1; + // Don't split graph size in half for mobile devices. + if ($(window).width() < 768) { + this.width = $('.content').width() - 80; + } else { + this.width = ($('.content').width() / 2) - 100; } - - ContributorsMasterGraph.prototype.process_dates = function(data) { - var dates; - dates = this.get_dates(data); - this.parse_dates(data); - return ContributorsGraph.set_dates(dates); - }; - - ContributorsMasterGraph.prototype.get_dates = function(data) { - return _.pluck(data, 'date'); - }; - - ContributorsMasterGraph.prototype.parse_dates = function(data) { + this.height = 200; + this.x = null; + this.y = null; + this.x_axis = null; + this.y_axis = null; + this.area = null; + this.svg = null; + this.list_item = null; + } + + ContributorsAuthorGraph.prototype.create_scale = function() { + return ContributorsAuthorGraph.__super__.create_scale.call(this, this.width, this.height); + }; + + ContributorsAuthorGraph.prototype.create_axes = function() { + this.x_axis = d3.svg.axis().scale(this.x).orient("bottom").ticks(8); + return this.y_axis = d3.svg.axis().scale(this.y).orient("left").ticks(5); + }; + + ContributorsAuthorGraph.prototype.create_area = function(x, y) { + return this.area = d3.svg.area().x(function(d) { var parseDate; parseDate = d3.time.format("%Y-%m-%d").parse; - return data.forEach(function(d) { - return d.date = parseDate(d.date); - }); - }; - - ContributorsMasterGraph.prototype.create_scale = function() { - return ContributorsMasterGraph.__super__.create_scale.call(this, this.width, this.height); - }; - - ContributorsMasterGraph.prototype.create_axes = function() { - this.x_axis = d3.svg.axis().scale(this.x).orient("bottom"); - return this.y_axis = d3.svg.axis().scale(this.y).orient("left").ticks(5); - }; - - ContributorsMasterGraph.prototype.create_svg = function() { - return this.svg = d3.select("#contributors-master").append("svg").attr("width", this.width + this.MARGIN.left + this.MARGIN.right).attr("height", this.height + this.MARGIN.top + this.MARGIN.bottom).attr("class", "tint-box").append("g").attr("transform", "translate(" + this.MARGIN.left + "," + this.MARGIN.top + ")"); - }; - - ContributorsMasterGraph.prototype.create_area = function(x, y) { - return this.area = d3.svg.area().x(function(d) { - return x(d.date); - }).y0(this.height).y1(function(d) { - d.commits = d.commits || d.additions || d.deletions; - return y(d.commits); - }).interpolate("basis"); - }; - - ContributorsMasterGraph.prototype.create_brush = function() { - return this.brush = d3.svg.brush().x(this.x).on("brushend", this.update_content); - }; - - ContributorsMasterGraph.prototype.draw_path = function(data) { - return this.svg.append("path").datum(data).attr("class", "area").attr("d", this.area); - }; - - ContributorsMasterGraph.prototype.add_brush = function() { - return this.svg.append("g").attr("class", "selection").call(this.brush).selectAll("rect").attr("height", this.height); - }; - - ContributorsMasterGraph.prototype.update_content = function() { - ContributorsGraph.set_x_domain(this.brush.empty() ? this.x_max_domain : this.brush.extent()); - return $("#brush_change").trigger('change'); - }; - - ContributorsMasterGraph.prototype.draw = function() { - this.process_dates(this.data); - this.create_scale(); - this.create_axes(); - ContributorsGraph.init_domain(this.data); - this.x_max_domain = this.x_domain; - this.set_domain(); - this.create_area(this.x, this.y); - this.create_svg(); - this.create_brush(); - this.draw_path(this.data); - this.draw_x_axis(); - this.draw_y_axis(); - return this.add_brush(); - }; - - ContributorsMasterGraph.prototype.redraw = function() { - this.process_dates(this.data); - ContributorsGraph.set_y_domain(this.data); - this.set_y_domain(); - this.svg.select("path").datum(this.data); - this.svg.select("path").attr("d", this.area); - return this.svg.select(".y.axis").call(this.y_axis); - }; - - return ContributorsMasterGraph; - })(ContributorsGraph); - - this.ContributorsAuthorGraph = (function(superClass) { - extend(ContributorsAuthorGraph, superClass); - - function ContributorsAuthorGraph(data1) { - this.data = data1; - // Don't split graph size in half for mobile devices. - if ($(window).width() < 768) { - this.width = $('.content').width() - 80; - } else { - this.width = ($('.content').width() / 2) - 100; - } - this.height = 200; - this.x = null; - this.y = null; - this.x_axis = null; - this.y_axis = null; - this.area = null; - this.svg = null; - this.list_item = null; - } - - ContributorsAuthorGraph.prototype.create_scale = function() { - return ContributorsAuthorGraph.__super__.create_scale.call(this, this.width, this.height); - }; - - ContributorsAuthorGraph.prototype.create_axes = function() { - this.x_axis = d3.svg.axis().scale(this.x).orient("bottom").ticks(8); - return this.y_axis = d3.svg.axis().scale(this.y).orient("left").ticks(5); - }; - - ContributorsAuthorGraph.prototype.create_area = function(x, y) { - return this.area = d3.svg.area().x(function(d) { - var parseDate; - parseDate = d3.time.format("%Y-%m-%d").parse; - return x(parseDate(d)); - }).y0(this.height).y1((function(_this) { - return function(d) { - if (_this.data[d] != null) { - return y(_this.data[d]); - } else { - return y(0); - } - }; - })(this)).interpolate("basis"); - }; - - ContributorsAuthorGraph.prototype.create_svg = function() { - this.list_item = d3.selectAll(".person")[0].pop(); - return this.svg = d3.select(this.list_item).append("svg").attr("width", this.width + this.MARGIN.left + this.MARGIN.right).attr("height", this.height + this.MARGIN.top + this.MARGIN.bottom).attr("class", "spark").append("g").attr("transform", "translate(" + this.MARGIN.left + "," + this.MARGIN.top + ")"); - }; - - ContributorsAuthorGraph.prototype.draw_path = function(data) { - return this.svg.append("path").datum(data).attr("class", "area-contributor").attr("d", this.area); - }; - - ContributorsAuthorGraph.prototype.draw = function() { - this.create_scale(); - this.create_axes(); - this.set_domain(); - this.create_area(this.x, this.y); - this.create_svg(); - this.draw_path(this.dates); - this.draw_x_axis(); - return this.draw_y_axis(); - }; - - ContributorsAuthorGraph.prototype.redraw = function() { - this.set_domain(); - this.svg.select("path").datum(this.dates); - this.svg.select("path").attr("d", this.area); - this.svg.select(".x.axis").call(this.x_axis); - return this.svg.select(".y.axis").call(this.y_axis); - }; - - return ContributorsAuthorGraph; - })(ContributorsGraph); -}).call(window); + return x(parseDate(d)); + }).y0(this.height).y1((function(_this) { + return function(d) { + if (_this.data[d] != null) { + return y(_this.data[d]); + } else { + return y(0); + } + }; + })(this)).interpolate("basis"); + }; + + ContributorsAuthorGraph.prototype.create_svg = function() { + this.list_item = d3.selectAll(".person")[0].pop(); + return this.svg = d3.select(this.list_item).append("svg").attr("width", this.width + this.MARGIN.left + this.MARGIN.right).attr("height", this.height + this.MARGIN.top + this.MARGIN.bottom).attr("class", "spark").append("g").attr("transform", "translate(" + this.MARGIN.left + "," + this.MARGIN.top + ")"); + }; + + ContributorsAuthorGraph.prototype.draw_path = function(data) { + return this.svg.append("path").datum(data).attr("class", "area-contributor").attr("d", this.area); + }; + + ContributorsAuthorGraph.prototype.draw = function() { + this.create_scale(); + this.create_axes(); + this.set_domain(); + this.create_area(this.x, this.y); + this.create_svg(); + this.draw_path(this.dates); + this.draw_x_axis(); + return this.draw_y_axis(); + }; + + ContributorsAuthorGraph.prototype.redraw = function() { + this.set_domain(); + this.svg.select("path").datum(this.dates); + this.svg.select("path").attr("d", this.area); + this.svg.select(".x.axis").call(this.x_axis); + return this.svg.select(".y.axis").call(this.y_axis); + }; + + return ContributorsAuthorGraph; +})(ContributorsGraph); diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_util.js b/app/assets/javascripts/graphs/stat_graph_contributors_util.js index 7954c583598..c583757f3f2 100644 --- a/app/assets/javascripts/graphs/stat_graph_contributors_util.js +++ b/app/assets/javascripts/graphs/stat_graph_contributors_util.js @@ -1,138 +1,137 @@ /* eslint-disable func-names, space-before-function-paren, object-shorthand, no-var, one-var, camelcase, one-var-declaration-per-line, comma-dangle, no-param-reassign, no-return-assign, quotes, prefer-arrow-callback, wrap-iife, consistent-return, no-unused-vars, max-len, no-cond-assign, no-else-return, max-len */ -(function() { - window.ContributorsStatGraphUtil = { - parse_log: function(log) { - var by_author, by_email, data, entry, i, len, total, normalized_email; - total = {}; - by_author = {}; - by_email = {}; - for (i = 0, len = log.length; i < len; i += 1) { - entry = log[i]; - if (total[entry.date] == null) { - this.add_date(entry.date, total); - } - normalized_email = entry.author_email.toLowerCase(); - data = by_author[entry.author_name] || by_email[normalized_email]; - if (data == null) { - data = this.add_author(entry, by_author, by_email); - } - if (!data[entry.date]) { - this.add_date(entry.date, data); - } - this.store_data(entry, total[entry.date], data[entry.date]); - } - total = _.toArray(total); - by_author = _.toArray(by_author); - return { - total: total, - by_author: by_author - }; - }, - add_date: function(date, collection) { - collection[date] = {}; - return collection[date].date = date; - }, - add_author: function(author, by_author, by_email) { - var data, normalized_email; - data = {}; - data.author_name = author.author_name; - data.author_email = author.author_email; - normalized_email = author.author_email.toLowerCase(); - by_author[author.author_name] = data; - by_email[normalized_email] = data; - return data; - }, - store_data: function(entry, total, by_author) { - this.store_commits(total, by_author); - this.store_additions(entry, total, by_author); - return this.store_deletions(entry, total, by_author); - }, - store_commits: function(total, by_author) { - this.add(total, "commits", 1); - return this.add(by_author, "commits", 1); - }, - add: function(collection, field, value) { - if (collection[field] == null) { - collection[field] = 0; - } - return collection[field] += value; - }, - store_additions: function(entry, total, by_author) { - if (entry.additions == null) { - entry.additions = 0; + +export default { + parse_log: function(log) { + var by_author, by_email, data, entry, i, len, total, normalized_email; + total = {}; + by_author = {}; + by_email = {}; + for (i = 0, len = log.length; i < len; i += 1) { + entry = log[i]; + if (total[entry.date] == null) { + this.add_date(entry.date, total); } - this.add(total, "additions", entry.additions); - return this.add(by_author, "additions", entry.additions); - }, - store_deletions: function(entry, total, by_author) { - if (entry.deletions == null) { - entry.deletions = 0; + normalized_email = entry.author_email.toLowerCase(); + data = by_author[entry.author_name] || by_email[normalized_email]; + if (data == null) { + data = this.add_author(entry, by_author, by_email); } - this.add(total, "deletions", entry.deletions); - return this.add(by_author, "deletions", entry.deletions); - }, - get_total_data: function(parsed_log, field) { - var log, total_data; - log = parsed_log.total; - total_data = this.pick_field(log, field); - return _.sortBy(total_data, function(d) { - return d.date; - }); - }, - pick_field: function(log, field) { - var total_data; - total_data = []; - _.each(log, function(d) { - return total_data.push(_.pick(d, [field, 'date'])); - }); - return total_data; - }, - get_author_data: function(parsed_log, field, date_range) { - var author_data, log; - if (date_range == null) { - date_range = null; - } - log = parsed_log.by_author; - author_data = []; - _.each(log, (function(_this) { - return function(log_entry) { - var parsed_log_entry; - parsed_log_entry = _this.parse_log_entry(log_entry, field, date_range); - if (!_.isEmpty(parsed_log_entry.dates)) { - return author_data.push(parsed_log_entry); - } - }; - })(this)); - return _.sortBy(author_data, function(d) { - return d[field]; - }).reverse(); - }, - parse_log_entry: function(log_entry, field, date_range) { - var parsed_entry; - parsed_entry = {}; - parsed_entry.author_name = log_entry.author_name; - parsed_entry.author_email = log_entry.author_email; - parsed_entry.dates = {}; - parsed_entry.commits = parsed_entry.additions = parsed_entry.deletions = 0; - _.each(_.omit(log_entry, 'author_name', 'author_email'), (function(_this) { - return function(value, key) { - if (_this.in_range(value.date, date_range)) { - parsed_entry.dates[value.date] = value[field]; - parsed_entry.commits += value.commits; - parsed_entry.additions += value.additions; - return parsed_entry.deletions += value.deletions; - } - }; - })(this)); - return parsed_entry; - }, - in_range: function(date, date_range) { - var ref; - if (date_range === null || (date_range[0] <= (ref = new Date(date)) && ref <= date_range[1])) { - return true; - } else { - return false; + if (!data[entry.date]) { + this.add_date(entry.date, data); } + this.store_data(entry, total[entry.date], data[entry.date]); + } + total = _.toArray(total); + by_author = _.toArray(by_author); + return { + total: total, + by_author: by_author + }; + }, + add_date: function(date, collection) { + collection[date] = {}; + return collection[date].date = date; + }, + add_author: function(author, by_author, by_email) { + var data, normalized_email; + data = {}; + data.author_name = author.author_name; + data.author_email = author.author_email; + normalized_email = author.author_email.toLowerCase(); + by_author[author.author_name] = data; + by_email[normalized_email] = data; + return data; + }, + store_data: function(entry, total, by_author) { + this.store_commits(total, by_author); + this.store_additions(entry, total, by_author); + return this.store_deletions(entry, total, by_author); + }, + store_commits: function(total, by_author) { + this.add(total, "commits", 1); + return this.add(by_author, "commits", 1); + }, + add: function(collection, field, value) { + if (collection[field] == null) { + collection[field] = 0; + } + return collection[field] += value; + }, + store_additions: function(entry, total, by_author) { + if (entry.additions == null) { + entry.additions = 0; + } + this.add(total, "additions", entry.additions); + return this.add(by_author, "additions", entry.additions); + }, + store_deletions: function(entry, total, by_author) { + if (entry.deletions == null) { + entry.deletions = 0; + } + this.add(total, "deletions", entry.deletions); + return this.add(by_author, "deletions", entry.deletions); + }, + get_total_data: function(parsed_log, field) { + var log, total_data; + log = parsed_log.total; + total_data = this.pick_field(log, field); + return _.sortBy(total_data, function(d) { + return d.date; + }); + }, + pick_field: function(log, field) { + var total_data; + total_data = []; + _.each(log, function(d) { + return total_data.push(_.pick(d, [field, 'date'])); + }); + return total_data; + }, + get_author_data: function(parsed_log, field, date_range) { + var author_data, log; + if (date_range == null) { + date_range = null; + } + log = parsed_log.by_author; + author_data = []; + _.each(log, (function(_this) { + return function(log_entry) { + var parsed_log_entry; + parsed_log_entry = _this.parse_log_entry(log_entry, field, date_range); + if (!_.isEmpty(parsed_log_entry.dates)) { + return author_data.push(parsed_log_entry); + } + }; + })(this)); + return _.sortBy(author_data, function(d) { + return d[field]; + }).reverse(); + }, + parse_log_entry: function(log_entry, field, date_range) { + var parsed_entry; + parsed_entry = {}; + parsed_entry.author_name = log_entry.author_name; + parsed_entry.author_email = log_entry.author_email; + parsed_entry.dates = {}; + parsed_entry.commits = parsed_entry.additions = parsed_entry.deletions = 0; + _.each(_.omit(log_entry, 'author_name', 'author_email'), (function(_this) { + return function(value, key) { + if (_this.in_range(value.date, date_range)) { + parsed_entry.dates[value.date] = value[field]; + parsed_entry.commits += value.commits; + parsed_entry.additions += value.additions; + return parsed_entry.deletions += value.deletions; + } + }; + })(this)); + return parsed_entry; + }, + in_range: function(date, date_range) { + var ref; + if (date_range === null || (date_range[0] <= (ref = new Date(date)) && ref <= date_range[1])) { + return true; + } else { + return false; } - }; -}).call(window); + } +}; diff --git a/app/assets/javascripts/lib/utils/common_utils.js.es6 b/app/assets/javascripts/lib/utils/common_utils.js.es6 index 45a1d90a9d9..dbf40ec7fcf 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js.es6 +++ b/app/assets/javascripts/lib/utils/common_utils.js.es6 @@ -296,5 +296,57 @@ * @returns {Boolean} */ w.gl.utils.convertPermissionToBoolean = permission => permission === 'true'; + + /** + * Back Off exponential algorithm + * backOff :: (Function<next, stop>, Number) -> Promise<Any, Error> + * + * @param {Function<next, stop>} fn function to be called + * @param {Number} timeout + * @return {Promise<Any, Error>} + * @example + * ``` + * backOff(function (next, stop) { + * // Let's perform this function repeatedly for 60s or for the timeout provided. + * + * ourFunction() + * .then(function (result) { + * // continue if result is not what we need + * next(); + * + * // when result is what we need let's stop with the repetions and jump out of the cycle + * stop(result); + * }) + * .catch(function (error) { + * // if there is an error, we need to stop this with an error. + * stop(error); + * }) + * }, 60000) + * .then(function (result) {}) + * .catch(function (error) { + * // deal with errors passed to stop() + * }) + * ``` + */ + w.gl.utils.backOff = (fn, timeout = 60000) => { + let nextInterval = 2000; + + const startTime = (+new Date()); + + return new Promise((resolve, reject) => { + const stop = arg => ((arg instanceof Error) ? reject(arg) : resolve(arg)); + + const next = () => { + if (new Date().getTime() - startTime < timeout) { + setTimeout(fn.bind(null, next, stop), nextInterval); + nextInterval *= 2; + } else { + reject(new Error('BACKOFF_TIMEOUT')); + } + }; + + fn(next, stop); + }); + }; })(window); }).call(window); diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js index 7fbaeec7882..38c673e8907 100644 --- a/app/assets/javascripts/milestone.js +++ b/app/assets/javascripts/milestone.js @@ -78,7 +78,6 @@ } else { $(element).find('.assignee-icon').empty(); } - return $(element).effect('highlight'); }; function Milestone() { diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index 8df1c8e7f94..51fa5c828b3 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -39,7 +39,7 @@ $value = $block.find('.value'); $loading = $block.find('.block-loading').fadeOut(); if (issueUpdateURL) { - milestoneLinkTemplate = _.template('<a href="/<%- namespace %>/<%- path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>'); + milestoneLinkTemplate = _.template('<a href="/<%- full_path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>'); milestoneLinkNoneTemplate = '<span class="no-value">None</span>'; collapsedSidebarLabelTemplate = _.template('<span class="has-tooltip" data-container="body" title="<%- remaining %>" data-placement="left"> <%- title %> </span>'); } @@ -181,8 +181,7 @@ $selectbox.hide(); $value.css('display', ''); if (data.milestone != null) { - data.milestone.namespace = _this.currentProject.namespace; - data.milestone.path = _this.currentProject.path; + data.milestone.full_path = _this.currentProject.full_path; data.milestone.remaining = gl.utils.timeFor(data.milestone.due_date); $value.html(milestoneLinkTemplate(data.milestone)); return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone)); diff --git a/app/assets/javascripts/new_branch_form.js b/app/assets/javascripts/new_branch_form.js index cb24f212c66..3f678b93f73 100644 --- a/app/assets/javascripts/new_branch_form.js +++ b/app/assets/javascripts/new_branch_form.js @@ -1,4 +1,4 @@ -/* eslint-disable func-names, space-before-function-paren, no-var, one-var, prefer-rest-params, max-len, vars-on-top, wrap-iife, consistent-return, comma-dangle, one-var-declaration-per-line, quotes, no-return-assign, prefer-arrow-callback, prefer-template, no-shadow, no-else-return, max-len */ +/* eslint-disable func-names, space-before-function-paren, no-var, one-var, prefer-rest-params, max-len, vars-on-top, wrap-iife, consistent-return, comma-dangle, one-var-declaration-per-line, quotes, no-return-assign, prefer-arrow-callback, prefer-template, no-shadow, no-else-return, max-len, object-shorthand */ (function() { var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; }, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i += 1) { if (i in this && this[i] === item) return i; } return -1; }; @@ -20,15 +20,35 @@ }; NewBranchForm.prototype.init = function() { - if (this.name.val().length > 0) { + if (this.name.length && this.name.val().length > 0) { return this.name.trigger('blur'); } }; NewBranchForm.prototype.setupAvailableRefs = function(availableRefs) { - return this.ref.autocomplete({ - source: availableRefs, - minLength: 1 + var $branchSelect = $('.js-branch-select'); + + $branchSelect.glDropdown({ + data: availableRefs, + filterable: true, + filterByText: true, + remote: false, + fieldName: $branchSelect.data('field-name'), + selectable: true, + isSelectable: function(branch, $el) { + return !$el.hasClass('is-active'); + }, + text: function(branch) { + return branch; + }, + id: function(branch) { + return branch; + }, + toggleLabel: function(branch) { + if (branch) { + return branch; + } + } }); }; diff --git a/app/assets/javascripts/profile/profile_bundle.js b/app/assets/javascripts/profile/profile_bundle.js index d7f3c9fd37e..15d32825583 100644 --- a/app/assets/javascripts/profile/profile_bundle.js +++ b/app/assets/javascripts/profile/profile_bundle.js @@ -1,3 +1,2 @@ -// require everything else in this directory -function requireAll(context) { return context.keys().map(context); } -requireAll(require.context('.', false, /^\.\/(?!profile_bundle).*\.(js|es6)$/)); +require('./gl_crop'); +require('./profile'); diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_edit.js.es6 index 149e511451e..6ef59e94384 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_edit.js.es6 +++ b/app/assets/javascripts/protected_branches/protected_branch_edit.js.es6 @@ -36,6 +36,9 @@ // Do not update if one dropdown has not selected any option if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return; + this.$allowedToMergeDropdown.disable(); + this.$allowedToPushDropdown.disable(); + $.ajax({ type: 'POST', url: this.$wrap.data('url'), @@ -53,13 +56,13 @@ }] } }, - success: () => { - this.$wrap.effect('highlight'); - }, error() { $.scrollTo(0); new Flash('Failed to update branch!'); } + }).always(() => { + this.$allowedToMergeDropdown.enable(); + this.$allowedToPushDropdown.enable(); }); } }; diff --git a/app/assets/javascripts/protected_branches/protected_branches_bundle.js b/app/assets/javascripts/protected_branches/protected_branches_bundle.js index ffb66caf5f4..849c1e31623 100644 --- a/app/assets/javascripts/protected_branches/protected_branches_bundle.js +++ b/app/assets/javascripts/protected_branches/protected_branches_bundle.js @@ -1,3 +1,5 @@ -// require everything else in this directory -function requireAll(context) { return context.keys().map(context); } -requireAll(require.context('.', false, /^\.\/(?!protected_branches_bundle).*\.(js|es6)$/)); +require('./protected_branch_access_dropdown'); +require('./protected_branch_create'); +require('./protected_branch_dropdown'); +require('./protected_branch_edit'); +require('./protected_branch_edit_list'); diff --git a/app/assets/javascripts/snippet/snippet_bundle.js b/app/assets/javascripts/snippet/snippet_bundle.js index 89822246bb8..a98403f4cf2 100644 --- a/app/assets/javascripts/snippet/snippet_bundle.js +++ b/app/assets/javascripts/snippet/snippet_bundle.js @@ -1,10 +1,6 @@ /* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, max-len */ /* global ace */ -// require everything else in this directory -function requireAll(context) { return context.keys().map(context); } -requireAll(require.context('.', false, /^\.\/(?!snippet_bundle).*\.(js|es6)$/)); - (function() { $(function() { var editor = ace.edit("editor"); diff --git a/app/assets/javascripts/user_callout.js b/app/assets/javascripts/user_callout.js new file mode 100644 index 00000000000..74b869502a4 --- /dev/null +++ b/app/assets/javascripts/user_callout.js @@ -0,0 +1,58 @@ +/* global Cookies */ + +const userCalloutElementName = '.user-callout'; +const closeButton = '.close-user-callout'; +const userCalloutBtn = '.user-callout-btn'; +const userCalloutSvgAttrName = 'callout-svg'; + +const USER_CALLOUT_COOKIE = 'user_callout_dismissed'; + +const USER_CALLOUT_TEMPLATE = ` + <div class="bordered-box landing content-block"> + <button class="btn btn-default close close-user-callout" type="button"> + <i class="fa fa-times dismiss-icon"></i> + </button> + <div class="row"> + <div class="col-sm-3 col-xs-12 svg-container"> + </div> + <div class="col-sm-8 col-xs-12 inner-content"> + <h4> + Customize your experience + </h4> + <p> + Change syntax themes, default project pages, and more in preferences. + </p> + <a class="btn user-callout-btn" href="/profile/preferences">Check it out</a> + </div> + </div> +</div>`; + +class UserCallout { + constructor() { + this.isCalloutDismissed = Cookies.get(USER_CALLOUT_COOKIE); + this.userCalloutBody = $(userCalloutElementName); + this.userCalloutSvg = $(userCalloutElementName).attr(userCalloutSvgAttrName); + $(userCalloutElementName).removeAttr(userCalloutSvgAttrName); + this.init(); + } + + init() { + const $template = $(USER_CALLOUT_TEMPLATE); + if (!this.isCalloutDismissed || this.isCalloutDismissed === 'false') { + $template.find('.svg-container').append(this.userCalloutSvg); + this.userCalloutBody.append($template); + $template.find(closeButton).on('click', e => this.dismissCallout(e)); + $template.find(userCalloutBtn).on('click', e => this.dismissCallout(e)); + } + } + + dismissCallout(e) { + Cookies.set(USER_CALLOUT_COOKIE, 'true'); + const $currentTarget = $(e.currentTarget); + if ($currentTarget.hasClass('close-user-callout')) { + this.userCalloutBody.empty(); + } + } +} + +module.exports = UserCallout; diff --git a/app/assets/javascripts/users/users_bundle.js b/app/assets/javascripts/users/users_bundle.js index 4cad60a59b1..580e2d84be5 100644 --- a/app/assets/javascripts/users/users_bundle.js +++ b/app/assets/javascripts/users/users_bundle.js @@ -1,3 +1 @@ -// require everything else in this directory -function requireAll(context) { return context.keys().map(context); } -requireAll(require.context('.', false, /^\.\/(?!users_bundle).*\.(js|es6)$/)); +require('./calendar'); diff --git a/app/assets/javascripts/vue_pipelines_index/pipeline_actions.js.es6 b/app/assets/javascripts/vue_pipelines_index/pipeline_actions.js.es6 index 04029c47668..e20085d1fd2 100644 --- a/app/assets/javascripts/vue_pipelines_index/pipeline_actions.js.es6 +++ b/app/assets/javascripts/vue_pipelines_index/pipeline_actions.js.es6 @@ -1,5 +1,5 @@ /* global Vue, Flash, gl */ -/* eslint-disable no-param-reassign */ +/* eslint-disable no-param-reassign, no-alert */ ((gl) => { gl.VuePipelineActions = Vue.extend({ @@ -16,6 +16,20 @@ download(name) { return `Download ${name} artifacts`; }, + + /** + * Shows a dialog when the user clicks in the cancel button. + * We need to prevent the default behavior and stop propagation because the + * link relies on UJS. + * + * @param {Event} event + */ + confirmAction(event) { + if (!confirm('Are you sure you want to cancel this pipeline?')) { + event.preventDefault(); + event.stopPropagation(); + } + }, }, template: ` <td class="pipeline-actions hidden-xs"> diff --git a/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 b/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 index 0265c00a414..9d66d28cc62 100644 --- a/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 +++ b/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 @@ -23,7 +23,7 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s apiScope: 'all', pageInfo: {}, pagenum: 1, - count: { all: 0, running_or_pending: 0 }, + count: {}, pageRequest: false, }; }, diff --git a/app/assets/javascripts/vue_pipelines_index/stage.js.es6 b/app/assets/javascripts/vue_pipelines_index/stage.js.es6 index 8cc417a9966..67fdd729e41 100644 --- a/app/assets/javascripts/vue_pipelines_index/stage.js.es6 +++ b/app/assets/javascripts/vue_pipelines_index/stage.js.es6 @@ -23,6 +23,13 @@ required: true, }, }, + + updated() { + if (this.builds) { + this.stopDropdownClickPropagation(); + } + }, + methods: { fetchBuilds(e) { const areaExpanded = e.currentTarget.attributes['aria-expanded']; @@ -37,17 +44,19 @@ return flash; }); }, - keepGraph(e) { - const { target } = e; - - if (target.className.indexOf('js-ci-action-icon') >= 0) return null; - - if ( - target.parentElement && - (target.parentElement.className.indexOf('js-ci-action-icon') >= 0) - ) return null; - return e.stopPropagation(); + /** + * When the user right clicks or cmd/ctrl + click in the job name + * the dropdown should not be closed and the link should open in another tab, + * so we stop propagation of the click event inside the dropdown. + * + * Since this component is rendered multiple times per page we need to guarantee we only + * target the click event of this component. + */ + stopDropdownClickPropagation() { + $(this.$el.querySelectorAll('.js-builds-dropdown-list a.mini-pipeline-graph-dropdown-item')).on('click', (e) => { + e.stopPropagation(); + }); }, }, computed: { @@ -76,13 +85,13 @@ template: ` <div> <button - @click='fetchBuilds($event)' + @click="fetchBuilds($event)" :class="triggerButtonClass" - :title='stage.title' + :title="stage.title" data-placement="top" data-toggle="dropdown" type="button" - :aria-label='stage.title' + :aria-label="stage.title" > <span v-html="svg" aria-hidden="true"></span> <i class="fa fa-caret-down" aria-hidden="true"></i> @@ -90,7 +99,6 @@ <ul class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container"> <div class="arrow-up" aria-hidden="true"></div> <div - @click='keepGraph($event)' :class="dropdownClass" class="js-builds-dropdown-list scrollable-menu" v-html="buildsOrSpinner" diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 1dcd1f8a6fc..83a8eeaafde 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -2,7 +2,6 @@ * This is a manifest file that'll automatically include all the stylesheets available in this directory * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at * the top of the compiled file, but it's generally better to create a new file per style scope. - *= require jquery-ui/autocomplete *= require jquery.atwho *= require select2 *= require_self diff --git a/app/assets/stylesheets/framework/jquery.scss b/app/assets/stylesheets/framework/jquery.scss index d335fedefe2..300ba4f2de6 100644 --- a/app/assets/stylesheets/framework/jquery.scss +++ b/app/assets/stylesheets/framework/jquery.scss @@ -2,17 +2,6 @@ font-family: $regular_font; font-size: $font-size-base; - &.ui-autocomplete { - border-color: $jq-ui-border; - padding: 0; - margin-top: 2px; - z-index: 1001; - - .ui-menu-item a { - padding: 4px 10px; - } - } - .ui-state-default { border: 1px solid $white-light; background: $white-light; diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 040a7ce0c16..8978e284f55 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -29,16 +29,14 @@ } } -@media (min-width: $screen-sm-min) { - .content-wrapper { - padding-right: $gutter_collapsed_width; - } -} - .right-sidebar-collapsed { padding-right: 0; @media (min-width: $screen-sm-min) { + .content-wrapper { + padding-right: $gutter_collapsed_width; + } + .merge-request-tabs-holder.affix { right: $gutter_collapsed_width; } @@ -56,6 +54,12 @@ .right-sidebar-expanded { padding-right: 0; + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + .content-wrapper { + padding-right: $gutter_collapsed_width; + } + } + @media (min-width: $screen-md-min) { .content-wrapper { padding-right: $gutter_width; diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 8031c4467a4..aad1a8986b0 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -277,3 +277,41 @@ table.u2f-registrations { padding-left: 18px; } } + +.user-callout { + margin: 24px auto 0; + + .bordered-box { + border: 1px solid $border-color; + border-radius: $border-radius-default; + } + + .landing { + margin-bottom: $gl-padding; + + .close { + margin-right: 20px; + } + + .dismiss-icon { + float: right; + cursor: pointer; + color: $cycle-analytics-dismiss-icon-color; + } + + .svg-container { + text-align: center; + + svg { + width: 136px; + height: 136px; + } + } + } + + @media(max-width: $screen-xs-max) { + .inner-content { + padding-left: 30px; + } + } +} diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index b0f5d4a9933..d807e6263ee 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -83,6 +83,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :akismet_api_key, :akismet_enabled, :container_registry_token_expire_delay, + :default_artifacts_expire_in, :default_branch_protection, :default_group_visibility, :default_project_visibility, diff --git a/app/controllers/admin/system_info_controller.rb b/app/controllers/admin/system_info_controller.rb index 1330399a836..99039724521 100644 --- a/app/controllers/admin/system_info_controller.rb +++ b/app/controllers/admin/system_info_controller.rb @@ -3,7 +3,7 @@ class Admin::SystemInfoController < Admin::ApplicationController 'nobrowse', 'read-only', 'ro' - ] + ].freeze EXCLUDED_MOUNT_TYPES = [ 'autofs', @@ -27,7 +27,7 @@ class Admin::SystemInfoController < Admin::ApplicationController 'tmpfs', 'tracefs', 'vfat' - ] + ].freeze def show @cpus = Vmstat.cpu rescue nil diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 5e7af3bff0d..e42e48f87d2 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -181,7 +181,7 @@ class ApplicationController < ActionController::Base end def gitlab_ldap_access(&block) - Gitlab::LDAP::Access.open { |access| block.call(access) } + Gitlab::LDAP::Access.open { |access| yield(access) } end # JSON for infinite scroll via Pager object diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb index 88d180fcc2e..2fe03020d2d 100644 --- a/app/controllers/concerns/creates_commit.rb +++ b/app/controllers/concerns/creates_commit.rb @@ -101,13 +101,14 @@ module CreatesCommit # TODO: We should really clean this up def set_commit_variables - if can?(current_user, :push_code, @project) - # Edit file in this project - @mr_source_project = @project - else - # Merge request from fork to this project - @mr_source_project = current_user.fork_of(@project) - end + @mr_source_project = + if can?(current_user, :push_code, @project) + # Edit file in this project + @project + else + # Merge request from fork to this project + current_user.fork_of(@project) + end # Merge request to this project @mr_target_project = @project diff --git a/app/controllers/concerns/service_params.rb b/app/controllers/concerns/service_params.rb index d7f5a4e4682..e610ccaec96 100644 --- a/app/controllers/concerns/service_params.rb +++ b/app/controllers/concerns/service_params.rb @@ -59,10 +59,10 @@ module ServiceParams :user_key, :username, :webhook - ] + ].freeze # Parameters to ignore if no value is specified - FILTER_BLANK_PARAMS = [:password] + FILTER_BLANK_PARAMS = [:password].freeze def service_params dynamic_params = @service.event_channel_names + @service.event_names diff --git a/app/controllers/concerns/spammable_actions.rb b/app/controllers/concerns/spammable_actions.rb index da225d8f1c7..d0a692070d9 100644 --- a/app/controllers/concerns/spammable_actions.rb +++ b/app/controllers/concerns/spammable_actions.rb @@ -27,7 +27,7 @@ module SpammableActions render :verify else - fallback.call + yield end end diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb index c2e4d62b50b..3109439b2ff 100644 --- a/app/controllers/jwt_controller.rb +++ b/app/controllers/jwt_controller.rb @@ -5,7 +5,7 @@ class JwtController < ApplicationController SERVICES = { Auth::ContainerRegistryAuthenticationService::AUDIENCE => Auth::ContainerRegistryAuthenticationService, - } + }.freeze def auth service = SERVICES[params[:service]] @@ -39,7 +39,8 @@ class JwtController < ApplicationController message: "HTTP Basic: Access denied\n" \ "You have 2FA enabled, please use a personal access token for Git over HTTP.\n" \ "You can generate one at #{profile_personal_access_tokens_url}" } - ] }, status: 401 + ] + }, status: 401 end def render_unauthorized @@ -47,7 +48,8 @@ class JwtController < ApplicationController errors: [ { code: 'UNAUTHORIZED', message: 'HTTP Basic: Access denied' } - ] }, status: 401 + ] + }, status: 401 end def auth_params diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb index 18044ca78e2..26e7e93533e 100644 --- a/app/controllers/profiles/two_factor_auths_controller.rb +++ b/app/controllers/profiles/two_factor_auths_controller.rb @@ -80,7 +80,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController def build_qr_code uri = current_user.otp_provisioning_uri(account_string, issuer: issuer_host) - RQRCode::render_qrcode(uri, :svg, level: :m, unit: 3) + RQRCode.render_qrcode(uri, :svg, level: :m, unit: 3) end def account_string diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index 89d84809e3a..a01c0caa959 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -1,8 +1,9 @@ class Projects::BranchesController < Projects::ApplicationController include ActionView::Helpers::SanitizeHelper include SortingHelper + # Authorize - before_action :require_non_empty_project + before_action :require_non_empty_project, except: :create before_action :authorize_download_code! before_action :authorize_push_code!, only: [:new, :create, :destroy, :destroy_all_merged] @@ -32,6 +33,8 @@ class Projects::BranchesController < Projects::ApplicationController branch_name = sanitize(strip_tags(params[:branch_name])) branch_name = Addressable::URI.unescape(branch_name) + redirect_to_autodeploy = project.empty_repo? && project.deployment_services.present? + result = CreateBranchService.new(project, current_user). execute(branch_name, ref) @@ -42,8 +45,15 @@ class Projects::BranchesController < Projects::ApplicationController if result[:status] == :success @branch = result[:branch] - redirect_to namespace_project_tree_path(@project.namespace, @project, - @branch.name) + + if redirect_to_autodeploy + redirect_to( + url_to_autodeploy_setup(project, branch_name), + notice: view_context.autodeploy_flash_notice(branch_name)) + else + redirect_to namespace_project_tree_path(@project.namespace, @project, + @branch.name) + end else @error = result[:message] render action: 'new' @@ -76,7 +86,19 @@ class Projects::BranchesController < Projects::ApplicationController ref_escaped = sanitize(strip_tags(params[:ref])) Addressable::URI.unescape(ref_escaped) else - @project.default_branch + @project.default_branch || 'master' end end + + def url_to_autodeploy_setup(project, branch_name) + namespace_project_new_blob_path( + project.namespace, + project, + branch_name, + file_name: '.gitlab-ci.yml', + commit_message: 'Set up auto deploy', + target_branch: branch_name, + context: 'autodeploy' + ) + end end diff --git a/app/controllers/projects/git_http_client_controller.rb b/app/controllers/projects/git_http_client_controller.rb index 216c158e41e..9a1bf037a95 100644 --- a/app/controllers/projects/git_http_client_controller.rb +++ b/app/controllers/projects/git_http_client_controller.rb @@ -76,11 +76,12 @@ class Projects::GitHttpClientController < Projects::ApplicationController return @project if defined?(@project) project_id, _ = project_id_with_suffix - if project_id.blank? - @project = nil - else - @project = Project.find_by_full_path("#{params[:namespace_id]}/#{project_id}") - end + @project = + if project_id.blank? + nil + else + Project.find_by_full_path("#{params[:namespace_id]}/#{project_id}") + end end # This method returns two values so that we can parse diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 365c49a20d4..d122c7fdcb2 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -381,14 +381,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def merge_widget_refresh - if merge_request.merge_when_build_succeeds - @status = :merge_when_build_succeeds - else - # Only MRs that can be merged end in this action - # MR can be already picked up for merge / merged already or can be waiting for worker to be picked up - # in last case it does not have any special status. Possible error is handled inside widget js function - @status = :success - end + @status = + if merge_request.merge_when_build_succeeds + :merge_when_build_succeeds + else + # Only MRs that can be merged end in this action + # MR can be already picked up for merge / merged already or can be waiting for worker to be picked up + # in last case it does not have any special status. Possible error is handled inside widget js function + :success + end render 'merge' end diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 84451257b98..8657bc4dfdc 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -13,9 +13,15 @@ class Projects::PipelinesController < Projects::ApplicationController .page(params[:page]) .per(30) - @running_or_pending_count = PipelinesFinder + @running_count = PipelinesFinder .new(project).execute(scope: 'running').count + @pending_count = PipelinesFinder + .new(project).execute(scope: 'pending').count + + @finished_count = PipelinesFinder + .new(project).execute(scope: 'finished').count + @pipelines_count = PipelinesFinder .new(project).execute.count @@ -29,7 +35,9 @@ class Projects::PipelinesController < Projects::ApplicationController .represent(@pipelines), count: { all: @pipelines_count, - running_or_pending: @running_or_pending_count + running: @running_count, + pending: @pending_count, + finished: @finished_count, } } end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 93a180b9036..7d81c96262f 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -15,11 +15,12 @@ class SessionsController < Devise::SessionsController def new set_minimum_password_length - if Gitlab.config.ldap.enabled - @ldap_servers = Gitlab::LDAP::Config.servers - else - @ldap_servers = [] - end + @ldap_servers = + if Gitlab.config.ldap.enabled + Gitlab::LDAP::Config.servers + else + [] + end super end diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 2d26718873f..f3fd3da8b20 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -28,8 +28,9 @@ class SnippetsController < ApplicationController @snippets = SnippetsFinder.new.execute(current_user, { filter: :by_user, user: @user, - scope: params[:scope] }). - page(params[:page]) + scope: params[:scope] + }) + .page(params[:page]) render 'index' else diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 206c92fe82a..f49301e2631 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -19,7 +19,7 @@ # iids: integer[] # class IssuableFinder - NONE = '0' + NONE = '0'.freeze attr_accessor :current_user, :params diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb index 4bd8c83081a..6630c6384f2 100644 --- a/app/finders/notes_finder.rb +++ b/app/finders/notes_finder.rb @@ -28,11 +28,12 @@ class NotesFinder private def init_collection - if @params[:target_id] - @notes = on_target(@params[:target_type], @params[:target_id]) - else - @notes = notes_of_any_type - end + @notes = + if @params[:target_id] + on_target(@params[:target_type], @params[:target_id]) + else + notes_of_any_type + end end def notes_of_any_type diff --git a/app/finders/pipelines_finder.rb b/app/finders/pipelines_finder.rb index 32aea75486d..a9172f6767f 100644 --- a/app/finders/pipelines_finder.rb +++ b/app/finders/pipelines_finder.rb @@ -10,7 +10,11 @@ class PipelinesFinder scoped_pipelines = case scope when 'running' - pipelines.running_or_pending + pipelines.running + when 'pending' + pipelines.pending + when 'finished' + pipelines.finished when 'branches' from_ids(ids_for_ref(branches)) when 'tags' diff --git a/app/finders/todos_finder.rb b/app/finders/todos_finder.rb index a93a63bdb9b..b7f091f334d 100644 --- a/app/finders/todos_finder.rb +++ b/app/finders/todos_finder.rb @@ -13,7 +13,7 @@ # class TodosFinder - NONE = '0' + NONE = '0'.freeze attr_accessor :current_user, :params @@ -99,7 +99,7 @@ class TodosFinder end def type? - type.present? && ['Issue', 'MergeRequest'].include?(type) + type.present? && %w(Issue MergeRequest).include?(type) end def type diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 6db813d4a02..70419eb4bde 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -69,11 +69,12 @@ module ApplicationHelper end def avatar_icon(user_or_email = nil, size = nil, scale = 2) - if user_or_email.is_a?(User) - user = user_or_email - else - user = User.find_by_any_email(user_or_email.try(:downcase)) - end + user = + if user_or_email.is_a?(User) + user_or_email + else + User.find_by_any_email(user_or_email.try(:downcase)) + end if user user.avatar_url(size) || default_avatar diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 60485160495..4b025669f69 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -1,28 +1,15 @@ module ApplicationSettingsHelper - def gravatar_enabled? - current_application_settings.gravatar_enabled? - end - - def signup_enabled? - current_application_settings.signup_enabled? - end - - def signin_enabled? - current_application_settings.signin_enabled? - end + delegate :gravatar_enabled?, + :signup_enabled?, + :signin_enabled?, + :akismet_enabled?, + :koding_enabled?, + to: :current_application_settings def user_oauth_applications? current_application_settings.user_oauth_applications end - def askimet_enabled? - current_application_settings.akismet_enabled? - end - - def koding_enabled? - current_application_settings.koding_enabled? - end - def allowed_protocols_present? current_application_settings.enabled_git_access_protocol.present? end diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 311a70725ab..7f32c1b5300 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -153,16 +153,17 @@ module BlobHelper # Because we are opionated we set the cache headers ourselves. response.cache_control[:public] = @project.public? - if @ref && @commit && @ref == @commit.id - # This is a link to a commit by its commit SHA. That means that the blob - # is immutable. The only reason to invalidate the cache is if the commit - # was deleted or if the user lost access to the repository. - response.cache_control[:max_age] = Blob::CACHE_TIME_IMMUTABLE - else - # A branch or tag points at this blob. That means that the expected blob - # value may change over time. - response.cache_control[:max_age] = Blob::CACHE_TIME - end + response.cache_control[:max_age] = + if @ref && @commit && @ref == @commit.id + # This is a link to a commit by its commit SHA. That means that the blob + # is immutable. The only reason to invalidate the cache is if the commit + # was deleted or if the user lost access to the repository. + Blob::CACHE_TIME_IMMUTABLE + else + # A branch or tag points at this blob. That means that the expected blob + # value may change over time. + Blob::CACHE_TIME + end response.etag = @blob.id !stale diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb index 4c7c16d694c..195094730aa 100644 --- a/app/helpers/button_helper.rb +++ b/app/helpers/button_helper.rb @@ -34,7 +34,7 @@ module ButtonHelper content_tag (append_link ? :a : :span), protocol, class: klass, - href: (project.http_url_to_repo if append_link), + href: (project.http_url_to_repo(current_user) if append_link), data: { html: true, placement: placement, diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb index a6d9e37ac76..f927cfc998f 100644 --- a/app/helpers/emails_helper.rb +++ b/app/helpers/emails_helper.rb @@ -24,7 +24,7 @@ module EmailsHelper def action_title(url) return unless url - ["merge_requests", "issues", "commit"].each do |action| + %w(merge_requests issues commit).each do |action| if url.split("/").include?(action) return "View #{action.humanize.singularize}" end diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 03354c235eb..860a665ae26 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -23,7 +23,7 @@ module IssuablesHelper def issuable_json_path(issuable) project = issuable.project - if issuable.kind_of?(MergeRequest) + if issuable.is_a?(MergeRequest) namespace_project_merge_request_path(project.namespace, project, issuable.iid, :json) else namespace_project_issue_path(project.namespace, project, issuable.iid, :json) @@ -52,7 +52,7 @@ module IssuablesHelper field_name: 'issuable_template', selected: selected_template(issuable), project_path: ref_project.path, - namespace_path: ref_project.namespace.path + namespace_path: ref_project.namespace.full_path } } @@ -198,7 +198,7 @@ module IssuablesHelper @counts[issuable_type][state] end - IRRELEVANT_PARAMS_FOR_CACHE_KEY = %i[utf8 sort page] + IRRELEVANT_PARAMS_FOR_CACHE_KEY = %i[utf8 sort page].freeze private_constant :IRRELEVANT_PARAMS_FOR_CACHE_KEY def issuables_state_counter_cache_key(issuable_type, state) diff --git a/app/helpers/javascript_helper.rb b/app/helpers/javascript_helper.rb index 320dd89c9d3..68c09c922a6 100644 --- a/app/helpers/javascript_helper.rb +++ b/app/helpers/javascript_helper.rb @@ -2,6 +2,7 @@ module JavascriptHelper def page_specific_javascript_tag(js) javascript_include_tag asset_path(js) end + def page_specific_javascript_bundle_tag(js) javascript_include_tag(*webpack_asset_paths(js)) end diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb index dc5ae8edbb2..2e3a15bc1b9 100644 --- a/app/helpers/namespaces_helper.rb +++ b/app/helpers/namespaces_helper.rb @@ -33,7 +33,7 @@ module NamespacesHelper end def namespace_icon(namespace, size = 40) - if namespace.kind_of?(Group) + if namespace.is_a?(Group) group_icon(namespace) else avatar_icon(namespace.owner.email, size) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index eb98204285d..4befeacc135 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -150,6 +150,15 @@ module ProjectsHelper ).html_safe end + def link_to_autodeploy_doc + link_to 'About auto deploy', help_page_path('ci/autodeploy/index'), target: '_blank' + end + + def autodeploy_flash_notice(branch_name) + "Branch <strong>#{truncate(sanitize(branch_name))}</strong> was created. To set up auto deploy, \ + choose a GitLab CI Yaml template and commit your changes. #{link_to_autodeploy_doc}".html_safe + end + private def repo_children_classes(field) @@ -232,7 +241,7 @@ module ProjectsHelper when 'ssh' project.ssh_url_to_repo else - project.http_url_to_repo + project.http_url_to_repo(current_user) end end diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index ff787fb4131..8ad3851fb9a 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -30,7 +30,7 @@ module SortingHelper } if current_controller?('admin/projects') - options.merge!(sort_value_largest_repo => sort_title_largest_repo) + options[sort_value_largest_repo] = sort_title_largest_repo end options diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb index 9a748aaaf33..fb95f2b565e 100644 --- a/app/helpers/submodule_helper.rb +++ b/app/helpers/submodule_helper.rb @@ -37,8 +37,8 @@ module SubmoduleHelper end def self_url?(url, namespace, project) - return true if url == [ Gitlab.config.gitlab.url, '/', namespace, '/', - project, '.git' ].join('') + return true if url == [Gitlab.config.gitlab.url, '/', namespace, '/', + project, '.git'].join('') url == gitlab_shell.url_to_repo([namespace, '/', project].join('')) end @@ -48,8 +48,8 @@ module SubmoduleHelper end def standard_links(host, namespace, project, commit) - base = [ 'https://', host, '/', namespace, '/', project ].join('') - [base, [ base, '/tree/', commit ].join('')] + base = ['https://', host, '/', namespace, '/', project].join('') + [base, [base, '/tree/', commit].join('')] end def relative_self_links(url, commit) diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb index 547f6258909..1a55ee05996 100644 --- a/app/helpers/tab_helper.rb +++ b/app/helpers/tab_helper.rb @@ -99,7 +99,7 @@ module TabHelper return 'active' end - if ['services', 'hooks', 'deploy_keys', 'protected_branches'].include? controller.controller_name + if %w(services hooks deploy_keys protected_branches).include? controller.controller_name "active" end end diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index c52afd6db1c..7f8efb0a4ac 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -150,6 +150,6 @@ module TodosHelper private def show_todo_state?(todo) - (todo.target.is_a?(MergeRequest) || todo.target.is_a?(Issue)) && ['closed', 'merged'].include?(todo.target.state) + (todo.target.is_a?(MergeRequest) || todo.target.is_a?(Issue)) && %w(closed merged).include?(todo.target.state) end end diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb index fc93acfe63e..169cedeb796 100644 --- a/app/helpers/visibility_level_helper.rb +++ b/app/helpers/visibility_level_helper.rb @@ -89,13 +89,9 @@ module VisibilityLevelHelper current_application_settings.restricted_visibility_levels || [] end - def default_project_visibility - current_application_settings.default_project_visibility - end - - def default_group_visibility - current_application_settings.default_group_visibility - end + delegate :default_project_visibility, + :default_group_visibility, + to: :current_application_settings def skip_level?(form_model, level) form_model.is_a?(Project) && !form_model.visibility_level_allowed?(level) diff --git a/app/mailers/repository_check_mailer.rb b/app/mailers/repository_check_mailer.rb index 21db2fe04a0..22a9f5da646 100644 --- a/app/mailers/repository_check_mailer.rb +++ b/app/mailers/repository_check_mailer.rb @@ -1,10 +1,11 @@ class RepositoryCheckMailer < BaseMailer def notify(failed_count) - if failed_count == 1 - @message = "One project failed its last repository check" - else - @message = "#{failed_count} projects failed their last repository check" - end + @message = + if failed_count == 1 + "One project failed its last repository check" + else + "#{failed_count} projects failed their last repository check" + end mail( to: User.admins.pluck(:email), diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 74b358d8c40..dc36c754438 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -5,7 +5,7 @@ class ApplicationSetting < ActiveRecord::Base add_authentication_token_field :runners_registration_token add_authentication_token_field :health_check_access_token - CACHE_KEY = 'application_setting.last' + CACHE_KEY = 'application_setting.last'.freeze DOMAIN_LIST_SEPARATOR = %r{\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace | # or \s # any whitespace character @@ -76,6 +76,12 @@ class ApplicationSetting < ActiveRecord::Base presence: true, numericality: { only_integer: true, greater_than: 0 } + validates :max_artifacts_size, + presence: true, + numericality: { only_integer: true, greater_than: 0 } + + validates :default_artifacts_expire_in, presence: true, duration: true + validates :container_registry_token_expire_delay, presence: true, numericality: { only_integer: true, greater_than: 0 } @@ -168,6 +174,7 @@ class ApplicationSetting < ActiveRecord::Base after_sign_up_text: nil, akismet_enabled: false, container_registry_token_expire_delay: 5, + default_artifacts_expire_in: '30 days', default_branch_protection: Settings.gitlab['default_branch_protection'], default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_projects_limit: Settings.gitlab['default_projects_limit'], @@ -201,9 +208,9 @@ class ApplicationSetting < ActiveRecord::Base sign_in_text: nil, signin_enabled: Settings.gitlab['signin_enabled'], signup_enabled: Settings.gitlab['signup_enabled'], + terminal_max_session_time: 0, two_factor_grace_period: 48, - user_default_external: false, - terminal_max_session_time: 0 + user_default_external: false } end @@ -215,6 +222,14 @@ class ApplicationSetting < ActiveRecord::Base create(defaults) end + def self.human_attribute_name(attr, _options = {}) + if attr == :default_artifacts_expire_in + 'Default artifacts expiration' + else + super + end + end + def home_page_url_column_exist ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url) end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index e018f8e7c4e..77aba91f65c 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -22,8 +22,10 @@ module Ci serialize :options serialize :yaml_variables, Gitlab::Serializer::Ci::Variables + delegate :name, to: :project, prefix: true + validates :coverage, numericality: true, allow_blank: true - validates_presence_of :ref + validates :ref, presence: true scope :unstarted, ->() { where(runner_id: nil) } scope :ignore_failures, ->() { where(allow_failure: false) } @@ -233,10 +235,6 @@ module Ci gl_project_id end - def project_name - project.name - end - def repo_url auth = "gitlab-ci-token:#{ensure_token!}@" project.http_url_to_repo.sub(/^https?:\/\//) do |prefix| @@ -257,7 +255,7 @@ module Ci return unless regex matches = text.scan(Regexp.new(regex)).last - matches = matches.last if matches.kind_of?(Array) + matches = matches.last if matches.is_a?(Array) coverage = matches.gsub(/\d+(\.\d+)?/).first if coverage.present? @@ -486,7 +484,7 @@ module Ci def artifacts_expire_in=(value) self.artifacts_expire_at = if value - Time.now + ChronicDuration.parse(value) + ChronicDuration.parse(value)&.seconds&.from_now end end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index dc4590a9923..80e11a5b58f 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -14,9 +14,11 @@ module Ci has_many :builds, foreign_key: :commit_id has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id - validates_presence_of :sha, unless: :importing? - validates_presence_of :ref, unless: :importing? - validates_presence_of :status, unless: :importing? + delegate :id, to: :project, prefix: true + + validates :sha, presence: { unless: :importing? } + validates :ref, presence: { unless: :importing? } + validates :status, presence: { unless: :importing? } validate :valid_commit_sha, unless: :importing? after_create :keep_around_commits, unless: :importing? @@ -93,8 +95,11 @@ module Ci .select("max(#{quoted_table_name}.id)") .group(:ref, :sha) - relation = ref ? where(ref: ref) : self - relation.where(id: max_id) + if ref + where(ref: ref, id: max_id.where(ref: ref)) + else + where(id: max_id) + end end def self.latest_status(ref = nil) @@ -150,10 +155,6 @@ module Ci builds.latest.with_artifacts_not_expired.includes(project: [:namespace]) end - def project_id - project.id - end - # For now the only user who participates is the user who triggered def participants(_current_user = nil) Array(user) diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 07a086b0aca..4863c34a6a6 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -4,8 +4,8 @@ module Ci RUNNER_QUEUE_EXPIRY_TIME = 60.minutes LAST_CONTACT_TIME = 1.hour.ago - AVAILABLE_SCOPES = %w[specific shared active paused online] - FORM_EDITABLE = %i[description tag_list active run_untagged locked] + AVAILABLE_SCOPES = %w[specific shared active paused online].freeze + FORM_EDITABLE = %i[description tag_list active run_untagged locked].freeze has_many :builds has_many :runner_projects, dependent: :destroy diff --git a/app/models/ci/runner_project.rb b/app/models/ci/runner_project.rb index 1f9baeca5b1..234376a7e4c 100644 --- a/app/models/ci/runner_project.rb +++ b/app/models/ci/runner_project.rb @@ -5,6 +5,6 @@ module Ci belongs_to :runner belongs_to :project, foreign_key: :gl_project_id - validates_uniqueness_of :runner_id, scope: :gl_project_id + validates :runner_id, uniqueness: { scope: :gl_project_id } end end diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb index 62889fe80d8..39a1dd86241 100644 --- a/app/models/ci/trigger.rb +++ b/app/models/ci/trigger.rb @@ -7,8 +7,8 @@ module Ci belongs_to :project, foreign_key: :gl_project_id has_many :trigger_requests, dependent: :destroy - validates_presence_of :token - validates_uniqueness_of :token + validates :token, presence: true + validates :token, uniqueness: true before_validation :set_default_values diff --git a/app/models/commit.rb b/app/models/commit.rb index 46f06733da1..0a18986ef26 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -22,12 +22,12 @@ class Commit DIFF_HARD_LIMIT_LINES = 50000 # The SHA can be between 7 and 40 hex characters. - COMMIT_SHA_PATTERN = '\h{7,40}' + COMMIT_SHA_PATTERN = '\h{7,40}'.freeze class << self def decorate(commits, project) commits.map do |commit| - if commit.kind_of?(Commit) + if commit.is_a?(Commit) commit else self.new(commit, project) @@ -105,7 +105,7 @@ class Commit end def diff_line_count - @diff_line_count ||= Commit::diff_line_count(raw_diffs) + @diff_line_count ||= Commit.diff_line_count(raw_diffs) @diff_line_count end @@ -122,11 +122,12 @@ class Commit def full_title return @full_title if @full_title - if safe_message.blank? - @full_title = no_commit_message - else - @full_title = safe_message.split("\n", 2).first - end + @full_title = + if safe_message.blank? + no_commit_message + else + safe_message.split("\n", 2).first + end end # Returns the commits description diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 99a6326309d..fc750a3e5e9 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -10,10 +10,11 @@ class CommitStatus < ActiveRecord::Base belongs_to :user delegate :commit, to: :pipeline + delegate :sha, :short_sha, to: :pipeline validates :pipeline, presence: true, unless: :importing? - validates_presence_of :name + validates :name, presence: true alias_attribute :author, :user @@ -102,8 +103,6 @@ class CommitStatus < ActiveRecord::Base end end - delegate :sha, :short_sha, to: :pipeline - def before_sha pipeline.before_sha || Gitlab::Git::BLANK_SHA end diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb index a600f9c14c5..8ea95beed79 100644 --- a/app/models/concerns/cache_markdown_field.rb +++ b/app/models/concerns/cache_markdown_field.rb @@ -11,14 +11,15 @@ module CacheMarkdownField # Knows about the relationship between markdown and html field names, and # stores the rendering contexts for the latter class FieldData - extend Forwardable - def initialize @data = {} end - def_delegators :@data, :[], :[]= - def_delegator :@data, :keys, :markdown_fields + delegate :[], :[]=, to: :@data + + def markdown_fields + @data.keys + end def html_field(markdown_field) "#{markdown_field}_html" @@ -45,7 +46,7 @@ module CacheMarkdownField Project Release Snippet - ] + ].freeze def self.caching_classes CACHING_CLASSES.map(&:constantize) diff --git a/app/models/concerns/case_sensitivity.rb b/app/models/concerns/case_sensitivity.rb index fe0cea8465f..034e9f40ff0 100644 --- a/app/models/concerns/case_sensitivity.rb +++ b/app/models/concerns/case_sensitivity.rb @@ -13,11 +13,12 @@ module CaseSensitivity params.each do |key, value| column = ActiveRecord::Base.connection.quote_table_name(key) - if cast_lower - condition = "LOWER(#{column}) = LOWER(:value)" - else - condition = "#{column} = :value" - end + condition = + if cast_lower + "LOWER(#{column}) = LOWER(:value)" + else + "#{column} = :value" + end criteria = criteria.where(condition, value: value) end diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb index 431c0354969..aea359e70bb 100644 --- a/app/models/concerns/has_status.rb +++ b/app/models/concerns/has_status.rb @@ -1,12 +1,12 @@ module HasStatus extend ActiveSupport::Concern - DEFAULT_STATUS = 'created' - AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped] - STARTED_STATUSES = %w[running success failed skipped] - ACTIVE_STATUSES = %w[pending running] - COMPLETED_STATUSES = %w[success failed canceled skipped] - ORDERED_STATUSES = %w[failed pending running canceled success skipped] + DEFAULT_STATUS = 'created'.freeze + AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped].freeze + STARTED_STATUSES = %w[running success failed skipped].freeze + ACTIVE_STATUSES = %w[pending running].freeze + COMPLETED_STATUSES = %w[success failed canceled skipped].freeze + ORDERED_STATUSES = %w[failed pending running canceled success skipped].freeze class_methods do def status_sql diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index c9c6bd24d75..37c727b5d9f 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -46,6 +46,17 @@ module Issuable has_one :metrics + delegate :name, + :email, + to: :author, + prefix: true + + delegate :name, + :email, + to: :assignee, + allow_nil: true, + prefix: true + validates :author, presence: true validates :title, presence: true, length: { maximum: 255 } @@ -68,21 +79,10 @@ module Issuable scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) } scope :join_project, -> { joins(:project) } - scope :inc_notes_with_associations, -> { includes(notes: [ :project, :author, :award_emoji ]) } + scope :inc_notes_with_associations, -> { includes(notes: [:project, :author, :award_emoji]) } scope :references_project, -> { references(:project) } scope :non_archived, -> { join_project.where(projects: { archived: false }) } - delegate :name, - :email, - to: :author, - prefix: true - - delegate :name, - :email, - to: :assignee, - allow_nil: true, - prefix: true - attr_mentionable :title, pipeline: :single_line attr_mentionable :description @@ -182,7 +182,7 @@ module Issuable def grouping_columns(sort) grouping_columns = [arel_table[:id]] - if ["milestone_due_desc", "milestone_due_asc"].include?(sort) + if %w(milestone_due_desc milestone_due_asc).include?(sort) milestone_table = Milestone.arel_table grouping_columns << milestone_table[:id] grouping_columns << milestone_table[:due_date] @@ -235,7 +235,7 @@ module Issuable # DEPRECATED repository: project.hook_attrs.slice(:name, :url, :description, :homepage) } - hook_data.merge!(assignee: assignee.hook_attrs) if assignee + hook_data[:assignee] = assignee.hook_attrs if assignee hook_data end diff --git a/app/models/concerns/reactive_service.rb b/app/models/concerns/reactive_service.rb index e1f868a299b..713246039c1 100644 --- a/app/models/concerns/reactive_service.rb +++ b/app/models/concerns/reactive_service.rb @@ -5,6 +5,6 @@ module ReactiveService include ReactiveCaching # Default cache key: class name + project_id - self.reactive_cache_key = ->(service) { [ service.class.model_name.singular, service.project_id ] } + self.reactive_cache_key = ->(service) { [service.class.model_name.singular, service.project_id] } end end diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb index 7edb0acd56c..b9a2d812edd 100644 --- a/app/models/concerns/sortable.rb +++ b/app/models/concerns/sortable.rb @@ -46,11 +46,12 @@ module Sortable where("label_links.target_id = #{target_column}"). reorder(nil) - if target_type_column - query = query.where("label_links.target_type = #{target_type_column}") - else - query = query.where(label_links: { target_type: target_type }) - end + query = + if target_type_column + query.where("label_links.target_type = #{target_type_column}") + else + query.where(label_links: { target_type: target_type }) + end query = query.where.not(title: excluded_labels) if excluded_labels.present? diff --git a/app/models/concerns/uniquify.rb b/app/models/concerns/uniquify.rb new file mode 100644 index 00000000000..a7fe5951b6e --- /dev/null +++ b/app/models/concerns/uniquify.rb @@ -0,0 +1,30 @@ +class Uniquify + # Return a version of the given 'base' string that is unique + # by appending a counter to it. Uniqueness is determined by + # repeated calls to the passed block. + # + # If `base` is a function/proc, we expect that calling it with a + # candidate counter returns a string to test/return. + def string(base) + @base = base + @counter = nil + + increment_counter! while yield(base_string) + base_string + end + + private + + def base_string + if @base.respond_to?(:call) + @base.call(@counter) + else + "#{@base}#{@counter}" + end + end + + def increment_counter! + @counter ||= 0 + @counter += 1 + end +end diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb index 559b3075905..895a91139c9 100644 --- a/app/models/diff_note.rb +++ b/app/models/diff_note.rb @@ -8,7 +8,7 @@ class DiffNote < Note validates :position, presence: true validates :diff_line, presence: true validates :line_code, presence: true, line_code: true - validates :noteable_type, inclusion: { in: ['Commit', 'MergeRequest'] } + validates :noteable_type, inclusion: { in: %w(Commit MergeRequest) } validates :resolved_by, presence: true, if: :resolved? validate :positions_complete validate :verify_supported diff --git a/app/models/event.rb b/app/models/event.rb index e5027df3f8a..d7ca8e3c599 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -36,7 +36,7 @@ class Event < ActiveRecord::Base scope :code_push, -> { where(action: PUSHED) } scope :in_projects, ->(projects) do - where(project_id: projects).recent + where(project_id: projects.pluck(:id)).recent end scope :with_associations, -> { includes(:author, :project, project: :namespace).preload(:target) } @@ -47,7 +47,7 @@ class Event < ActiveRecord::Base def contributions where("action = ? OR (target_type IN (?) AND action IN (?)) OR (target_type = ? AND action = ?)", Event::PUSHED, - ["MergeRequest", "Issue"], [Event::CREATED, Event::CLOSED, Event::MERGED], + %w(MergeRequest Issue), [Event::CREATED, Event::CLOSED, Event::MERGED], "Note", Event::COMMENTED) end diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb index 26712c19b5a..b973bbcd8da 100644 --- a/app/models/external_issue.rb +++ b/app/models/external_issue.rb @@ -43,7 +43,7 @@ class ExternalIssue end def reference_link_text(from_project = nil) - return "##{id}" if /^\d+$/.match(id) + return "##{id}" if id =~ /^\d+$/ id end diff --git a/app/models/issue.rb b/app/models/issue.rb index d8826b65fcc..de90f19f854 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -15,8 +15,6 @@ class Issue < ActiveRecord::Base DueThisWeek = DueDateStruct.new('Due This Week', 'week').freeze DueThisMonth = DueDateStruct.new('Due This Month', 'month').freeze - ActsAsTaggableOn.strict_case_match = true - belongs_to :project belongs_to :moved_to, class_name: 'Issue' diff --git a/app/models/label.rb b/app/models/label.rb index 5b6b9a7a736..f68a8c9cff2 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -11,7 +11,7 @@ class Label < ActiveRecord::Base cache_markdown_field :description, pipeline: :single_line - DEFAULT_COLOR = '#428BCA' + DEFAULT_COLOR = '#428BCA'.freeze default_value_for :color, DEFAULT_COLOR diff --git a/app/models/member.rb b/app/models/member.rb index d07f270b757..0545bd4eedf 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -10,6 +10,8 @@ class Member < ActiveRecord::Base belongs_to :user belongs_to :source, polymorphic: true + delegate :name, :username, :email, to: :user, prefix: true + validates :user, presence: true, unless: :invite? validates :source, presence: true validates :user_id, uniqueness: { scope: [:source_type, :source_id], @@ -73,8 +75,6 @@ class Member < ActiveRecord::Base after_destroy :post_destroy_hook, unless: :pending? after_commit :refresh_member_authorized_projects - delegate :name, :username, :email, to: :user, prefix: true - default_value_for :notification_level, NotificationSetting.levels[:global] class << self diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb index 204f34f0269..446f9f8f8a7 100644 --- a/app/models/members/group_member.rb +++ b/app/models/members/group_member.rb @@ -1,11 +1,11 @@ class GroupMember < Member - SOURCE_TYPE = 'Namespace' + SOURCE_TYPE = 'Namespace'.freeze belongs_to :group, foreign_key: 'source_id' # Make sure group member points only to group as it source default_value_for :source_type, SOURCE_TYPE - validates_format_of :source_type, with: /\ANamespace\z/ + validates :source_type, format: { with: /\ANamespace\z/ } default_scope { where(source_type: SOURCE_TYPE) } def self.access_level_roles diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb index 008fff0857c..912820b51ac 100644 --- a/app/models/members/project_member.rb +++ b/app/models/members/project_member.rb @@ -1,5 +1,5 @@ class ProjectMember < Member - SOURCE_TYPE = 'Project' + SOURCE_TYPE = 'Project'.freeze include Gitlab::ShellAdapter @@ -7,7 +7,7 @@ class ProjectMember < Member # Make sure project member points only to project as it source default_value_for :source_type, SOURCE_TYPE - validates_format_of :source_type, with: /\AProject\z/ + validates :source_type, format: { with: /\AProject\z/ } validates :access_level, inclusion: { in: Gitlab::Access.values } default_scope { where(source_type: SOURCE_TYPE) } diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 204d2b153ad..7eb875f1ef5 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -203,7 +203,11 @@ class MergeRequest < ActiveRecord::Base end def diff_size - opts = diff_options || {} + # The `#diffs` method ends up at an instance of a class inheriting from + # `Gitlab::Diff::FileCollection::Base`, so use those options as defaults + # here too, to get the same diff size without performing highlighting. + # + opts = Gitlab::Diff::FileCollection::Base.default_options.merge(diff_options || {}) raw_diffs(opts).size end @@ -527,7 +531,7 @@ class MergeRequest < ActiveRecord::Base } if diff_head_commit - attrs.merge!(last_commit: diff_head_commit.hook_attrs) + attrs[:last_commit] = diff_head_commit.hook_attrs end attributes.merge!(attrs) diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index 70bad2a4396..baee00b8fcd 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -7,7 +7,7 @@ class MergeRequestDiff < ActiveRecord::Base COMMITS_SAFE_SIZE = 100 # Valid types of serialized diffs allowed by Gitlab::Git::Diff - VALID_CLASSES = [Hash, Rugged::Patch, Rugged::Diff::Delta] + VALID_CLASSES = [Hash, Rugged::Patch, Rugged::Diff::Delta].freeze belongs_to :merge_request diff --git a/app/models/namespace.rb b/app/models/namespace.rb index bd0336c984a..3137dd32f93 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -98,14 +98,8 @@ class Namespace < ActiveRecord::Base # Work around that by setting their username to "blank", followed by a counter. path = "blank" if path.blank? - counter = 0 - base = path - while Namespace.find_by_path_or_name(path) - counter += 1 - path = "#{base}#{counter}" - end - - path + uniquify = Uniquify.new + uniquify.string(path) { |s| Namespace.find_by_path_or_name(s) } end end diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb index b524ca50ee8..0bbc9451ffd 100644 --- a/app/models/network/graph.rb +++ b/app/models/network/graph.rb @@ -188,11 +188,12 @@ module Network end # and mark it as reserved - if parent_time.nil? - min_time = leaves.first.time - else - min_time = parent_time + 1 - end + min_time = + if parent_time.nil? + leaves.first.time + else + parent_time + 1 + end max_time = leaves.last.time leaves.last.parents(@map).each do |parent| diff --git a/app/models/note.rb b/app/models/note.rb index 029fe667a45..d6d5396afa5 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -72,7 +72,7 @@ class Note < ActiveRecord::Base scope :inc_author, ->{ includes(:author) } scope :inc_relations_for_view, ->{ includes(:project, :author, :updated_by, :resolved_by, :award_emoji) } - scope :diff_notes, ->{ where(type: ['LegacyDiffNote', 'DiffNote']) } + scope :diff_notes, ->{ where(type: %w(LegacyDiffNote DiffNote)) } scope :non_diff_notes, ->{ where(type: ['Note', nil]) } scope :with_associations, -> do diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb index 58f6214bea7..52577bd52ea 100644 --- a/app/models/notification_setting.rb +++ b/app/models/notification_setting.rb @@ -35,11 +35,11 @@ class NotificationSetting < ActiveRecord::Base :merge_merge_request, :failed_pipeline, :success_pipeline - ] + ].freeze EXCLUDED_WATCHER_EVENTS = [ :success_pipeline - ] + ].freeze store :events, accessors: EMAIL_EVENTS, coder: JSON diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb index 0b9ebf1ffe2..f2f2fc1e32a 100644 --- a/app/models/pages_domain.rb +++ b/app/models/pages_domain.rb @@ -2,7 +2,7 @@ class PagesDomain < ActiveRecord::Base belongs_to :project validates :domain, hostname: true - validates_uniqueness_of :domain, case_sensitive: false + validates :domain, uniqueness: { case_sensitive: false } validates :certificate, certificate: true, allow_nil: true, allow_blank: true validates :key, certificate_key: true, allow_nil: true, allow_blank: true diff --git a/app/models/project.rb b/app/models/project.rb index 411299eef63..e06fc30dc8a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -22,7 +22,7 @@ class Project < ActiveRecord::Base class BoardLimitExceeded < StandardError; end NUMBER_OF_PERMITTED_BOARDS = 1 - UNKNOWN_IMPORT_URL = 'http://unknown.git' + UNKNOWN_IMPORT_URL = 'http://unknown.git'.freeze cache_markdown_field :description, pipeline: :description @@ -70,8 +70,7 @@ class Project < ActiveRecord::Base after_validation :check_pending_delete - ActsAsTaggableOn.strict_case_match = true - acts_as_taggable_on :tags + acts_as_taggable attr_accessor :new_default_branch attr_accessor :old_path_with_namespace @@ -172,9 +171,11 @@ class Project < ActiveRecord::Base accepts_nested_attributes_for :project_feature delegate :name, to: :owner, allow_nil: true, prefix: true + delegate :count, to: :forks, prefix: true delegate :members, to: :team, prefix: true delegate :add_user, to: :team delegate :add_guest, :add_reporter, :add_developer, :add_master, to: :team + delegate :empty_repo?, to: :repository # Validations validates :creator, presence: true, on: :create @@ -191,8 +192,8 @@ class Project < ActiveRecord::Base format: { with: Gitlab::Regex.project_path_regex, message: Gitlab::Regex.project_path_regex_message } validates :namespace, presence: true - validates_uniqueness_of :name, scope: :namespace_id - validates_uniqueness_of :path, scope: :namespace_id + validates :name, uniqueness: { scope: :namespace_id } + validates :path, uniqueness: { scope: :namespace_id } validates :import_url, addressable_url: true, if: :external_import? validates :star_count, numericality: { greater_than_or_equal_to: 0 } validate :check_limit, on: :create @@ -358,7 +359,7 @@ class Project < ActiveRecord::Base end def reference_pattern - name_pattern = Gitlab::Regex::NAMESPACE_REGEX_STR + name_pattern = Gitlab::Regex::FULL_NAMESPACE_REGEX_STR %r{ ((?<namespace>#{name_pattern})\/)? @@ -453,13 +454,14 @@ class Project < ActiveRecord::Base end def add_import_job - if forked? - job_id = RepositoryForkWorker.perform_async(id, forked_from_project.repository_storage_path, - forked_from_project.path_with_namespace, - self.namespace.full_path) - else - job_id = RepositoryImportWorker.perform_async(self.id) - end + job_id = + if forked? + RepositoryForkWorker.perform_async(id, forked_from_project.repository_storage_path, + forked_from_project.path_with_namespace, + self.namespace.full_path) + else + RepositoryImportWorker.perform_async(self.id) + end if job_id Rails.logger.info "Import job started for #{path_with_namespace} with job ID #{job_id}" @@ -837,10 +839,6 @@ class Project < ActiveRecord::Base false end - def empty_repo? - repository.empty_repo? - end - def repo repository.raw end @@ -849,10 +847,6 @@ class Project < ActiveRecord::Base gitlab_shell.url_to_repo(path_with_namespace) end - def namespace_dir - namespace.try(:path) || '' - end - def repo_exists? @repo_exists ||= repository.exists? rescue @@ -875,8 +869,14 @@ class Project < ActiveRecord::Base url_to_repo end - def http_url_to_repo - "#{web_url}.git" + def http_url_to_repo(user = nil) + url = web_url + + if user + url.sub!(%r{\Ahttps?://}) { |protocol| "#{protocol}#{user.username}@" } + end + + "#{url}.git" end # Check if current branch name is marked as protected in the system @@ -901,8 +901,8 @@ class Project < ActiveRecord::Base def rename_repo path_was = previous_changes['path'].first - old_path_with_namespace = File.join(namespace_dir, path_was) - new_path_with_namespace = File.join(namespace_dir, path) + old_path_with_namespace = File.join(namespace.full_path, path_was) + new_path_with_namespace = File.join(namespace.full_path, path) Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}" @@ -1028,10 +1028,6 @@ class Project < ActiveRecord::Base forked? && project == forked_from_project end - def forks_count - forks.count - end - def origin_merge_requests merge_requests.where(source_project_id: self.id) end diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb index 03194fc2141..e3ef4919b28 100644 --- a/app/models/project_feature.rb +++ b/app/models/project_feature.rb @@ -18,7 +18,7 @@ class ProjectFeature < ActiveRecord::Base PRIVATE = 10 ENABLED = 20 - FEATURES = %i(issues merge_requests wiki snippets builds repository) + FEATURES = %i(issues merge_requests wiki snippets builds repository).freeze class << self def access_level_attribute(feature) diff --git a/app/models/project_services/buildkite_service.rb b/app/models/project_services/buildkite_service.rb index 0956c4a4ede..5fb95050b83 100644 --- a/app/models/project_services/buildkite_service.rb +++ b/app/models/project_services/buildkite_service.rb @@ -3,7 +3,7 @@ require "addressable/uri" class BuildkiteService < CiService include ReactiveService - ENDPOINT = "https://buildkite.com" + ENDPOINT = "https://buildkite.com".freeze prop_accessor :project_url, :token boolean_accessor :enable_ssl_verification diff --git a/app/models/project_services/chat_message/issue_message.rb b/app/models/project_services/chat_message/issue_message.rb index b96aca47e65..791e5b0cec7 100644 --- a/app/models/project_services/chat_message/issue_message.rb +++ b/app/models/project_services/chat_message/issue_message.rb @@ -51,7 +51,8 @@ module ChatMessage title: issue_title, title_link: issue_url, text: format(description), - color: "#C95823" }] + color: "#C95823" + }] end def project_link diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index 1ad9efac196..2717c240f05 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -39,7 +39,7 @@ class DroneCiService < CiService def commit_status_path(sha, ref) url = [drone_url, "gitlab/#{project.full_path}/commits/#{sha}", - "?branch=#{URI::encode(ref.to_s)}&access_token=#{token}"] + "?branch=#{URI.encode(ref.to_s)}&access_token=#{token}"] URI.join(*url).to_s end @@ -74,7 +74,7 @@ class DroneCiService < CiService def build_page(sha, ref) url = [drone_url, "gitlab/#{project.full_path}/redirect/commits/#{sha}", - "?branch=#{URI::encode(ref.to_s)}"] + "?branch=#{URI.encode(ref.to_s)}"] URI.join(*url).to_s end @@ -114,7 +114,7 @@ class DroneCiService < CiService end def merge_request_valid?(data) - ['opened', 'reopened'].include?(data[:object_attributes][:state]) && + %w(opened reopened).include?(data[:object_attributes][:state]) && data[:object_attributes][:merge_status] == 'unchecked' end end diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 72da219df28..c4142c38b2f 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -6,7 +6,7 @@ class HipchatService < Service a b i strong em br img pre code table th tr td caption colgroup col thead tbody tfoot ul ol li dl dt dd - ] + ].freeze prop_accessor :token, :room, :server, :color, :api_version boolean_accessor :notify_only_broken_builds, :notify @@ -36,7 +36,7 @@ class HipchatService < Service { type: 'text', name: 'token', placeholder: 'Room token' }, { type: 'text', name: 'room', placeholder: 'Room name or ID' }, { type: 'checkbox', name: 'notify' }, - { type: 'select', name: 'color', choices: ['yellow', 'red', 'green', 'purple', 'gray', 'random'] }, + { type: 'select', name: 'color', choices: %w(yellow red green purple gray random) }, { type: 'text', name: 'api_version', placeholder: 'Leave blank for default (v2)' }, { type: 'text', name: 'server', diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb index 5d6862d9faa..c62bb4fa120 100644 --- a/app/models/project_services/irker_service.rb +++ b/app/models/project_services/irker_service.rb @@ -33,7 +33,8 @@ class IrkerService < Service end def settings - { server_host: server_host.present? ? server_host : 'localhost', + { + server_host: server_host.present? ? server_host : 'localhost', server_port: server_port.present? ? server_port : 6659 } end diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb index f2f019c43bb..9819e723fe8 100644 --- a/app/models/project_services/kubernetes_service.rb +++ b/app/models/project_services/kubernetes_service.rb @@ -3,7 +3,7 @@ class KubernetesService < DeploymentService include Gitlab::Kubernetes include ReactiveCaching - self.reactive_cache_key = ->(service) { [ service.class.model_name.singular, service.project_id ] } + self.reactive_cache_key = ->(service) { [service.class.model_name.singular, service.project_id] } # Namespace defaults to the project path, but can be overridden in case that # is an invalid or inappropriate name @@ -62,23 +62,19 @@ class KubernetesService < DeploymentService { type: 'text', name: 'namespace', title: 'Kubernetes namespace', - placeholder: 'Kubernetes namespace', - }, + placeholder: 'Kubernetes namespace' }, { type: 'text', name: 'api_url', title: 'API URL', - placeholder: 'Kubernetes API URL, like https://kube.example.com/', - }, + placeholder: 'Kubernetes API URL, like https://kube.example.com/' }, { type: 'text', name: 'token', title: 'Service token', - placeholder: 'Service token', - }, + placeholder: 'Service token' }, { type: 'textarea', name: 'ca_pem', title: 'Custom CA bundle', - placeholder: 'Certificate Authority bundle (PEM format)', - }, + placeholder: 'Certificate Authority bundle (PEM format)' }, ] end @@ -167,7 +163,7 @@ class KubernetesService < DeploymentService url = URI.parse(api_url) prefix = url.path.sub(%r{/+\z}, '') - url.path = [ prefix, *parts ].join("/") + url.path = [prefix, *parts].join("/") url.to_s end diff --git a/app/models/project_services/mattermost_service.rb b/app/models/project_services/mattermost_service.rb index 4ebc5318da1..c13538e9fea 100644 --- a/app/models/project_services/mattermost_service.rb +++ b/app/models/project_services/mattermost_service.rb @@ -15,10 +15,10 @@ class MattermostService < ChatNotificationService 'This service sends notifications about projects events to Mattermost channels.<br /> To set up this service: <ol> - <li><a href="https://docs.mattermost.com/developer/webhooks-incoming.html#enabling-incoming-webhooks">Enable incoming webhooks</a> in your Mattermost installation. </li> - <li><a href="https://docs.mattermost.com/developer/webhooks-incoming.html#creating-integrations-using-incoming-webhooks">Add an incoming webhook</a> in your Mattermost team. The default channel can be overridden for each event. </li> - <li>Paste the webhook <strong>URL</strong> into the field bellow. </li> - <li>Select events below to enable notifications. The channel and username are optional. </li> + <li><a href="https://docs.mattermost.com/developer/webhooks-incoming.html#enabling-incoming-webhooks">Enable incoming webhooks</a> in your Mattermost installation.</li> + <li><a href="https://docs.mattermost.com/developer/webhooks-incoming.html#creating-integrations-using-incoming-webhooks">Add an incoming webhook</a> in your Mattermost team. The default channel can be overridden for each event.</li> + <li>Paste the webhook <strong>URL</strong> into the field below.</li> + <li>Select events below to enable notifications. The <strong>Channel handle</strong> and <strong>Username</strong> fields are optional.</li> </ol>' end @@ -28,14 +28,14 @@ class MattermostService < ChatNotificationService def default_fields [ - { type: 'text', name: 'webhook', placeholder: 'http://mattermost_host/hooks/...' }, - { type: 'text', name: 'username', placeholder: 'username' }, + { type: 'text', name: 'webhook', placeholder: 'e.g. http://mattermost_host/hooks/…' }, + { type: 'text', name: 'username', placeholder: 'e.g. GitLab' }, { type: 'checkbox', name: 'notify_only_broken_builds' }, { type: 'checkbox', name: 'notify_only_broken_pipelines' }, ] end def default_channel_placeholder - "town-square" + "Channel handle (e.g. town-square)" end end diff --git a/app/models/project_services/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb index 9cc642591f4..d86f4f6f448 100644 --- a/app/models/project_services/pivotaltracker_service.rb +++ b/app/models/project_services/pivotaltracker_service.rb @@ -1,7 +1,7 @@ class PivotaltrackerService < Service include HTTParty - API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits' + API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits'.freeze prop_accessor :token, :restrict_to_branch validates :token, presence: true, if: :activated? diff --git a/app/models/project_services/pushover_service.rb b/app/models/project_services/pushover_service.rb index a963d27a376..3e618a8dbf1 100644 --- a/app/models/project_services/pushover_service.rb +++ b/app/models/project_services/pushover_service.rb @@ -29,25 +29,24 @@ class PushoverService < Service ['Normal Priority', 0], ['High Priority', 1] ], - default_choice: 0 - }, + default_choice: 0 }, { type: 'select', name: 'sound', choices: [ ['Device default sound', nil], ['Pushover (default)', 'pushover'], - ['Bike', 'bike'], - ['Bugle', 'bugle'], + %w(Bike bike), + %w(Bugle bugle), ['Cash Register', 'cashregister'], - ['Classical', 'classical'], - ['Cosmic', 'cosmic'], - ['Falling', 'falling'], - ['Gamelan', 'gamelan'], - ['Incoming', 'incoming'], - ['Intermission', 'intermission'], - ['Magic', 'magic'], - ['Mechanical', 'mechanical'], + %w(Classical classical), + %w(Cosmic cosmic), + %w(Falling falling), + %w(Gamelan gamelan), + %w(Incoming incoming), + %w(Intermission intermission), + %w(Magic magic), + %w(Mechanical mechanical), ['Piano Bar', 'pianobar'], - ['Siren', 'siren'], + %w(Siren siren), ['Space Alarm', 'spacealarm'], ['Tug Boat', 'tugboat'], ['Alien Alarm (long)', 'alien'], @@ -56,8 +55,7 @@ class PushoverService < Service ['Pushover Echo (long)', 'echo'], ['Up Down (long)', 'updown'], ['None (silent)', 'none'] - ] - }, + ] }, ] end @@ -72,13 +70,14 @@ class PushoverService < Service before = data[:before] after = data[:after] - if Gitlab::Git.blank_ref?(before) - message = "#{data[:user_name]} pushed new branch \"#{ref}\"." - elsif Gitlab::Git.blank_ref?(after) - message = "#{data[:user_name]} deleted branch \"#{ref}\"." - else - message = "#{data[:user_name]} push to branch \"#{ref}\"." - end + message = + if Gitlab::Git.blank_ref?(before) + "#{data[:user_name]} pushed new branch \"#{ref}\"." + elsif Gitlab::Git.blank_ref?(after) + "#{data[:user_name]} deleted branch \"#{ref}\"." + else + "#{data[:user_name]} push to branch \"#{ref}\"." + end if data[:total_commits_count] > 0 message << "\nTotal commits count: #{data[:total_commits_count]}" @@ -97,7 +96,7 @@ class PushoverService < Service # Sound parameter MUST NOT be sent to API if not selected if sound - pushover_data.merge!(sound: sound) + pushover_data[:sound] = sound end PushoverService.post('/messages.json', body: pushover_data) diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index f77d2d7c60b..da7496573ef 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -13,11 +13,11 @@ class SlackService < ChatNotificationService def help 'This service sends notifications about projects events to Slack channels.<br /> - To setup this service: + To set up this service: <ol> - <li><a href="https://slack.com/apps/A0F7XDUAZ-incoming-webhooks">Add an incoming webhook</a> in your Slack team. The default channel can be overridden for each event. </li> - <li>Paste the <strong>Webhook URL</strong> into the field below. </li> - <li>Select events below to enable notifications. The channel and username are optional. </li> + <li><a href="https://slack.com/apps/A0F7XDUAZ-incoming-webhooks">Add an incoming webhook</a> in your Slack team. The default channel can be overridden for each event.</li> + <li>Paste the <strong>Webhook URL</strong> into the field below.</li> + <li>Select events below to enable notifications. The <strong>Channel name</strong> and <strong>Username</strong> fields are optional.</li> </ol>' end @@ -27,14 +27,14 @@ class SlackService < ChatNotificationService def default_fields [ - { type: 'text', name: 'webhook', placeholder: 'https://hooks.slack.com/services/...' }, - { type: 'text', name: 'username', placeholder: 'username' }, + { type: 'text', name: 'webhook', placeholder: 'e.g. https://hooks.slack.com/services/…' }, + { type: 'text', name: 'username', placeholder: 'e.g. GitLab' }, { type: 'checkbox', name: 'notify_only_broken_builds' }, { type: 'checkbox', name: 'notify_only_broken_pipelines' }, ] end def default_channel_placeholder - "#general" + "Channel name (e.g. general)" end end diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb index 06abd406523..aeaf63abab9 100644 --- a/app/models/project_statistics.rb +++ b/app/models/project_statistics.rb @@ -4,7 +4,7 @@ class ProjectStatistics < ActiveRecord::Base before_save :update_storage_size - STORAGE_COLUMNS = [:repository_size, :lfs_objects_size, :build_artifacts_size] + STORAGE_COLUMNS = [:repository_size, :lfs_objects_size, :build_artifacts_size].freeze STATISTICS_COLUMNS = [:commit_count] + STORAGE_COLUMNS def total_repository_size diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index d0b991db112..9891f5edf41 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -5,7 +5,7 @@ class ProjectWiki 'Markdown' => :markdown, 'RDoc' => :rdoc, 'AsciiDoc' => :asciidoc - } unless defined?(MARKUPS) + }.freeze unless defined?(MARKUPS) class CouldNotCreateWikiError < StandardError; end @@ -19,6 +19,9 @@ class ProjectWiki @user = user end + delegate :empty?, to: :pages + delegate :repository_storage_path, to: :project + def path @project.path + '.wiki' end @@ -60,10 +63,6 @@ class ProjectWiki !!repository.exists? end - def empty? - pages.empty? - end - # Returns an Array of Gitlab WikiPage instances or an # empty Array if this Wiki has no pages. def pages @@ -160,10 +159,6 @@ class ProjectWiki } end - def repository_storage_path - project.repository_storage_path - end - private def init_repo(path_with_namespace) diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb index 6240912a6e1..39e979ef15b 100644 --- a/app/models/protected_branch.rb +++ b/app/models/protected_branch.rb @@ -8,8 +8,8 @@ class ProtectedBranch < ActiveRecord::Base has_many :merge_access_levels, dependent: :destroy has_many :push_access_levels, dependent: :destroy - validates_length_of :merge_access_levels, is: 1, message: "are restricted to a single instance per protected branch." - validates_length_of :push_access_levels, is: 1, message: "are restricted to a single instance per protected branch." + validates :merge_access_levels, length: { is: 1, message: "are restricted to a single instance per protected branch." } + validates :push_access_levels, length: { is: 1, message: "are restricted to a single instance per protected branch." } accepts_nested_attributes_for :push_access_levels accepts_nested_attributes_for :merge_access_levels diff --git a/app/models/repository.rb b/app/models/repository.rb index 56c582cd9be..0dbf246c3a4 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -18,7 +18,7 @@ class Repository CACHED_METHODS = %i(size commit_count readme version contribution_guide changelog license_blob license_key gitignore koding_yml gitlab_ci_yml branch_names tag_names branch_count - tag_count avatar exists? empty? root_ref) + tag_count avatar exists? empty? root_ref).freeze # Certain method caches should be refreshed when certain types of files are # changed. This Hash maps file types (as returned by Gitlab::FileDetector) to @@ -33,7 +33,7 @@ class Repository koding: :koding_yml, gitlab_ci: :gitlab_ci_yml, avatar: :avatar - } + }.freeze # Wraps around the given method and caches its output in Redis and an instance # variable. @@ -109,9 +109,7 @@ class Repository offset: offset, after: after, before: before, - # --follow doesn't play well with --skip. See: - # https://gitlab.com/gitlab-org/gitlab-ce/issues/3574#note_3040520 - follow: false, + follow: path.present?, skip_merges: skip_merges } @@ -487,9 +485,7 @@ class Repository end cache_method :exists? - def empty? - raw_repository.empty? - end + delegate :empty?, to: :raw_repository cache_method :empty? # The size of this repository in megabytes. @@ -508,9 +504,7 @@ class Repository end cache_method :branch_names, fallback: [] - def tag_names - raw_repository.tag_names - end + delegate :tag_names, to: :raw_repository cache_method :tag_names, fallback: [] def branch_count @@ -750,136 +744,63 @@ class Repository @tags ||= raw_repository.tags end - # rubocop:disable Metrics/ParameterLists - def commit_dir( - user, path, - message:, branch_name:, - author_email: nil, author_name: nil, - start_branch_name: nil, start_project: project) - check_tree_entry_for_dir(branch_name, path) - - if start_branch_name - start_project.repository. - check_tree_entry_for_dir(start_branch_name, path) - end + def create_dir(user, path, **options) + options[:user] = user + options[:actions] = [{ action: :create_dir, file_path: path }] - commit_file( - user, - "#{path}/.gitkeep", - '', - message: message, - branch_name: branch_name, - update: false, - author_email: author_email, - author_name: author_name, - start_branch_name: start_branch_name, - start_project: start_project) + multi_action(**options) end - # rubocop:enable Metrics/ParameterLists - # rubocop:disable Metrics/ParameterLists - def commit_file( - user, path, content, - message:, branch_name:, update: true, - author_email: nil, author_name: nil, - start_branch_name: nil, start_project: project) - unless update - error_message = "Filename already exists; update not allowed" + def create_file(user, path, content, **options) + options[:user] = user + options[:actions] = [{ action: :create, file_path: path, content: content }] - if tree_entry_at(branch_name, path) - raise Gitlab::Git::Repository::InvalidBlobName.new(error_message) - end + multi_action(**options) + end - if start_branch_name && - start_project.repository.tree_entry_at(start_branch_name, path) - raise Gitlab::Git::Repository::InvalidBlobName.new(error_message) - end - end + def update_file(user, path, content, **options) + previous_path = options.delete(:previous_path) + action = previous_path && previous_path != path ? :move : :update - multi_action( - user: user, - message: message, - branch_name: branch_name, - author_email: author_email, - author_name: author_name, - start_branch_name: start_branch_name, - start_project: start_project, - actions: [{ action: :create, - file_path: path, - content: content }]) - end - # rubocop:enable Metrics/ParameterLists + options[:user] = user + options[:actions] = [{ action: action, file_path: path, previous_path: previous_path, content: content }] - # rubocop:disable Metrics/ParameterLists - def update_file( - user, path, content, - message:, branch_name:, previous_path:, - author_email: nil, author_name: nil, - start_branch_name: nil, start_project: project) - action = if previous_path && previous_path != path - :move - else - :update - end - - multi_action( - user: user, - message: message, - branch_name: branch_name, - author_email: author_email, - author_name: author_name, - start_branch_name: start_branch_name, - start_project: start_project, - actions: [{ action: action, - file_path: path, - content: content, - previous_path: previous_path }]) + multi_action(**options) end - # rubocop:enable Metrics/ParameterLists - # rubocop:disable Metrics/ParameterLists - def remove_file( - user, path, - message:, branch_name:, - author_email: nil, author_name: nil, - start_branch_name: nil, start_project: project) - multi_action( - user: user, - message: message, - branch_name: branch_name, - author_email: author_email, - author_name: author_name, - start_branch_name: start_branch_name, - start_project: start_project, - actions: [{ action: :delete, - file_path: path }]) + def delete_file(user, path, **options) + options[:user] = user + options[:actions] = [{ action: :delete, file_path: path }] + + multi_action(**options) end - # rubocop:enable Metrics/ParameterLists # rubocop:disable Metrics/ParameterLists def multi_action( user:, branch_name:, message:, actions:, author_email: nil, author_name: nil, start_branch_name: nil, start_project: project) + GitOperationService.new(user, self).with_branch( branch_name, start_branch_name: start_branch_name, start_project: start_project) do |start_commit| - index = rugged.index - parents = if start_commit - index.read_tree(start_commit.raw_commit.tree) - [start_commit.sha] - else - [] - end + index = Gitlab::Git::Index.new(raw_repository) - actions.each do |act| - git_action(index, act) + if start_commit + index.read_tree(start_commit.raw_commit.tree) + parents = [start_commit.sha] + else + parents = [] + end + + actions.each do |options| + index.public_send(options.delete(:action), options) end options = { - tree: index.write_tree(rugged), + tree: index.write_tree, message: message, parents: parents } @@ -892,7 +813,7 @@ class Repository def get_committer_and_author(user, email: nil, name: nil) committer = user_to_committer(user) - author = Gitlab::Git::committer_hash(email: email, name: name) || committer + author = Gitlab::Git.committer_hash(email: email, name: name) || committer { author: author, @@ -1170,30 +1091,6 @@ class Repository blob_data_at(sha, '.gitlab-ci.yml') end - protected - - def tree_entry_at(branch_name, path) - branch_exists?(branch_name) && - # tree_entry is private - raw_repository.send(:tree_entry, commit(branch_name), path) - end - - def check_tree_entry_for_dir(branch_name, path) - return unless branch_exists?(branch_name) - - entry = tree_entry_at(branch_name, path) - - return unless entry - - if entry[:type] == :blob - raise Gitlab::Git::Repository::InvalidBlobName.new( - "Directory already exists as a file") - else - raise Gitlab::Git::Repository::InvalidBlobName.new( - "Directory already exists") - end - end - private def blob_data_at(sha, path) @@ -1204,58 +1101,6 @@ class Repository blob.data end - def git_action(index, action) - path = normalize_path(action[:file_path]) - - if action[:action] == :move - previous_path = normalize_path(action[:previous_path]) - end - - case action[:action] - when :create, :update, :move - mode = - case action[:action] - when :update - index.get(path)[:mode] - when :move - index.get(previous_path)[:mode] - end - mode ||= 0o100644 - - index.remove(previous_path) if action[:action] == :move - - content = if action[:encoding] == 'base64' - Base64.decode64(action[:content]) - else - action[:content] - end - - detect = CharlockHolmes::EncodingDetector.new.detect(content) if content - - unless detect && detect[:type] == :binary - # When writing to the repo directly as we are doing here, - # the `core.autocrlf` config isn't taken into account. - content.gsub!("\r\n", "\n") if self.autocrlf - end - - oid = rugged.write(content, :blob) - - index.add(path: path, oid: oid, mode: mode) - when :delete - index.remove(path) - end - end - - def normalize_path(path) - pathname = Gitlab::Git::PathHelper.normalize_path(path) - - if pathname.each_filename.include?('..') - raise Gitlab::Git::Repository::InvalidBlobName.new('Invalid path') - end - - pathname.to_s - end - def refs_directory_exists? return false unless path_with_namespace diff --git a/app/models/todo.rb b/app/models/todo.rb index 3dda7948d0b..47789a21133 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -17,7 +17,7 @@ class Todo < ActiveRecord::Base APPROVAL_REQUIRED => :approval_required, UNMERGEABLE => :unmergeable, DIRECTLY_ADDRESSED => :directly_addressed - } + }.freeze belongs_to :author, class_name: "User" belongs_to :note diff --git a/app/models/user.rb b/app/models/user.rb index f614eb66e1f..40264401b53 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -81,7 +81,6 @@ class User < ActiveRecord::Base has_many :authorized_projects, through: :project_authorizations, source: :project has_many :snippets, dependent: :destroy, foreign_key: :author_id - has_many :issues, dependent: :destroy, foreign_key: :author_id has_many :notes, dependent: :destroy, foreign_key: :author_id has_many :merge_requests, dependent: :destroy, foreign_key: :author_id has_many :events, dependent: :destroy, foreign_key: :author_id @@ -99,12 +98,17 @@ class User < ActiveRecord::Base has_many :assigned_issues, dependent: :nullify, foreign_key: :assignee_id, class_name: "Issue" has_many :assigned_merge_requests, dependent: :nullify, foreign_key: :assignee_id, class_name: "MergeRequest" + # Issues that a user owns are expected to be moved to the "ghost" user before + # the user is destroyed. If the user owns any issues during deletion, this + # should be treated as an exceptional condition. + has_many :issues, dependent: :restrict_with_exception, foreign_key: :author_id + # # Validations # # Note: devise :validatable above adds validations for :email and :password validates :name, presence: true - validates_confirmation_of :email + validates :email, confirmation: true validates :notification_email, presence: true validates :notification_email, email: true, if: ->(user) { user.notification_email != user.email } validates :public_email, presence: true, uniqueness: true, email: true, allow_blank: true @@ -120,6 +124,7 @@ class User < ActiveRecord::Base validate :unique_email, if: ->(user) { user.email_changed? } validate :owns_notification_email, if: ->(user) { user.notification_email_changed? } validate :owns_public_email, if: ->(user) { user.public_email_changed? } + validate :ghost_users_must_be_blocked validates :avatar, file_size: { maximum: 200.kilobytes.to_i } before_validation :generate_password, on: :create @@ -334,9 +339,15 @@ class User < ActiveRecord::Base def reference_pattern %r{ #{Regexp.escape(reference_prefix)} - (?<user>#{Gitlab::Regex::NAMESPACE_REF_REGEX_STR}) + (?<user>#{Gitlab::Regex::FULL_NAMESPACE_REGEX_STR}) }x end + + # Return (create if necessary) the ghost user. The ghost user + # owns records previously belonging to deleted users. + def ghost + User.find_by_ghost(true) || create_ghost_user + end end # @@ -435,6 +446,12 @@ class User < ActiveRecord::Base errors.add(:public_email, "is not an email you own") unless all_emails.include?(public_email) end + def ghost_users_must_be_blocked + if ghost? && !blocked? + errors.add(:ghost, 'cannot be enabled for a user who is not blocked.') + end + end + def update_emails_with_primary_email primary_email_record = emails.find_by(email: email) if primary_email_record @@ -580,8 +597,8 @@ class User < ActiveRecord::Base if project.repository.branch_exists?(event.branch_name) merge_requests = MergeRequest.where("created_at >= ?", event.created_at). - where(source_project_id: project.id, - source_branch: event.branch_name) + where(source_project_id: project.id, + source_branch: event.branch_name) merge_requests.empty? end end @@ -999,4 +1016,40 @@ class User < ActiveRecord::Base super end end + + def self.create_ghost_user + # Since we only want a single ghost user in an instance, we use an + # exclusive lease to ensure than this block is never run concurrently. + lease_key = "ghost_user_creation" + lease = Gitlab::ExclusiveLease.new(lease_key, timeout: 1.minute.to_i) + + until uuid = lease.try_obtain + # Keep trying until we obtain the lease. To prevent hammering Redis too + # much we'll wait for a bit between retries. + sleep(1) + end + + # Recheck if a ghost user is already present. One might have been + # added between the time we last checked (first line of this method) + # and the time we acquired the lock. + ghost_user = User.find_by_ghost(true) + return ghost_user if ghost_user.present? + + uniquify = Uniquify.new + + username = uniquify.string("ghost") { |s| User.find_by_username(s) } + + email = uniquify.string(-> (n) { "ghost#{n}@example.com" }) do |s| + User.find_by_email(s) + end + + bio = 'This is a "Ghost User", created to hold all issues authored by users that have since been deleted. This user cannot be removed.' + + User.create( + username: username, password: Devise.friendly_token, bio: bio, + email: email, name: "Ghost User", state: :blocked, ghost: true + ) + ensure + Gitlab::ExclusiveLease.cancel(lease_key, uuid) + end end diff --git a/app/policies/base_policy.rb b/app/policies/base_policy.rb index b9f1c29c32e..e07b144355a 100644 --- a/app/policies/base_policy.rb +++ b/app/policies/base_policy.rb @@ -6,9 +6,7 @@ class BasePolicy @cannot_set = cannot_set end - def size - to_set.size - end + delegate :size, to: :to_set def self.empty new(Set.new, Set.new) diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb index 03a2499e263..229846e368c 100644 --- a/app/policies/user_policy.rb +++ b/app/policies/user_policy.rb @@ -3,6 +3,14 @@ class UserPolicy < BasePolicy def rules can! :read_user if @user || !restricted_public_level? + + if @user + if @user.admin? || @subject == @user + can! :destroy_user + end + + cannot! :destroy_user if @subject.ghost? + end end def restricted_public_level? diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb index 5cb7a86a5ee..db82b8f6c30 100644 --- a/app/services/auth/container_registry_authentication_service.rb +++ b/app/services/auth/container_registry_authentication_service.rb @@ -2,7 +2,7 @@ module Auth class ContainerRegistryAuthenticationService < BaseService include Gitlab::CurrentSettings - AUDIENCE = 'container_registry' + AUDIENCE = 'container_registry'.freeze def execute(authentication_abilities:) @authentication_abilities = authentication_abilities diff --git a/app/services/base_service.rb b/app/services/base_service.rb index fa45506317e..745c2c4b681 100644 --- a/app/services/base_service.rb +++ b/app/services/base_service.rb @@ -28,9 +28,7 @@ class BaseService SystemHooksService.new end - def repository - project.repository - end + delegate :repository, to: :project # Add an error to the specified model for restricted visibility levels def deny_visibility_level(model, denied_visibility_level = nil) diff --git a/app/services/ci/create_pipeline_builds_service.rb b/app/services/ci/create_pipeline_builds_service.rb index b7da3f8e7eb..70fb2c5e38f 100644 --- a/app/services/ci/create_pipeline_builds_service.rb +++ b/app/services/ci/create_pipeline_builds_service.rb @@ -10,9 +10,7 @@ module Ci end end - def project - pipeline.project - end + delegate :project, to: :pipeline private diff --git a/app/services/ci/retry_build_service.rb b/app/services/ci/retry_build_service.rb index 4b47ee489cf..38ef323f6e5 100644 --- a/app/services/ci/retry_build_service.rb +++ b/app/services/ci/retry_build_service.rb @@ -1,16 +1,17 @@ module Ci class RetryBuildService < ::BaseService - CLONE_ATTRIBUTES = %i[pipeline ref tag options commands tag_list name + CLONE_ATTRIBUTES = %i[pipeline project ref tag options commands name allow_failure stage stage_idx trigger_request yaml_variables when environment coverage_regex] .freeze REJECT_ATTRIBUTES = %i[id status user token coverage trace runner - artifacts_file artifacts_metadata artifacts_size + artifacts_expire_at artifacts_file + artifacts_metadata artifacts_size created_at updated_at started_at finished_at queued_at erased_by erased_at].freeze - IGNORE_ATTRIBUTES = %i[trace type lock_version project target_url + IGNORE_ATTRIBUTES = %i[type lock_version gl_project_id target_url deploy job_id description].freeze def execute(build) diff --git a/app/services/ci/retry_pipeline_service.rb b/app/services/ci/retry_pipeline_service.rb index 2c5e130e5aa..574561adc4c 100644 --- a/app/services/ci/retry_pipeline_service.rb +++ b/app/services/ci/retry_pipeline_service.rb @@ -1,5 +1,7 @@ module Ci class RetryPipelineService < ::BaseService + include Gitlab::OptimisticLocking + def execute(pipeline) unless can?(current_user, :update_pipeline, pipeline) raise Gitlab::Access::AccessDeniedError @@ -12,6 +14,10 @@ module Ci .reprocess(build) end + pipeline.builds.skipped.find_each do |skipped| + retry_optimistic_lock(skipped) { |build| build.process } + end + MergeRequests::AddTodoWhenBuildFailsService .new(project, current_user) .close_all(pipeline) diff --git a/app/services/create_branch_service.rb b/app/services/create_branch_service.rb index 77459d8779d..b07338d500a 100644 --- a/app/services/create_branch_service.rb +++ b/app/services/create_branch_service.rb @@ -1,5 +1,7 @@ class CreateBranchService < BaseService def execute(branch_name, ref) + create_master_branch if project.empty_repo? + result = ValidateNewBranchService.new(project, current_user) .execute(branch_name) @@ -19,4 +21,16 @@ class CreateBranchService < BaseService def success(branch) super().merge(branch: branch) end + + private + + def create_master_branch + project.repository.commit_file( + current_user, + '/README.md', + '', + message: 'Add README.md', + branch_name: 'master', + update: false) + end end diff --git a/app/services/files/create_dir_service.rb b/app/services/files/create_dir_service.rb index 858de5f0538..083ffdc634c 100644 --- a/app/services/files/create_dir_service.rb +++ b/app/services/files/create_dir_service.rb @@ -1,7 +1,7 @@ module Files class CreateDirService < Files::BaseService def commit - repository.commit_dir( + repository.create_dir( current_user, @file_path, message: @commit_message, diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb index 88dd7bbaedb..65b5537fb68 100644 --- a/app/services/files/create_service.rb +++ b/app/services/files/create_service.rb @@ -1,13 +1,12 @@ module Files class CreateService < Files::BaseService def commit - repository.commit_file( + repository.create_file( current_user, @file_path, @file_content, message: @commit_message, branch_name: @target_branch, - update: false, author_email: @author_email, author_name: @author_name, start_project: @start_project, @@ -17,6 +16,10 @@ module Files def validate super + if @file_content.nil? + raise_error("You must provide content.") + end + if @file_path =~ Gitlab::Regex.directory_traversal_regex raise_error( 'Your changes could not be committed, because the file name ' + diff --git a/app/services/files/destroy_service.rb b/app/services/files/destroy_service.rb index c3be806a42d..e294659bc98 100644 --- a/app/services/files/destroy_service.rb +++ b/app/services/files/destroy_service.rb @@ -1,7 +1,7 @@ module Files class DestroyService < Files::BaseService def commit - repository.remove_file( + repository.delete_file( current_user, @file_path, message: @commit_message, diff --git a/app/services/files/multi_service.rb b/app/services/files/multi_service.rb index af6da5b9d56..0609c6219e7 100644 --- a/app/services/files/multi_service.rb +++ b/app/services/files/multi_service.rb @@ -2,6 +2,8 @@ module Files class MultiService < Files::BaseService class FileChangedError < StandardError; end + ACTIONS = %w[create update delete move].freeze + def commit repository.multi_action( user: current_user, @@ -21,10 +23,19 @@ module Files super params[:actions].each_with_index do |action, index| + if ACTIONS.include?(action[:action].to_s) + action[:action] = action[:action].to_sym + else + raise_error("Unknown action type `#{action[:action]}`.") + end + unless action[:file_path].present? raise_error("You must specify a file_path.") end + action[:file_path].slice!(0) if action[:file_path] && action[:file_path].start_with?('/') + action[:previous_path].slice!(0) if action[:previous_path] && action[:previous_path].start_with?('/') + regex_check(action[:file_path]) regex_check(action[:previous_path]) if action[:previous_path] @@ -43,8 +54,6 @@ module Files validate_delete(action) when :move validate_move(action, index) - else - raise_error("Unknown action type `#{action[:action]}`.") end end end @@ -92,6 +101,20 @@ module Files if repository.blob_at_branch(params[:branch], action[:file_path]) raise_error("Your changes could not be committed because a file with the name `#{action[:file_path]}` already exists.") end + + if action[:content].nil? + raise_error("You must provide content.") + end + end + + def validate_update(action) + if action[:content].nil? + raise_error("You must provide content.") + end + + if file_has_changed? + raise FileChangedError.new("You are attempting to update a file `#{action[:file_path]}` that has changed since you started editing it.") + end end def validate_delete(action) @@ -114,11 +137,5 @@ module Files params[:actions][index][:content] = blob.data end end - - def validate_update(action) - if file_has_changed? - raise FileChangedError.new("You are attempting to update a file `#{action[:file_path]}` that has changed since you started editing it.") - end - end end end diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb index a71fe61a4b6..54e1aaf3f67 100644 --- a/app/services/files/update_service.rb +++ b/app/services/files/update_service.rb @@ -18,6 +18,10 @@ module Files def validate super + if @file_content.nil? + raise_error("You must provide content.") + end + if file_has_changed? raise FileChangedError.new("You are attempting to update a file that has changed since you started editing it.") end diff --git a/app/services/members/destroy_service.rb b/app/services/members/destroy_service.rb index 431da8372c9..2e089149ca8 100644 --- a/app/services/members/destroy_service.rb +++ b/app/services/members/destroy_service.rb @@ -4,7 +4,7 @@ module Members attr_accessor :source - ALLOWED_SCOPES = %i[members requesters all] + ALLOWED_SCOPES = %i[members requesters all].freeze def initialize(source, current_user, params = {}) @source = source diff --git a/app/services/notes/slash_commands_service.rb b/app/services/notes/slash_commands_service.rb index 56913568cae..ad1e6f6774a 100644 --- a/app/services/notes/slash_commands_service.rb +++ b/app/services/notes/slash_commands_service.rb @@ -3,7 +3,7 @@ module Notes UPDATE_SERVICES = { 'Issue' => Issues::UpdateService, 'MergeRequest' => MergeRequests::UpdateService - } + }.freeze def self.noteable_update_service(note) UPDATE_SERVICES[note.noteable_type] diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index 9716a1780a9..2e06826c311 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -4,7 +4,7 @@ module Projects class DestroyError < StandardError; end - DELETED_FLAG = '+deleted' + DELETED_FLAG = '+deleted'.freeze def async_execute project.transaction do diff --git a/app/services/projects/download_service.rb b/app/services/projects/download_service.rb index f06a3d44c17..604747e39d0 100644 --- a/app/services/projects/download_service.rb +++ b/app/services/projects/download_service.rb @@ -2,7 +2,7 @@ module Projects class DownloadService < BaseService WHITELIST = [ /^[^.]+\.fogbugz.com$/ - ] + ].freeze def initialize(project, url) @project, @url = project, url @@ -25,7 +25,7 @@ module Projects end def http?(url) - url =~ /\A#{URI::regexp(['http', 'https'])}\z/ + url =~ /\A#{URI.regexp(%w(http https))}\z/ end def valid_domain?(url) diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb index f5f9ee88912..2d42c4fc04a 100644 --- a/app/services/projects/update_pages_service.rb +++ b/app/services/projects/update_pages_service.rb @@ -2,7 +2,7 @@ module Projects class UpdatePagesService < BaseService BLOCK_SIZE = 32.kilobytes MAX_SIZE = 1.terabyte - SITE_PATH = 'public/' + SITE_PATH = 'public/'.freeze attr_reader :build diff --git a/app/services/protected_branches/api_update_service.rb b/app/services/protected_branches/api_update_service.rb index 050cb3b738b..bdb0e0cc8bf 100644 --- a/app/services/protected_branches/api_update_service.rb +++ b/app/services/protected_branches/api_update_service.rb @@ -15,16 +15,16 @@ module ProtectedBranches case @developers_can_push when true - params.merge!(push_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }]) + params[:push_access_levels_attributes] = [{ access_level: Gitlab::Access::DEVELOPER }] when false - params.merge!(push_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }]) + params[:push_access_levels_attributes] = [{ access_level: Gitlab::Access::MASTER }] end case @developers_can_merge when true - params.merge!(merge_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }]) + params[:merge_access_levels_attributes] = [{ access_level: Gitlab::Access::DEVELOPER }] when false - params.merge!(merge_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }]) + params[:merge_access_levels_attributes] = [{ access_level: Gitlab::Access::MASTER }] end service = ProtectedBranches::UpdateService.new(@project, @current_user, @params) diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index a2bfa422c9d..9b6dd013e3a 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -33,9 +33,7 @@ class SystemHooksService data.merge!(project_data(model)) if event == :rename || event == :transfer - data.merge!({ - old_path_with_namespace: model.old_path_with_namespace - }) + data[:old_path_with_namespace] = model.old_path_with_namespace end data diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 87ba72cf991..55b548a12f9 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -356,10 +356,10 @@ module SystemNoteService note: cross_reference_note_content(gfm_reference) } - if noteable.kind_of?(Commit) + if noteable.is_a?(Commit) note_options.merge!(noteable_type: 'Commit', commit_id: noteable.id) else - note_options.merge!(noteable: noteable) + note_options[:noteable] = noteable end if noteable.is_a?(ExternalIssue) @@ -408,12 +408,13 @@ module SystemNoteService # Initial scope should be system notes of this noteable type notes = Note.system.where(noteable_type: noteable.class) - if noteable.is_a?(Commit) - # Commits have non-integer IDs, so they're stored in `commit_id` - notes = notes.where(commit_id: noteable.id) - else - notes = notes.where(noteable_id: noteable.id) - end + notes = + if noteable.is_a?(Commit) + # Commits have non-integer IDs, so they're stored in `commit_id` + notes.where(commit_id: noteable.id) + else + notes.where(noteable_id: noteable.id) + end notes_for_mentioner(mentioner, noteable, notes).exists? end diff --git a/app/services/users/destroy_service.rb b/app/services/users/destroy_service.rb index bc0653cb634..833da5bc5d1 100644 --- a/app/services/users/destroy_service.rb +++ b/app/services/users/destroy_service.rb @@ -7,7 +7,7 @@ module Users end def execute(user, options = {}) - unless current_user.admin? || current_user == user + unless Ability.allowed?(current_user, :destroy_user, user) raise Gitlab::Access::AccessDeniedError, "#{current_user} tried to destroy user #{user}!" end @@ -26,6 +26,8 @@ module Users ::Projects::DestroyService.new(project, current_user, skip_repo: true).async_execute end + move_issues_to_ghost_user(user) + # Destroy the namespace after destroying the user since certain methods may depend on the namespace existing namespace = user.namespace user_data = user.destroy @@ -33,5 +35,22 @@ module Users user_data end + + private + + def move_issues_to_ghost_user(user) + # Block the user before moving issues to prevent a data race. + # If the user creates an issue after `move_issues_to_ghost_user` + # runs and before the user is destroyed, the destroy will fail with + # an exception. We block the user so that issues can't be created + # after `move_issues_to_ghost_user` runs and before the destroy happens. + user.block + + ghost_user = User.ghost + + user.issues.update_all(author_id: ghost_user.id) + + user.reload + end end end diff --git a/app/uploaders/uploader_helper.rb b/app/uploaders/uploader_helper.rb index 35fd1ed23f8..bee311583ea 100644 --- a/app/uploaders/uploader_helper.rb +++ b/app/uploaders/uploader_helper.rb @@ -1,15 +1,15 @@ # Extra methods for uploader module UploaderHelper - IMAGE_EXT = %w[png jpg jpeg gif bmp tiff] + IMAGE_EXT = %w[png jpg jpeg gif bmp tiff].freeze # We recommend using the .mp4 format over .mov. Videos in .mov format can # still be used but you really need to make sure they are served with the # proper MIME type video/mp4 and not video/quicktime or your videos won't play # on IE >= 9. # http://archive.sublimevideo.info/20150912/docs.sublimevideo.net/troubleshooting.html - VIDEO_EXT = %w[mp4 m4v mov webm ogv] + VIDEO_EXT = %w[mp4 m4v mov webm ogv].freeze # These extension types can contain dangerous code and should only be embedded inline with # proper filtering. They should always be tagged as "Content-Disposition: attachment", not "inline". - DANGEROUS_EXT = %w[svg] + DANGEROUS_EXT = %w[svg].freeze def image? extension_match?(IMAGE_EXT) diff --git a/app/validators/addressable_url_validator.rb b/app/validators/addressable_url_validator.rb index 09bfa613cbe..94542125d43 100644 --- a/app/validators/addressable_url_validator.rb +++ b/app/validators/addressable_url_validator.rb @@ -18,7 +18,7 @@ # end # class AddressableUrlValidator < ActiveModel::EachValidator - DEFAULT_OPTIONS = { protocols: %w(http https ssh git) } + DEFAULT_OPTIONS = { protocols: %w(http https ssh git) }.freeze def validate_each(record, attribute, value) unless valid_url?(value) diff --git a/app/validators/duration_validator.rb b/app/validators/duration_validator.rb new file mode 100644 index 00000000000..10ff44031c6 --- /dev/null +++ b/app/validators/duration_validator.rb @@ -0,0 +1,17 @@ +# DurationValidator +# +# Validate the format conforms with ChronicDuration +# +# Example: +# +# class ApplicationSetting < ActiveRecord::Base +# validates :default_artifacts_expire_in, presence: true, duration: true +# end +# +class DurationValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + ChronicDuration.parse(value) + rescue ChronicDuration::DurationParseError + record.errors.add(attribute, "is not a correct duration") + end +end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 749c74b8110..057b584e1bc 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -212,8 +212,16 @@ .col-sm-10 = f.number_field :max_artifacts_size, class: 'form-control' .help-block - Set the maximum file size each jobs's artifacts can have - = link_to "(?)", help_page_path("user/admin_area/settings/continuous_integration", anchor: "maximum-artifacts-size") + Set the maximum file size for each job's artifacts + = link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size') + .form-group + = f.label :default_artifacts_expire_in, 'Default artifacts expiration', class: 'control-label col-sm-2' + .col-sm-10 + = f.text_field :default_artifacts_expire_in, class: 'form-control' + .help-block + Set the default expiration time for each job's artifacts. + 0 for unlimited. + = link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'default-artifacts-expiration') - if Gitlab.config.registry.enabled %fieldset diff --git a/app/views/admin/users/_user.html.haml b/app/views/admin/users/_user.html.haml index 3b5c713ac2d..a756cb7243a 100644 --- a/app/views/admin/users/_user.html.haml +++ b/app/views/admin/users/_user.html.haml @@ -34,7 +34,7 @@ - if user.access_locked? %li = link_to 'Unlock', unlock_admin_user_path(user), method: :put, class: 'btn-grouped btn btn-xs btn-success', data: { confirm: 'Are you sure?' } - - if user.can_be_removed? + - if user.can_be_removed? && can?(current_user, :destroy_user, @user) %li.divider %li = link_to 'Delete User', [:admin, user], data: { confirm: "USER #{user.name} WILL BE REMOVED! All issues, merge requests and groups linked to this user will also be removed! Consider cancelling this deletion and blocking the user instead. Are you sure?" }, diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index 76b1291fe10..840d843f069 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -173,7 +173,7 @@ .panel-heading Remove user .panel-body - - if @user.can_be_removed? + - if @user.can_be_removed? && can?(current_user, :destroy_user, @user) %p Deleting a user has the following effects: %ul %li All user content like authored issues, snippets, comments will be removed @@ -189,3 +189,6 @@ %strong= @user.solo_owned_groups.map(&:name).join(', ') %p You must transfer ownership or delete these groups before you can delete this user. + - else + %p + You don't have access to delete this user. diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml index 4f36a4a1c73..b82b933c3ad 100644 --- a/app/views/dashboard/projects/index.html.haml +++ b/app/views/dashboard/projects/index.html.haml @@ -5,6 +5,8 @@ - page_title "Projects" - header_title "Projects", dashboard_projects_path +.user-callout{ 'callout-svg' => custom_icon('icon_customization') } + - if @projects.any? || params[:filter_projects] = render 'dashboard/projects_head' diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml index 5a44ec45b7b..30e63d991bb 100644 --- a/app/views/devise/shared/_signup_box.html.haml +++ b/app/views/devise/shared/_signup_box.html.haml @@ -8,7 +8,7 @@ = f.text_field :name, class: "form-control top", required: true, title: "This field is required." .username.form-group = f.label :username - = f.text_field :username, class: "form-control middle", pattern: Gitlab::Regex::NAMESPACE_REGEX_STR_SIMPLE, required: true, title: 'Please create a username with only alphanumeric characters.' + = f.text_field :username, class: "form-control middle", pattern: Gitlab::Regex::NAMESPACE_REGEX_STR_JS, required: true, title: 'Please create a username with only alphanumeric characters.' %p.validation-error.hide Username is already taken. %p.validation-success.hide Username is available. %p.validation-pending.hide Checking username availability... diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml index 19a947af4ca..d068c895fa3 100644 --- a/app/views/layouts/nav/_admin.html.haml +++ b/app/views/layouts/nav/_admin.html.haml @@ -33,7 +33,7 @@ Abuse Reports %span.badge.count= number_with_delimiter(AbuseReport.count(:all)) - - if askimet_enabled? + - if akismet_enabled? = nav_link(controller: :spam_logs) do = link_to admin_spam_logs_path, title: "Spam Logs" do %span diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index a4f4079d556..02fb47ec981 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -115,7 +115,7 @@ %h4.prepend-top-0.danger-title Remove account .col-lg-9 - - if @user.can_be_removed? + - if @user.can_be_removed? && can?(current_user, :destroy_user, @user) %p Deleting an account has the following effects: %ul @@ -131,4 +131,7 @@ %strong= @user.solo_owned_groups.map(&:name).join(', ') %p You must transfer ownership or delete these groups before you can delete your account. + - else + %p + You don't have access to delete this user. .append-bottom-default diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml index b10f5fc08e2..903b957c26b 100644 --- a/app/views/profiles/personal_access_tokens/index.html.haml +++ b/app/views/profiles/personal_access_tokens/index.html.haml @@ -101,5 +101,3 @@ $("#created-personal-access-token").click(function() { this.select(); }); - - $("#created-personal-access-token").effect('highlight', { color: '#ffff99' }, 2000); diff --git a/app/views/projects/blob/diff.html.haml b/app/views/projects/blob/diff.html.haml index d1f7f65bf53..c48d9dd982c 100644 --- a/app/views/projects/blob/diff.html.haml +++ b/app/views/projects/blob/diff.html.haml @@ -19,10 +19,10 @@ = line_content - when :parallel %td.old_line.diff-line-num{ data: { linenumber: line_old } } - = link_to raw(line_old), "##{line_old}" + %a{ href: "##{line_old}", data: { linenumber: line_old } } = line_content %td.new_line.diff-line-num{ data: { linenumber: line_new } } - = link_to raw(line_new), "##{line_new}" + %a{ href: "##{line_new}", data: { linenumber: line_new } } = line_content - if @form.unfold? && @form.bottom? && @form.to < @blob.lines.size diff --git a/app/views/projects/boards/_show.html.haml b/app/views/projects/boards/_show.html.haml index f5ca9607823..b3bc6010efb 100644 --- a/app/views/projects/boards/_show.html.haml +++ b/app/views/projects/boards/_show.html.haml @@ -8,7 +8,6 @@ %script#js-board-template{ type: "text/x-template" }= render "projects/boards/components/board" %script#js-board-list-template{ type: "text/x-template" }= render "projects/boards/components/board_list" - %script#js-board-list-card{ type: "text/x-template" }= render "projects/boards/components/card" = render "projects/issues/head" diff --git a/app/views/projects/boards/components/_card.html.haml b/app/views/projects/boards/components/_card.html.haml deleted file mode 100644 index 891c2c46251..00000000000 --- a/app/views/projects/boards/components/_card.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -%li.card{ ":class" => '{ "user-can-drag": !disabled && issue.id, "is-disabled": disabled || !issue.id, "is-active": issueDetailVisible }', - ":index" => "index", - ":data-issue-id" => "issue.id", - "@mousedown" => "mouseDown", - "@mousemove" => "mouseMove", - "@mouseup" => "showIssue($event)" } - %issue-card-inner{ ":list" => "list", - ":issue" => "issue", - ":issue-link-base" => "issueLinkBase", - ":root-path" => "rootPath" } diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 19ffe73a08d..ae63f8184df 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -31,7 +31,7 @@ - if can?(current_user, :push_code, @project) = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), - class: "btn btn-remove remove-row #{can_remove_branch?(@project, branch.name) ? '' : 'disabled'}", + class: "btn btn-remove remove-row js-ajax-loading-spinner #{can_remove_branch?(@project, branch.name) ? '' : 'disabled'}", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?" }, remote: true, diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index e63bdb38bd8..d3c3e40d518 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -12,12 +12,16 @@ .form-group = label_tag :branch_name, nil, class: 'control-label' .col-sm-10 - = text_field_tag :branch_name, params[:branch_name], required: true, tabindex: 1, autofocus: true, class: 'form-control js-branch-name' + = text_field_tag :branch_name, params[:branch_name], required: true, autofocus: true, class: 'form-control js-branch-name' .help-block.text-danger.js-branch-name-error .form-group = label_tag :ref, 'Create from', class: 'control-label' .col-sm-10 - = text_field_tag :ref, params[:ref] || @project.default_branch, required: true, tabindex: 2, class: 'form-control' + = hidden_field_tag :ref, params[:ref] || @project.default_branch + = dropdown_tag(params[:ref] || @project.default_branch, + options: { toggle_class: 'js-branch-select wide', + filter: true, dropdown_class: "dropdown-menu-selectable", placeholder: "Search branches", + data: { selected: params[:ref] || @project.default_branch, field_name: 'ref' } }) .help-block Existing branch name, tag, or commit SHA .form-actions = button_tag 'Create branch', class: 'btn btn-create', tabindex: 3 diff --git a/app/views/projects/diffs/_file_header.html.haml b/app/views/projects/diffs/_file_header.html.haml index 1dbfe830d52..f809c52c367 100644 --- a/app/views/projects/diffs/_file_header.html.haml +++ b/app/views/projects/diffs/_file_header.html.haml @@ -10,10 +10,10 @@ - if diff_file.renamed_file - old_path, new_path = mark_inline_diffs(diff_file.old_path, diff_file.new_path) - %strong.file-title-name.has-tooltip{ data: { title: old_path, container: 'body' } } + %strong.file-title-name.has-tooltip{ data: { title: diff_file.old_path, container: 'body' } } = old_path → - %strong.file-title-name.has-tooltip{ data: { title: new_path, container: 'body' } } + %strong.file-title-name.has-tooltip{ data: { title: diff_file.new_path, container: 'body' } } = new_path - else %strong.file-title-name.has-tooltip{ data: { title: diff_file.new_path, container: 'body' } } diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index b9300efd04f..83ae9fd10ec 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -120,7 +120,7 @@ .form-group - if @project.avatar? .avatar-container.s160 - = project_icon("#{@project.namespace.to_param}/#{@project.to_param}", alt: '', class: 'avatar project-avatar s160') + = project_icon(@project.full_path, alt: '', class: 'avatar project-avatar s160') %p.light - if @project.avatar_in_git Project avatar in repository: #{ @project.avatar_in_git } diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml index 6e0428e2a31..4147a617d95 100644 --- a/app/views/projects/pipelines/index.html.haml +++ b/app/views/projects/pipelines/index.html.haml @@ -5,23 +5,35 @@ %div{ class: container_class } .top-area %ul.nav-links - %li{ class: active_when(@scope.nil?) }> + %li.js-pipelines-tab-all{ class: active_when(@scope.nil?) }> = link_to project_pipelines_path(@project) do All %span.badge.js-totalbuilds-count = number_with_delimiter(@pipelines_count) - %li{ class: active_when(@scope == 'running') }> + %li.js-pipelines-tab-pending{ class: active_when(@scope == 'pending') }> + = link_to project_pipelines_path(@project, scope: :pending) do + Pending + %span.badge + = number_with_delimiter(@pending_count) + + %li.js-pipelines-tab-running{ class: active_when(@scope == 'running') }> = link_to project_pipelines_path(@project, scope: :running) do Running %span.badge.js-running-count - = number_with_delimiter(@running_or_pending_count) + = number_with_delimiter(@running_count) + + %li.js-pipelines-tab-finished{ class: active_when(@scope == 'finished') }> + = link_to project_pipelines_path(@project, scope: :finished) do + Finished + %span.badge + = number_with_delimiter(@finished_count) - %li{ class: active_when(@scope == 'branches') }> + %li.js-pipelines-tab-branches{ class: active_when(@scope == 'branches') }> = link_to project_pipelines_path(@project, scope: :branches) do Branches - %li{ class: active_when(@scope == 'tags') }> + %li.js-pipelines-tab-tags{ class: active_when(@scope == 'tags') }> = link_to project_pipelines_path(@project, scope: :tags) do Tags diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml index 55202725b9e..14a270a3039 100644 --- a/app/views/projects/pipelines/new.html.haml +++ b/app/views/projects/pipelines/new.html.haml @@ -9,7 +9,11 @@ .form-group = f.label :ref, 'Create for', class: 'control-label' .col-sm-10 - = f.text_field :ref, required: true, tabindex: 2, class: 'form-control js-branch-name ui-autocomplete-input', autocomplete: :false, id: :ref + = hidden_field_tag 'pipeline[ref]', params[:ref] || @project.default_branch + = dropdown_tag(params[:ref] || @project.default_branch, + options: { toggle_class: 'js-branch-select wide', + filter: true, dropdown_class: "dropdown-menu-selectable", placeholder: "Search branches", + data: { selected: params[:ref] || @project.default_branch, field_name: 'pipeline[ref]' } }) .help-block Existing branch name, tag .form-actions = f.submit 'Create pipeline', class: 'btn btn-create', tabindex: 3 diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml index efb207b9916..02b7b2447ed 100644 --- a/app/views/shared/_group_form.html.haml +++ b/app/views/shared/_group_form.html.haml @@ -17,7 +17,7 @@ %strong= parent.full_path + '/' = f.text_field :path, placeholder: 'open-source', class: 'form-control', autofocus: local_assigns[:autofocus] || false, required: true, - pattern: Gitlab::Regex::NAMESPACE_REGEX_STR_SIMPLE, + pattern: Gitlab::Regex::NAMESPACE_REGEX_STR_JS, title: 'Please choose a group name with no special characters.' - if parent = f.hidden_field :parent_id, value: parent.id diff --git a/app/views/shared/icons/_icon_customization.svg b/app/views/shared/icons/_icon_customization.svg new file mode 100644 index 00000000000..eb1f8ba129b --- /dev/null +++ b/app/views/shared/icons/_icon_customization.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 112 90" xmlns:xlink="http://www.w3.org/1999/xlink"><g fill="none" fill-rule="evenodd"><rect width="112" height="90" fill="#fff" rx="6"/><path fill="#eee" fill-rule="nonzero" d="m4 6.01v77.98c0 1.11.899 2.01 2 2.01h100c1.105 0 2-.898 2-2.01v-77.98c0-1.11-.899-2.01-2-2.01h-100c-1.105 0-2 .898-2 2.01m-4 0c0-3.319 2.686-6.01 6-6.01h100c3.315 0 6 2.694 6 6.01v77.98c0 3.319-2.686 6.01-6 6.01h-100c-3.315 0-6-2.694-6-6.01v-77.98"/><g transform="translate(26 35)"><rect width="4" height="39" x="5" fill="#eee" rx="2" id="0"/><rect width="4" height="21" x="5" y="18" fill="#fef0ea" rx="2"/><circle cx="7" cy="13" r="5" fill="#fff"/><path fill="#fb722e" fill-rule="nonzero" d="m7 20c-3.866 0-7-3.134-7-7 0-3.866 3.134-7 7-7 3.866 0 7 3.134 7 7 0 3.866-3.134 7-7 7m0-4c1.657 0 3-1.343 3-3 0-1.657-1.343-3-3-3-1.657 0-3 1.343-3 3 0 1.657 1.343 3 3 3"/></g><g transform="translate(49 35)"><use xlink:href="#0"/><rect width="4" height="21" x="5" y="18" fill="#b5a7dd" rx="2"/><circle cx="7" cy="25" r="5" fill="#fff"/><path fill="#6b4fbb" fill-rule="nonzero" d="m7 32c-3.866 0-7-3.134-7-7 0-3.866 3.134-7 7-7 3.866 0 7 3.134 7 7 0 3.866-3.134 7-7 7m0-4c1.657 0 3-1.343 3-3 0-1.657-1.343-3-3-3-1.657 0-3 1.343-3 3 0 1.657 1.343 3 3 3"/></g><g transform="translate(72 33)"><rect width="4" height="39" x="5" y="2" fill="#eee" rx="2"/><rect width="4" height="34" x="5" y="7" fill="#fef0ea" rx="2"/><circle cx="7" cy="7" r="5" fill="#fff"/><path fill="#fb722e" fill-rule="nonzero" d="m7 14c-3.866 0-7-3.134-7-7 0-3.866 3.134-7 7-7 3.866 0 7 3.134 7 7 0 3.866-3.134 7-7 7m0-4c1.657 0 3-1.343 3-3 0-1.657-1.343-3-3-3-1.657 0-3 1.343-3 3 0 1.657 1.343 3 3 3"/></g><g fill="#6b4fbb"><circle cx="13.5" cy="11.5" r="2.5"/><circle cx="23.5" cy="11.5" r="2.5" opacity=".5"/><circle cx="33.5" cy="11.5" r="2.5" opacity=".5"/></g><path fill="#eee" d="m0 19h111v4h-111z"/></g></svg> diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 3f7f1a86b9f..6c730e16f67 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -173,7 +173,7 @@ :javascript gl.IssuableResource = new gl.SubbableResource('#{issuable_json_path(issuable)}'); new gl.IssuableTimeTracking("#{escape_javascript(serialize_issuable(issuable))}"); - new MilestoneSelect('{"namespace":"#{@project.namespace.path}","path":"#{@project.path}"}'); + new MilestoneSelect('{"full_path":"#{@project.full_path}"}'); new LabelsSelect(); new IssuableContext('#{escape_javascript(current_user.to_json(only: [:username, :id, :name]))}'); gl.Subscription.bindAll('.subscription'); diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index dc2fea450bd..c130f3d9e17 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -98,6 +98,7 @@ Snippets %div{ class: container_class } + .user-callout{ 'callout-svg' => custom_icon('icon_customization') } .tab-content #activity.tab-pane .row-content-block.calender-block.white.second-block.hidden-xs diff --git a/app/workers/irker_worker.rb b/app/workers/irker_worker.rb index 7e44b241743..c9658b3fe17 100644 --- a/app/workers/irker_worker.rb +++ b/app/workers/irker_worker.rb @@ -120,8 +120,8 @@ class IrkerWorker end def compare_url(data, repo_path) - sha1 = Commit::truncate_sha(data['before']) - sha2 = Commit::truncate_sha(data['after']) + sha1 = Commit.truncate_sha(data['before']) + sha2 = Commit.truncate_sha(data['after']) compare_url = "#{Gitlab.config.gitlab.url}/#{repo_path}/compare" compare_url += "/#{sha1}...#{sha2}" colorize_url compare_url @@ -129,7 +129,7 @@ class IrkerWorker def send_one_commit(project, hook_attrs, repo_name, branch) commit = commit_from_id project, hook_attrs['id'] - sha = colorize_sha Commit::truncate_sha(hook_attrs['id']) + sha = colorize_sha Commit.truncate_sha(hook_attrs['id']) author = hook_attrs['author']['name'] files = colorize_nb_files(files_count commit) title = commit.title diff --git a/changelogs/unreleased/12726-preserve-issues-after-deleting-users.yml b/changelogs/unreleased/12726-preserve-issues-after-deleting-users.yml new file mode 100644 index 00000000000..4a1a199673c --- /dev/null +++ b/changelogs/unreleased/12726-preserve-issues-after-deleting-users.yml @@ -0,0 +1,4 @@ +--- +title: Deleting a user doesn't delete issues they've created/are assigned to +merge_request: 7393 +author: diff --git a/changelogs/unreleased/1937-https-clone-url-username.yml b/changelogs/unreleased/1937-https-clone-url-username.yml new file mode 100644 index 00000000000..fa89d94e0f3 --- /dev/null +++ b/changelogs/unreleased/1937-https-clone-url-username.yml @@ -0,0 +1,4 @@ +--- +title: Add the Username to the HTTP(S) clone URL of a Repository +merge_request: 9347 +author: Jan Christophersen diff --git a/changelogs/unreleased/23062-allow-git-log-to-accept-follow-and-skip.yml b/changelogs/unreleased/23062-allow-git-log-to-accept-follow-and-skip.yml new file mode 100644 index 00000000000..f7c856040e0 --- /dev/null +++ b/changelogs/unreleased/23062-allow-git-log-to-accept-follow-and-skip.yml @@ -0,0 +1,4 @@ +--- +title: Make Git history follow renames again by performing the --skip in Ruby +merge_request: +author: diff --git a/changelogs/unreleased/26875-builds-api-endpoint-skipped-scope.yml b/changelogs/unreleased/26875-builds-api-endpoint-skipped-scope.yml new file mode 100644 index 00000000000..3d6400cba76 --- /dev/null +++ b/changelogs/unreleased/26875-builds-api-endpoint-skipped-scope.yml @@ -0,0 +1,4 @@ +--- +title: Add all available statuses to scope filter for project builds endpoint +merge_request: +author: George Andrinopoulos diff --git a/changelogs/unreleased/26900-pipelines-tabs.yml b/changelogs/unreleased/26900-pipelines-tabs.yml new file mode 100644 index 00000000000..f08514c621f --- /dev/null +++ b/changelogs/unreleased/26900-pipelines-tabs.yml @@ -0,0 +1,4 @@ +--- +title: Adds Pending and Finished tabs to pipelines page +merge_request: +author: diff --git a/changelogs/unreleased/27267-events-project-query-performance-regression.yml b/changelogs/unreleased/27267-events-project-query-performance-regression.yml new file mode 100644 index 00000000000..a1697b57eac --- /dev/null +++ b/changelogs/unreleased/27267-events-project-query-performance-regression.yml @@ -0,0 +1,4 @@ +--- +title: 'Add performance query regression fix for !9088 affecting #27267' +merge_request: +author: diff --git a/changelogs/unreleased/27530-fix-job-dropdown-pipeline-console-error.yml b/changelogs/unreleased/27530-fix-job-dropdown-pipeline-console-error.yml new file mode 100644 index 00000000000..4436b4bee68 --- /dev/null +++ b/changelogs/unreleased/27530-fix-job-dropdown-pipeline-console-error.yml @@ -0,0 +1,4 @@ +--- +title: Fixes job dropdown action throws error in js console +merge_request: 9182 +author: diff --git a/changelogs/unreleased/27762-add-default-artifacts-expiration.yml b/changelogs/unreleased/27762-add-default-artifacts-expiration.yml new file mode 100644 index 00000000000..27fa77ed04d --- /dev/null +++ b/changelogs/unreleased/27762-add-default-artifacts-expiration.yml @@ -0,0 +1,4 @@ +--- +title: Add admin setting for default artifacts expiration +merge_request: 9219 +author: diff --git a/changelogs/unreleased/27989-disable-counting-tags.yml b/changelogs/unreleased/27989-disable-counting-tags.yml new file mode 100644 index 00000000000..988785ac454 --- /dev/null +++ b/changelogs/unreleased/27989-disable-counting-tags.yml @@ -0,0 +1,4 @@ +--- +title: Disable unused tags count cache for Projects, Builds and Runners +merge_request: +author: diff --git a/changelogs/unreleased/28366-renamed-file-tooltip-contains-html.yml b/changelogs/unreleased/28366-renamed-file-tooltip-contains-html.yml new file mode 100644 index 00000000000..faf1e89ed94 --- /dev/null +++ b/changelogs/unreleased/28366-renamed-file-tooltip-contains-html.yml @@ -0,0 +1,4 @@ +--- +title: Remove markup that was showing in tooltip for renamed files +merge_request: 9374 +author: diff --git a/changelogs/unreleased/28367-fix-unfold-diff-line-number-copy-paste.yml b/changelogs/unreleased/28367-fix-unfold-diff-line-number-copy-paste.yml new file mode 100644 index 00000000000..6fc89fd91dd --- /dev/null +++ b/changelogs/unreleased/28367-fix-unfold-diff-line-number-copy-paste.yml @@ -0,0 +1,4 @@ +--- +title: Fixes includes line number during unfold copy n paste in parallel diff view +merge_request: 9365 +author: diff --git a/changelogs/unreleased/28450-test-compiling-frontend-assets-for-production-in-ci.yml b/changelogs/unreleased/28450-test-compiling-frontend-assets-for-production-in-ci.yml new file mode 100644 index 00000000000..196a9b788ea --- /dev/null +++ b/changelogs/unreleased/28450-test-compiling-frontend-assets-for-production-in-ci.yml @@ -0,0 +1,4 @@ +--- +title: test compiling production assets and generate webpack bundle report in CI +merge_request: 9396 +author: diff --git a/changelogs/unreleased/3874-correctly-return-json-on-delete-responses.yml b/changelogs/unreleased/3874-correctly-return-json-on-delete-responses.yml new file mode 100644 index 00000000000..4a4932288b4 --- /dev/null +++ b/changelogs/unreleased/3874-correctly-return-json-on-delete-responses.yml @@ -0,0 +1,4 @@ +--- +title: Return 202 with JSON body on async removals on V4 API +merge_request: +author: diff --git a/changelogs/unreleased/api-notes-entity-fields.yml b/changelogs/unreleased/api-notes-entity-fields.yml new file mode 100644 index 00000000000..f7631df31e2 --- /dev/null +++ b/changelogs/unreleased/api-notes-entity-fields.yml @@ -0,0 +1,4 @@ +--- +title: 'API: Remove deprecated fields Notes#upvotes and Notes#downvotes' +merge_request: 9384 +author: Robert Schilling diff --git a/changelogs/unreleased/api-remove-owned-groups.yml b/changelogs/unreleased/api-remove-owned-groups.yml new file mode 100644 index 00000000000..cf0301b7fe0 --- /dev/null +++ b/changelogs/unreleased/api-remove-owned-groups.yml @@ -0,0 +1,4 @@ +--- +title: 'API: Remove /groups/owned endpoint' +merge_request: 9505 +author: Robert Schilling diff --git a/changelogs/unreleased/branch_deletion.yml b/changelogs/unreleased/branch_deletion.yml new file mode 100644 index 00000000000..dbc9265a1fb --- /dev/null +++ b/changelogs/unreleased/branch_deletion.yml @@ -0,0 +1,4 @@ +--- +title: on branch deletion show loading icon and disabled the button +merge_request: 6761 +author: wendy0402 diff --git a/changelogs/unreleased/create_branch_repo_less.yml b/changelogs/unreleased/create_branch_repo_less.yml new file mode 100644 index 00000000000..e8b14fa3b67 --- /dev/null +++ b/changelogs/unreleased/create_branch_repo_less.yml @@ -0,0 +1,4 @@ +--- +title: Creating a new branch from an issue will automatically initialize a repository if one doesn't already exist. +merge_request: +author: diff --git a/changelogs/unreleased/feature-runners-registration-deletion-v4-api.yml b/changelogs/unreleased/feature-runners-registration-deletion-v4-api.yml new file mode 100644 index 00000000000..e646a6a17b7 --- /dev/null +++ b/changelogs/unreleased/feature-runners-registration-deletion-v4-api.yml @@ -0,0 +1,4 @@ +--- +title: Add Runner's registration/deletion v4 API +merge_request: 9246 +author: diff --git a/changelogs/unreleased/fix-mr-size-with-over-100-files.yml b/changelogs/unreleased/fix-mr-size-with-over-100-files.yml new file mode 100644 index 00000000000..eecf3c99a75 --- /dev/null +++ b/changelogs/unreleased/fix-mr-size-with-over-100-files.yml @@ -0,0 +1,4 @@ +--- +title: Fix MR changes tab size count when there are over 100 files in the diff +merge_request: +author: diff --git a/changelogs/unreleased/moving-issue-with-two-list-labels.yml b/changelogs/unreleased/moving-issue-with-two-list-labels.yml new file mode 100644 index 00000000000..d5ea81e3810 --- /dev/null +++ b/changelogs/unreleased/moving-issue-with-two-list-labels.yml @@ -0,0 +1,4 @@ +--- +title: Removes label when moving issue to another list that it is currently in +merge_request: +author: diff --git a/changelogs/unreleased/pages-0-3-2.yml b/changelogs/unreleased/pages-0-3-2.yml new file mode 100644 index 00000000000..f660379f2e6 --- /dev/null +++ b/changelogs/unreleased/pages-0-3-2.yml @@ -0,0 +1,4 @@ +--- +title: Upgrade GitLab Pages to v0.3.2 +merge_request: +author: diff --git a/changelogs/unreleased/remove-jquery-ui-plugins.yml b/changelogs/unreleased/remove-jquery-ui-plugins.yml new file mode 100644 index 00000000000..c768f702ba2 --- /dev/null +++ b/changelogs/unreleased/remove-jquery-ui-plugins.yml @@ -0,0 +1,4 @@ +--- +title: Removed jQuery UI highlight & autocomplete +merge_request: +author: diff --git a/changelogs/unreleased/sh-bump-hashie-to-3-5-5.yml b/changelogs/unreleased/sh-bump-hashie-to-3-5-5.yml new file mode 100644 index 00000000000..57f1474093a --- /dev/null +++ b/changelogs/unreleased/sh-bump-hashie-to-3-5-5.yml @@ -0,0 +1,4 @@ +--- +title: Bump Hashie to 3.5.5 and omniauth to 1.4.2 to eliminate warning noise +merge_request: +author: diff --git a/changelogs/unreleased/unified-member-api-response.yml b/changelogs/unreleased/unified-member-api-response.yml new file mode 100644 index 00000000000..0a60b4d46a3 --- /dev/null +++ b/changelogs/unreleased/unified-member-api-response.yml @@ -0,0 +1,4 @@ +--- +title: 'API: Return 400 for all validation erros in the mebers API' +merge_request: 9523 +author: Robert Schilling diff --git a/changelogs/unreleased/update-vue-2-1.yml b/changelogs/unreleased/update-vue-2-1.yml new file mode 100644 index 00000000000..acc42bf00b1 --- /dev/null +++ b/changelogs/unreleased/update-vue-2-1.yml @@ -0,0 +1,4 @@ +--- +title: update Vue to v2.1.10 +merge_request: 9386 +author: diff --git a/changelogs/unreleased/user-callouts.yml b/changelogs/unreleased/user-callouts.yml new file mode 100644 index 00000000000..f6ce06a3d8f --- /dev/null +++ b/changelogs/unreleased/user-callouts.yml @@ -0,0 +1,4 @@ +--- +title: Added user callouts to the projects dashboard and user profile +merge_request: +author: diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml index 7336d7c842a..072ed8a3864 100644 --- a/config/dependency_decisions.yml +++ b/config/dependency_decisions.yml @@ -320,3 +320,9 @@ :why: https://github.com/shinnn/spdx-license-ids/blob/v1.2.2/LICENSE :versions: [] :when: 2017-02-08 22:35:00.225232000 Z +- - :approve + - opener + - :who: Mike Greiling + :why: https://github.com/domenic/opener/blob/1.4.3/LICENSE.txt + :versions: [] + :when: 2017-02-21 22:33:41.729629000 Z diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 3f716dd8833..c64ae15fa92 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -14,12 +14,15 @@ class Settings < Settingslogic end def build_gitlab_ci_url - if on_standard_port?(gitlab) - custom_port = nil - else - custom_port = ":#{gitlab.port}" - end - [ gitlab.protocol, + custom_port = + if on_standard_port?(gitlab) + nil + else + ":#{gitlab.port}" + end + + [ + gitlab.protocol, "://", gitlab.host, custom_port, @@ -80,7 +83,9 @@ class Settings < Settingslogic def base_url(config) custom_port = on_standard_port?(config) ? nil : ":#{config.port}" - [ config.protocol, + + [ + config.protocol, "://", config.host, custom_port @@ -160,15 +165,16 @@ if github_settings github_settings["args"] ||= Settingslogic.new({}) - if github_settings["url"].include?(github_default_url) - github_settings["args"]["client_options"] = OmniAuth::Strategies::GitHub.default_options[:client_options] - else - github_settings["args"]["client_options"] = { - "site" => File.join(github_settings["url"], "api/v3"), - "authorize_url" => File.join(github_settings["url"], "login/oauth/authorize"), - "token_url" => File.join(github_settings["url"], "login/oauth/access_token") - } - end + github_settings["args"]["client_options"] = + if github_settings["url"].include?(github_default_url) + OmniAuth::Strategies::GitHub.default_options[:client_options] + else + { + "site" => File.join(github_settings["url"], "api/v3"), + "authorize_url" => File.join(github_settings["url"], "login/oauth/authorize"), + "token_url" => File.join(github_settings["url"], "login/oauth/access_token") + } + end end Settings['shared'] ||= Settingslogic.new({}) diff --git a/config/initializers/acts_as_taggable.rb b/config/initializers/acts_as_taggable.rb new file mode 100644 index 00000000000..c564c0cab11 --- /dev/null +++ b/config/initializers/acts_as_taggable.rb @@ -0,0 +1,5 @@ +ActsAsTaggableOn.strict_case_match = true + +# tags_counter enables caching count of tags which results in an update whenever a tag is added or removed +# since the count is not used anywhere its better performance wise to disable this cache +ActsAsTaggableOn.tags_counter = false diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 738dbeefc11..3b1317030bc 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -24,7 +24,7 @@ Devise.setup do |config| # session. If you need permissions, you should implement that in a before filter. # You can also supply a hash where the value is a boolean determining whether # or not authentication should be aborted when the value is not present. - config.authentication_keys = [ :login ] + config.authentication_keys = [:login] # Configure parameters from the request object used for authentication. Each entry # given should be a request method and it will automatically be passed to the @@ -36,12 +36,12 @@ Devise.setup do |config| # Configure which authentication keys should be case-insensitive. # These keys will be downcased upon creating or modifying a user and when used # to authenticate or find a user. Default is :email. - config.case_insensitive_keys = [ :email ] + config.case_insensitive_keys = [:email] # Configure which authentication keys should have whitespace stripped. # These keys will have whitespace before and after removed upon creating or # modifying a user and when used to authenticate or find a user. Default is :email. - config.strip_whitespace_keys = [ :email ] + config.strip_whitespace_keys = [:email] # Tell if authentication through request.params is enabled. True by default. # config.params_authenticatable = true @@ -124,7 +124,7 @@ Devise.setup do |config| config.lock_strategy = :failed_attempts # Defines which key will be used when locking and unlocking an account - config.unlock_keys = [ :email ] + config.unlock_keys = [:email] # Defines which strategy will be used to unlock an account. # :email = Sends an unlock link to the user email diff --git a/config/initializers/gollum.rb b/config/initializers/gollum.rb index 703f24f93b2..1ebe3c7a742 100644 --- a/config/initializers/gollum.rb +++ b/config/initializers/gollum.rb @@ -1,5 +1,5 @@ module Gollum - GIT_ADAPTER = "rugged" + GIT_ADAPTER = "rugged".freeze end require "gollum-lib" diff --git a/config/initializers/health_check.rb b/config/initializers/health_check.rb index 4c91a61fb4a..959daa93f78 100644 --- a/config/initializers/health_check.rb +++ b/config/initializers/health_check.rb @@ -1,4 +1,4 @@ HealthCheck.setup do |config| - config.standard_checks = ['database', 'migrations', 'cache'] - config.full_checks = ['database', 'migrations', 'cache'] + config.standard_checks = %w(database migrations cache) + config.full_checks = %w(database migrations cache) end diff --git a/config/initializers/metrics.rb b/config/initializers/metrics.rb index e0702e06cc9..a1517e6afc8 100644 --- a/config/initializers/metrics.rb +++ b/config/initializers/metrics.rb @@ -20,13 +20,13 @@ def instrument_classes(instrumentation) # Path to search => prefix to strip from constant paths_to_instrument = { - ['app', 'finders'] => ['app', 'finders'], - ['app', 'mailers', 'emails'] => ['app', 'mailers'], - ['app', 'services', '**'] => ['app', 'services'], - ['lib', 'gitlab', 'conflicts'] => ['lib'], - ['lib', 'gitlab', 'diff'] => ['lib'], - ['lib', 'gitlab', 'email', 'message'] => ['lib'], - ['lib', 'gitlab', 'checks'] => ['lib'] + %w(app finders) => %w(app finders), + %w(app mailers emails) => %w(app mailers), + %w(app services **) => %w(app services), + %w(lib gitlab conflicts) => ['lib'], + %w(lib gitlab diff) => ['lib'], + %w(lib gitlab email message) => ['lib'], + %w(lib gitlab checks) => ['lib'] } paths_to_instrument.each do |(path, prefix)| diff --git a/config/initializers/trusted_proxies.rb b/config/initializers/trusted_proxies.rb index cd869657c53..fc4f02453d7 100644 --- a/config/initializers/trusted_proxies.rb +++ b/config/initializers/trusted_proxies.rb @@ -21,4 +21,4 @@ gitlab_trusted_proxies = Array(Gitlab.config.gitlab.trusted_proxies).map do |pro end.compact Rails.application.config.action_dispatch.trusted_proxies = ( - [ '127.0.0.1', '::1' ] + gitlab_trusted_proxies) + ['127.0.0.1', '::1'] + gitlab_trusted_proxies) diff --git a/config/initializers/workhorse_multipart.rb b/config/initializers/workhorse_multipart.rb index 84d809741c4..064e5964f09 100644 --- a/config/initializers/workhorse_multipart.rb +++ b/config/initializers/workhorse_multipart.rb @@ -10,7 +10,7 @@ end # module Gitlab module StrongParameterScalars - GITLAB_PERMITTED_SCALAR_TYPES = [::UploadedFile] + GITLAB_PERMITTED_SCALAR_TYPES = [::UploadedFile].freeze def permitted_scalar?(value) super || GITLAB_PERMITTED_SCALAR_TYPES.any? { |type| value.is_a?(type) } diff --git a/config/routes/wiki.rb b/config/routes/wiki.rb index dad746d59a1..a6b3f5d4693 100644 --- a/config/routes/wiki.rb +++ b/config/routes/wiki.rb @@ -1,4 +1,4 @@ -WIKI_SLUG_ID = { id: /\S+/ } unless defined? WIKI_SLUG_ID +WIKI_SLUG_ID = { id: /\S+/ }.freeze unless defined? WIKI_SLUG_ID scope(controller: :wikis) do scope(path: 'wikis', as: :wikis) do diff --git a/config/webpack.config.js b/config/webpack.config.js index 15899993874..a71ec0c5f52 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -5,12 +5,14 @@ var path = require('path'); var webpack = require('webpack'); var StatsPlugin = require('stats-webpack-plugin'); var CompressionPlugin = require('compression-webpack-plugin'); +var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; var ROOT_PATH = path.resolve(__dirname, '..'); var IS_PRODUCTION = process.env.NODE_ENV === 'production'; var IS_DEV_SERVER = process.argv[1].indexOf('webpack-dev-server') !== -1; var DEV_SERVER_PORT = parseInt(process.env.DEV_SERVER_PORT, 10) || 3808; var DEV_SERVER_LIVERELOAD = process.env.DEV_SERVER_LIVERELOAD !== 'false'; +var WEBPACK_REPORT = process.env.WEBPACK_REPORT; var config = { context: path.join(ROOT_PATH, 'app/assets/javascripts'), @@ -85,7 +87,7 @@ var config = { 'bootstrap/js': 'bootstrap-sass/assets/javascripts/bootstrap', 'emoji-aliases$': path.join(ROOT_PATH, 'fixtures/emojis/aliases.json'), 'vendor': path.join(ROOT_PATH, 'vendor/assets/javascripts'), - 'vue$': IS_PRODUCTION ? 'vue/dist/vue.min.js' : 'vue/dist/vue.js', + 'vue$': 'vue/dist/vue.common.js', } } } @@ -120,4 +122,16 @@ if (IS_DEV_SERVER) { config.output.publicPath = '//localhost:' + DEV_SERVER_PORT + config.output.publicPath; } +if (WEBPACK_REPORT) { + config.plugins.push( + new BundleAnalyzerPlugin({ + analyzerMode: 'static', + generateStatsFile: true, + openAnalyzer: false, + reportFilename: path.join(ROOT_PATH, 'webpack-report/index.html'), + statsFilename: path.join(ROOT_PATH, 'webpack-report/stats.json'), + }) + ); +} + module.exports = config; diff --git a/db/fixtures/development/17_cycle_analytics.rb b/db/fixtures/development/17_cycle_analytics.rb index 747901dd634..aea0a72b633 100644 --- a/db/fixtures/development/17_cycle_analytics.rb +++ b/db/fixtures/development/17_cycle_analytics.rb @@ -155,17 +155,9 @@ class Gitlab::Seeder::CycleAnalytics issue.project.repository.add_branch(@user, branch_name, 'master') - options = { - committer: issue.project.repository.user_to_committer(@user), - author: issue.project.repository.user_to_committer(@user), - commit: { message: "Commit for ##{issue.iid}", branch: branch_name, update_ref: true }, - file: { content: "content", path: filename, update: false } - } - - commit_sha = Gitlab::Git::Blob.commit(issue.project.repository, options) + commit_sha = issue.project.repository.create_file(@user, filename, "content", options, message: "Commit for ##{issue.iid}", branch_name: branch_name) issue.project.repository.commit(commit_sha) - GitPushService.new(issue.project, @user, oldrev: issue.project.repository.commit("master").sha, diff --git a/db/migrate/20160610201627_migrate_users_notification_level.rb b/db/migrate/20160610201627_migrate_users_notification_level.rb index 760b766828e..ce4f00e25fa 100644 --- a/db/migrate/20160610201627_migrate_users_notification_level.rb +++ b/db/migrate/20160610201627_migrate_users_notification_level.rb @@ -2,6 +2,8 @@ class MigrateUsersNotificationLevel < ActiveRecord::Migration # Migrates only users who changed their default notification level :participating # creating a new record on notification settings table + DOWNTIME = false + def up execute(%Q{ INSERT INTO notification_settings diff --git a/db/migrate/20160829114652_add_markdown_cache_columns.rb b/db/migrate/20160829114652_add_markdown_cache_columns.rb index 8753e55e058..9cb44dfa9f9 100644 --- a/db/migrate/20160829114652_add_markdown_cache_columns.rb +++ b/db/migrate/20160829114652_add_markdown_cache_columns.rb @@ -26,7 +26,7 @@ class AddMarkdownCacheColumns < ActiveRecord::Migration projects: [:description], releases: [:description], snippets: [:title, :content], - } + }.freeze def change COLUMNS.each do |table, columns| diff --git a/db/migrate/20160831214543_migrate_project_features.rb b/db/migrate/20160831214543_migrate_project_features.rb index 93f9821bc76..79a5fb29d64 100644 --- a/db/migrate/20160831214543_migrate_project_features.rb +++ b/db/migrate/20160831214543_migrate_project_features.rb @@ -3,7 +3,7 @@ class MigrateProjectFeatures < ActiveRecord::Migration DOWNTIME = true DOWNTIME_REASON = - <<-EOT + <<-EOT.freeze Migrating issues_enabled, merge_requests_enabled, wiki_enabled, builds_enabled, snippets_enabled fields from projects to a new table called project_features. EOT diff --git a/db/migrate/20161019190736_migrate_sidekiq_queues_from_default.rb b/db/migrate/20161019190736_migrate_sidekiq_queues_from_default.rb index e875213ab96..9f502a8df73 100644 --- a/db/migrate/20161019190736_migrate_sidekiq_queues_from_default.rb +++ b/db/migrate/20161019190736_migrate_sidekiq_queues_from_default.rb @@ -71,7 +71,7 @@ class MigrateSidekiqQueuesFromDefault < ActiveRecord::Migration 'StuckCiBuildsWorker' => :cronjob, 'UpdateMergeRequestsWorker' => :update_merge_requests } - } + }.freeze def up Sidekiq.redis do |redis| @@ -93,7 +93,7 @@ class MigrateSidekiqQueuesFromDefault < ActiveRecord::Migration def migrate_from_queue(redis, queue, job_mapping) while job = redis.lpop("queue:#{queue}") - payload = JSON.load(job) + payload = JSON.parse(job) new_queue = job_mapping[payload['class']] # If we have no target queue to migrate to we're probably dealing with diff --git a/db/migrate/20161024042317_migrate_mailroom_queue_from_default.rb b/db/migrate/20161024042317_migrate_mailroom_queue_from_default.rb index 06d07bdb835..fc2e4c12b30 100644 --- a/db/migrate/20161024042317_migrate_mailroom_queue_from_default.rb +++ b/db/migrate/20161024042317_migrate_mailroom_queue_from_default.rb @@ -25,7 +25,7 @@ class MigrateMailroomQueueFromDefault < ActiveRecord::Migration incoming_email: { 'EmailReceiverWorker' => :email_receiver } - } + }.freeze def up Sidekiq.redis do |redis| @@ -47,7 +47,7 @@ class MigrateMailroomQueueFromDefault < ActiveRecord::Migration def migrate_from_queue(redis, queue, job_mapping) while job = redis.lpop("queue:#{queue}") - payload = JSON.load(job) + payload = JSON.parse(job) new_queue = job_mapping[payload['class']] # If we have no target queue to migrate to we're probably dealing with diff --git a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb index 77e0c40d850..3e1f6b1627d 100644 --- a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb +++ b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb @@ -34,7 +34,7 @@ class MigrateProcessCommitWorkerJobs < ActiveRecord::Migration new_jobs = [] while job = redis.lpop('queue:process_commit') - payload = JSON.load(job) + payload = JSON.parse(job) project = Project.find_including_path(payload['args'][0]) next unless project @@ -75,7 +75,7 @@ class MigrateProcessCommitWorkerJobs < ActiveRecord::Migration new_jobs = [] while job = redis.lpop('queue:process_commit') - payload = JSON.load(job) + payload = JSON.parse(job) payload['args'][2] = payload['args'][2]['id'] diff --git a/db/migrate/20161128142110_remove_unnecessary_indexes.rb b/db/migrate/20161128142110_remove_unnecessary_indexes.rb index 9deab19782e..8100287ef48 100644 --- a/db/migrate/20161128142110_remove_unnecessary_indexes.rb +++ b/db/migrate/20161128142110_remove_unnecessary_indexes.rb @@ -12,7 +12,7 @@ class RemoveUnnecessaryIndexes < ActiveRecord::Migration remove_index :award_emoji, column: :user_id if index_exists?(:award_emoji, :user_id) remove_index :ci_builds, column: :commit_id if index_exists?(:ci_builds, :commit_id) remove_index :deployments, column: :project_id if index_exists?(:deployments, :project_id) - remove_index :deployments, column: ["project_id", "environment_id"] if index_exists?(:deployments, ["project_id", "environment_id"]) + remove_index :deployments, column: %w(project_id environment_id) if index_exists?(:deployments, %w(project_id environment_id)) remove_index :lists, column: :board_id if index_exists?(:lists, :board_id) remove_index :milestones, column: :project_id if index_exists?(:milestones, :project_id) remove_index :notes, column: :project_id if index_exists?(:notes, :project_id) @@ -24,7 +24,7 @@ class RemoveUnnecessaryIndexes < ActiveRecord::Migration add_concurrent_index :award_emoji, :user_id add_concurrent_index :ci_builds, :commit_id add_concurrent_index :deployments, :project_id - add_concurrent_index :deployments, ["project_id", "environment_id"] + add_concurrent_index :deployments, %w(project_id environment_id) add_concurrent_index :lists, :board_id add_concurrent_index :milestones, :project_id add_concurrent_index :notes, :project_id diff --git a/db/migrate/20161207231620_fixup_environment_name_uniqueness.rb b/db/migrate/20161207231620_fixup_environment_name_uniqueness.rb index b74552e762d..a20a903a752 100644 --- a/db/migrate/20161207231620_fixup_environment_name_uniqueness.rb +++ b/db/migrate/20161207231620_fixup_environment_name_uniqueness.rb @@ -42,10 +42,10 @@ class FixupEnvironmentNameUniqueness < ActiveRecord::Migration conflicts.each do |id, name| update_sql = Arel::UpdateManager.new(ActiveRecord::Base). - table(environments). - set(environments[:name] => name + "-" + id.to_s). - where(environments[:id].eq(id)). - to_sql + table(environments). + set(environments[:name] => name + "-" + id.to_s). + where(environments[:id].eq(id)). + to_sql connection.exec_update(update_sql, self.class.name, []) end diff --git a/db/migrate/20170206115204_add_column_ghost_to_users.rb b/db/migrate/20170206115204_add_column_ghost_to_users.rb new file mode 100644 index 00000000000..cc1eeda1160 --- /dev/null +++ b/db/migrate/20170206115204_add_column_ghost_to_users.rb @@ -0,0 +1,11 @@ +class AddColumnGhostToUsers < ActiveRecord::Migration + DOWNTIME = false + + def up + add_column :users, :ghost, :boolean + end + + def down + remove_column :users, :ghost + end +end diff --git a/db/migrate/20170214084746_add_default_artifacts_expiration_to_application_settings.rb b/db/migrate/20170214084746_add_default_artifacts_expiration_to_application_settings.rb new file mode 100644 index 00000000000..e0e3ff8957a --- /dev/null +++ b/db/migrate/20170214084746_add_default_artifacts_expiration_to_application_settings.rb @@ -0,0 +1,11 @@ +class AddDefaultArtifactsExpirationToApplicationSettings < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + add_column :application_settings, + :default_artifacts_expire_in, :string, + null: false, default: '0' + end +end diff --git a/db/migrate/20170216135621_add_index_for_latest_successful_pipeline.rb b/db/migrate/20170216135621_add_index_for_latest_successful_pipeline.rb new file mode 100644 index 00000000000..7b1e687977b --- /dev/null +++ b/db/migrate/20170216135621_add_index_for_latest_successful_pipeline.rb @@ -0,0 +1,10 @@ +class AddIndexForLatestSuccessfulPipeline < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + DOWNTIME = false + + disable_ddl_transaction! + + def change + add_concurrent_index(:ci_commits, [:gl_project_id, :ref, :status]) + end +end diff --git a/db/migrate/20170216141440_drop_index_for_builds_project_status.rb b/db/migrate/20170216141440_drop_index_for_builds_project_status.rb new file mode 100644 index 00000000000..906711b9f3f --- /dev/null +++ b/db/migrate/20170216141440_drop_index_for_builds_project_status.rb @@ -0,0 +1,8 @@ +class DropIndexForBuildsProjectStatus < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + DOWNTIME = false + + def change + remove_index(:ci_commits, [:gl_project_id, :status]) + end +end diff --git a/db/post_migrate/20161109150329_fix_project_records_with_invalid_visibility.rb b/db/post_migrate/20161109150329_fix_project_records_with_invalid_visibility.rb index df38591a333..14b5ef476f0 100644 --- a/db/post_migrate/20161109150329_fix_project_records_with_invalid_visibility.rb +++ b/db/post_migrate/20161109150329_fix_project_records_with_invalid_visibility.rb @@ -14,15 +14,15 @@ class FixProjectRecordsWithInvalidVisibility < ActiveRecord::Migration finder_sql = projects. - join(namespaces, Arel::Nodes::InnerJoin). - on(projects[:namespace_id].eq(namespaces[:id])). - where(projects[:visibility_level].gt(namespaces[:visibility_level])). - project(projects[:id], namespaces[:visibility_level]). - take(BATCH_SIZE). - to_sql + join(namespaces, Arel::Nodes::InnerJoin). + on(projects[:namespace_id].eq(namespaces[:id])). + where(projects[:visibility_level].gt(namespaces[:visibility_level])). + project(projects[:id], namespaces[:visibility_level]). + take(BATCH_SIZE). + to_sql # Update matching rows in batches. Each batch can cause up to 3 UPDATE - # statements, in addition to the SELECT: one per visibility_level + # statements, in addition to the SELECT: one per visibility_level loop do to_update = connection.exec_query(finder_sql) break if to_update.rows.count == 0 diff --git a/db/post_migrate/20161221153951_rename_reserved_project_names.rb b/db/post_migrate/20161221153951_rename_reserved_project_names.rb index 282837be1fa..49a6bc884a8 100644 --- a/db/post_migrate/20161221153951_rename_reserved_project_names.rb +++ b/db/post_migrate/20161221153951_rename_reserved_project_names.rb @@ -37,7 +37,7 @@ class RenameReservedProjectNames < ActiveRecord::Migration unsubscribes update users - wikis) + wikis).freeze def up queues = Array.new(THREAD_COUNT) { Queue.new } diff --git a/db/schema.rb b/db/schema.rb index 88aaa6c3c55..1d94368f66e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170215200045) do +ActiveRecord::Schema.define(version: 20170216141440) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -111,6 +111,7 @@ ActiveRecord::Schema.define(version: 20170215200045) do t.boolean "plantuml_enabled" t.integer "max_pages_size", default: 100, null: false t.integer "terminal_max_session_time", default: 0, null: false + t.string "default_artifacts_expire_in", default: '0', null: false end create_table "audit_events", force: :cascade do |t| @@ -251,8 +252,8 @@ ActiveRecord::Schema.define(version: 20170215200045) do t.integer "lock_version" end + add_index "ci_commits", ["gl_project_id", "ref", "status"], name: "index_ci_commits_on_gl_project_id_and_ref_and_status", using: :btree 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", "status"], name: "index_ci_commits_on_gl_project_id_and_status", using: :btree add_index "ci_commits", ["gl_project_id"], name: "index_ci_commits_on_gl_project_id", 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 @@ -1277,10 +1278,11 @@ ActiveRecord::Schema.define(version: 20170215200045) do t.datetime "otp_grace_period_started_at" t.boolean "ldap_email", default: false, null: false t.boolean "external", default: false - t.string "organization" t.string "incoming_email_token" + t.string "organization" t.boolean "authorized_projects_populated" t.boolean "notified_of_own_activity", default: false, null: false + t.boolean "ghost" end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree diff --git a/doc/administration/monitoring/prometheus/gitlab_monitor_exporter.md b/doc/administration/monitoring/prometheus/gitlab_monitor_exporter.md index 86ef9d167e2..edb9c911aac 100644 --- a/doc/administration/monitoring/prometheus/gitlab_monitor_exporter.md +++ b/doc/administration/monitoring/prometheus/gitlab_monitor_exporter.md @@ -13,7 +13,7 @@ To enable the GitLab monitor exporter: 1. Add or find and uncomment the following line, making sure it's set to `true`: ```ruby - gitlab_monitor_exporter['enable'] = true + gitlab_monitor['enable'] = true ``` 1. Save the file and [reconfigure GitLab][reconfigure] for the changes to diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md index 1c444cf0d50..62b0468da79 100644 --- a/doc/administration/pages/index.md +++ b/doc/administration/pages/index.md @@ -26,22 +26,24 @@ it works. --- -In the case of custom domains, the Pages daemon needs to listen on ports `80` -and/or `443`. For that reason, there is some flexibility in the way which you -can set it up: +In the case of [custom domains](#custom-domains) (but not +[wildcard domains](#wildcard-domains)), the Pages daemon needs to listen on +ports `80` and/or `443`. For that reason, there is some flexibility in the way +which you can set it up: -1. Run the pages daemon in the same server as GitLab, listening on a secondary IP. -1. Run the pages daemon in a separate server. In that case, the +1. Run the Pages daemon in the same server as GitLab, listening on a secondary IP. +1. Run the Pages daemon in a separate server. In that case, the [Pages path](#change-storage-path) must also be present in the server that - the pages daemon is installed, so you will have to share it via network. -1. Run the pages daemon in the same server as GitLab, listening on the same IP + the Pages daemon is installed, so you will have to share it via network. +1. Run the Pages daemon in the same server as GitLab, listening on the same IP but on different ports. In that case, you will have to proxy the traffic with a loadbalancer. If you choose that route note that you should use TCP load balancing for HTTPS. If you use TLS-termination (HTTPS-load balancing) the pages will not be able to be served with user provided certificates. For HTTP it's OK to use HTTP or TCP load balancing. -In this document, we will proceed assuming the first option. +In this document, we will proceed assuming the first option. If you are not +supporting custom domains a secondary IP is not needed. ## Prerequisites @@ -54,6 +56,7 @@ Before proceeding with the Pages configuration, you will need to: serve Pages under HTTPS. 1. (Optional but recommended) Enable [Shared runners](../../ci/runners/README.md) so that your users don't have to bring their own. +1. (Only for custom domains) Have a **secondary IP**. ### DNS configuration @@ -150,7 +153,7 @@ that without TLS certificates. > URL scheme: `http://page.example.io` and `http://domain.com` -In that case, the pages daemon is running, Nginx still proxies requests to +In that case, the Pages daemon is running, Nginx still proxies requests to the daemon but the daemon is also able to receive requests from the outside world. Custom domains are supported, but no TLS. @@ -179,7 +182,7 @@ world. Custom domains are supported, but no TLS. > URL scheme: `https://page.example.io` and `https://domain.com` -In that case, the pages daemon is running, Nginx still proxies requests to +In that case, the Pages daemon is running, Nginx still proxies requests to the daemon but the daemon is also able to receive requests from the outside world. Custom domains and TLS are supported. diff --git a/doc/administration/reply_by_email.md b/doc/administration/reply_by_email.md index 00494e7e9d6..4f5c22e2d29 100644 --- a/doc/administration/reply_by_email.md +++ b/doc/administration/reply_by_email.md @@ -69,7 +69,9 @@ please consult [RFC 5322](https://tools.ietf.org/html/rfc5322#section-3.6.4). If you want to use Gmail / Google Apps with Reply by email, make sure you have [IMAP access enabled](https://support.google.com/mail/troubleshooter/1668960?hl=en#ts=1665018) -and [allowed less secure apps to access the account](https://support.google.com/accounts/answer/6010255). +and [allowed less secure apps to access the account](https://support.google.com/accounts/answer/6010255) +or [turn-on 2-step validation](https://support.google.com/accounts/answer/185839) +and use [an application password](https://support.google.com/mail/answer/185833). To set up a basic Postfix mail server with IMAP access on Ubuntu, follow the [Postfix setup documentation](reply_by_email_postfix_setup.md). diff --git a/doc/api/groups.md b/doc/api/groups.md index 4a39dbc5555..39adb5be502 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -14,6 +14,7 @@ Parameters: | `order_by` | string | no | Order groups by `name` or `path`. Default is `name` | | `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` | | `statistics` | boolean | no | Include group statistics (admins only) | +| `owned` | boolean | no | Limit by groups owned by the current user | ``` GET /groups @@ -40,20 +41,6 @@ GET /groups You can search for groups by name or path, see below. -## List owned groups - -Get a list of groups which are owned by the authenticated user. - -``` -GET /groups/owned -``` - -Parameters: - -| Attribute | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `statistics` | boolean | no | Include group statistics | - ## List a group's projects Get a list of projects in this group. diff --git a/doc/api/issues.md b/doc/api/issues.md index 6cd701215e9..5266077e098 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -569,7 +569,7 @@ from it. If the user is not subscribed to the issue, the status code `304` is returned. ``` -DELETE /projects/:id/issues/:issue_id/unsubscribe +POST /projects/:id/issues/:issue_id/unsubscribe ``` | Attribute | Type | Required | Description | diff --git a/doc/api/labels.md b/doc/api/labels.md index a1e7eb1a7b1..8e0855fe9e2 100644 --- a/doc/api/labels.md +++ b/doc/api/labels.md @@ -228,7 +228,7 @@ from it. If the user is not subscribed to the label, the status code `304` is returned. ``` -DELETE /projects/:id/labels/:label_id/unsubscribe +POST /projects/:id/labels/:label_id/unsubscribe ``` | Attribute | Type | Required | Description | diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 2a99ae822d7..ea30a163a12 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -741,7 +741,7 @@ notifications from that merge request. If the user is not subscribed to the merge request, the status code `304` is returned. ``` -DELETE /projects/:id/merge_requests/:merge_request_id/unsubscribe +POST /projects/:id/merge_requests/:merge_request_id/unsubscribe ``` | Attribute | Type | Required | Description | diff --git a/doc/api/notes.md b/doc/api/notes.md index 214dfa4068d..dced821cc6d 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -34,8 +34,6 @@ Parameters: "created_at": "2013-10-02T09:22:45Z", "updated_at": "2013-10-02T10:22:45Z", "system": true, - "upvote": false, - "downvote": false, "noteable_id": 377, "noteable_type": "Issue" }, @@ -54,8 +52,6 @@ Parameters: "created_at": "2013-10-02T09:56:03Z", "updated_at": "2013-10-02T09:56:03Z", "system": true, - "upvote": false, - "downvote": false, "noteable_id": 121, "noteable_type": "Issue" } @@ -147,9 +143,7 @@ Example Response: "created_at": "2016-04-05T22:10:44.164Z", "system": false, "noteable_id": 11, - "noteable_type": "Issue", - "upvote": false, - "downvote": false + "noteable_type": "Issue" } ``` @@ -271,9 +265,7 @@ Example Response: "created_at": "2016-04-06T16:51:53.239Z", "system": false, "noteable_id": 52, - "noteable_type": "Snippet", - "upvote": false, - "downvote": false + "noteable_type": "Snippet" } ``` @@ -322,8 +314,6 @@ Parameters: "created_at": "2013-10-02T08:57:14Z", "updated_at": "2013-10-02T08:57:14Z", "system": false, - "upvote": false, - "downvote": false, "noteable_id": 2, "noteable_type": "MergeRequest" } @@ -400,8 +390,6 @@ Example Response: "created_at": "2016-04-05T22:11:59.923Z", "system": false, "noteable_id": 7, - "noteable_type": "MergeRequest", - "upvote": false, - "downvote": false + "noteable_type": "MergeRequest" } ``` diff --git a/doc/api/projects.md b/doc/api/projects.md index 872f570e0f6..1a8c0ae758f 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -407,8 +407,6 @@ Parameters: }, "created_at": "2015-12-04T10:33:56.698Z", "system": false, - "upvote": false, - "downvote": false, "noteable_id": 377, "noteable_type": "Issue" }, diff --git a/doc/api/users.md b/doc/api/users.md index 852c7ac8ec2..d14548e8bbb 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -812,8 +812,6 @@ Example response: }, "created_at": "2015-12-04T10:33:56.698Z", "system": false, - "upvote": false, - "downvote": false, "noteable_id": 377, "noteable_type": "Issue" }, diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md index 1fea3d3407f..c178e224cc5 100644 --- a/doc/api/v3_to_v4.md +++ b/doc/api/v3_to_v4.md @@ -40,3 +40,7 @@ changes are in V4: - POST/PUT/DELETE `:id/repository/files` - Renamed `branch_name` to `branch` on DELETE `id/repository/branches/:branch` response [!8936](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8936) - Remove `public` param from create and edit actions of projects [!8736](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8736) +- Notes do not return deprecated field `upvote` and `downvote` [!9384](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9384) +- Return HTTP status code `400` for all validation errors when creating or updating a member instead of sometimes `422` error. [!9523](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9523) +- Remove `GET /groups/owned`. Use `GET /groups?owned=true` instead [!9505](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9505) +- Return 202 with JSON body on async removals on V4 API (DELETE `/projects/:id/repository/merged_branches` and DELETE `/projects/:id`) [!9449](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9449) diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 8a638ed3df8..620d4744685 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -148,7 +148,8 @@ available in the build environment. It's the recommended method to use for storing things like passwords, secret keys and credentials. Secret variables can be added by going to your project's -**Settings ➔ Variables ➔ Add variable**. +**Settings ➔ CI/CD Pipelines**, then finding the section called +**Secret Variables**. Once you set them, they will be available for all subsequent jobs. diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index dd3ba1283f8..a586b095ef5 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -1018,7 +1018,7 @@ A simple example: ```yaml job1: - coverage: /Code coverage: \d+\.\d+/ + coverage: '/Code coverage: \d+\.\d+/' ``` ## Git Strategy diff --git a/doc/development/ux_guide/users.md b/doc/development/ux_guide/users.md index da410a8de7a..137154e24f3 100644 --- a/doc/development/ux_guide/users.md +++ b/doc/development/ux_guide/users.md @@ -27,19 +27,19 @@ - **Hobbies / interests**<br>Functional programming, open source, gaming, web development and web security. #### Motivations -Steven works for a software development company which currently hires around 80 people. When Steven first joined the company, the engineering team were using Subversion (SVN) as their primary form of source control. However, Steven felt SVN was not flexible enough to work with many feature branches and noticed that developers with less experience of source control struggled with the central-repository nature of SVN. Armed with a wishlist of features, Steven began comparing source control tools. A search for “self-hosted Git server repository management” returned GitLab. In his own words, Steven explains why he wanted the engineering team to start using GitLab: +Nazim works for a software development company which currently hires around 80 people. When Nazim first joined the company, the engineering team were using Subversion (SVN) as their primary form of source control. However, Nazim felt SVN was not flexible enough to work with many feature branches and noticed that developers with less experience of source control struggled with the central-repository nature of SVN. Armed with a wishlist of features, Nazim began comparing source control tools. A search for “self-hosted Git server repository management” returned GitLab. In his own words, Nazim explains why he wanted the engineering team to start using GitLab: > “I wanted them to switch away from SVN. I needed a server application to manage repositories. The common tools that were around just didn’t meet the requirements. Most of them were too simple or plain...GitLab provided all the required features. Also costs had to be low, since we don’t have a big budget for those things...the Community Edition was perfect in this regard.” > -In his role as a full-stack web developer, Steven could recommend products that he would like the engineering team to use, but final approval lay with his line manager, Mike, VP of Engineering. Steven recalls that he was met with reluctance from his colleagues when he raised moving to Git and using GitLab. +In his role as a full-stack web developer, Nazim could recommend products that he would like the engineering team to use, but final approval lay with his line manager, Mike, VP of Engineering. Nazim recalls that he was met with reluctance from his colleagues when he raised moving to Git and using GitLab. > “The biggest challenge...why should we change anything at all from the status quo? We needed to switch from SVN to Git. They knew they needed to learn Git and a Git workflow...using Git was scary to my colleagues...they thought it was more complex than SVN to use.” > -Undeterred, Steven decided to migrate a couple of projects across to GitLab. +Undeterred, Nazim decided to migrate a couple of projects across to GitLab. > “Old SVN users couldn’t see the benefits of Git at first. It took a month or two to convince them.” @@ -47,17 +47,17 @@ Undeterred, Steven decided to migrate a couple of projects across to GitLab. Slowly, by showing his colleagues how easy it was to use Git, the majority of the team’s projects were migrated to GitLab. -The engineering team have been using GitLab CE for around 2 years now. Steven credits himself as being entirely responsible for his company’s decision to move to GitLab. +The engineering team have been using GitLab CE for around 2 years now. Nazim credits himself as being entirely responsible for his company’s decision to move to GitLab. #### Frustrations ##### Adoption to GitLab has been slow -Not only has the engineering team had to get to grips with Git, they’ve also had to adapt to using GitLab. Due to lack of training and existing skills in other tools, the full feature set of GitLab CE is not being utilised. Steven sold GitLab to his manager as an ‘all in one’ tool which would replace multiple tools used within the company, thus saving costs. Steven hasn’t had the time to integrate the legacy tools to GitLab and he’s struggling to convince his peers to change their habits. +Not only has the engineering team had to get to grips with Git, they’ve also had to adapt to using GitLab. Due to lack of training and existing skills in other tools, the full feature set of GitLab CE is not being utilised. Nazim sold GitLab to his manager as an ‘all in one’ tool which would replace multiple tools used within the company, thus saving costs. Nazim hasn’t had the time to integrate the legacy tools to GitLab and he’s struggling to convince his peers to change their habits. ##### Missing Features -Steven’s company want GitLab to be able to do everything. There isn’t a large budget for software, so they’re selective about what tools are implemented. It needs to add real value to the company. In order for GitLab to be widely adopted and to meet the requirements of different roles within the company, it needs a host of features. When an individual within Steven’s company wants to know if GitLab has a specific feature or does a particular thing, Steven is the person to ask. He becomes the point of contact to investigate, build or sometimes just raise the feature request. Steven gets frustrated when GitLab isn’t able to do what he or his colleagues need it to do. +Nazim’s company want GitLab to be able to do everything. There isn’t a large budget for software, so they’re selective about what tools are implemented. It needs to add real value to the company. In order for GitLab to be widely adopted and to meet the requirements of different roles within the company, it needs a host of features. When an individual within Nazim’s company wants to know if GitLab has a specific feature or does a particular thing, Nazim is the person to ask. He becomes the point of contact to investigate, build or sometimes just raise the feature request. Nazim gets frustrated when GitLab isn’t able to do what he or his colleagues need it to do. ##### Regressions and bugs -Steven often has to calm down his colleagues, when a release contains regressions or new bugs. As he puts it “every new version adds something awesome, but breaks something”. He feels that “old issues for "minor" annoyances get quickly buried in the mass of open issues and linger for a very long time. More generally, I have the feeling that GitLab focus on adding new functionalities, but overlook a bunch of annoying minor regressions or introduced bugs.” Due to limited resource and expertise within the team, not only is it difficult to remain up-to-date with the frequent release cycle, it’s also counterproductive to fix workflows every month. +Nazim often has to calm down his colleagues, when a release contains regressions or new bugs. As he puts it “every new version adds something awesome, but breaks something”. He feels that “old issues for "minor" annoyances get quickly buried in the mass of open issues and linger for a very long time. More generally, I have the feeling that GitLab focus on adding new functionalities, but overlook a bunch of annoying minor regressions or introduced bugs.” Due to limited resource and expertise within the team, not only is it difficult to remain up-to-date with the frequent release cycle, it’s also counterproductive to fix workflows every month. ##### Uses too much RAM and CPU > @@ -65,7 +65,7 @@ Steven often has to calm down his colleagues, when a release contains regression > ##### UI/UX -GitLab’s interface initially attracted Steven when he was comparing version control software. He thought it would help his less technical colleagues to adapt to using Git and perhaps, GitLab could be rolled out to other areas of the business, beyond engineering. However, using GitLab’s interface daily has left him frustrated at the lack of personalisation / control over his user experience. He’s also regularly lost in a maze of navigation. Whilst he acknowledges that GitLab listens to its users and that the interface is improving, he becomes annoyed when the changes are too progressive. “Too frequent UI changes. Most of them tend to turn out great after a few cycles of fixes, but the frequency is still far too high for me to feel comfortable to always stay on the current release.” +GitLab’s interface initially attracted Nazim when he was comparing version control software. He thought it would help his less technical colleagues to adapt to using Git and perhaps, GitLab could be rolled out to other areas of the business, beyond engineering. However, using GitLab’s interface daily has left him frustrated at the lack of personalisation / control over his user experience. He’s also regularly lost in a maze of navigation. Whilst he acknowledges that GitLab listens to its users and that the interface is improving, he becomes annoyed when the changes are too progressive. “Too frequent UI changes. Most of them tend to turn out great after a few cycles of fixes, but the frequency is still far too high for me to feel comfortable to always stay on the current release.” #### Goals * To convince his colleagues to fully adopt GitLab CE, thus improving workflow and collaboration. @@ -121,8 +121,8 @@ James and his team use CI quite heavily for several projects. Whilst they’ve w #### Goals * To be able to integrate third party tools easily with GitLab EE and to create custom integrations and patches where needed. -* To use GitLab EE primarily for code hosting, merge requests, continuous integration and issue management. Steven and his team want to be able to understand and use these particular features easily. -* To able to share one instance of GitLab EE with multiple teams across the business. Advanced user management, the ability to separate permissions on different parts of the source code, etc are important to Steven. +* To use GitLab EE primarily for code hosting, merge requests, continuous integration and issue management. James and his team want to be able to understand and use these particular features easily. +* To able to share one instance of GitLab EE with multiple teams across the business. Advanced user management, the ability to separate permissions on different parts of the source code, etc are important to James. <hr> @@ -144,21 +144,21 @@ James and his team use CI quite heavily for several projects. Whilst they’ve w - **Hobbies / interests**<br>Web development, mobile development, UX, open source, gaming and travel. #### Motivations -Harry has been using GitLab.com for around a year. He roughly spends 8 hours every week programming, of that, 2 hours is spent contributing to open source projects. Harry contributes to open source projects to gain programming experience and to give back to the community. He likes GitLab.com for its free private repositories and range of features which provide him with everything he needs for his personal projects. Harry is also a massive fan of GitLab’s values and the fact that it isn’t a “behemoth of a company”. He explains that “displaying every single thing (doc, culture, assumptions, development...) in the open gives me greater confidence to choose Gitlab personally and to recommend it at work.” He’s also an avid reader of GitLab’s blog. +Karolina has been using GitLab.com for around a year. She roughly spends 8 hours every week programming, of that, 2 hours is spent contributing to open source projects. Karolina contributes to open source projects to gain programming experience and to give back to the community. She likes GitLab.com for its free private repositories and range of features which provide her with everything she needs for her personal projects. Karolina is also a massive fan of GitLab’s values and the fact that it isn’t a “behemoth of a company”. She explains that “displaying every single thing (doc, culture, assumptions, development...) in the open gives me greater confidence to choose Gitlab personally and to recommend it at work.” She’s also an avid reader of GitLab’s blog. -Harry works for a software development company which currently hires around 500 people. Harry would love to use GitLab at work but the company has used GitHub Enterprise for a number of years. He describes management at his company as “old fashioned” and explains that it’s “less of a technical issue and more of a cultural issue” to convince upper management to move to GitLab. Harry is also relatively new to the company so he’s apprehensive about pushing too hard to change version control platforms. +Karolina works for a software development company which currently hires around 500 people. Karolina would love to use GitLab at work but the company has used GitHub Enterprise for a number of years. She describes management at her company as “old fashioned” and explains that it’s “less of a technical issue and more of a cultural issue” to convince upper management to move to GitLab. Karolina is also relatively new to the company so she’s apprehensive about pushing too hard to change version control platforms. #### Frustrations ##### Unable to use GitLab at work -Harry wants to use GitLab at work but isn’t sure how to approach the subject with management. In his current role, he doesn’t feel that he has the authority to request GitLab. +Karolina wants to use GitLab at work but isn’t sure how to approach the subject with management. In her current role, she doesn’t feel that she has the authority to request GitLab. ##### Performance -GitLab.com is frequently slow and unavailable. Harry has also heard that GitLab is a “memory hog” which has deterred him from running GitLab on his own machine for just hobby / personal projects. +GitLab.com is frequently slow and unavailable. Karolina has also heard that GitLab is a “memory hog” which has deterred her from running GitLab on her own machine for just hobby / personal projects. ##### UX/UI -Harry has an interest in UX and therefore has strong opinions about how GitLab should look and feel. He feels the interface is cluttered, “it has too many links/buttons” and the navigation “feels a bit weird sometimes. I get lost if I don’t pay attention.” As Harry also enjoys contributing to open-source projects, it’s important to him that GitLab is well designed for public repositories, he doesn’t feel that GitLab currently achieves this. +Karolina has an interest in UX and therefore has strong opinions about how GitLab should look and feel. She feels the interface is cluttered, “it has too many links/buttons” and the navigation “feels a bit weird sometimes. I get lost if I don’t pay attention.” As Karolina also enjoys contributing to open-source projects, it’s important to her that GitLab is well designed for public repositories, she doesn’t feel that GitLab currently achieves this. #### Goals -* To develop his programming experience and to learn from other developers. -* To contribute to both his own and other open source projects. +* To develop her programming experience and to learn from other developers. +* To contribute to both her own and other open source projects. * To use a fast and intuitive version control platform.
\ No newline at end of file diff --git a/doc/integration/auth0.md b/doc/integration/auth0.md index 212b4854dd7..c39d7ab57c6 100644 --- a/doc/integration/auth0.md +++ b/doc/integration/auth0.md @@ -54,7 +54,7 @@ for initial settings. gitlab_rails['omniauth_providers'] = [ { "name" => "auth0", - "args" => { client_id: 'YOUR_AUTH0_CLIENT_ID'', + "args" => { client_id: 'YOUR_AUTH0_CLIENT_ID', client_secret: 'YOUR_AUTH0_CLIENT_SECRET', namespace: 'YOUR_AUTH0_DOMAIN' } diff --git a/doc/integration/saml.md b/doc/integration/saml.md index 7a809eddac0..2277aa827b7 100644 --- a/doc/integration/saml.md +++ b/doc/integration/saml.md @@ -74,7 +74,7 @@ in your SAML IdP: idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', idp_sso_target_url: 'https://login.example.com/idp', issuer: 'https://gitlab.example.com', - name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' + name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent' }, label: 'Company Login' # optional label for SAML login button, defaults to "Saml" } @@ -91,7 +91,7 @@ in your SAML IdP: idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', idp_sso_target_url: 'https://login.example.com/idp', issuer: 'https://gitlab.example.com', - name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' + name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent' }, label: 'Company Login' # optional label for SAML login button, defaults to "Saml" } @@ -172,7 +172,7 @@ tell GitLab which groups are external via the `external_groups:` element: idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', idp_sso_target_url: 'https://login.example.com/idp', issuer: 'https://gitlab.example.com', - name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' + name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent' } } ``` @@ -227,7 +227,7 @@ args: { idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', idp_sso_target_url: 'https://login.example.com/idp', issuer: 'https://gitlab.example.com', - name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient', + name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', attribute_statements: { email: ['EmailAddress'] } } ``` @@ -245,7 +245,7 @@ args: { idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', idp_sso_target_url: 'https://login.example.com/idp', issuer: 'https://gitlab.example.com', - name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient', + name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', attribute_statements: { email: ['EmailAddress'] }, allowed_clock_drift: 1 # for one second clock drift } diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md index b8d24cb2d3b..eb6f915f3f4 100644 --- a/doc/user/admin_area/settings/continuous_integration.md +++ b/doc/user/admin_area/settings/continuous_integration.md @@ -3,18 +3,36 @@ ## Maximum artifacts size The maximum size of the [job artifacts][art-yml] can be set in the Admin area -of your GitLab instance. The value is in MB and the default is 100MB. Note that -this setting is set for each job. +of your GitLab instance. The value is in *MB* and the default is 100MB. Note +that this setting is set for each job. 1. Go to **Admin area > Settings** (`/admin/application_settings`). ![Admin area settings button](img/admin_area_settings_button.png) -1. Change the value of the maximum artifacts size (in MB): +1. Change the value of maximum artifacts size (in MB): ![Admin area maximum artifacts size](img/admin_area_maximum_artifacts_size.png) 1. Hit **Save** for the changes to take effect. +## Default artifacts expiration + +The default expiration time of the [job artifacts][art-yml] can be set in +the Admin area of your GitLab instance. The syntax of duration is described +in [artifacts:expire_in][duration-syntax]. The default is `30 days`. Note that +this setting is set for each job. Set it to 0 if you don't want default +expiration. + +1. Go to **Admin area > Settings** (`/admin/application_settings`). + + ![Admin area settings button](img/admin_area_settings_button.png) + +1. Change the value of default expiration time ([syntax][duration-syntax]): + + ![Admin area default artifacts expiration](img/admin_area_default_artifacts_expiration.png) + +1. Hit **Save** for the changes to take effect. [art-yml]: ../../../administration/job_artifacts.md +[duration-syntax]: ../../../ci/yaml/README.md#artifactsexpire_in diff --git a/doc/user/admin_area/settings/img/admin_area_default_artifacts_expiration.png b/doc/user/admin_area/settings/img/admin_area_default_artifacts_expiration.png Binary files differnew file mode 100644 index 00000000000..50a86ede56b --- /dev/null +++ b/doc/user/admin_area/settings/img/admin_area_default_artifacts_expiration.png diff --git a/doc/user/admin_area/settings/img/admin_area_maximum_artifacts_size.png b/doc/user/admin_area/settings/img/admin_area_maximum_artifacts_size.png Binary files differindex b7d6671902a..33fd29e2039 100644 --- a/doc/user/admin_area/settings/img/admin_area_maximum_artifacts_size.png +++ b/doc/user/admin_area/settings/img/admin_area_maximum_artifacts_size.png diff --git a/doc/user/project/integrations/img/mattermost_configuration.png b/doc/user/project/integrations/img/mattermost_configuration.png Binary files differindex 3c5ff5ee317..f52acf4ef3b 100644 --- a/doc/user/project/integrations/img/mattermost_configuration.png +++ b/doc/user/project/integrations/img/mattermost_configuration.png diff --git a/doc/user/project/integrations/img/slack_configuration.png b/doc/user/project/integrations/img/slack_configuration.png Binary files differindex fc8e58e686b..527824fc3eb 100644 --- a/doc/user/project/integrations/img/slack_configuration.png +++ b/doc/user/project/integrations/img/slack_configuration.png diff --git a/doc/user/project/integrations/mattermost.md b/doc/user/project/integrations/mattermost.md index 09ba9994d3a..cfb0931273d 100644 --- a/doc/user/project/integrations/mattermost.md +++ b/doc/user/project/integrations/mattermost.md @@ -24,23 +24,24 @@ There, you will see a checkbox with the following events that can be triggered: - Push - Issue +- Confidential issue - Merge request - Note - Tag push - Build +- Pipeline - Wiki page -Bellow each of these event checkboxes, you will have an input field to insert -which Mattermost channel you want to send that event message, with `#town-square` -being the default. The hash sign is optional. +Below each of these event checkboxes, you have an input field to enter +which Mattermost channel you want to send that event message. Enter your preferred channel handle (the hash sign `#` is optional). At the end, fill in your Mattermost details: | Field | Description | | ----- | ----------- | -| **Webhook** | The incoming webhooks which you have to setup on Mattermost, it will be something like: http://mattermost.example/hooks/5xo... | +| **Webhook** | The incoming webhook URL which you have to setup on Mattermost, it will be something like: http://mattermost.example/hooks/5xo… | | **Username** | Optional username which can be on messages sent to Mattermost. Fill this in if you want to change the username of the bot. | | **Notify only broken builds** | If you choose to enable the **Build** event and you want to be only notified about failed builds. | - +| **Notify only broken pipelines** | If you choose to enable the **Pipeline** event and you want to be only notified about failed pipelines. | ![Mattermost configuration](img/mattermost_configuration.png) diff --git a/doc/user/project/integrations/slack.md b/doc/user/project/integrations/slack.md index 57a9492044b..f27f9a726fc 100644 --- a/doc/user/project/integrations/slack.md +++ b/doc/user/project/integrations/slack.md @@ -21,23 +21,25 @@ There, you will see a checkbox with the following events that can be triggered: - Push - Issue +- Confidential issue - Merge request - Note - Tag push - Build +- Pipeline - Wiki page -Bellow each of these event checkboxes, you will have an input field to insert -which Slack channel you want to send that event message, with `#general` -being the default. Enter your preferred channel **without** the hash sign (`#`). +Below each of these event checkboxes, you have an input field to enter +which Slack channel you want to send that event message. Enter your preferred channel name **without** the hash sign (`#`). At the end, fill in your Slack details: | Field | Description | | ----- | ----------- | | **Webhook** | The [incoming webhook URL][slackhook] which you have to setup on Slack. | -| **Username** | Optional username which can be on messages sent to slack. Fill this in if you want to change the username of the bot. | +| **Username** | Optional username which can be on messages sent to Slack. Fill this in if you want to change the username of the bot. | | **Notify only broken builds** | If you choose to enable the **Build** event and you want to be only notified about failed builds. | +| **Notify only broken pipelines** | If you choose to enable the **Pipeline** event and you want to be only notified about failed pipelines. | After you are all done, click **Save changes** for the changes to take effect. diff --git a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md index 8c5020bee37..9cc45065eb2 100644 --- a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md +++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md @@ -63,6 +63,12 @@ git commit -am "Added Debian iso" # commit the file meta data git push origin master # sync the git repo and large file to the GitLab server ``` +>**Note**: Make sure that `.gitattributes` is tracked by git. Otherwise Git + LFS will not be working properly for people cloning the project. + ```bash + git add .gitattributes + ``` + Cloning the repository works the same as before. Git automatically detects the LFS-tracked files and clones them via HTTP. If you performed the git clone command with a SSH URL, you have to enter your GitLab credentials for HTTP diff --git a/features/project/commits/branches.feature b/features/project/commits/branches.feature index 88fef674c0c..c57376aecff 100644 --- a/features/project/commits/branches.feature +++ b/features/project/commits/branches.feature @@ -13,6 +13,7 @@ Feature: Project Commits Branches Given I visit project protected branches page Then I should see "Shop" protected branches list + @javascript Scenario: I create a branch Given I visit project branches page And I click new branch link @@ -33,12 +34,7 @@ Feature: Project Commits Branches And I submit new branch form with invalid name Then I should see new an error that branch is invalid - Scenario: I create a branch with invalid reference - Given I visit project branches page - And I click new branch link - And I submit new branch form with invalid reference - Then I should see new an error that ref is invalid - + @javascript Scenario: I create a branch that already exists Given I visit project branches page And I click new branch link diff --git a/features/steps/explore/projects.rb b/features/steps/explore/projects.rb index 2b4a5ab0864..7dc33ab5683 100644 --- a/features/steps/explore/projects.rb +++ b/features/steps/explore/projects.rb @@ -49,7 +49,7 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps step 'I should see an http link to the repository' do project = Project.find_by(name: 'Community') - expect(page).to have_field('project_clone', with: project.http_url_to_repo) + expect(page).to have_field('project_clone', with: project.http_url_to_repo(@user)) end step 'I should see an ssh link to the repository' do diff --git a/features/steps/project/builds/artifacts.rb b/features/steps/project/builds/artifacts.rb index 055fca036d3..be0f6eee55a 100644 --- a/features/steps/project/builds/artifacts.rb +++ b/features/steps/project/builds/artifacts.rb @@ -76,7 +76,7 @@ class Spinach::Features::ProjectBuildsArtifacts < Spinach::FeatureSteps base64_params = send_data.sub(/\Aartifacts\-entry:/, '') params = JSON.parse(Base64.urlsafe_decode64(base64_params)) - expect(params.keys).to eq(['Archive', 'Entry']) + expect(params.keys).to eq(%w(Archive Entry)) expect(params['Archive']).to end_with('build_artifacts.zip') expect(params['Entry']).to eq(Base64.encode64('ci_artifacts.txt')) end diff --git a/features/steps/project/commits/branches.rb b/features/steps/project/commits/branches.rb index 5f9b9e0445e..ccaf3237815 100644 --- a/features/steps/project/commits/branches.rb +++ b/features/steps/project/commits/branches.rb @@ -34,25 +34,19 @@ class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps step 'I submit new branch form' do fill_in 'branch_name', with: 'deploy_keys' - fill_in 'ref', with: 'master' + select_branch('master') click_button 'Create branch' end step 'I submit new branch form with invalid name' do fill_in 'branch_name', with: '1.0 stable' - fill_in 'ref', with: 'master' - click_button 'Create branch' - end - - step 'I submit new branch form with invalid reference' do - fill_in 'branch_name', with: 'foo' - fill_in 'ref', with: 'foo' + select_branch('master') click_button 'Create branch' end step 'I submit new branch form with branch that already exists' do fill_in 'branch_name', with: 'master' - fill_in 'ref', with: 'master' + select_branch('master') click_button 'Create branch' end @@ -65,10 +59,6 @@ class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps expect(page).to have_content "can't contain spaces" end - step 'I should see new an error that ref is invalid' do - expect(page).to have_content 'Invalid reference name' - end - step 'I should see new an error that branch already exists' do expect(page).to have_content 'Branch already exists' end @@ -88,4 +78,12 @@ class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps step "I should not see branch 'improve/awesome'" do expect(page.all(visible: true)).not_to have_content 'improve/awesome' end + + def select_branch(branch_name) + click_button 'master' + + page.within '#new-branch-form .dropdown-menu' do + click_link branch_name + end + end end diff --git a/features/steps/shared/issuable.rb b/features/steps/shared/issuable.rb index 79dde620265..3d9cedf5c2d 100644 --- a/features/steps/shared/issuable.rb +++ b/features/steps/shared/issuable.rb @@ -153,7 +153,7 @@ module SharedIssuable case type when :issue - attrs.merge!(project: project) + attrs[:project] = project when :merge_request attrs.merge!( source_project: project, diff --git a/lib/api/api.rb b/lib/api/api.rb index a0282ff8deb..7aa95a4a3c1 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -10,11 +10,13 @@ module API mount ::API::V3::Commits mount ::API::V3::DeployKeys mount ::API::V3::Files + mount ::API::V3::Groups mount ::API::V3::Issues mount ::API::V3::Labels mount ::API::V3::Members mount ::API::V3::MergeRequestDiffs mount ::API::V3::MergeRequests + mount ::API::V3::Notes mount ::API::V3::ProjectHooks mount ::API::V3::Projects mount ::API::V3::ProjectSnippets @@ -89,6 +91,7 @@ module API mount ::API::Projects mount ::API::ProjectSnippets mount ::API::Repositories + mount ::API::Runner mount ::API::Runners mount ::API::Services mount ::API::Session diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb index df6db140d0e..c11f8529183 100644 --- a/lib/api/api_guard.rb +++ b/lib/api/api_guard.rb @@ -6,7 +6,7 @@ module API module APIGuard extend ActiveSupport::Concern - PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN" + PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN".freeze PRIVATE_TOKEN_PARAM = :private_token included do |base| @@ -114,8 +114,8 @@ module API private def install_error_responders(base) - error_classes = [ MissingTokenError, TokenNotFoundError, - ExpiredError, RevokedError, InsufficientScopeError] + error_classes = [MissingTokenError, TokenNotFoundError, + ExpiredError, RevokedError, InsufficientScopeError] base.send :rescue_from, *error_classes, oauth2_bearer_token_error_handler end diff --git a/lib/api/award_emoji.rb b/lib/api/award_emoji.rb index 2ef327217ea..301271118d4 100644 --- a/lib/api/award_emoji.rb +++ b/lib/api/award_emoji.rb @@ -3,7 +3,7 @@ module API include PaginationParams before { authenticate! } - AWARDABLES = %w[issue merge_request snippet] + AWARDABLES = %w[issue merge_request snippet].freeze resource :projects do AWARDABLES.each do |awardable_type| @@ -15,7 +15,8 @@ module API requires :"#{awardable_id_string}", type: Integer, desc: "The ID of an Issue, Merge Request or Snippet" end - [ ":id/#{awardable_string}/:#{awardable_id_string}/award_emoji", + [ + ":id/#{awardable_string}/:#{awardable_id_string}/award_emoji", ":id/#{awardable_string}/:#{awardable_id_string}/notes/:note_id/award_emoji" ].each do |endpoint| diff --git a/lib/api/branches.rb b/lib/api/branches.rb index c65de90cca2..34f136948c2 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -137,7 +137,7 @@ module API delete ":id/repository/merged_branches" do DeleteMergedBranchesService.new(user_project, current_user).async_execute - status(200) + accepted! end end end diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 44fe0fc4a95..5b76913fe45 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -11,7 +11,7 @@ module API helpers do params :optional_scope do optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show', - values: ['pending', 'running', 'failed', 'success', 'canceled'], + values: ::CommitStatus::AVAILABLE_STATUSES, coerce_with: ->(scope) { if scope.is_a?(String) [scope] diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index 0b6076bd28c..dba0831664c 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -40,7 +40,7 @@ module API requires :id, type: String, desc: 'The ID of a project' requires :sha, type: String, desc: 'The commit hash' requires :state, type: String, desc: 'The state of the status', - values: ['pending', 'running', 'success', 'failed', 'canceled'] + values: %w(pending running success failed canceled) optional :ref, type: String, desc: 'The ref' optional :target_url, type: String, desc: 'The target URL to associate with this status' optional :description, type: String, desc: 'A short description of the status' diff --git a/lib/api/commits.rb b/lib/api/commits.rb index 0cd817f9352..fd03e92264d 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -52,13 +52,6 @@ module API attrs = declared_params.merge(start_branch: declared_params[:branch], target_branch: declared_params[:branch]) - attrs[:actions].map! do |action| - action[:action] = action[:action].to_sym - action[:file_path].slice!(0) if action[:file_path] && action[:file_path].start_with?('/') - action[:previous_path].slice!(0) if action[:previous_path] && action[:previous_path].start_with?('/') - action - end - result = ::Files::MultiService.new(user_project, current_user, attrs).execute if result[:status] == :success @@ -157,7 +150,7 @@ module API optional :path, type: String, desc: 'The file path' given :path do requires :line, type: Integer, desc: 'The line number' - requires :line_type, type: String, values: ['new', 'old'], default: 'new', desc: 'The type of the line' + requires :line_type, type: String, values: %w(new old), default: 'new', desc: 'The type of the line' end end post ':id/repository/commits/:sha/comments' do diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 400ee7c92aa..a99d9cadc8a 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -339,9 +339,6 @@ module API expose :created_at, :updated_at expose :system?, as: :system expose :noteable_id, :noteable_type - # upvote? and downvote? are deprecated, always return false - expose(:upvote?) { |note| false } - expose(:downvote?) { |note| false } end class AwardEmoji < Grape::Entity @@ -560,6 +557,7 @@ module API expose :default_project_visibility expose :default_snippet_visibility expose :default_group_visibility + expose :default_artifacts_expire_in expose :domain_whitelist expose :domain_blacklist_enabled expose :domain_blacklist @@ -620,6 +618,10 @@ module API end end + class RunnerRegistrationDetails < Grape::Entity + expose :id, :token + end + class BuildArtifactFile < Grape::Entity expose :filename, :size end diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 9f29c4466ab..9cffd6180ae 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -36,12 +36,15 @@ module API optional :skip_groups, type: Array[Integer], desc: 'Array of group ids to exclude from list' optional :all_available, type: Boolean, desc: 'Show all group that you have access to' optional :search, type: String, desc: 'Search for a specific group' + optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user' optional :order_by, type: String, values: %w[name path], default: 'name', desc: 'Order by name or path' optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)' use :pagination end get do - groups = if current_user.admin + groups = if params[:owned] + current_user.owned_groups + elsif current_user.admin Group.all elsif params[:all_available] GroupsFinder.new.execute(current_user) @@ -56,17 +59,6 @@ module API present_groups groups, statistics: params[:statistics] && current_user.is_admin? end - desc 'Get list of owned groups for authenticated user' do - success Entities::Group - end - params do - use :pagination - use :statistics_params - end - get '/owned' do - present_groups current_user.owned_groups, statistics: params[:statistics] - end - desc 'Create a group. Available only for users who can create groups.' do success Entities::Group end diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index a1db2099693..72d2b320077 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -3,7 +3,7 @@ module API include Gitlab::Utils include Helpers::Pagination - SUDO_HEADER = "HTTP_SUDO" + SUDO_HEADER = "HTTP_SUDO".freeze SUDO_PARAM = :sudo def declared_params(options = {}) @@ -209,6 +209,10 @@ module API render_api_error!('204 No Content', 204) end + def accepted! + render_api_error!('202 Accepted', 202) + end + def render_validation_error!(model) if model.errors.any? render_api_error!(model.errors.messages || '400 Bad Request', 400) diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb new file mode 100644 index 00000000000..119ca81b883 --- /dev/null +++ b/lib/api/helpers/runner.rb @@ -0,0 +1,23 @@ +module API + module Helpers + module Runner + def runner_registration_token_valid? + ActiveSupport::SecurityUtils.variable_size_secure_compare(params[:token], + current_application_settings.runners_registration_token) + end + + def get_runner_version_from_params + return unless params['info'].present? + attributes_for_keys(%w(name version revision platform architecture), params['info']) + end + + def authenticate_runner! + forbidden! unless current_runner + end + + def current_runner + @runner ||= ::Ci::Runner.find_by_token(params[:token].to_s) + end + end + end +end diff --git a/lib/api/members.rb b/lib/api/members.rb index d1d78775c6d..5f6913d1a27 100644 --- a/lib/api/members.rb +++ b/lib/api/members.rb @@ -55,7 +55,6 @@ module API authorize_admin_source!(source_type, source) member = source.members.find_by(user_id: params[:user_id]) - conflict!('Member already exists') if member member = source.add_user(params[:user_id], params[:access_level], current_user: current_user, expires_at: params[:expires_at]) @@ -63,9 +62,6 @@ module API if member.persisted? && member.valid? present member.user, with: Entities::Member, member: member else - # This is to ensure back-compatibility but 400 behavior should be used - # for all validation errors in 9.0! - render_api_error!('Access level is not known', 422) if member.errors.key?(:access_level) render_validation_error!(member) end end @@ -79,18 +75,14 @@ module API optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY' end put ":id/members/:user_id" do - source = find_source(source_type, params[:id]) + source = find_source(source_type, params.delete(:id)) authorize_admin_source!(source_type, source) - member = source.members.find_by!(user_id: params[:user_id]) - attrs = attributes_for_keys [:access_level, :expires_at] + member = source.members.find_by!(user_id: params.delete(:user_id)) - if member.update_attributes(attrs) + if member.update_attributes(declared_params(include_missing: false)) present member.user, with: Entities::Member, member: member else - # This is to ensure back-compatibility but 400 behavior should be used - # for all validation errors in 9.0! - render_api_error!('Access level is not known', 422) if member.errors.key?(:access_level) render_validation_error!(member) end end diff --git a/lib/api/notes.rb b/lib/api/notes.rb index 8beccaaabd1..f559a7f74a0 100644 --- a/lib/api/notes.rb +++ b/lib/api/notes.rb @@ -4,7 +4,7 @@ module API before { authenticate! } - NOTEABLE_TYPES = [Issue, MergeRequest, Snippet] + NOTEABLE_TYPES = [Issue, MergeRequest, Snippet].freeze params do requires :id, type: String, desc: 'The ID of a project' @@ -85,7 +85,7 @@ module API note = ::Notes::CreateService.new(user_project, current_user, opts).execute if note.valid? - present note, with: Entities::const_get(note.class.name) + present note, with: Entities.const_get(note.class.name) else not_found!("Note #{note.errors.messages}") end diff --git a/lib/api/pipelines.rb b/lib/api/pipelines.rb index f59f7959173..3afc1e385fe 100644 --- a/lib/api/pipelines.rb +++ b/lib/api/pipelines.rb @@ -14,7 +14,7 @@ module API end params do use :pagination - optional :scope, type: String, values: ['running', 'branches', 'tags'], + optional :scope, type: String, values: %w(running branches tags), desc: 'Either running, branches, or tags' end get ':id/pipelines' do diff --git a/lib/api/projects.rb b/lib/api/projects.rb index f1cb1b22143..b89bddc7e29 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -19,7 +19,8 @@ module API optional :visibility_level, type: Integer, values: [ Gitlab::VisibilityLevel::PRIVATE, Gitlab::VisibilityLevel::INTERNAL, - Gitlab::VisibilityLevel::PUBLIC ], desc: 'Create a public project. The same as visibility_level = 20.' + Gitlab::VisibilityLevel::PUBLIC + ], desc: 'Create a public project. The same as visibility_level = 20.' optional :public_builds, type: Boolean, desc: 'Perform public builds' optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access' optional :only_allow_merge_if_build_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed' @@ -281,6 +282,8 @@ module API delete ":id" do authorize! :remove_project, user_project ::Projects::DestroyService.new(user_project, current_user, {}).async_execute + + accepted! end desc 'Mark this project as forked from another' diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb index bfda6f45b0a..36166780149 100644 --- a/lib/api/repositories.rb +++ b/lib/api/repositories.rb @@ -45,7 +45,7 @@ module API requires :sha, type: String, desc: 'The commit, branch name, or tag name' requires :filepath, type: String, desc: 'The path to the file to display' end - get [ ":id/repository/blobs/:sha", ":id/repository/commits/:sha/blob" ] do + get [":id/repository/blobs/:sha", ":id/repository/commits/:sha/blob"] do repo = user_project.repository commit = repo.commit(params[:sha]) diff --git a/lib/api/runner.rb b/lib/api/runner.rb new file mode 100644 index 00000000000..804b27d40a7 --- /dev/null +++ b/lib/api/runner.rb @@ -0,0 +1,52 @@ +module API + class Runner < Grape::API + helpers ::API::Helpers::Runner + + resource :runners do + desc 'Registers a new Runner' do + success Entities::RunnerRegistrationDetails + http_codes [[201, 'Runner was created'], [403, 'Forbidden']] + end + params do + requires :token, type: String, desc: 'Registration token' + optional :description, type: String, desc: %q(Runner's description) + optional :info, type: Hash, desc: %q(Runner's metadata) + optional :locked, type: Boolean, desc: 'Should Runner be locked for current project' + optional :run_untagged, type: Boolean, desc: 'Should Runner handle untagged jobs' + optional :tag_list, type: Array[String], desc: %q(List of Runner's tags) + end + post '/' do + attributes = attributes_for_keys [:description, :locked, :run_untagged, :tag_list] + + runner = + if runner_registration_token_valid? + # Create shared runner. Requires admin access + Ci::Runner.create(attributes.merge(is_shared: true)) + elsif project = Project.find_by(runners_token: params[:token]) + # Create a specific runner for project. + project.runners.create(attributes) + end + + return forbidden! unless runner + + if runner.id + runner.update(get_runner_version_from_params) + present runner, with: Entities::RunnerRegistrationDetails + else + not_found! + end + end + + desc 'Deletes a registered Runner' do + http_codes [[200, 'Runner was deleted'], [403, 'Forbidden']] + end + params do + requires :token, type: String, desc: %q(Runner's authentication token) + end + delete '/' do + authenticate_runner! + Ci::Runner.find_by_token(params[:token]).destroy + end + end + end +end diff --git a/lib/api/runners.rb b/lib/api/runners.rb index 4fbd4096533..252e59bfa58 100644 --- a/lib/api/runners.rb +++ b/lib/api/runners.rb @@ -14,7 +14,7 @@ module API use :pagination end get do - runners = filter_runners(current_user.ci_authorized_runners, params[:scope], without: ['specific', 'shared']) + runners = filter_runners(current_user.ci_authorized_runners, params[:scope], without: %w(specific shared)) present paginate(runners), with: Entities::Runner end diff --git a/lib/api/settings.rb b/lib/api/settings.rb index 747ceb4e3e0..936c7e0930b 100644 --- a/lib/api/settings.rb +++ b/lib/api/settings.rb @@ -56,7 +56,8 @@ module API given shared_runners_enabled: ->(val) { val } do requires :shared_runners_text, type: String, desc: 'Shared runners text ' end - optional :max_artifacts_size, type: Integer, desc: "Set the maximum file size each build's artifacts can have" + optional :max_artifacts_size, type: Integer, desc: "Set the maximum file size for each job's artifacts" + optional :default_artifacts_expire_in, type: String, desc: "Set the default expiration time for each job's artifacts" optional :max_pages_size, type: Integer, desc: 'Maximum size of pages in MB' optional :container_registry_token_expire_delay, type: Integer, desc: 'Authorization token duration (minutes)' optional :metrics_enabled, type: Boolean, desc: 'Enable the InfluxDB metrics' @@ -117,7 +118,9 @@ module API :send_user_confirmation_email, :domain_whitelist, :domain_blacklist_enabled, :after_sign_up_text, :signin_enabled, :require_two_factor_authentication, :home_page_url, :after_sign_out_path, :sign_in_text, :help_page_text, - :shared_runners_enabled, :max_artifacts_size, :max_pages_size, :container_registry_token_expire_delay, + :shared_runners_enabled, :max_artifacts_size, + :default_artifacts_expire_in, :max_pages_size, + :container_registry_token_expire_delay, :metrics_enabled, :sidekiq_throttling_enabled, :recaptcha_enabled, :akismet_enabled, :admin_notification_email, :sentry_enabled, :repository_storage, :repository_checks_enabled, :koding_enabled, :plantuml_enabled, diff --git a/lib/api/subscriptions.rb b/lib/api/subscriptions.rb index acf11dbdf26..772b5cca017 100644 --- a/lib/api/subscriptions.rb +++ b/lib/api/subscriptions.rb @@ -3,7 +3,6 @@ module API before { authenticate! } subscribable_types = { - 'merge_request' => proc { |id| find_merge_request_with_access(id, :update_merge_request) }, 'merge_requests' => proc { |id| find_merge_request_with_access(id, :update_merge_request) }, 'issues' => proc { |id| find_project_issue(id) }, 'labels' => proc { |id| find_project_label(id) }, diff --git a/lib/api/todos.rb b/lib/api/todos.rb index 0b9650b296c..e59030428da 100644 --- a/lib/api/todos.rb +++ b/lib/api/todos.rb @@ -7,7 +7,7 @@ module API ISSUABLE_TYPES = { 'merge_requests' => ->(id) { find_merge_request_with_access(id) }, 'issues' => ->(id) { find_project_issue(id) } - } + }.freeze params do requires :id, type: String, desc: 'The ID of a project' diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb index 87a717ba751..ea0ad852633 100644 --- a/lib/api/triggers.rb +++ b/lib/api/triggers.rb @@ -21,14 +21,9 @@ module API unauthorized! unless trigger.project == project # validate variables - variables = params[:variables] - if variables - unless variables.all? { |key, value| key.is_a?(String) && value.is_a?(String) } - render_api_error!('variables needs to be a map of key-valued strings', 400) - end - - # convert variables from Mash to Hash - variables = variables.to_h + variables = params[:variables].to_h + unless variables.all? { |key, value| key.is_a?(String) && value.is_a?(String) } + render_api_error!('variables needs to be a map of key-valued strings', 400) end # create request and trigger builds diff --git a/lib/api/users.rb b/lib/api/users.rb index fbc17953691..94b2b6653d2 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -172,7 +172,7 @@ module API end end - user_params.merge!(password_expires_at: Time.now) if user_params[:password].present? + user_params[:password_expires_at] = Time.now if user_params[:password].present? if user.update_attributes(user_params.except(:extern_uid, :provider)) present user, with: Entities::UserPublic diff --git a/lib/api/v3/branches.rb b/lib/api/v3/branches.rb index 733c6b21be5..51eb566cf7d 100644 --- a/lib/api/v3/branches.rb +++ b/lib/api/v3/branches.rb @@ -18,6 +18,13 @@ module API present branches, with: ::API::Entities::RepoBranch, project: user_project end + + desc 'Delete all merged branches' + delete ":id/repository/merged_branches" do + DeleteMergedBranchesService.new(user_project, current_user).async_execute + + status(200) + end end end end diff --git a/lib/api/v3/commits.rb b/lib/api/v3/commits.rb index 477e22fd25e..506204b3517 100644 --- a/lib/api/v3/commits.rb +++ b/lib/api/v3/commits.rb @@ -55,13 +55,6 @@ module API branch = attrs.delete(:branch_name) attrs.merge!(branch: branch, start_branch: branch, target_branch: branch) - attrs[:actions].map! do |action| - action[:action] = action[:action].to_sym - action[:file_path].slice!(0) if action[:file_path] && action[:file_path].start_with?('/') - action[:previous_path].slice!(0) if action[:previous_path] && action[:previous_path].start_with?('/') - action - end - result = ::Files::MultiService.new(user_project, current_user, attrs).execute if result[:status] == :success @@ -162,7 +155,7 @@ module API optional :path, type: String, desc: 'The file path' given :path do requires :line, type: Integer, desc: 'The line number' - requires :line_type, type: String, values: ['new', 'old'], default: 'new', desc: 'The type of the line' + requires :line_type, type: String, values: %w(new old), default: 'new', desc: 'The type of the line' end end post ':id/repository/commits/:sha/comments' do diff --git a/lib/api/v3/entities.rb b/lib/api/v3/entities.rb index 3cc0dc968a8..11d0e6dbf71 100644 --- a/lib/api/v3/entities.rb +++ b/lib/api/v3/entities.rb @@ -11,6 +11,40 @@ module API Gitlab::UrlBuilder.build(snippet) end end + + class Note < Grape::Entity + expose :id + expose :note, as: :body + expose :attachment_identifier, as: :attachment + expose :author, using: ::API::Entities::UserBasic + expose :created_at, :updated_at + expose :system?, as: :system + expose :noteable_id, :noteable_type + # upvote? and downvote? are deprecated, always return false + expose(:upvote?) { |note| false } + expose(:downvote?) { |note| false } + end + + class Event < Grape::Entity + expose :title, :project_id, :action_name + expose :target_id, :target_type, :author_id + expose :data, :target_title + expose :created_at + expose :note, using: Entities::Note, if: ->(event, options) { event.note? } + expose :author, using: ::API::Entities::UserBasic, if: ->(event, options) { event.author } + + expose :author_username do |event, options| + event.author&.username + end + end + + class AwardEmoji < Grape::Entity + expose :id + expose :name + expose :user, using: ::API::Entities::UserBasic + expose :created_at, :updated_at + expose :awardable_id, :awardable_type + end end end end diff --git a/lib/api/v3/groups.rb b/lib/api/v3/groups.rb new file mode 100644 index 00000000000..c826bc4fe0b --- /dev/null +++ b/lib/api/v3/groups.rb @@ -0,0 +1,38 @@ +module API + module V3 + class Groups < Grape::API + include PaginationParams + + before { authenticate! } + + helpers do + params :statistics_params do + optional :statistics, type: Boolean, default: false, desc: 'Include project statistics' + end + + def present_groups(groups, options = {}) + options = options.reverse_merge( + with: ::API::Entities::Group, + current_user: current_user, + ) + + groups = groups.with_statistics if options[:statistics] + present paginate(groups), options + end + end + + resource :groups do + desc 'Get list of owned groups for authenticated user' do + success ::API::Entities::Group + end + params do + use :pagination + use :statistics_params + end + get '/owned' do + present_groups current_user.owned_groups, statistics: params[:statistics] + end + end + end + end +end diff --git a/lib/api/v3/members.rb b/lib/api/v3/members.rb index 4e6cb2e3c52..19f276d5484 100644 --- a/lib/api/v3/members.rb +++ b/lib/api/v3/members.rb @@ -86,13 +86,12 @@ module API optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY' end put ":id/members/:user_id" do - source = find_source(source_type, params[:id]) + source = find_source(source_type, params.delete(:id)) authorize_admin_source!(source_type, source) - member = source.members.find_by!(user_id: params[:user_id]) - attrs = attributes_for_keys [:access_level, :expires_at] + member = source.members.find_by!(user_id: params.delete(:user_id)) - if member.update_attributes(attrs) + if member.update_attributes(declared_params(include_missing: false)) present member.user, with: ::API::Entities::Member, member: member else # This is to ensure back-compatibility but 400 behavior should be used diff --git a/lib/api/v3/notes.rb b/lib/api/v3/notes.rb new file mode 100644 index 00000000000..0796bb62e68 --- /dev/null +++ b/lib/api/v3/notes.rb @@ -0,0 +1,148 @@ +module API + module V3 + class Notes < Grape::API + include PaginationParams + + before { authenticate! } + + NOTEABLE_TYPES = [Issue, MergeRequest, Snippet].freeze + + params do + requires :id, type: String, desc: 'The ID of a project' + end + resource :projects do + NOTEABLE_TYPES.each do |noteable_type| + noteables_str = noteable_type.to_s.underscore.pluralize + + desc 'Get a list of project +noteable+ notes' do + success ::API::V3::Entities::Note + end + params do + requires :noteable_id, type: Integer, desc: 'The ID of the noteable' + use :pagination + end + get ":id/#{noteables_str}/:noteable_id/notes" do + noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id]) + + if can?(current_user, noteable_read_ability_name(noteable), noteable) + # We exclude notes that are cross-references and that cannot be viewed + # by the current user. By doing this exclusion at this level and not + # at the DB query level (which we cannot in that case), the current + # page can have less elements than :per_page even if + # there's more than one page. + notes = + # paginate() only works with a relation. This could lead to a + # mismatch between the pagination headers info and the actual notes + # array returned, but this is really a edge-case. + paginate(noteable.notes). + reject { |n| n.cross_reference_not_visible_for?(current_user) } + present notes, with: ::API::V3::Entities::Note + else + not_found!("Notes") + end + end + + desc 'Get a single +noteable+ note' do + success ::API::V3::Entities::Note + end + params do + requires :note_id, type: Integer, desc: 'The ID of a note' + requires :noteable_id, type: Integer, desc: 'The ID of the noteable' + end + get ":id/#{noteables_str}/:noteable_id/notes/:note_id" do + noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id]) + note = noteable.notes.find(params[:note_id]) + can_read_note = can?(current_user, noteable_read_ability_name(noteable), noteable) && !note.cross_reference_not_visible_for?(current_user) + + if can_read_note + present note, with: ::API::V3::Entities::Note + else + not_found!("Note") + end + end + + desc 'Create a new +noteable+ note' do + success ::API::V3::Entities::Note + end + params do + requires :noteable_id, type: Integer, desc: 'The ID of the noteable' + requires :body, type: String, desc: 'The content of a note' + optional :created_at, type: String, desc: 'The creation date of the note' + end + post ":id/#{noteables_str}/:noteable_id/notes" do + opts = { + note: params[:body], + noteable_type: noteables_str.classify, + noteable_id: params[:noteable_id] + } + + noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id]) + + if can?(current_user, noteable_read_ability_name(noteable), noteable) + if params[:created_at] && (current_user.is_admin? || user_project.owner == current_user) + opts[:created_at] = params[:created_at] + end + + note = ::Notes::CreateService.new(user_project, current_user, opts).execute + if note.valid? + present note, with: ::API::V3::Entities.const_get(note.class.name) + else + not_found!("Note #{note.errors.messages}") + end + else + not_found!("Note") + end + end + + desc 'Update an existing +noteable+ note' do + success ::API::V3::Entities::Note + end + params do + requires :noteable_id, type: Integer, desc: 'The ID of the noteable' + requires :note_id, type: Integer, desc: 'The ID of a note' + requires :body, type: String, desc: 'The content of a note' + end + put ":id/#{noteables_str}/:noteable_id/notes/:note_id" do + note = user_project.notes.find(params[:note_id]) + + authorize! :admin_note, note + + opts = { + note: params[:body] + } + + note = ::Notes::UpdateService.new(user_project, current_user, opts).execute(note) + + if note.valid? + present note, with: ::API::V3::Entities::Note + else + render_api_error!("Failed to save note #{note.errors.messages}", 400) + end + end + + desc 'Delete a +noteable+ note' do + success ::API::V3::Entities::Note + end + params do + requires :noteable_id, type: Integer, desc: 'The ID of the noteable' + requires :note_id, type: Integer, desc: 'The ID of a note' + end + delete ":id/#{noteables_str}/:noteable_id/notes/:note_id" do + note = user_project.notes.find(params[:note_id]) + authorize! :admin_note, note + + ::Notes::DestroyService.new(user_project, current_user).execute(note) + + present note, with: ::API::V3::Entities::Note + end + end + end + + helpers do + def noteable_read_ability_name(noteable) + "read_#{noteable.class.to_s.underscore}".to_sym + end + end + end + end +end diff --git a/lib/api/v3/projects.rb b/lib/api/v3/projects.rb index 6796da83f07..c3821555452 100644 --- a/lib/api/v3/projects.rb +++ b/lib/api/v3/projects.rb @@ -20,7 +20,8 @@ module API optional :visibility_level, type: Integer, values: [ Gitlab::VisibilityLevel::PRIVATE, Gitlab::VisibilityLevel::INTERNAL, - Gitlab::VisibilityLevel::PUBLIC ], desc: 'Create a public project. The same as visibility_level = 20.' + Gitlab::VisibilityLevel::PUBLIC + ], desc: 'Create a public project. The same as visibility_level = 20.' optional :public_builds, type: Boolean, desc: 'Perform public builds' optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access' optional :only_allow_merge_if_build_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed' @@ -232,13 +233,13 @@ module API end desc 'Get events for a single project' do - success ::API::Entities::Event + success ::API::V3::Entities::Event end params do use :pagination end get ":id/events" do - present paginate(user_project.events.recent), with: ::API::Entities::Event + present paginate(user_project.events.recent), with: ::API::V3::Entities::Event end desc 'Fork new project for the current user or provided namespace.' do diff --git a/lib/api/v3/users.rb b/lib/api/v3/users.rb index e05e457a5df..7838cdc46a7 100644 --- a/lib/api/v3/users.rb +++ b/lib/api/v3/users.rb @@ -71,6 +71,27 @@ module API user.activate end end + + desc 'Get the contribution events of a specified user' do + detail 'This feature was introduced in GitLab 8.13.' + success ::API::V3::Entities::Event + end + params do + requires :id, type: Integer, desc: 'The ID of the user' + use :pagination + end + get ':id/events' do + user = User.find_by(id: params[:id]) + not_found!('User') unless user + + events = user.events. + merge(ProjectsFinder.new.execute(current_user)). + references(:project). + with_associations. + recent + + present paginate(events), with: ::API::V3::Entities::Event + end end resource :user do diff --git a/lib/backup/database.rb b/lib/backup/database.rb index 22319ec6623..4016ac76348 100644 --- a/lib/backup/database.rb +++ b/lib/backup/database.rb @@ -5,7 +5,7 @@ module Backup attr_reader :config, :db_file_name def initialize - @config = YAML.load_file(File.join(Rails.root,'config','database.yml'))[Rails.env] + @config = YAML.load_file(File.join(Rails.root, 'config', 'database.yml'))[Rails.env] @db_file_name = File.join(Gitlab.config.backup.path, 'db', 'database.sql.gz') end @@ -13,28 +13,32 @@ module Backup FileUtils.mkdir_p(File.dirname(db_file_name)) FileUtils.rm_f(db_file_name) compress_rd, compress_wr = IO.pipe - compress_pid = spawn(*%W(gzip -1 -c), in: compress_rd, out: [db_file_name, 'w', 0600]) + compress_pid = spawn(*%w(gzip -1 -c), in: compress_rd, out: [db_file_name, 'w', 0600]) compress_rd.close - dump_pid = case config["adapter"] - when /^mysql/ then - $progress.print "Dumping MySQL database #{config['database']} ... " - # Workaround warnings from MySQL 5.6 about passwords on cmd line - ENV['MYSQL_PWD'] = config["password"].to_s if config["password"] - spawn('mysqldump', *mysql_args, config['database'], out: compress_wr) - when "postgresql" then - $progress.print "Dumping PostgreSQL database #{config['database']} ... " - pg_env - pgsql_args = ["--clean"] # Pass '--clean' to include 'DROP TABLE' statements in the DB dump. - if Gitlab.config.backup.pg_schema - pgsql_args << "-n" - pgsql_args << Gitlab.config.backup.pg_schema + dump_pid = + case config["adapter"] + when /^mysql/ then + $progress.print "Dumping MySQL database #{config['database']} ... " + # Workaround warnings from MySQL 5.6 about passwords on cmd line + ENV['MYSQL_PWD'] = config["password"].to_s if config["password"] + spawn('mysqldump', *mysql_args, config['database'], out: compress_wr) + when "postgresql" then + $progress.print "Dumping PostgreSQL database #{config['database']} ... " + pg_env + pgsql_args = ["--clean"] # Pass '--clean' to include 'DROP TABLE' statements in the DB dump. + if Gitlab.config.backup.pg_schema + pgsql_args << "-n" + pgsql_args << Gitlab.config.backup.pg_schema + end + spawn('pg_dump', *pgsql_args, config['database'], out: compress_wr) end - spawn('pg_dump', *pgsql_args, config['database'], out: compress_wr) - end compress_wr.close - success = [compress_pid, dump_pid].all? { |pid| Process.waitpid(pid); $?.success? } + success = [compress_pid, dump_pid].all? do |pid| + Process.waitpid(pid) + $?.success? + end report_success(success) abort 'Backup failed' unless success @@ -42,23 +46,27 @@ module Backup def restore decompress_rd, decompress_wr = IO.pipe - decompress_pid = spawn(*%W(gzip -cd), out: decompress_wr, in: db_file_name) + decompress_pid = spawn(*%w(gzip -cd), out: decompress_wr, in: db_file_name) decompress_wr.close - restore_pid = case config["adapter"] - when /^mysql/ then - $progress.print "Restoring MySQL database #{config['database']} ... " - # Workaround warnings from MySQL 5.6 about passwords on cmd line - ENV['MYSQL_PWD'] = config["password"].to_s if config["password"] - spawn('mysql', *mysql_args, config['database'], in: decompress_rd) - when "postgresql" then - $progress.print "Restoring PostgreSQL database #{config['database']} ... " - pg_env - spawn('psql', config['database'], in: decompress_rd) - end + restore_pid = + case config["adapter"] + when /^mysql/ then + $progress.print "Restoring MySQL database #{config['database']} ... " + # Workaround warnings from MySQL 5.6 about passwords on cmd line + ENV['MYSQL_PWD'] = config["password"].to_s if config["password"] + spawn('mysql', *mysql_args, config['database'], in: decompress_rd) + when "postgresql" then + $progress.print "Restoring PostgreSQL database #{config['database']} ... " + pg_env + spawn('psql', config['database'], in: decompress_rd) + end decompress_rd.close - success = [decompress_pid, restore_pid].all? { |pid| Process.waitpid(pid); $?.success? } + success = [decompress_pid, restore_pid].all? do |pid| + Process.waitpid(pid) + $?.success? + end report_success(success) abort 'Restore failed' unless success diff --git a/lib/backup/files.rb b/lib/backup/files.rb index 247c32c1c0a..30a91647b77 100644 --- a/lib/backup/files.rb +++ b/lib/backup/files.rb @@ -26,10 +26,10 @@ module Backup abort 'Backup failed' end - run_pipeline!([%W(tar -C #{@backup_files_dir} -cf - .), %W(gzip -c -1)], out: [backup_tarball, 'w', 0600]) + run_pipeline!([%W(tar -C #{@backup_files_dir} -cf - .), %w(gzip -c -1)], out: [backup_tarball, 'w', 0600]) FileUtils.rm_rf(@backup_files_dir) else - run_pipeline!([%W(tar -C #{app_files_dir} -cf - .), %W(gzip -c -1)], out: [backup_tarball, 'w', 0600]) + run_pipeline!([%W(tar -C #{app_files_dir} -cf - .), %w(gzip -c -1)], out: [backup_tarball, 'w', 0600]) end end @@ -37,7 +37,7 @@ module Backup backup_existing_files_dir create_files_dir - run_pipeline!([%W(gzip -cd), %W(tar -C #{app_files_dir} -xf -)], in: backup_tarball) + run_pipeline!([%w(gzip -cd), %W(tar -C #{app_files_dir} -xf -)], in: backup_tarball) end def backup_existing_files_dir @@ -47,7 +47,7 @@ module Backup end end - def run_pipeline!(cmd_list, options={}) + def run_pipeline!(cmd_list, options = {}) status_list = Open3.pipeline(*cmd_list, options) abort 'Backup failed' unless status_list.compact.all?(&:success?) end diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index f099c0651ac..5cc164a6325 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -1,8 +1,8 @@ module Backup class Manager - ARCHIVES_TO_BACKUP = %w[uploads builds artifacts pages lfs registry] - FOLDERS_TO_BACKUP = %w[repositories db] - FILE_NAME_SUFFIX = '_gitlab_backup.tar' + ARCHIVES_TO_BACKUP = %w[uploads builds artifacts pages lfs registry].freeze + FOLDERS_TO_BACKUP = %w[repositories db].freeze + FILE_NAME_SUFFIX = '_gitlab_backup.tar'.freeze def pack # Make sure there is a connection @@ -20,13 +20,13 @@ module Backup Dir.chdir(Gitlab.config.backup.path) do File.open("#{Gitlab.config.backup.path}/backup_information.yml", "w+") do |file| - file << s.to_yaml.gsub(/^---\n/,'') + file << s.to_yaml.gsub(/^---\n/, '') end # create archive $progress.print "Creating backup archive: #{tar_file} ... " # Set file permissions on open to prevent chmod races. - tar_system_options = {out: [tar_file, 'w', Gitlab.config.backup.archive_permissions]} + tar_system_options = { out: [tar_file, 'w', Gitlab.config.backup.archive_permissions] } if Kernel.system('tar', '-cf', '-', *backup_contents, tar_system_options) $progress.puts "done".color(:green) else @@ -50,8 +50,8 @@ module Backup directory = connect_to_remote_directory(connection_settings) if directory.files.create(key: tar_file, body: File.open(tar_file), public: false, - multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size, - encryption: Gitlab.config.backup.upload.encryption) + multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size, + encryption: Gitlab.config.backup.upload.encryption) $progress.puts "done".color(:green) else puts "uploading backup to #{remote_directory} failed".color(:red) @@ -123,11 +123,11 @@ module Backup exit 1 end - if ENV['BACKUP'].present? - tar_file = "#{ENV['BACKUP']}#{FILE_NAME_SUFFIX}" - else - tar_file = file_list.first - end + tar_file = if ENV['BACKUP'].present? + "#{ENV['BACKUP']}#{FILE_NAME_SUFFIX}" + else + file_list.first + end unless File.exist?(tar_file) $progress.puts "The backup file #{tar_file} does not exist!" @@ -158,7 +158,7 @@ module Backup end def tar_version - tar_version, _ = Gitlab::Popen.popen(%W(tar --version)) + tar_version, _ = Gitlab::Popen.popen(%w(tar --version)) tar_version.force_encoding('locale').split("\n").first end diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 91e43dcb114..d16d5ba4960 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -2,7 +2,7 @@ require 'yaml' module Backup class Repository - + # rubocop:disable Metrics/AbcSize def dump prepare @@ -85,11 +85,11 @@ module Backup project.ensure_dir_exist - if File.exists?(path_to_project_bundle) - cmd = %W(#{Gitlab.config.git.bin_path} clone --bare #{path_to_project_bundle} #{path_to_project_repo}) - else - cmd = %W(#{Gitlab.config.git.bin_path} init --bare #{path_to_project_repo}) - end + cmd = if File.exist?(path_to_project_bundle) + %W(#{Gitlab.config.git.bin_path} clone --bare #{path_to_project_bundle} #{path_to_project_repo}) + else + %W(#{Gitlab.config.git.bin_path} init --bare #{path_to_project_repo}) + end output, status = Gitlab::Popen.popen(cmd) if status.zero? @@ -150,6 +150,7 @@ module Backup puts output end end + # rubocop:enable Metrics/AbcSize protected @@ -193,7 +194,7 @@ module Backup end def silent - {err: '/dev/null', out: '/dev/null'} + { err: '/dev/null', out: '/dev/null' } end private diff --git a/lib/backup/uploads.rb b/lib/backup/uploads.rb index 9261f77f3c9..35118375499 100644 --- a/lib/backup/uploads.rb +++ b/lib/backup/uploads.rb @@ -2,7 +2,6 @@ require 'backup/files' module Backup class Uploads < Files - def initialize super('uploads', Rails.root.join('public/uploads')) end diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb index 3b15ff6566f..02d5ad70fa7 100644 --- a/lib/banzai/filter/abstract_reference_filter.rb +++ b/lib/banzai/filter/abstract_reference_filter.rb @@ -160,11 +160,12 @@ module Banzai data = data_attributes_for(link_content || match, project, object, link: !!link_content) - if matches.names.include?("url") && matches[:url] - url = matches[:url] - else - url = url_for_object_cached(object, project) - end + url = + if matches.names.include?("url") && matches[:url] + matches[:url] + else + url_for_object_cached(object, project) + end content = link_content || object_link_text(object, matches) diff --git a/lib/banzai/filter/autolink_filter.rb b/lib/banzai/filter/autolink_filter.rb index 80c844baecd..b8d2673c1a6 100644 --- a/lib/banzai/filter/autolink_filter.rb +++ b/lib/banzai/filter/autolink_filter.rb @@ -37,7 +37,7 @@ module Banzai and contains(., '://') and not(starts-with(., 'http')) and not(starts-with(., 'ftp')) - ]) + ]).freeze def call return doc if context[:autolink] == false diff --git a/lib/banzai/filter/gollum_tags_filter.rb b/lib/banzai/filter/gollum_tags_filter.rb index d08267a9d6c..0ea4eeaed5b 100644 --- a/lib/banzai/filter/gollum_tags_filter.rb +++ b/lib/banzai/filter/gollum_tags_filter.rb @@ -149,11 +149,12 @@ module Banzai name, reference = *parts.compact.map(&:strip) end - if url?(reference) - href = reference - else - href = ::File.join(project_wiki_base_path, reference) - end + href = + if url?(reference) + reference + else + ::File.join(project_wiki_base_path, reference) + end content_tag(:a, name || reference, href: href, class: 'gfm') end diff --git a/lib/banzai/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb index fd6b9704132..044d18ff824 100644 --- a/lib/banzai/filter/issue_reference_filter.rb +++ b/lib/banzai/filter/issue_reference_filter.rb @@ -39,11 +39,12 @@ module Banzai projects_per_reference.each do |path, project| issue_ids = references_per_project[path] - if project.default_issues_tracker? - issues = project.issues.where(iid: issue_ids.to_a) - else - issues = issue_ids.map { |id| ExternalIssue.new(id, project) } - end + issues = + if project.default_issues_tracker? + project.issues.where(iid: issue_ids.to_a) + else + issue_ids.map { |id| ExternalIssue.new(id, project) } + end issues.each do |issue| hash[project][issue.iid.to_i] = issue diff --git a/lib/bitbucket/connection.rb b/lib/bitbucket/connection.rb index 7e55cf4deab..b9279c33f5b 100644 --- a/lib/bitbucket/connection.rb +++ b/lib/bitbucket/connection.rb @@ -1,8 +1,8 @@ module Bitbucket class Connection - DEFAULT_API_VERSION = '2.0' - DEFAULT_BASE_URI = 'https://api.bitbucket.org/' - DEFAULT_QUERY = {} + DEFAULT_API_VERSION = '2.0'.freeze + DEFAULT_BASE_URI = 'https://api.bitbucket.org/'.freeze + DEFAULT_QUERY = {}.freeze attr_reader :expires_at, :expires_in, :refresh_token, :token @@ -24,9 +24,7 @@ module Bitbucket response.parsed end - def expired? - connection.expired? - end + delegate :expired?, to: :connection def refresh! response = connection.refresh! diff --git a/lib/bitbucket/representation/repo.rb b/lib/bitbucket/representation/repo.rb index 423eff8f2a5..59b0fda8e14 100644 --- a/lib/bitbucket/representation/repo.rb +++ b/lib/bitbucket/representation/repo.rb @@ -23,7 +23,7 @@ module Bitbucket url = raw['links']['clone'].find { |link| link['name'] == 'https' }.fetch('href') if token.present? - clone_url = URI::parse(url) + clone_url = URI.parse(url) clone_url.user = "x-token-auth:#{token}" clone_url.to_s else diff --git a/lib/ci/ansi2html.rb b/lib/ci/ansi2html.rb index 158a33f26fe..b3ccad7b28d 100644 --- a/lib/ci/ansi2html.rb +++ b/lib/ci/ansi2html.rb @@ -13,7 +13,7 @@ module Ci 5 => 'magenta', 6 => 'cyan', 7 => 'white', # not that this is gray in the dark (aka default) color table - } + }.freeze STYLE_SWITCHES = { bold: 0x01, @@ -21,7 +21,7 @@ module Ci underline: 0x04, conceal: 0x08, cross: 0x10, - } + }.freeze def self.convert(ansi, state = nil) Converter.new.convert(ansi, state) @@ -29,64 +29,108 @@ module Ci class Converter def on_0(s) reset() end + def on_1(s) enable(STYLE_SWITCHES[:bold]) end + def on_3(s) enable(STYLE_SWITCHES[:italic]) end + def on_4(s) enable(STYLE_SWITCHES[:underline]) end + def on_8(s) enable(STYLE_SWITCHES[:conceal]) end + def on_9(s) enable(STYLE_SWITCHES[:cross]) end def on_21(s) disable(STYLE_SWITCHES[:bold]) end + def on_22(s) disable(STYLE_SWITCHES[:bold]) end + def on_23(s) disable(STYLE_SWITCHES[:italic]) end + def on_24(s) disable(STYLE_SWITCHES[:underline]) end + def on_28(s) disable(STYLE_SWITCHES[:conceal]) end + def on_29(s) disable(STYLE_SWITCHES[:cross]) end def on_30(s) set_fg_color(0) end + def on_31(s) set_fg_color(1) end + def on_32(s) set_fg_color(2) end + def on_33(s) set_fg_color(3) end + def on_34(s) set_fg_color(4) end + def on_35(s) set_fg_color(5) end + def on_36(s) set_fg_color(6) end + def on_37(s) set_fg_color(7) end + def on_38(s) set_fg_color_256(s) end + def on_39(s) set_fg_color(9) end def on_40(s) set_bg_color(0) end + def on_41(s) set_bg_color(1) end + def on_42(s) set_bg_color(2) end + def on_43(s) set_bg_color(3) end + def on_44(s) set_bg_color(4) end + def on_45(s) set_bg_color(5) end + def on_46(s) set_bg_color(6) end + def on_47(s) set_bg_color(7) end + def on_48(s) set_bg_color_256(s) end + def on_49(s) set_bg_color(9) end def on_90(s) set_fg_color(0, 'l') end + def on_91(s) set_fg_color(1, 'l') end + def on_92(s) set_fg_color(2, 'l') end + def on_93(s) set_fg_color(3, 'l') end + def on_94(s) set_fg_color(4, 'l') end + def on_95(s) set_fg_color(5, 'l') end + def on_96(s) set_fg_color(6, 'l') end + def on_97(s) set_fg_color(7, 'l') end + def on_99(s) set_fg_color(9, 'l') end def on_100(s) set_bg_color(0, 'l') end + def on_101(s) set_bg_color(1, 'l') end + def on_102(s) set_bg_color(2, 'l') end + def on_103(s) set_bg_color(3, 'l') end + def on_104(s) set_bg_color(4, 'l') end + def on_105(s) set_bg_color(5, 'l') end + def on_106(s) set_bg_color(6, 'l') end + def on_107(s) set_bg_color(7, 'l') end + def on_109(s) set_bg_color(9, 'l') end attr_accessor :offset, :n_open_tags, :fg_color, :bg_color, :style_mask - STATE_PARAMS = [:offset, :n_open_tags, :fg_color, :bg_color, :style_mask] + STATE_PARAMS = [:offset, :n_open_tags, :fg_color, :bg_color, :style_mask].freeze def convert(raw, new_state) reset_state diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb index 8b939663ffd..0e17ac24d5a 100644 --- a/lib/ci/api/builds.rb +++ b/lib/ci/api/builds.rb @@ -167,7 +167,10 @@ module Ci build.artifacts_file = artifacts build.artifacts_metadata = metadata - build.artifacts_expire_in = params['expire_in'] + build.artifacts_expire_in = + params['expire_in'] || + Gitlab::CurrentSettings.current_application_settings + .default_artifacts_expire_in if build.save present(build, with: Entities::BuildDetails) diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb index 5ff25a3a9b2..996990b464f 100644 --- a/lib/ci/api/helpers.rb +++ b/lib/ci/api/helpers.rb @@ -1,7 +1,7 @@ module Ci module API module Helpers - BUILD_TOKEN_HEADER = "HTTP_BUILD_TOKEN" + BUILD_TOKEN_HEADER = "HTTP_BUILD_TOKEN".freeze BUILD_TOKEN_PARAM = :token UPDATE_RUNNER_EVERY = 10 * 60 @@ -60,7 +60,7 @@ module Ci end def build_not_found! - if headers['User-Agent'].to_s.match(/gitlab-ci-multi-runner \d+\.\d+\.\d+(~beta\.\d+\.g[0-9a-f]+)? /) + if headers['User-Agent'].to_s =~ /gitlab-ci-multi-runner \d+\.\d+\.\d+(~beta\.\d+\.g[0-9a-f]+)? / no_content! else not_found! @@ -73,7 +73,7 @@ module Ci def get_runner_version_from_params return unless params["info"].present? - attributes_for_keys(["name", "version", "revision", "platform", "architecture"], params["info"]) + attributes_for_keys(%w(name version revision platform architecture), params["info"]) end def max_artifacts_size diff --git a/lib/container_registry/client.rb b/lib/container_registry/client.rb index 2edddb84fc3..7f5f6d9ddb6 100644 --- a/lib/container_registry/client.rb +++ b/lib/container_registry/client.rb @@ -5,7 +5,7 @@ module ContainerRegistry class Client attr_accessor :uri - MANIFEST_VERSION = 'application/vnd.docker.distribution.manifest.v2+json' + MANIFEST_VERSION = 'application/vnd.docker.distribution.manifest.v2+json'.freeze # Taken from: FaradayMiddleware::FollowRedirects REDIRECT_CODES = Set.new [301, 302, 303, 307] diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb index 82551f1f222..9ece84cc469 100644 --- a/lib/extracts_path.rb +++ b/lib/extracts_path.rb @@ -42,7 +42,7 @@ module ExtractsPath return pair unless @project - if id.match(/^([[:alnum:]]{40})(.+)/) + if id =~ /^(\h{40})(.+)/ # If the ref appears to be a SHA, we're done, just split the string pair = $~.captures else diff --git a/lib/file_size_validator.rb b/lib/file_size_validator.rb index 440dd44ece7..eb19ab45ac3 100644 --- a/lib/file_size_validator.rb +++ b/lib/file_size_validator.rb @@ -32,9 +32,9 @@ class FileSizeValidator < ActiveModel::EachValidator end def validate_each(record, attribute, value) - raise(ArgumentError, "A CarrierWave::Uploader::Base object was expected") unless value.kind_of? CarrierWave::Uploader::Base + raise(ArgumentError, "A CarrierWave::Uploader::Base object was expected") unless value.is_a? CarrierWave::Uploader::Base - value = (options[:tokenizer] || DEFAULT_TOKENIZER).call(value) if value.kind_of?(String) + value = (options[:tokenizer] || DEFAULT_TOKENIZER).call(value) if value.is_a?(String) CHECKS.each do |key, validity_check| next unless check_value = options[key] diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb index 9b484a2ecfd..3b210eeda9d 100644 --- a/lib/gitlab/access.rb +++ b/lib/gitlab/access.rb @@ -21,9 +21,7 @@ module Gitlab PROTECTION_DEV_CAN_MERGE = 3 class << self - def values - options.values - end + delegate :values, to: :options def all_values options_with_owner.values diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index f638905a1e0..89db6c3da46 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -2,8 +2,8 @@ module Gitlab module Auth class MissingPersonalTokenError < StandardError; end - SCOPES = [:api, :read_user] - DEFAULT_SCOPES = [:api] + SCOPES = [:api, :read_user].freeze + DEFAULT_SCOPES = [:api].freeze OPTIONAL_SCOPES = SCOPES - DEFAULT_SCOPES class << self diff --git a/lib/gitlab/award_emoji.rb b/lib/gitlab/award_emoji.rb index 39b43ab5489..7555326d384 100644 --- a/lib/gitlab/award_emoji.rb +++ b/lib/gitlab/award_emoji.rb @@ -69,11 +69,12 @@ module Gitlab end JSON.parse(File.read(path)).map do |hash| - if digest - fname = "#{hash['unicode']}-#{hash['digest']}" - else - fname = hash['unicode'] - end + fname = + if digest + "#{hash['unicode']}-#{hash['digest']}" + else + hash['unicode'] + end { name: hash['name'], path: File.join(base, prefix, "#{fname}.png") } end diff --git a/lib/gitlab/badge/build/template.rb b/lib/gitlab/badge/build/template.rb index 2b95ddfcb53..bc0e0cd441d 100644 --- a/lib/gitlab/badge/build/template.rb +++ b/lib/gitlab/badge/build/template.rb @@ -15,7 +15,7 @@ module Gitlab canceled: '#9f9f9f', skipped: '#9f9f9f', unknown: '#9f9f9f' - } + }.freeze def initialize(badge) @entity = badge.entity diff --git a/lib/gitlab/badge/coverage/template.rb b/lib/gitlab/badge/coverage/template.rb index 06e0d084e9f..fcecb1d9665 100644 --- a/lib/gitlab/badge/coverage/template.rb +++ b/lib/gitlab/badge/coverage/template.rb @@ -13,7 +13,7 @@ module Gitlab medium: '#dfb317', low: '#e05d44', unknown: '#9f9f9f' - } + }.freeze def initialize(badge) @entity = badge.entity diff --git a/lib/gitlab/changes_list.rb b/lib/gitlab/changes_list.rb index 95308aca95f..5b32fca00a4 100644 --- a/lib/gitlab/changes_list.rb +++ b/lib/gitlab/changes_list.rb @@ -5,7 +5,7 @@ module Gitlab attr_reader :raw_changes def initialize(changes) - @raw_changes = changes.kind_of?(String) ? changes.lines : changes + @raw_changes = changes.is_a?(String) ? changes.lines : changes end def each(&block) diff --git a/lib/gitlab/ci/build/artifacts/metadata/entry.rb b/lib/gitlab/ci/build/artifacts/metadata/entry.rb index 7f4c750b6fd..6f799c2f031 100644 --- a/lib/gitlab/ci/build/artifacts/metadata/entry.rb +++ b/lib/gitlab/ci/build/artifacts/metadata/entry.rb @@ -27,6 +27,8 @@ module Gitlab end end + delegate :empty?, to: :children + def directory? blank_node? || @path.end_with?('/') end @@ -91,10 +93,6 @@ module Gitlab blank_node? || @entries.include?(@path) end - def empty? - children.empty? - end - def total_size descendant_pattern = %r{^#{Regexp.escape(@path)}} entries.sum do |path, entry| diff --git a/lib/gitlab/ci/config/entry/artifacts.rb b/lib/gitlab/ci/config/entry/artifacts.rb index b756b0d4555..8275aacee9b 100644 --- a/lib/gitlab/ci/config/entry/artifacts.rb +++ b/lib/gitlab/ci/config/entry/artifacts.rb @@ -9,7 +9,7 @@ module Gitlab include Validatable include Attributable - ALLOWED_KEYS = %i[name untracked paths when expire_in] + ALLOWED_KEYS = %i[name untracked paths when expire_in].freeze attributes ALLOWED_KEYS diff --git a/lib/gitlab/ci/config/entry/cache.rb b/lib/gitlab/ci/config/entry/cache.rb index 7653cab668b..066643ccfcc 100644 --- a/lib/gitlab/ci/config/entry/cache.rb +++ b/lib/gitlab/ci/config/entry/cache.rb @@ -8,7 +8,7 @@ module Gitlab class Cache < Node include Configurable - ALLOWED_KEYS = %i[key untracked paths] + ALLOWED_KEYS = %i[key untracked paths].freeze validations do validates :config, allowed_keys: ALLOWED_KEYS diff --git a/lib/gitlab/ci/config/entry/environment.rb b/lib/gitlab/ci/config/entry/environment.rb index f7c530c7d9f..0c1f9eb7cbf 100644 --- a/lib/gitlab/ci/config/entry/environment.rb +++ b/lib/gitlab/ci/config/entry/environment.rb @@ -8,7 +8,7 @@ module Gitlab class Environment < Node include Validatable - ALLOWED_KEYS = %i[name url action on_stop] + ALLOWED_KEYS = %i[name url action on_stop].freeze validations do validate do @@ -21,12 +21,14 @@ module Gitlab validates :name, type: { with: String, - message: Gitlab::Regex.environment_name_regex_message } + message: Gitlab::Regex.environment_name_regex_message + } validates :name, format: { with: Gitlab::Regex.environment_name_regex, - message: Gitlab::Regex.environment_name_regex_message } + message: Gitlab::Regex.environment_name_regex_message + } with_options if: :hash? do validates :config, allowed_keys: ALLOWED_KEYS diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb index 69a5e6f433d..7f7662f2776 100644 --- a/lib/gitlab/ci/config/entry/job.rb +++ b/lib/gitlab/ci/config/entry/job.rb @@ -11,7 +11,7 @@ module Gitlab ALLOWED_KEYS = %i[tags script only except type image services allow_failure type stage when artifacts cache dependencies before_script - after_script variables environment coverage] + after_script variables environment coverage].freeze validations do validates :config, allowed_keys: ALLOWED_KEYS diff --git a/lib/gitlab/conflict/file.rb b/lib/gitlab/conflict/file.rb index c843315782d..d80bc748209 100644 --- a/lib/gitlab/conflict/file.rb +++ b/lib/gitlab/conflict/file.rb @@ -91,11 +91,12 @@ module Gitlab our_highlight = Gitlab::Highlight.highlight(our_path, our_file, repository: repository).lines lines.each do |line| - if line.type == 'old' - line.rich_text = their_highlight[line.old_line - 1].try(:html_safe) - else - line.rich_text = our_highlight[line.new_line - 1].try(:html_safe) - end + line.rich_text = + if line.type == 'old' + their_highlight[line.old_line - 1].try(:html_safe) + else + our_highlight[line.new_line - 1].try(:html_safe) + end end end diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index e20f5f6f514..82576d197fe 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -25,9 +25,7 @@ module Gitlab settings || in_memory_application_settings end - def sidekiq_throttling_enabled? - current_application_settings.sidekiq_throttling_enabled? - end + delegate :sidekiq_throttling_enabled?, to: :current_application_settings def in_memory_application_settings @in_memory_application_settings ||= ::ApplicationSetting.new(::ApplicationSetting.defaults) diff --git a/lib/gitlab/database/median.rb b/lib/gitlab/database/median.rb index 08607c27c09..23890e5f493 100644 --- a/lib/gitlab/database/median.rb +++ b/lib/gitlab/database/median.rb @@ -108,6 +108,7 @@ module Gitlab Arel.sql(%Q{EXTRACT(EPOCH FROM (#{diff.to_sql}))}) end + # Need to cast '0' to an INTERVAL before we can check if the interval is positive def zero_interval Arel::Nodes::NamedFunction.new("CAST", [Arel.sql("'0' AS INTERVAL")]) diff --git a/lib/gitlab/diff/highlight.rb b/lib/gitlab/diff/highlight.rb index 9ea976e18fa..7db896522a9 100644 --- a/lib/gitlab/diff/highlight.rb +++ b/lib/gitlab/diff/highlight.rb @@ -50,7 +50,7 @@ module Gitlab # Only update text if line is found. This will prevent # issues with submodules given the line only exists in diff content. if rich_line - line_prefix = diff_line.text.match(/\A(.)/) ? $1 : ' ' + line_prefix = diff_line.text =~ /\A(.)/ ? $1 : ' ' "#{line_prefix}#{rich_line}".html_safe end end diff --git a/lib/gitlab/diff/inline_diff_marker.rb b/lib/gitlab/diff/inline_diff_marker.rb index 87a9b1e23ac..736933b1c4b 100644 --- a/lib/gitlab/diff/inline_diff_marker.rb +++ b/lib/gitlab/diff/inline_diff_marker.rb @@ -4,7 +4,7 @@ module Gitlab MARKDOWN_SYMBOLS = { addition: "+", deletion: "-" - } + }.freeze attr_accessor :raw_line, :rich_line diff --git a/lib/gitlab/diff/parser.rb b/lib/gitlab/diff/parser.rb index 89320f5d9dc..8f844224a7a 100644 --- a/lib/gitlab/diff/parser.rb +++ b/lib/gitlab/diff/parser.rb @@ -20,7 +20,7 @@ module Gitlab full_line = line.delete("\n") - if line.match(/^@@ -/) + if line =~ /^@@ -/ type = "match" line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0 diff --git a/lib/gitlab/diff/position.rb b/lib/gitlab/diff/position.rb index ecf62dead35..fc728123c97 100644 --- a/lib/gitlab/diff/position.rb +++ b/lib/gitlab/diff/position.rb @@ -140,15 +140,16 @@ module Gitlab def find_diff_file(repository) # We're at the initial commit, so just get that as we can't compare to anything. - if Gitlab::Git.blank_ref?(start_sha) - compare = Gitlab::Git::Commit.find(repository.raw_repository, head_sha) - else - compare = Gitlab::Git::Compare.new( - repository.raw_repository, - start_sha, - head_sha - ) - end + compare = + if Gitlab::Git.blank_ref?(start_sha) + Gitlab::Git::Commit.find(repository.raw_repository, head_sha) + else + Gitlab::Git::Compare.new( + repository.raw_repository, + start_sha, + head_sha + ) + end diff = compare.diffs(paths: paths).first diff --git a/lib/gitlab/downtime_check/message.rb b/lib/gitlab/downtime_check/message.rb index 40a4815a9a0..543e62794c5 100644 --- a/lib/gitlab/downtime_check/message.rb +++ b/lib/gitlab/downtime_check/message.rb @@ -3,8 +3,8 @@ module Gitlab class Message attr_reader :path, :offline - OFFLINE = "\e[31moffline\e[0m" - ONLINE = "\e[32monline\e[0m" + OFFLINE = "\e[31moffline\e[0m".freeze + ONLINE = "\e[32monline\e[0m".freeze # path - The file path of the migration. # offline - When set to `true` the migration will require downtime. diff --git a/lib/gitlab/email/handler.rb b/lib/gitlab/email/handler.rb index bd2f5d3615e..35ea2e0ef59 100644 --- a/lib/gitlab/email/handler.rb +++ b/lib/gitlab/email/handler.rb @@ -5,7 +5,7 @@ require 'gitlab/email/handler/unsubscribe_handler' module Gitlab module Email module Handler - HANDLERS = [UnsubscribeHandler, CreateNoteHandler, CreateIssueHandler] + HANDLERS = [UnsubscribeHandler, CreateNoteHandler, CreateIssueHandler].freeze def self.for(mail, mail_key) HANDLERS.find do |klass| diff --git a/lib/gitlab/email/reply_parser.rb b/lib/gitlab/email/reply_parser.rb index 8c8dd1b9cef..558df87f36d 100644 --- a/lib/gitlab/email/reply_parser.rb +++ b/lib/gitlab/email/reply_parser.rb @@ -31,11 +31,12 @@ module Gitlab private def select_body(message) - if message.multipart? - part = message.text_part || message.html_part || message - else - part = message - end + part = + if message.multipart? + message.text_part || message.html_part || message + else + message + end decoded = fix_charset(part) diff --git a/lib/gitlab/exclusive_lease.rb b/lib/gitlab/exclusive_lease.rb index 2dd42704396..62ddd45785d 100644 --- a/lib/gitlab/exclusive_lease.rb +++ b/lib/gitlab/exclusive_lease.rb @@ -10,7 +10,7 @@ module Gitlab # ExclusiveLease. # class ExclusiveLease - LUA_CANCEL_SCRIPT = <<-EOS + LUA_CANCEL_SCRIPT = <<-EOS.freeze local key, uuid = KEYS[1], ARGV[1] if redis.call("get", key) == uuid then redis.call("del", key) diff --git a/lib/gitlab/file_detector.rb b/lib/gitlab/file_detector.rb index 1d93a67dc56..c9ca4cadd1c 100644 --- a/lib/gitlab/file_detector.rb +++ b/lib/gitlab/file_detector.rb @@ -14,7 +14,7 @@ module Gitlab koding: '.koding.yml', gitlab_ci: '.gitlab-ci.yml', avatar: /\Alogo\.(png|jpg|gif)\z/ - } + }.freeze # Returns an Array of file types based on the given paths. # diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb index b742d9e1e4b..e56eb0d3beb 100644 --- a/lib/gitlab/git/blob.rb +++ b/lib/gitlab/git/blob.rb @@ -93,163 +93,6 @@ module Gitlab commit_id: sha, ) end - - # Commit file in repository and return commit sha - # - # options should contain next structure: - # file: { - # content: 'Lorem ipsum...', - # path: 'documents/story.txt', - # update: true - # }, - # author: { - # email: 'user@example.com', - # name: 'Test User', - # time: Time.now - # }, - # committer: { - # email: 'user@example.com', - # name: 'Test User', - # time: Time.now - # }, - # commit: { - # message: 'Wow such commit', - # branch: 'master', - # update_ref: false - # } - # - # rubocop:disable Metrics/AbcSize - # rubocop:disable Metrics/CyclomaticComplexity - # rubocop:disable Metrics/PerceivedComplexity - def commit(repository, options, action = :add) - file = options[:file] - update = file[:update].nil? ? true : file[:update] - author = options[:author] - committer = options[:committer] - commit = options[:commit] - repo = repository.rugged - ref = commit[:branch] - update_ref = commit[:update_ref].nil? ? true : commit[:update_ref] - parents = [] - mode = 0o100644 - - unless ref.start_with?('refs/') - ref = 'refs/heads/' + ref - end - - path_name = Gitlab::Git::PathHelper.normalize_path(file[:path]) - # Abort if any invalid characters remain (e.g. ../foo) - raise Gitlab::Git::Repository::InvalidBlobName.new("Invalid path") if path_name.each_filename.to_a.include?('..') - - filename = path_name.to_s - index = repo.index - - unless repo.empty? - rugged_ref = repo.references[ref] - raise Gitlab::Git::Repository::InvalidRef.new("Invalid branch name") unless rugged_ref - last_commit = rugged_ref.target - index.read_tree(last_commit.tree) - parents = [last_commit] - end - - if action == :remove - index.remove(filename) - else - file_entry = index.get(filename) - - if action == :rename - old_path_name = Gitlab::Git::PathHelper.normalize_path(file[:previous_path]) - old_filename = old_path_name.to_s - file_entry = index.get(old_filename) - index.remove(old_filename) unless file_entry.blank? - end - - if file_entry - raise Gitlab::Git::Repository::InvalidBlobName.new("Filename already exists; update not allowed") unless update - - # Preserve the current file mode if one is available - mode = file_entry[:mode] if file_entry[:mode] - end - - content = file[:content] - detect = CharlockHolmes::EncodingDetector.new.detect(content) if content - - unless detect && detect[:type] == :binary - # When writing to the repo directly as we are doing here, - # the `core.autocrlf` config isn't taken into account. - content.gsub!("\r\n", "\n") if repository.autocrlf - end - - oid = repo.write(content, :blob) - index.add(path: filename, oid: oid, mode: mode) - end - - opts = {} - opts[:tree] = index.write_tree(repo) - opts[:author] = author - opts[:committer] = committer - opts[:message] = commit[:message] - opts[:parents] = parents - opts[:update_ref] = ref if update_ref - - Rugged::Commit.create(repo, opts) - end - # rubocop:enable Metrics/AbcSize - # rubocop:enable Metrics/CyclomaticComplexity - # rubocop:enable Metrics/PerceivedComplexity - - # Remove file from repository and return commit sha - # - # options should contain next structure: - # file: { - # path: 'documents/story.txt' - # }, - # author: { - # email: 'user@example.com', - # name: 'Test User', - # time: Time.now - # }, - # committer: { - # email: 'user@example.com', - # name: 'Test User', - # time: Time.now - # }, - # commit: { - # message: 'Remove FILENAME', - # branch: 'master' - # } - # - def remove(repository, options) - commit(repository, options, :remove) - end - - # Rename file from repository and return commit sha - # - # options should contain next structure: - # file: { - # previous_path: 'documents/old_story.txt' - # path: 'documents/story.txt' - # content: 'Lorem ipsum...', - # update: true - # }, - # author: { - # email: 'user@example.com', - # name: 'Test User', - # time: Time.now - # }, - # committer: { - # email: 'user@example.com', - # name: 'Test User', - # time: Time.now - # }, - # commit: { - # message: 'Rename FILENAME', - # branch: 'master' - # } - # - def rename(repository, options) - commit(repository, options, :rename) - end end def initialize(options) diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb index d785516ebdd..3a73697dc5d 100644 --- a/lib/gitlab/git/commit.rb +++ b/lib/gitlab/git/commit.rb @@ -14,6 +14,8 @@ module Gitlab attr_accessor *SERIALIZE_KEYS # rubocop:disable Lint/AmbiguousOperator + delegate :tree, to: :raw_commit + def ==(other) return false unless other.is_a?(Gitlab::Git::Commit) @@ -218,10 +220,6 @@ module Gitlab raw_commit.parents.map { |c| Gitlab::Git::Commit.new(c) } end - def tree - raw_commit.tree - end - def stats Gitlab::Git::CommitStats.new(self) end diff --git a/lib/gitlab/git/index.rb b/lib/gitlab/git/index.rb new file mode 100644 index 00000000000..af1744c9c46 --- /dev/null +++ b/lib/gitlab/git/index.rb @@ -0,0 +1,126 @@ +module Gitlab + module Git + class Index + DEFAULT_MODE = 0o100644 + + attr_reader :repository, :raw_index + + def initialize(repository) + @repository = repository + @raw_index = repository.rugged.index + end + + delegate :read_tree, :get, to: :raw_index + + def write_tree + raw_index.write_tree(repository.rugged) + end + + def dir_exists?(path) + raw_index.find { |entry| entry[:path].start_with?("#{path}/") } + end + + def create(options) + options = normalize_options(options) + + file_entry = get(options[:file_path]) + if file_entry + raise Gitlab::Git::Repository::InvalidBlobName.new("Filename already exists") + end + + add_blob(options) + end + + def create_dir(options) + options = normalize_options(options) + + file_entry = get(options[:file_path]) + if file_entry + raise Gitlab::Git::Repository::InvalidBlobName.new("Directory already exists as a file") + end + + if dir_exists?(options[:file_path]) + raise Gitlab::Git::Repository::InvalidBlobName.new("Directory already exists") + end + + options = options.dup + options[:file_path] += '/.gitkeep' + options[:content] = '' + + add_blob(options) + end + + def update(options) + options = normalize_options(options) + + file_entry = get(options[:file_path]) + unless file_entry + raise Gitlab::Git::Repository::InvalidBlobName.new("File doesn't exist") + end + + add_blob(options, mode: file_entry[:mode]) + end + + def move(options) + options = normalize_options(options) + + file_entry = get(options[:previous_path]) + unless file_entry + raise Gitlab::Git::Repository::InvalidBlobName.new("File doesn't exist") + end + + raw_index.remove(options[:previous_path]) + + add_blob(options, mode: file_entry[:mode]) + end + + def delete(options) + options = normalize_options(options) + + file_entry = get(options[:file_path]) + unless file_entry + raise Gitlab::Git::Repository::InvalidBlobName.new("File doesn't exist") + end + + raw_index.remove(options[:file_path]) + end + + private + + def normalize_options(options) + options = options.dup + options[:file_path] = normalize_path(options[:file_path]) if options[:file_path] + options[:previous_path] = normalize_path(options[:previous_path]) if options[:previous_path] + options + end + + def normalize_path(path) + pathname = Gitlab::Git::PathHelper.normalize_path(path.dup) + + if pathname.each_filename.include?('..') + raise Gitlab::Git::Repository::InvalidBlobName.new('Invalid path') + end + + pathname.to_s + end + + def add_blob(options, mode: nil) + content = options[:content] + content = Base64.decode64(content) if options[:encoding] == 'base64' + + detect = CharlockHolmes::EncodingDetector.new.detect(content) + unless detect && detect[:type] == :binary + # When writing to the repo directly as we are doing here, + # the `core.autocrlf` config isn't taken into account. + content.gsub!("\r\n", "\n") if repository.autocrlf + end + + oid = repository.rugged.write(content, :blob) + + raw_index.add(path: options[:file_path], oid: oid, mode: mode || DEFAULT_MODE) + rescue Rugged::IndexError => e + raise Gitlab::Git::Repository::InvalidBlobName.new(e.message) + end + end + end +end diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 7068e68a855..8ec90885231 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -1,5 +1,4 @@ # Gitlab::Git::Repository is a wrapper around native Rugged::Repository object -require 'forwardable' require 'tempfile' require 'forwardable' require "rubygems/package" @@ -7,7 +6,6 @@ require "rubygems/package" module Gitlab module Git class Repository - extend Forwardable include Gitlab::Git::Popen SEARCH_CONTEXT_LINES = 3 @@ -33,6 +31,10 @@ module Gitlab @attributes = Gitlab::Git::Attributes.new(path) end + delegate :empty?, + :bare?, + to: :rugged + # Default branch in the repository def root_ref @root_ref ||= discover_default_branch @@ -162,14 +164,6 @@ module Gitlab !empty? end - def empty? - rugged.empty? - end - - def bare? - rugged.bare? - end - def repo_exists? !!rugged end @@ -330,24 +324,30 @@ module Gitlab end def log_by_shell(sha, options) - cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{path} log) - cmd += %W(-n #{options[:limit].to_i}) - cmd += %w(--format=%H) - cmd += %W(--skip=#{options[:offset].to_i}) - cmd += %w(--follow) if options[:follow] - cmd += %w(--no-merges) if options[:skip_merges] - cmd += %W(--after=#{options[:after].iso8601}) if options[:after] - cmd += %W(--before=#{options[:before].iso8601}) if options[:before] - cmd += [sha] - cmd += %W(-- #{options[:path]}) if options[:path].present? - - raw_output = IO.popen(cmd) {|io| io.read } - - log = raw_output.lines.map do |c| - Rugged::Commit.new(rugged, c.strip) - end + limit = options[:limit].to_i + offset = options[:offset].to_i + use_follow_flag = options[:follow] && options[:path].present? - log.is_a?(Array) ? log : [] + # We will perform the offset in Ruby because --follow doesn't play well with --skip. + # See: https://gitlab.com/gitlab-org/gitlab-ce/issues/3574#note_3040520 + offset_in_ruby = use_follow_flag && options[:offset].present? + limit += offset if offset_in_ruby + + cmd = %W[#{Gitlab.config.git.bin_path} --git-dir=#{path} log] + cmd << "--max-count=#{limit}" + cmd << '--format=%H' + cmd << "--skip=#{offset}" unless offset_in_ruby + cmd << '--follow' if use_follow_flag + cmd << '--no-merges' if options[:skip_merges] + cmd << "--after=#{options[:after].iso8601}" if options[:after] + cmd << "--before=#{options[:before].iso8601}" if options[:before] + cmd << sha + cmd += %W[-- #{options[:path]}] if options[:path].present? + + raw_output = IO.popen(cmd) { |io| io.read } + lines = offset_in_ruby ? raw_output.lines.drop(offset) : raw_output.lines + + lines.map! { |c| Rugged::Commit.new(rugged, c.strip) } end def sha_from_ref(ref) @@ -565,9 +565,7 @@ module Gitlab # will trigger a +:mixed+ reset and the working directory will be # replaced with the content of the index. (Untracked and ignored files # will be left alone) - def reset(ref, reset_type) - rugged.reset(ref, reset_type) - end + delegate :reset, to: :rugged # Mimic the `git clean` command and recursively delete untracked files. # Valid keys that can be passed in the +options+ hash are: @@ -845,57 +843,6 @@ module Gitlab rugged.config['core.autocrlf'] = AUTOCRLF_VALUES.invert[value] end - # Create a new directory with a .gitkeep file. Creates - # all required nested directories (i.e. mkdir -p behavior) - # - # options should contain next structure: - # author: { - # email: 'user@example.com', - # name: 'Test User', - # time: Time.now - # }, - # committer: { - # email: 'user@example.com', - # name: 'Test User', - # time: Time.now - # }, - # commit: { - # message: 'Wow such commit', - # branch: 'master', - # update_ref: false - # } - def mkdir(path, options = {}) - # Check if this directory exists; if it does, then don't bother - # adding .gitkeep file. - ref = options[:commit][:branch] - path = Gitlab::Git::PathHelper.normalize_path(path).to_s - rugged_ref = rugged.ref(ref) - - raise InvalidRef.new("Invalid ref") if rugged_ref.nil? - - target_commit = rugged_ref.target - - raise InvalidRef.new("Invalid target commit") if target_commit.nil? - - entry = tree_entry(target_commit, path) - - if entry - if entry[:type] == :blob - raise InvalidBlobName.new("Directory already exists as a file") - else - raise InvalidBlobName.new("Directory already exists") - end - end - - options[:file] = { - content: '', - path: "#{path}/.gitkeep", - update: true - } - - Gitlab::Git::Blob.commit(self, options) - end - # Returns result like "git ls-files" , recursive and full file path # # Ex. diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 7e1484613f2..ffb178334bc 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -10,10 +10,10 @@ module Gitlab deploy_key_upload: 'This deploy key does not have write access to this project.', no_repo: 'A repository for this project does not exist yet.' - } + }.freeze - DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive } - PUSH_COMMANDS = %w{ git-receive-pack } + DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive }.freeze + PUSH_COMMANDS = %w{ git-receive-pack }.freeze ALL_COMMANDS = DOWNLOAD_COMMANDS + PUSH_COMMANDS attr_reader :actor, :project, :protocol, :user_access, :authentication_abilities diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index d95ff4fd104..dc73cad93a5 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -285,7 +285,7 @@ module Gitlab def fetch_resources(resource_type, *opts) return if imported?(resource_type) - opts.last.merge!(page: current_page(resource_type)) + opts.last[:page] = current_page(resource_type) client.public_send(resource_type, *opts) do |resources| yield resources diff --git a/lib/gitlab/github_import/issuable_formatter.rb b/lib/gitlab/github_import/issuable_formatter.rb index 29fb0f9d333..27b171d6ddb 100644 --- a/lib/gitlab/github_import/issuable_formatter.rb +++ b/lib/gitlab/github_import/issuable_formatter.rb @@ -7,9 +7,7 @@ module Gitlab raise NotImplementedError end - def number - raw_data.number - end + delegate :number, to: :raw_data def find_condition { iid: number } diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb index b8a5ac907a4..101b1b80c1e 100644 --- a/lib/gitlab/gon_helper.rb +++ b/lib/gitlab/gon_helper.rb @@ -2,7 +2,7 @@ module Gitlab module GonHelper def add_gon_variables gon.api_version = API::API.version - gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s + gon.default_avatar_url = URI.join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s gon.max_file_size = current_application_settings.max_attachment_size gon.relative_url_root = Gitlab.config.gitlab.relative_url_root gon.shortcuts_path = help_page_path('shortcuts') diff --git a/lib/gitlab/import_export.rb b/lib/gitlab/import_export.rb index a46a41bc56e..8b327cfc226 100644 --- a/lib/gitlab/import_export.rb +++ b/lib/gitlab/import_export.rb @@ -3,7 +3,7 @@ module Gitlab extend self # For every version update, the version history in import_export.md has to be kept up to date. - VERSION = '0.1.6' + VERSION = '0.1.6'.freeze FILENAME_LIMIT = 50 def export_path(relative_path:) @@ -35,7 +35,7 @@ module Gitlab end def export_filename(project:) - basename = "#{Time.now.strftime('%Y-%m-%d_%H-%M-%3N')}_#{project.namespace.full_path}_#{project.path}" + basename = "#{Time.now.strftime('%Y-%m-%d_%H-%M-%3N')}_#{project.full_path.tr('/', '_')}" "#{basename[0..FILENAME_LIMIT]}_export.tar.gz" end diff --git a/lib/gitlab/ldap/person.rb b/lib/gitlab/ldap/person.rb index 7084fd1767d..43eb73250b7 100644 --- a/lib/gitlab/ldap/person.rb +++ b/lib/gitlab/ldap/person.rb @@ -43,9 +43,7 @@ module Gitlab attribute_value(:email) end - def dn - entry.dn - end + delegate :dn, to: :entry private diff --git a/lib/gitlab/metrics/instrumentation.rb b/lib/gitlab/metrics/instrumentation.rb index 4b7a791e497..6aa38542cb4 100644 --- a/lib/gitlab/metrics/instrumentation.rb +++ b/lib/gitlab/metrics/instrumentation.rb @@ -143,11 +143,12 @@ module Gitlab # signature this would break things. As a result we'll make sure the # generated method _only_ accepts regular arguments if the underlying # method also accepts them. - if method.arity == 0 - args_signature = '' - else - args_signature = '*args' - end + args_signature = + if method.arity == 0 + '' + else + '*args' + end proxy_module.class_eval <<-EOF, __FILE__, __LINE__ + 1 def #{name}(#{args_signature}) diff --git a/lib/gitlab/metrics/rack_middleware.rb b/lib/gitlab/metrics/rack_middleware.rb index 47f88727fc8..adc0db1a874 100644 --- a/lib/gitlab/metrics/rack_middleware.rb +++ b/lib/gitlab/metrics/rack_middleware.rb @@ -2,8 +2,8 @@ module Gitlab module Metrics # Rack middleware for tracking Rails and Grape requests. class RackMiddleware - CONTROLLER_KEY = 'action_controller.instance' - ENDPOINT_KEY = 'api.endpoint' + CONTROLLER_KEY = 'action_controller.instance'.freeze + ENDPOINT_KEY = 'api.endpoint'.freeze CONTENT_TYPES = { 'text/html' => :html, 'text/plain' => :txt, @@ -14,7 +14,7 @@ module Gitlab 'image/jpeg' => :jpeg, 'image/gif' => :gif, 'image/svg+xml' => :svg - } + }.freeze def initialize(app) @app = app diff --git a/lib/gitlab/metrics/subscribers/action_view.rb b/lib/gitlab/metrics/subscribers/action_view.rb index 2e9dd4645e3..d435a33e9c7 100644 --- a/lib/gitlab/metrics/subscribers/action_view.rb +++ b/lib/gitlab/metrics/subscribers/action_view.rb @@ -5,7 +5,7 @@ module Gitlab class ActionView < ActiveSupport::Subscriber attach_to :action_view - SERIES = 'views' + SERIES = 'views'.freeze def render_template(event) track(event) if current_transaction diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 7bc16181be6..4f9fb1c7853 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -5,7 +5,7 @@ module Gitlab THREAD_KEY = :_gitlab_metrics_transaction # The series to store events (e.g. Git pushes) in. - EVENT_SERIES = 'events' + EVENT_SERIES = 'events'.freeze attr_reader :tags, :values, :method, :metrics diff --git a/lib/gitlab/middleware/multipart.rb b/lib/gitlab/middleware/multipart.rb index dd99f9bb7d7..fee741b47be 100644 --- a/lib/gitlab/middleware/multipart.rb +++ b/lib/gitlab/middleware/multipart.rb @@ -26,7 +26,7 @@ module Gitlab module Middleware class Multipart - RACK_ENV_KEY = 'HTTP_GITLAB_WORKHORSE_MULTIPART_FIELDS' + RACK_ENV_KEY = 'HTTP_GITLAB_WORKHORSE_MULTIPART_FIELDS'.freeze class Handler def initialize(env, message) diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb index 96ed20af918..95d2f559588 100644 --- a/lib/gitlab/o_auth/user.rb +++ b/lib/gitlab/o_auth/user.rb @@ -29,12 +29,11 @@ module Gitlab def save(provider = 'OAuth') unauthorized_to_create unless gl_user - if needs_blocking? - gl_user.save! - gl_user.block - else - gl_user.save! - end + block_after_save = needs_blocking? + + gl_user.save! + + gl_user.block if block_after_save log.info "(#{provider}) saving user #{auth_hash.email} from login with extern_uid => #{auth_hash.uid}" gl_user diff --git a/lib/gitlab/optimistic_locking.rb b/lib/gitlab/optimistic_locking.rb index 879d46446b3..962ff4d3985 100644 --- a/lib/gitlab/optimistic_locking.rb +++ b/lib/gitlab/optimistic_locking.rb @@ -1,12 +1,12 @@ module Gitlab module OptimisticLocking - extend self + module_function def retry_lock(subject, retries = 100, &block) loop do begin ActiveRecord::Base.transaction do - return block.call(subject) + return yield(subject) end rescue ActiveRecord::StaleObjectError retries -= 1 @@ -15,5 +15,7 @@ module Gitlab end end end + + alias_method :retry_optimistic_lock, :retry_lock end end diff --git a/lib/gitlab/redis.rb b/lib/gitlab/redis.rb index 9384102acec..62dbd429156 100644 --- a/lib/gitlab/redis.rb +++ b/lib/gitlab/redis.rb @@ -4,24 +4,15 @@ require 'active_support/core_ext/hash/keys' module Gitlab class Redis - CACHE_NAMESPACE = 'cache:gitlab' - SESSION_NAMESPACE = 'session:gitlab' - SIDEKIQ_NAMESPACE = 'resque:gitlab' - MAILROOM_NAMESPACE = 'mail_room:gitlab' - DEFAULT_REDIS_URL = 'redis://localhost:6379' + CACHE_NAMESPACE = 'cache:gitlab'.freeze + SESSION_NAMESPACE = 'session:gitlab'.freeze + SIDEKIQ_NAMESPACE = 'resque:gitlab'.freeze + MAILROOM_NAMESPACE = 'mail_room:gitlab'.freeze + DEFAULT_REDIS_URL = 'redis://localhost:6379'.freeze CONFIG_FILE = File.expand_path('../../config/resque.yml', __dir__) class << self - # Do NOT cache in an instance variable. Result may be mutated by caller. - def params - new.params - end - - # Do NOT cache in an instance variable. Result may be mutated by caller. - # @deprecated Use .params instead to get sentinel support - def url - new.url - end + delegate :params, :url, to: :new def with @pool ||= ConnectionPool.new(size: pool_size) { ::Redis.new(params) } diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb index 437a339dd2b..7668ecacc4b 100644 --- a/lib/gitlab/reference_extractor.rb +++ b/lib/gitlab/reference_extractor.rb @@ -1,7 +1,7 @@ module Gitlab # Extract possible GFM references from an arbitrary String for further processing. class ReferenceExtractor < Banzai::ReferenceExtractor - REFERABLES = %i(user issue label milestone merge_request snippet commit commit_range directly_addressed_user) + REFERABLES = %i(user issue label milestone merge_request snippet commit commit_range directly_addressed_user).freeze attr_accessor :project, :current_user, :author def initialize(project, current_user = nil) diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb index c77fe2d8bdc..5e5f5ff1589 100644 --- a/lib/gitlab/regex.rb +++ b/lib/gitlab/regex.rb @@ -5,17 +5,18 @@ module Gitlab # The namespace regex is used in Javascript to validate usernames in the "Register" form. However, Javascript # does not support the negative lookbehind assertion (?<!) that disallows usernames ending in `.git` and `.atom`. # Since this is a non-trivial problem to solve in Javascript (heavily complicate the regex, modify view code to - # allow non-regex validatiions, etc), `NAMESPACE_REGEX_STR_SIMPLE` serves as a Javascript-compatible version of + # allow non-regex validatiions, etc), `NAMESPACE_REGEX_STR_JS` serves as a Javascript-compatible version of # `NAMESPACE_REGEX_STR`, with the negative lookbehind assertion removed. This means that the client-side validation # will pass for usernames ending in `.atom` and `.git`, but will be caught by the server-side validation. PATH_REGEX_STR = '[a-zA-Z0-9_\.][a-zA-Z0-9_\-\.]*'.freeze - NAMESPACE_REGEX_STR_SIMPLE = PATH_REGEX_STR + '[a-zA-Z0-9_\-]|[a-zA-Z0-9_]'.freeze - NAMESPACE_REGEX_STR = '(?:' + NAMESPACE_REGEX_STR_SIMPLE + ')(?<!\.git|\.atom)'.freeze - PROJECT_REGEX_STR = PATH_REGEX_STR + '(?<!\.git|\.atom)'.freeze + NAMESPACE_REGEX_STR_JS = PATH_REGEX_STR + '[a-zA-Z0-9_\-]|[a-zA-Z0-9_]'.freeze + NO_SUFFIX_REGEX_STR = '(?<!\.git|\.atom)'.freeze + NAMESPACE_REGEX_STR = "(?:#{NAMESPACE_REGEX_STR_JS})#{NO_SUFFIX_REGEX_STR}".freeze + PROJECT_REGEX_STR = "(?:#{PATH_REGEX_STR})#{NO_SUFFIX_REGEX_STR}".freeze # Same as NAMESPACE_REGEX_STR but allows `/` in the path. # So `group/subgroup` will match this regex but not NAMESPACE_REGEX_STR - NAMESPACE_REF_REGEX_STR = '(?:[a-zA-Z0-9_\.][a-zA-Z0-9_\-\.\/]*[a-zA-Z0-9_\-]|[a-zA-Z0-9_])(?<!\.git|\.atom)'.freeze + FULL_NAMESPACE_REGEX_STR = "(?:#{NAMESPACE_REGEX_STR}/)*#{NAMESPACE_REGEX_STR}".freeze def namespace_regex @namespace_regex ||= /\A#{NAMESPACE_REGEX_STR}\z/.freeze diff --git a/lib/gitlab/request_profiler.rb b/lib/gitlab/request_profiler.rb index 8130e55351e..0c9ab759e81 100644 --- a/lib/gitlab/request_profiler.rb +++ b/lib/gitlab/request_profiler.rb @@ -2,7 +2,7 @@ require 'fileutils' module Gitlab module RequestProfiler - PROFILES_DIR = "#{Gitlab.config.shared.path}/tmp/requests_profiles" + PROFILES_DIR = "#{Gitlab.config.shared.path}/tmp/requests_profiles".freeze def profile_token Rails.cache.fetch('profile-token') do diff --git a/lib/gitlab/saml/user.rb b/lib/gitlab/saml/user.rb index f253dc7477e..8a7cc690046 100644 --- a/lib/gitlab/saml/user.rb +++ b/lib/gitlab/saml/user.rb @@ -28,11 +28,12 @@ module Gitlab if external_users_enabled? && @user # Check if there is overlap between the user's groups and the external groups # setting then set user as external or internal. - if (auth_hash.groups & Gitlab::Saml::Config.external_groups).empty? - @user.external = false - else - @user.external = true - end + @user.external = + if (auth_hash.groups & Gitlab::Saml::Config.external_groups).empty? + false + else + true + end end @user diff --git a/lib/gitlab/sanitizers/svg/whitelist.rb b/lib/gitlab/sanitizers/svg/whitelist.rb index 7b6b70d8dbc..d50f826f924 100644 --- a/lib/gitlab/sanitizers/svg/whitelist.rb +++ b/lib/gitlab/sanitizers/svg/whitelist.rb @@ -6,18 +6,19 @@ module Gitlab module SVG class Whitelist ALLOWED_ELEMENTS = %w[ - a altGlyph altGlyphDef altGlyphItem animate - animateColor animateMotion animateTransform circle clipPath color-profile - cursor defs desc ellipse feBlend feColorMatrix feComponentTransfer - feComposite feConvolveMatrix feDiffuseLighting feDisplacementMap - feDistantLight feFlood feFuncA feFuncB feFuncG feFuncR feGaussianBlur - feImage feMerge feMergeNode feMorphology feOffset fePointLight - feSpecularLighting feSpotLight feTile feTurbulence filter font font-face - font-face-format font-face-name font-face-src font-face-uri foreignObject - g glyph glyphRef hkern image line linearGradient marker mask metadata - missing-glyph mpath path pattern polygon polyline radialGradient rect - script set stop style svg switch symbol text textPath title tref tspan use - view vkern].freeze + a altGlyph altGlyphDef altGlyphItem animate + animateColor animateMotion animateTransform circle clipPath color-profile + cursor defs desc ellipse feBlend feColorMatrix feComponentTransfer + feComposite feConvolveMatrix feDiffuseLighting feDisplacementMap + feDistantLight feFlood feFuncA feFuncB feFuncG feFuncR feGaussianBlur + feImage feMerge feMergeNode feMorphology feOffset fePointLight + feSpecularLighting feSpotLight feTile feTurbulence filter font font-face + font-face-format font-face-name font-face-src font-face-uri foreignObject + g glyph glyphRef hkern image line linearGradient marker mask metadata + missing-glyph mpath path pattern polygon polyline radialGradient rect + script set stop style svg switch symbol text textPath title tref tspan use + view vkern + ].freeze ALLOWED_DATA_ATTRIBUTES_IN_ELEMENTS = %w[svg].freeze diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb index c9c65f76f4b..ccfa517e04b 100644 --- a/lib/gitlab/search_results.rb +++ b/lib/gitlab/search_results.rb @@ -56,11 +56,12 @@ module Gitlab def issues issues = IssuesFinder.new(current_user).execute.where(project_id: project_ids_relation) - if query =~ /#(\d+)\z/ - issues = issues.where(iid: $1) - else - issues = issues.full_search(query) - end + issues = + if query =~ /#(\d+)\z/ + issues.where(iid: $1) + else + issues.full_search(query) + end issues.order('updated_at DESC') end @@ -73,11 +74,12 @@ module Gitlab def merge_requests merge_requests = MergeRequestsFinder.new(current_user).execute.in_projects(project_ids_relation) - if query =~ /[#!](\d+)\z/ - merge_requests = merge_requests.where(iid: $1) - else - merge_requests = merge_requests.full_search(query) - end + merge_requests = + if query =~ /[#!](\d+)\z/ + merge_requests.where(iid: $1) + else + merge_requests.full_search(query) + end merge_requests.order('updated_at DESC') end diff --git a/lib/gitlab/seeder.rb b/lib/gitlab/seeder.rb index 7cf506ebe64..b7f825e8284 100644 --- a/lib/gitlab/seeder.rb +++ b/lib/gitlab/seeder.rb @@ -18,7 +18,7 @@ def Notify.deliver_later self end eos - eval(code) + eval(code) # rubocop:disable Security/Eval end end end diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb index 3faa336f142..7374d2bc8b8 100644 --- a/lib/gitlab/shell.rb +++ b/lib/gitlab/shell.rb @@ -82,8 +82,8 @@ module Gitlab def import_repository(storage, name, url) # Timeout should be less than 900 ideally, to prevent the memory killer # to silently kill the process without knowing we are timing out here. - output, status = Popen::popen([gitlab_shell_projects_path, 'import-project', - storage, "#{name}.git", url, '800']) + output, status = Popen.popen([gitlab_shell_projects_path, 'import-project', + storage, "#{name}.git", url, '800']) raise Error, output unless status.zero? true end @@ -145,7 +145,7 @@ module Gitlab # batch_add_keys { |adder| adder.add_key("key-42", "sha-rsa ...") } def batch_add_keys(&block) IO.popen(%W(#{gitlab_shell_path}/bin/gitlab-keys batch-add-keys), 'w') do |io| - block.call(KeyAdder.new(io)) + yield(KeyAdder.new(io)) end end diff --git a/lib/gitlab/sherlock/query.rb b/lib/gitlab/sherlock/query.rb index 4917c4ae2ac..99e56e923eb 100644 --- a/lib/gitlab/sherlock/query.rb +++ b/lib/gitlab/sherlock/query.rb @@ -94,11 +94,12 @@ module Gitlab private def raw_explain(query) - if Gitlab::Database.postgresql? - explain = "EXPLAIN ANALYZE #{query};" - else - explain = "EXPLAIN #{query};" - end + explain = + if Gitlab::Database.postgresql? + "EXPLAIN ANALYZE #{query};" + else + "EXPLAIN #{query};" + end ActiveRecord::Base.connection.execute(explain) end diff --git a/lib/gitlab/template/gitlab_ci_yml_template.rb b/lib/gitlab/template/gitlab_ci_yml_template.rb index 9d2ecee9756..fd040148a1e 100644 --- a/lib/gitlab/template/gitlab_ci_yml_template.rb +++ b/lib/gitlab/template/gitlab_ci_yml_template.rb @@ -28,7 +28,7 @@ module Gitlab end def dropdown_names(context) - categories = context == 'autodeploy' ? ['Auto deploy'] : ['General', 'Pages'] + categories = context == 'autodeploy' ? ['Auto deploy'] : %w(General Pages) super().slice(*categories) end end diff --git a/lib/gitlab/upgrader.rb b/lib/gitlab/upgrader.rb index 4cc34e34460..961df0468a4 100644 --- a/lib/gitlab/upgrader.rb +++ b/lib/gitlab/upgrader.rb @@ -46,7 +46,7 @@ module Gitlab git_tags = fetch_git_tags git_tags = git_tags.select { |version| version =~ /v\d+\.\d+\.\d+\Z/ } git_versions = git_tags.map { |tag| Gitlab::VersionInfo.parse(tag.match(/v\d+\.\d+\.\d+/).to_s) } - "v#{git_versions.sort.last.to_s}" + "v#{git_versions.sort.last}" end def fetch_git_tags @@ -59,10 +59,10 @@ module Gitlab "Stash changed files" => %W(#{Gitlab.config.git.bin_path} stash), "Get latest code" => %W(#{Gitlab.config.git.bin_path} fetch), "Switch to new version" => %W(#{Gitlab.config.git.bin_path} checkout v#{latest_version}), - "Install gems" => %W(bundle), - "Migrate DB" => %W(bundle exec rake db:migrate), - "Recompile assets" => %W(bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile), - "Clear cache" => %W(bundle exec rake cache:clear) + "Install gems" => %w(bundle), + "Migrate DB" => %w(bundle exec rake db:migrate), + "Recompile assets" => %w(bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile), + "Clear cache" => %w(bundle exec rake cache:clear) } end diff --git a/lib/gitlab/url_sanitizer.rb b/lib/gitlab/url_sanitizer.rb index 19dad699edf..1f0d96088cf 100644 --- a/lib/gitlab/url_sanitizer.rb +++ b/lib/gitlab/url_sanitizer.rb @@ -1,7 +1,7 @@ module Gitlab class UrlSanitizer def self.sanitize(content) - regexp = URI::Parser.new.make_regexp(['http', 'https', 'ssh', 'git']) + regexp = URI::Parser.new.make_regexp(%w(http https ssh git)) content.gsub(regexp) { |url| new(url).masked_url } rescue Addressable::URI::InvalidURIError diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb index a4e966e4016..b28708c34e1 100644 --- a/lib/gitlab/visibility_level.rb +++ b/lib/gitlab/visibility_level.rb @@ -33,9 +33,7 @@ module Gitlab PUBLIC = 20 unless const_defined?(:PUBLIC) class << self - def values - options.values - end + delegate :values, to: :options def options { diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb index c8872df8a93..3ff9f9eb5e7 100644 --- a/lib/gitlab/workhorse.rb +++ b/lib/gitlab/workhorse.rb @@ -4,10 +4,10 @@ require 'securerandom' module Gitlab class Workhorse - SEND_DATA_HEADER = 'Gitlab-Workhorse-Send-Data' - VERSION_FILE = 'GITLAB_WORKHORSE_VERSION' - INTERNAL_API_CONTENT_TYPE = 'application/vnd.gitlab-workhorse+json' - INTERNAL_API_REQUEST_HEADER = 'Gitlab-Workhorse-Api-Request' + SEND_DATA_HEADER = 'Gitlab-Workhorse-Send-Data'.freeze + VERSION_FILE = 'GITLAB_WORKHORSE_VERSION'.freeze + INTERNAL_API_CONTENT_TYPE = 'application/vnd.gitlab-workhorse+json'.freeze + INTERNAL_API_REQUEST_HEADER = 'Gitlab-Workhorse-Api-Request'.freeze # Supposedly the effective key size for HMAC-SHA256 is 256 bits, i.e. 32 # bytes https://tools.ietf.org/html/rfc4868#section-2.6 diff --git a/lib/tasks/brakeman.rake b/lib/tasks/brakeman.rake index d5a402907d8..2301ec9b228 100644 --- a/lib/tasks/brakeman.rake +++ b/lib/tasks/brakeman.rake @@ -2,7 +2,7 @@ desc 'Security check via brakeman' task :brakeman do # We get 0 warnings at level 'w3' but we would like to reach 'w2'. Merge # requests are welcome! - if system(*%W(brakeman --no-progress --skip-files lib/backup/repository.rb -w3 -z)) + if system(*%w(brakeman --no-progress --skip-files lib/backup/repository.rb -w3 -z)) puts 'Security check succeed' else puts 'Security check failed' diff --git a/lib/tasks/cache.rake b/lib/tasks/cache.rake index 78ae187817a..d55923673b1 100644 --- a/lib/tasks/cache.rake +++ b/lib/tasks/cache.rake @@ -1,7 +1,7 @@ namespace :cache do namespace :clear do REDIS_CLEAR_BATCH_SIZE = 1000 # There seems to be no speedup when pushing beyond 1,000 - REDIS_SCAN_START_STOP = '0' # Magic value, see http://redis.io/commands/scan + REDIS_SCAN_START_STOP = '0'.freeze # Magic value, see http://redis.io/commands/scan desc "GitLab | Clear redis cache" task redis: :environment do diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake index 5e94fba97bf..e65609d7001 100644 --- a/lib/tasks/dev.rake +++ b/lib/tasks/dev.rake @@ -2,7 +2,7 @@ task dev: ["dev:setup"] namespace :dev do desc "GitLab | Setup developer environment (db, fixtures)" - task :setup => :environment do + task setup: :environment do ENV['force'] = 'yes' Rake::Task["gitlab:setup"].invoke Rake::Task["gitlab:shell:setup"].invoke diff --git a/lib/tasks/downtime_check.rake b/lib/tasks/downtime_check.rake index afe5d42910c..557f4fef10b 100644 --- a/lib/tasks/downtime_check.rake +++ b/lib/tasks/downtime_check.rake @@ -1,10 +1,10 @@ desc 'Checks if migrations in a branch require downtime' task downtime_check: :environment do - if defined?(Gitlab::License) - repo = 'gitlab-ee' - else - repo = 'gitlab-ce' - end + repo = if defined?(Gitlab::License) + 'gitlab-ee' + else + 'gitlab-ce' + end `git fetch https://gitlab.com/gitlab-org/#{repo}.git --depth 1` diff --git a/lib/tasks/flay.rake b/lib/tasks/flay.rake index e9587595fef..7ad2b2e4d39 100644 --- a/lib/tasks/flay.rake +++ b/lib/tasks/flay.rake @@ -1,6 +1,6 @@ desc 'Code duplication analyze via flay' task :flay do - output = %x(bundle exec flay --mass 35 app/ lib/gitlab/) + output = `bundle exec flay --mass 35 app/ lib/gitlab/` if output.include? "Similar code found" puts output diff --git a/lib/tasks/gitlab/assets.rake b/lib/tasks/gitlab/assets.rake index 3eb5fc07b3c..098f9851b45 100644 --- a/lib/tasks/gitlab/assets.rake +++ b/lib/tasks/gitlab/assets.rake @@ -20,7 +20,7 @@ namespace :gitlab do desc 'GitLab | Assets | Fix all absolute url references in CSS' task :fix_urls do css_files = Dir['public/assets/*.css'] - css_files.each do | file | + css_files.each do |file| # replace url(/assets/*) with url(./*) puts "Fixing #{file}" system "sed", "-i", "-e", 's/url(\([\"\']\?\)\/assets\//url(\1.\//g', file diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index 6102517e730..38edd49b6ed 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -6,8 +6,6 @@ namespace :gitlab do gitlab:ldap:check gitlab:app:check} - - namespace :app do desc "GitLab | Check the configuration of the GitLab Rails app" task check: :environment do @@ -34,7 +32,6 @@ namespace :gitlab do finished_checking "GitLab" end - # Checks ######################## @@ -194,7 +191,7 @@ namespace :gitlab do def check_migrations_are_up print "All migrations up? ... " - migration_status, _ = Gitlab::Popen.popen(%W(bundle exec rake db:migrate:status)) + migration_status, _ = Gitlab::Popen.popen(%w(bundle exec rake db:migrate:status)) unless migration_status =~ /down\s+\d{14}/ puts "yes".color(:green) @@ -279,7 +276,7 @@ namespace :gitlab do upload_path_tmp = File.join(upload_path, 'tmp') if File.stat(upload_path).mode == 040700 - unless Dir.exists?(upload_path_tmp) + unless Dir.exist?(upload_path_tmp) puts 'skipped (no tmp uploads folder yet)'.color(:magenta) return end @@ -316,7 +313,7 @@ namespace :gitlab do min_redis_version = "2.8.0" print "Redis version >= #{min_redis_version}? ... " - redis_version = run_command(%W(redis-cli --version)) + redis_version = run_command(%w(redis-cli --version)) redis_version = redis_version.try(:match, /redis-cli (\d+\.\d+\.\d+)/) if redis_version && (Gem::Version.new(redis_version[1]) > Gem::Version.new(min_redis_version)) @@ -351,7 +348,6 @@ namespace :gitlab do finished_checking "GitLab Shell" end - # Checks ######################## @@ -387,7 +383,7 @@ namespace :gitlab do unless File.exist?(repo_base_path) puts "can't check because of previous errors".color(:magenta) - return + break end unless File.symlink?(repo_base_path) @@ -410,7 +406,7 @@ namespace :gitlab do unless File.exist?(repo_base_path) puts "can't check because of previous errors".color(:magenta) - return + break end if File.stat(repo_base_path).mode.to_s(8).ends_with?("2770") @@ -440,7 +436,7 @@ namespace :gitlab do unless File.exist?(repo_base_path) puts "can't check because of previous errors".color(:magenta) - return + break end uid = uid_for(gitlab_shell_ssh_user) @@ -493,7 +489,6 @@ namespace :gitlab do ) fix_and_rerun end - end end @@ -565,8 +560,6 @@ namespace :gitlab do end end - - namespace :sidekiq do desc "GitLab | Check the configuration of Sidekiq" task check: :environment do @@ -579,7 +572,6 @@ namespace :gitlab do finished_checking "Sidekiq" end - # Checks ######################## @@ -621,12 +613,11 @@ namespace :gitlab do end def sidekiq_process_count - ps_ux, _ = Gitlab::Popen.popen(%W(ps ux)) + ps_ux, _ = Gitlab::Popen.popen(%w(ps ux)) ps_ux.scan(/sidekiq \d+\.\d+\.\d+/).count end end - namespace :incoming_email do desc "GitLab | Check the configuration of Reply by email" task check: :environment do @@ -649,7 +640,6 @@ namespace :gitlab do finished_checking "Reply by email" end - # Checks ######################## @@ -757,7 +747,7 @@ namespace :gitlab do end def mail_room_running? - ps_ux, _ = Gitlab::Popen.popen(%W(ps ux)) + ps_ux, _ = Gitlab::Popen.popen(%w(ps ux)) ps_ux.include?("mail_room") end end @@ -805,13 +795,13 @@ namespace :gitlab do def check_ldap_auth(adapter) auth = adapter.config.has_auth? - if auth && adapter.ldap.bind - message = 'Success'.color(:green) - elsif auth - message = 'Failed. Check `bind_dn` and `password` configuration values'.color(:red) - else - message = 'Anonymous. No `bind_dn` or `password` configured'.color(:yellow) - end + message = if auth && adapter.ldap.bind + 'Success'.color(:green) + elsif auth + 'Failed. Check `bind_dn` and `password` configuration values'.color(:red) + else + 'Anonymous. No `bind_dn` or `password` configured'.color(:yellow) + end puts "LDAP authentication... #{message}" end @@ -838,11 +828,11 @@ namespace :gitlab do user = User.find_by(username: username) if user repo_dirs = user.authorized_projects.map do |p| - File.join( - p.repository_storage_path, - "#{p.path_with_namespace}.git" - ) - end + File.join( + p.repository_storage_path, + "#{p.path_with_namespace}.git" + ) + end repo_dirs.each { |repo_dir| check_repo_integrity(repo_dir) } else @@ -855,7 +845,7 @@ namespace :gitlab do ########################## def fix_and_rerun - puts " Please #{"fix the error above"} and rerun the checks.".color(:red) + puts " Please fix the error above and rerun the checks.".color(:red) end def for_more_information(*sources) @@ -917,7 +907,7 @@ namespace :gitlab do def check_ruby_version required_version = Gitlab::VersionInfo.new(2, 1, 0) - current_version = Gitlab::VersionInfo.parse(run_command(%W(ruby --version))) + current_version = Gitlab::VersionInfo.parse(run_command(%w(ruby --version))) print "Ruby version >= #{required_version} ? ... " @@ -988,13 +978,13 @@ namespace :gitlab do end def check_config_lock(repo_dir) - config_exists = File.exist?(File.join(repo_dir,'config.lock')) + config_exists = File.exist?(File.join(repo_dir, 'config.lock')) config_output = config_exists ? 'yes'.color(:red) : 'no'.color(:green) puts "'config.lock' file exists?".color(:yellow) + " ... #{config_output}" end def check_ref_locks(repo_dir) - lock_files = Dir.glob(File.join(repo_dir,'refs/heads/*.lock')) + lock_files = Dir.glob(File.join(repo_dir, 'refs/heads/*.lock')) if lock_files.present? puts "Ref lock files exist:".color(:red) lock_files.each do |lock_file| diff --git a/lib/tasks/gitlab/cleanup.rake b/lib/tasks/gitlab/cleanup.rake index 967f630ef20..daf7382dd02 100644 --- a/lib/tasks/gitlab/cleanup.rake +++ b/lib/tasks/gitlab/cleanup.rake @@ -25,7 +25,6 @@ namespace :gitlab do end all_dirs.each do |dir_path| - if remove_flag if FileUtils.rm_rf dir_path puts "Removed...#{dir_path}".color(:red) @@ -53,11 +52,11 @@ namespace :gitlab do IO.popen(%W(find #{repo_root} -mindepth 1 -maxdepth 2 -name *.git)) do |find| find.each_line do |path| path.chomp! - repo_with_namespace = path. - sub(repo_root, ''). - sub(%r{^/*}, ''). - chomp('.git'). - chomp('.wiki') + repo_with_namespace = path + .sub(repo_root, '') + .sub(%r{^/*}, '') + .chomp('.git') + .chomp('.wiki') next if Project.find_by_full_path(repo_with_namespace) new_path = path + move_suffix puts path.inspect + ' -> ' + new_path.inspect diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake index 7c96bc864ce..ecf6b6e068b 100644 --- a/lib/tasks/gitlab/db.rake +++ b/lib/tasks/gitlab/db.rake @@ -23,7 +23,7 @@ namespace :gitlab do end desc 'Drop all tables' - task :drop_tables => :environment do + task drop_tables: :environment do connection = ActiveRecord::Base.connection # If MySQL, turn off foreign key checks @@ -62,9 +62,9 @@ namespace :gitlab do ref = Shellwords.escape(args[:ref]) - migrations = `git diff #{ref}.. --name-only -- db/migrate`.lines. - map { |file| Rails.root.join(file.strip).to_s }. - select { |file| File.file?(file) } + migrations = `git diff #{ref}.. --name-only -- db/migrate`.lines + .map { |file| Rails.root.join(file.strip).to_s } + .select { |file| File.file?(file) } Gitlab::DowntimeCheck.new.check_and_print(migrations) end diff --git a/lib/tasks/gitlab/git.rake b/lib/tasks/gitlab/git.rake index a67c1fe1f27..cf82134d97e 100644 --- a/lib/tasks/gitlab/git.rake +++ b/lib/tasks/gitlab/git.rake @@ -1,6 +1,5 @@ namespace :gitlab do namespace :git do - desc "GitLab | Git | Repack" task repack: :environment do failures = perform_git_cmd(%W(#{Gitlab.config.git.bin_path} repack -a --quiet), "Repacking repo") @@ -50,6 +49,5 @@ namespace :gitlab do puts "The following repositories reported errors:".color(:red) failures.each { |f| puts "- #{f}" } end - end end diff --git a/lib/tasks/gitlab/import.rake b/lib/tasks/gitlab/import.rake index b4015f5238e..66e7b7685f7 100644 --- a/lib/tasks/gitlab/import.rake +++ b/lib/tasks/gitlab/import.rake @@ -46,7 +46,7 @@ namespace :gitlab do group = Namespace.find_by(path: group_name) # create group namespace unless group - group = Group.new(:name => group_name) + group = Group.new(name: group_name) group.path = group_name group.owner = user if group.save diff --git a/lib/tasks/gitlab/import_export.rake b/lib/tasks/gitlab/import_export.rake index c2c6031db67..dd1825c8a9e 100644 --- a/lib/tasks/gitlab/import_export.rake +++ b/lib/tasks/gitlab/import_export.rake @@ -7,7 +7,7 @@ namespace :gitlab do desc "GitLab | Display exported DB structure" task data: :environment do - puts YAML.load_file(Gitlab::ImportExport.config_file)['project_tree'].to_yaml(:SortKeys => true) + puts YAML.load_file(Gitlab::ImportExport.config_file)['project_tree'].to_yaml(SortKeys: true) end end end diff --git a/lib/tasks/gitlab/info.rake b/lib/tasks/gitlab/info.rake index f7c831892ee..ae78fe64eb8 100644 --- a/lib/tasks/gitlab/info.rake +++ b/lib/tasks/gitlab/info.rake @@ -2,24 +2,23 @@ namespace :gitlab do namespace :env do desc "GitLab | Show information about GitLab and its environment" task info: :environment do - # check if there is an RVM environment - rvm_version = run_and_match(%W(rvm --version), /[\d\.]+/).try(:to_s) + rvm_version = run_and_match(%w(rvm --version), /[\d\.]+/).try(:to_s) # check Ruby version - ruby_version = run_and_match(%W(ruby --version), /[\d\.p]+/).try(:to_s) + ruby_version = run_and_match(%w(ruby --version), /[\d\.p]+/).try(:to_s) # check Gem version - gem_version = run_command(%W(gem --version)) + gem_version = run_command(%w(gem --version)) # check Bundler version - bunder_version = run_and_match(%W(bundle --version), /[\d\.]+/).try(:to_s) + bunder_version = run_and_match(%w(bundle --version), /[\d\.]+/).try(:to_s) # check Rake version - rake_version = run_and_match(%W(rake --version), /[\d\.]+/).try(:to_s) + rake_version = run_and_match(%w(rake --version), /[\d\.]+/).try(:to_s) # check redis version - redis_version = run_and_match(%W(redis-cli --version), /redis-cli (\d+\.\d+\.\d+)/).to_a + redis_version = run_and_match(%w(redis-cli --version), /redis-cli (\d+\.\d+\.\d+)/).to_a puts "" puts "System information".color(:yellow) puts "System:\t\t#{os_name || "unknown".color(:red)}" - puts "Current User:\t#{run_command(%W(whoami))}" + puts "Current User:\t#{run_command(%w(whoami))}" puts "Using RVM:\t#{rvm_version.present? ? "yes".color(:green) : "no"}" puts "RVM Version:\t#{rvm_version}" if rvm_version.present? puts "Ruby Version:\t#{ruby_version || "unknown".color(:red)}" @@ -29,7 +28,6 @@ namespace :gitlab do puts "Redis Version:\t#{redis_version[1] || "unknown".color(:red)}" puts "Sidekiq Version:#{Sidekiq::VERSION}" - # check database adapter database_adapter = ActiveRecord::Base.connection.adapter_name.downcase @@ -54,8 +52,6 @@ namespace :gitlab do puts "Using Omniauth:\t#{Gitlab.config.omniauth.enabled ? "yes".color(:green) : "no"}" puts "Omniauth Providers: #{omniauth_providers.join(', ')}" if Gitlab.config.omniauth.enabled - - # check Gitolite version gitlab_shell_version_file = "#{Gitlab.config.gitlab_shell.hooks_path}/../VERSION" if File.readable?(gitlab_shell_version_file) @@ -71,7 +67,6 @@ namespace :gitlab do end puts "Hooks:\t\t#{Gitlab.config.gitlab_shell.hooks_path}" puts "Git:\t\t#{Gitlab.config.git.bin_path}" - end end end diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake index 5a09cd7ce41..dd2fda54e62 100644 --- a/lib/tasks/gitlab/shell.rake +++ b/lib/tasks/gitlab/shell.rake @@ -20,10 +20,10 @@ namespace :gitlab do config = { user: Gitlab.config.gitlab.user, gitlab_url: gitlab_url, - http_settings: {self_signed_cert: false}.stringify_keys, + http_settings: { self_signed_cert: false }.stringify_keys, auth_file: File.join(user_home, ".ssh", "authorized_keys"), redis: { - bin: %x{which redis-cli}.chomp, + bin: `which redis-cli`.chomp, namespace: "resque:gitlab" }.stringify_keys, log_level: "INFO", @@ -43,7 +43,7 @@ namespace :gitlab do File.open("config.yml", "w+") {|f| f.puts config.to_yaml} # Launch installation process - system(*%W(bin/install) + repository_storage_paths_args) + system(*%w(bin/install) + repository_storage_paths_args) end # (Re)create hooks diff --git a/lib/tasks/gitlab/sidekiq.rake b/lib/tasks/gitlab/sidekiq.rake index f2e12d85045..6cbc83b8973 100644 --- a/lib/tasks/gitlab/sidekiq.rake +++ b/lib/tasks/gitlab/sidekiq.rake @@ -1,9 +1,9 @@ namespace :gitlab do namespace :sidekiq do - QUEUE = 'queue:post_receive' + QUEUE = 'queue:post_receive'.freeze desc 'Drop all Sidekiq PostReceive jobs for a given project' - task :drop_post_receive , [:project] => :environment do |t, args| + task :drop_post_receive, [:project] => :environment do |t, args| unless args.project.present? abort "Please specify the project you want to drop PostReceive jobs for:\n rake gitlab:sidekiq:drop_post_receive[group/project]" end @@ -21,7 +21,7 @@ namespace :gitlab do # new jobs already. We will repopulate it with the old jobs, skipping the # ones we want to drop. dropped = 0 - while (job = redis.lpop(temp_queue)) do + while (job = redis.lpop(temp_queue)) if repo_path(job) == project_path dropped += 1 else diff --git a/lib/tasks/gitlab/task_helpers.rb b/lib/tasks/gitlab/task_helpers.rb index e128738b5f8..2a999ad6959 100644 --- a/lib/tasks/gitlab/task_helpers.rb +++ b/lib/tasks/gitlab/task_helpers.rb @@ -19,23 +19,20 @@ module Gitlab # It will primarily use lsb_relase to determine the OS. # It has fallbacks to Debian, SuSE, OS X and systems running systemd. def os_name - os_name = run_command(%W(lsb_release -irs)) - os_name ||= if File.readable?('/etc/system-release') - File.read('/etc/system-release') - end - os_name ||= if File.readable?('/etc/debian_version') - debian_version = File.read('/etc/debian_version') - "Debian #{debian_version}" - end - os_name ||= if File.readable?('/etc/SuSE-release') - File.read('/etc/SuSE-release') - end - os_name ||= if os_x_version = run_command(%W(sw_vers -productVersion)) - "Mac OS X #{os_x_version}" - end - os_name ||= if File.readable?('/etc/os-release') - File.read('/etc/os-release').match(/PRETTY_NAME=\"(.+)\"/)[1] - end + os_name = run_command(%w(lsb_release -irs)) + os_name ||= + if File.readable?('/etc/system-release') + File.read('/etc/system-release') + elsif File.readable?('/etc/debian_version') + "Debian #{File.read('/etc/debian_version')}" + elsif File.readable?('/etc/SuSE-release') + File.read('/etc/SuSE-release') + elsif os_x_version = run_command(%w(sw_vers -productVersion)) + "Mac OS X #{os_x_version}" + elsif File.readable?('/etc/os-release') + File.read('/etc/os-release').match(/PRETTY_NAME=\"(.+)\"/)[1] + end + os_name.try(:squish!) end @@ -104,7 +101,7 @@ module Gitlab def warn_user_is_not_gitlab unless @warned_user_not_gitlab gitlab_user = Gitlab.config.gitlab.user - current_user = run_command(%W(whoami)).chomp + current_user = run_command(%w(whoami)).chomp unless current_user == gitlab_user puts " Warning ".color(:black).background(:yellow) puts " You are running as user #{current_user.color(:magenta)}, we hope you know what you are doing." @@ -171,14 +168,14 @@ module Gitlab def reset_to_tag(tag_wanted, target_dir) tag = - begin - # First try to checkout without fetching - # to avoid stalling tests if the Internet is down. - run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} describe -- #{tag_wanted}]) - rescue Gitlab::TaskFailedError - run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} fetch origin]) - run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} describe -- origin/#{tag_wanted}]) - end + begin + # First try to checkout without fetching + # to avoid stalling tests if the Internet is down. + run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} describe -- #{tag_wanted}]) + rescue Gitlab::TaskFailedError + run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} fetch origin]) + run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} describe -- origin/#{tag_wanted}]) + end if tag run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} reset --hard #{tag.strip}]) diff --git a/lib/tasks/gitlab/test.rake b/lib/tasks/gitlab/test.rake index 84810b489ce..523b0fa055b 100644 --- a/lib/tasks/gitlab/test.rake +++ b/lib/tasks/gitlab/test.rake @@ -2,15 +2,15 @@ namespace :gitlab do desc "GitLab | Run all tests" task :test do cmds = [ - %W(rake brakeman), - %W(rake rubocop), - %W(rake spinach), - %W(rake spec), - %W(rake karma) + %w(rake brakeman), + %w(rake rubocop), + %w(rake spinach), + %w(rake spec), + %w(rake karma) ] cmds.each do |cmd| - system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd) or raise("#{cmd} failed!") + system({ 'RAILS_ENV' => 'test', 'force' => 'yes' }, *cmd) || raise("#{cmd} failed!") end end end diff --git a/lib/tasks/gitlab/track_deployment.rake b/lib/tasks/gitlab/track_deployment.rake index 84aa2e8507a..6f101aea303 100644 --- a/lib/tasks/gitlab/track_deployment.rake +++ b/lib/tasks/gitlab/track_deployment.rake @@ -1,8 +1,8 @@ namespace :gitlab do desc 'GitLab | Tracks a deployment in GitLab Performance Monitoring' task track_deployment: :environment do - metric = Gitlab::Metrics::Metric. - new('deployments', version: Gitlab::VERSION) + metric = Gitlab::Metrics::Metric + .new('deployments', version: Gitlab::VERSION) Gitlab::Metrics.submit_metrics([metric.to_hash]) end diff --git a/lib/tasks/gitlab/update_templates.rake b/lib/tasks/gitlab/update_templates.rake index b77a5bb62d1..dbdfb335a5c 100644 --- a/lib/tasks/gitlab/update_templates.rake +++ b/lib/tasks/gitlab/update_templates.rake @@ -46,7 +46,7 @@ namespace :gitlab do "https://gitlab.com/gitlab-org/gitlab-ci-yml.git", /(\.{1,2}|LICENSE|Pages|autodeploy|\.gitlab-ci.yml)\z/ ) - ] + ].freeze def vendor_directory Rails.root.join('vendor') diff --git a/lib/tasks/gitlab/web_hook.rake b/lib/tasks/gitlab/web_hook.rake index 49530e7a372..5a1c8006052 100644 --- a/lib/tasks/gitlab/web_hook.rake +++ b/lib/tasks/gitlab/web_hook.rake @@ -1,7 +1,7 @@ namespace :gitlab do namespace :web_hook do desc "GitLab | Adds a webhook to the projects" - task :add => :environment do + task add: :environment do web_hook_url = ENV['URL'] namespace_path = ENV['NAMESPACE'] @@ -21,7 +21,7 @@ namespace :gitlab do end desc "GitLab | Remove a webhook from the projects" - task :rm => :environment do + task rm: :environment do web_hook_url = ENV['URL'] namespace_path = ENV['NAMESPACE'] @@ -34,7 +34,7 @@ namespace :gitlab do end desc "GitLab | List webhooks" - task :list => :environment do + task list: :environment do namespace_path = ENV['NAMESPACE'] projects = find_projects(namespace_path) diff --git a/lib/tasks/lint.rake b/lib/tasks/lint.rake index 32b668df3bf..7b63e93db0e 100644 --- a/lib/tasks/lint.rake +++ b/lib/tasks/lint.rake @@ -6,4 +6,3 @@ unless Rails.env.production? end end end - diff --git a/lib/tasks/migrate/migrate_iids.rake b/lib/tasks/migrate/migrate_iids.rake index 4f2486157b7..fc2cea8c016 100644 --- a/lib/tasks/migrate/migrate_iids.rake +++ b/lib/tasks/migrate/migrate_iids.rake @@ -24,7 +24,7 @@ task migrate_iids: :environment do else print 'F' end - rescue => ex + rescue print 'F' end end diff --git a/lib/tasks/services.rake b/lib/tasks/services.rake index 39541c0b9c6..56b81106c5f 100644 --- a/lib/tasks/services.rake +++ b/lib/tasks/services.rake @@ -76,23 +76,23 @@ namespace :services do end param_hash - end.sort_by { |p| p[:required] ? 0 : 1 } + end + service_hash[:params].sort_by! { |p| p[:required] ? 0 : 1 } - puts "Collected data for: #{service.title}, #{Time.now-service_start}" + puts "Collected data for: #{service.title}, #{Time.now - service_start}" service_hash end doc_start = Time.now doc_path = File.join(Rails.root, 'doc', 'api', 'services.md') - result = ERB.new(services_template, 0 , '>') + result = ERB.new(services_template, 0, '>') .result(OpenStruct.new(services: services).instance_eval { binding }) File.open(doc_path, 'w') do |f| f.write result end - puts "write a new service.md to: #{doc_path.to_s}, #{Time.now-doc_start}" - + puts "write a new service.md to: #{doc_path}, #{Time.now - doc_start}" end end diff --git a/lib/tasks/sidekiq.rake b/lib/tasks/sidekiq.rake index d1f6ed87704..dd9ce86f7ca 100644 --- a/lib/tasks/sidekiq.rake +++ b/lib/tasks/sidekiq.rake @@ -1,21 +1,21 @@ namespace :sidekiq do desc "GitLab | Stop sidekiq" task :stop do - system *%W(bin/background_jobs stop) + system(*%w(bin/background_jobs stop)) end desc "GitLab | Start sidekiq" task :start do - system *%W(bin/background_jobs start) + system(*%w(bin/background_jobs start)) end desc 'GitLab | Restart sidekiq' task :restart do - system *%W(bin/background_jobs restart) + system(*%w(bin/background_jobs restart)) end desc "GitLab | Start sidekiq with launchd on Mac OS X" task :launchd do - system *%W(bin/background_jobs start_no_deamonize) + system(*%w(bin/background_jobs start_no_deamonize)) end end diff --git a/lib/tasks/spec.rake b/lib/tasks/spec.rake index 2cf7a25a0fd..602c60be828 100644 --- a/lib/tasks/spec.rake +++ b/lib/tasks/spec.rake @@ -4,8 +4,8 @@ namespace :spec do desc 'GitLab | Rspec | Run request specs' task :api do cmds = [ - %W(rake gitlab:setup), - %W(rspec spec --tag @api) + %w(rake gitlab:setup), + %w(rspec spec --tag @api) ] run_commands(cmds) end @@ -13,8 +13,8 @@ namespace :spec do desc 'GitLab | Rspec | Run feature specs' task :feature do cmds = [ - %W(rake gitlab:setup), - %W(rspec spec --tag @feature) + %w(rake gitlab:setup), + %w(rspec spec --tag @feature) ] run_commands(cmds) end @@ -22,8 +22,8 @@ namespace :spec do desc 'GitLab | Rspec | Run model specs' task :models do cmds = [ - %W(rake gitlab:setup), - %W(rspec spec --tag @models) + %w(rake gitlab:setup), + %w(rspec spec --tag @models) ] run_commands(cmds) end @@ -31,8 +31,8 @@ namespace :spec do desc 'GitLab | Rspec | Run service specs' task :services do cmds = [ - %W(rake gitlab:setup), - %W(rspec spec --tag @services) + %w(rake gitlab:setup), + %w(rspec spec --tag @services) ] run_commands(cmds) end @@ -40,8 +40,8 @@ namespace :spec do desc 'GitLab | Rspec | Run lib specs' task :lib do cmds = [ - %W(rake gitlab:setup), - %W(rspec spec --tag @lib) + %w(rake gitlab:setup), + %w(rspec spec --tag @lib) ] run_commands(cmds) end @@ -49,8 +49,8 @@ namespace :spec do desc 'GitLab | Rspec | Run other specs' task :other do cmds = [ - %W(rake gitlab:setup), - %W(rspec spec --tag ~@api --tag ~@feature --tag ~@models --tag ~@lib --tag ~@services) + %w(rake gitlab:setup), + %w(rspec spec --tag ~@api --tag ~@feature --tag ~@models --tag ~@lib --tag ~@services) ] run_commands(cmds) end @@ -59,14 +59,14 @@ end desc "GitLab | Run specs" task :spec do cmds = [ - %W(rake gitlab:setup), - %W(rspec spec), + %w(rake gitlab:setup), + %w(rspec spec), ] run_commands(cmds) end def run_commands(cmds) cmds.each do |cmd| - system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd) or raise("#{cmd} failed!") + system({ 'RAILS_ENV' => 'test', 'force' => 'yes' }, *cmd) || raise("#{cmd} failed!") end end diff --git a/lib/tasks/spinach.rake b/lib/tasks/spinach.rake index 8dbfa7751dc..19ff13f06c0 100644 --- a/lib/tasks/spinach.rake +++ b/lib/tasks/spinach.rake @@ -35,7 +35,7 @@ task :spinach do end def run_system_command(cmd) - system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd) + system({ 'RAILS_ENV' => 'test', 'force' => 'yes' }, *cmd) end def run_spinach_command(args) diff --git a/package.json b/package.json index ad0aaef1897..5528303ab21 100644 --- a/package.json +++ b/package.json @@ -30,9 +30,10 @@ "stats-webpack-plugin": "^0.4.3", "timeago.js": "^2.0.5", "underscore": "^1.8.3", - "vue": "^2.0.3", + "vue": "^2.1.10", "vue-resource": "^0.9.3", - "webpack": "^2.2.1" + "webpack": "^2.2.1", + "webpack-bundle-analyzer": "^2.3.0" }, "devDependencies": { "babel-plugin-istanbul": "^4.0.0", diff --git a/rubocop/cop/gem_fetcher.rb b/rubocop/cop/gem_fetcher.rb index c199f6acab2..e157d8e0791 100644 --- a/rubocop/cop/gem_fetcher.rb +++ b/rubocop/cop/gem_fetcher.rb @@ -4,9 +4,9 @@ module RuboCop # `Gemfile` in order to avoid additional points of failure beyond # rubygems.org. class GemFetcher < RuboCop::Cop::Cop - MSG = 'Do not use gems from git repositories, only use gems from RubyGems.' + MSG = 'Do not use gems from git repositories, only use gems from RubyGems.'.freeze - GIT_KEYS = [:git, :github] + GIT_KEYS = [:git, :github].freeze def on_send(node) return unless gemfile?(node) diff --git a/rubocop/cop/migration/add_column.rb b/rubocop/cop/migration/add_column.rb index 1490fcdd814..d2cf36c454a 100644 --- a/rubocop/cop/migration/add_column.rb +++ b/rubocop/cop/migration/add_column.rb @@ -8,10 +8,10 @@ module RuboCop class AddColumn < RuboCop::Cop::Cop include MigrationHelpers - WHITELISTED_TABLES = [:application_settings] + WHITELISTED_TABLES = [:application_settings].freeze MSG = '`add_column` with a default value requires downtime, ' \ - 'use `add_column_with_default` instead' + 'use `add_column_with_default` instead'.freeze def on_send(node) return unless in_migration?(node) diff --git a/rubocop/cop/migration/add_column_with_default.rb b/rubocop/cop/migration/add_column_with_default.rb index 747d7caf1ef..54a920d4b49 100644 --- a/rubocop/cop/migration/add_column_with_default.rb +++ b/rubocop/cop/migration/add_column_with_default.rb @@ -9,7 +9,7 @@ module RuboCop include MigrationHelpers MSG = '`add_column_with_default` is not reversible so you must manually define ' \ - 'the `up` and `down` methods in your migration class, using `remove_column` in `down`' + 'the `up` and `down` methods in your migration class, using `remove_column` in `down`'.freeze def on_send(node) return unless in_migration?(node) diff --git a/rubocop/cop/migration/add_concurrent_foreign_key.rb b/rubocop/cop/migration/add_concurrent_foreign_key.rb index e40a7087a47..d1fc94d55be 100644 --- a/rubocop/cop/migration/add_concurrent_foreign_key.rb +++ b/rubocop/cop/migration/add_concurrent_foreign_key.rb @@ -8,7 +8,7 @@ module RuboCop class AddConcurrentForeignKey < RuboCop::Cop::Cop include MigrationHelpers - MSG = '`add_foreign_key` requires downtime, use `add_concurrent_foreign_key` instead' + MSG = '`add_foreign_key` requires downtime, use `add_concurrent_foreign_key` instead'.freeze def on_send(node) return unless in_migration?(node) diff --git a/rubocop/cop/migration/add_index.rb b/rubocop/cop/migration/add_index.rb index 5e6766f6994..fa21a0d6555 100644 --- a/rubocop/cop/migration/add_index.rb +++ b/rubocop/cop/migration/add_index.rb @@ -7,7 +7,7 @@ module RuboCop class AddIndex < RuboCop::Cop::Cop include MigrationHelpers - MSG = '`add_index` requires downtime, use `add_concurrent_index` instead' + MSG = '`add_index` requires downtime, use `add_concurrent_index` instead'.freeze def on_def(node) return unless in_migration?(node) diff --git a/spec/controllers/blob_controller_spec.rb b/spec/controllers/blob_controller_spec.rb index 2fcb4a6a528..44e011fd3a8 100644 --- a/spec/controllers/blob_controller_spec.rb +++ b/spec/controllers/blob_controller_spec.rb @@ -19,8 +19,8 @@ describe Projects::BlobController do before do get(:show, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, id: id) end @@ -50,8 +50,8 @@ describe Projects::BlobController do before do get(:show, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, id: id) controller.instance_variable_set(:@blob, nil) end diff --git a/spec/controllers/profiles/personal_access_tokens_spec.rb b/spec/controllers/profiles/personal_access_tokens_spec.rb index 45534a3a587..9d5f4c99f6d 100644 --- a/spec/controllers/profiles/personal_access_tokens_spec.rb +++ b/spec/controllers/profiles/personal_access_tokens_spec.rb @@ -32,10 +32,10 @@ describe Profiles::PersonalAccessTokensController do context "scopes" do it "allows creation of a token with scopes" do - post :create, personal_access_token: { name: FFaker::Product.brand, scopes: ['api', 'read_user'] } + post :create, personal_access_token: { name: FFaker::Product.brand, scopes: %w(api read_user) } expect(created_token).not_to be_nil - expect(created_token.scopes).to eq(['api', 'read_user']) + expect(created_token.scopes).to eq(%w(api read_user)) end it "allows creation of a token with no scopes" do diff --git a/spec/controllers/projects/blame_controller_spec.rb b/spec/controllers/projects/blame_controller_spec.rb index addc5e7ec33..c086b386381 100644 --- a/spec/controllers/projects/blame_controller_spec.rb +++ b/spec/controllers/projects/blame_controller_spec.rb @@ -16,8 +16,8 @@ describe Projects::BlameController do before do get(:show, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, id: id) end diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb index 7d4636e98d1..ec36a64b415 100644 --- a/spec/controllers/projects/blob_controller_spec.rb +++ b/spec/controllers/projects/blob_controller_spec.rb @@ -14,8 +14,8 @@ describe Projects::BlobController do render_views def do_get(opts = {}) - params = { namespace_id: project.namespace.to_param, - project_id: project.to_param, + params = { namespace_id: project.namespace, + project_id: project, id: 'master/CHANGELOG' } get :diff, params.merge(opts) end @@ -40,8 +40,8 @@ describe Projects::BlobController do describe 'PUT update' do let(:default_params) do { - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, id: 'master/CHANGELOG', target_branch: 'master', content: 'Added changes', @@ -96,8 +96,8 @@ describe Projects::BlobController do context 'when editing on the fork' do before do - default_params[:namespace_id] = forked_project.namespace.to_param - default_params[:project_id] = forked_project.to_param + default_params[:namespace_id] = forked_project.namespace + default_params[:project_id] = forked_project end it 'redirects to blob' do diff --git a/spec/controllers/projects/boards/issues_controller_spec.rb b/spec/controllers/projects/boards/issues_controller_spec.rb index ad15e3942a5..3d0533cb516 100644 --- a/spec/controllers/projects/boards/issues_controller_spec.rb +++ b/spec/controllers/projects/boards/issues_controller_spec.rb @@ -90,7 +90,7 @@ describe Projects::Boards::IssuesController do params = { namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, board_id: board.to_param, list_id: list.try(:to_param) } @@ -146,7 +146,7 @@ describe Projects::Boards::IssuesController do sign_in(user) post :create, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, board_id: board.to_param, list_id: list.to_param, issue: { title: title }, @@ -209,7 +209,7 @@ describe Projects::Boards::IssuesController do sign_in(user) patch :update, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, board_id: board.to_param, id: issue.to_param, from_list_id: from_list_id, diff --git a/spec/controllers/projects/boards/lists_controller_spec.rb b/spec/controllers/projects/boards/lists_controller_spec.rb index b3f9f76a50c..432f3c53c90 100644 --- a/spec/controllers/projects/boards/lists_controller_spec.rb +++ b/spec/controllers/projects/boards/lists_controller_spec.rb @@ -47,7 +47,7 @@ describe Projects::Boards::ListsController do sign_in(user) get :index, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, board_id: board.to_param, format: :json end @@ -104,7 +104,7 @@ describe Projects::Boards::ListsController do sign_in(user) post :create, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, board_id: board.to_param, list: { label_id: label_id }, format: :json @@ -157,7 +157,7 @@ describe Projects::Boards::ListsController do sign_in(user) patch :update, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, board_id: board.to_param, id: list.to_param, list: { position: position }, @@ -200,7 +200,7 @@ describe Projects::Boards::ListsController do sign_in(user) delete :destroy, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, board_id: board.to_param, id: list.to_param, format: :json @@ -244,7 +244,7 @@ describe Projects::Boards::ListsController do sign_in(user) post :generate, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, board_id: board.to_param, format: :json end diff --git a/spec/controllers/projects/boards_controller_spec.rb b/spec/controllers/projects/boards_controller_spec.rb index cc19035740e..aed3a45c413 100644 --- a/spec/controllers/projects/boards_controller_spec.rb +++ b/spec/controllers/projects/boards_controller_spec.rb @@ -50,8 +50,8 @@ describe Projects::BoardsController do end def list_boards(format: :html) - get :index, namespace_id: project.namespace.to_param, - project_id: project.to_param, + get :index, namespace_id: project.namespace, + project_id: project, format: format end end @@ -100,8 +100,8 @@ describe Projects::BoardsController do end def read_board(board:, format: :html) - get :show, namespace_id: project.namespace.to_param, - project_id: project.to_param, + get :show, namespace_id: project.namespace, + project_id: project, id: board.to_param, format: format end diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb index 9de03876755..e70737376af 100644 --- a/spec/controllers/projects/branches_controller_spec.rb +++ b/spec/controllers/projects/branches_controller_spec.rb @@ -22,8 +22,8 @@ describe Projects::BranchesController do sign_in(user) post :create, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, branch_name: branch, ref: ref end @@ -68,7 +68,7 @@ describe Projects::BranchesController do describe "created from the new branch button on issues" do let(:branch) { "1-feature-branch" } - let!(:issue) { create(:issue, project: project) } + let(:issue) { create(:issue, project: project) } before do sign_in(user) @@ -76,8 +76,8 @@ describe Projects::BranchesController do it 'redirects' do post :create, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, branch_name: branch, issue_iid: issue.iid @@ -89,12 +89,49 @@ describe Projects::BranchesController do expect(SystemNoteService).to receive(:new_issue_branch).with(issue, project, user, "1-feature-branch") post :create, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, branch_name: branch, issue_iid: issue.iid end + context 'repository-less project' do + let(:project) { create :empty_project } + + it 'redirects to newly created branch' do + result = { status: :success, branch: double(name: branch) } + + expect_any_instance_of(CreateBranchService).to receive(:execute).and_return(result) + expect(SystemNoteService).to receive(:new_issue_branch).and_return(true) + + post :create, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + branch_name: branch, + issue_iid: issue.iid + + expect(response).to redirect_to namespace_project_tree_path(project.namespace, project, branch) + end + + it 'redirects to autodeploy setup page' do + result = { status: :success, branch: double(name: branch) } + + project.services << build(:kubernetes_service) + + expect_any_instance_of(CreateBranchService).to receive(:execute).and_return(result) + expect(SystemNoteService).to receive(:new_issue_branch).and_return(true) + + post :create, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + branch_name: branch, + issue_iid: issue.iid + + expect(response.location).to include(namespace_project_new_blob_path(project.namespace, project, branch)) + expect(response).to have_http_status(302) + end + end + context 'without issue feature access' do before do project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) @@ -106,8 +143,8 @@ describe Projects::BranchesController do expect(SystemNoteService).not_to receive(:new_issue_branch) post :create, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, branch_name: branch, issue_iid: issue.iid end @@ -126,8 +163,8 @@ describe Projects::BranchesController do post :destroy, format: :html, id: 'foo/bar/baz', - namespace_id: project.namespace.to_param, - project_id: project.to_param + namespace_id: project.namespace, + project_id: project expect(response).to have_http_status(303) end @@ -142,8 +179,8 @@ describe Projects::BranchesController do post :destroy, format: :js, id: branch, - namespace_id: project.namespace.to_param, - project_id: project.to_param + namespace_id: project.namespace, + project_id: project end context "valid branch name, valid source" do @@ -173,8 +210,8 @@ describe Projects::BranchesController do describe "DELETE destroy_all_merged" do def destroy_all_merged delete :destroy_all_merged, - namespace_id: project.namespace.to_param, - project_id: project.to_param + namespace_id: project.namespace, + project_id: project end context 'when user is allowed to push' do diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb index ebd2d0e092b..640baa3a01c 100644 --- a/spec/controllers/projects/commit_controller_spec.rb +++ b/spec/controllers/projects/commit_controller_spec.rb @@ -17,8 +17,8 @@ describe Projects::CommitController do def go(extra_params = {}) params = { - namespace_id: project.namespace.to_param, - project_id: project.to_param + namespace_id: project.namespace, + project_id: project } get :show, params.merge(extra_params) @@ -125,8 +125,8 @@ describe Projects::CommitController do it 'renders it' do get(:show, - namespace_id: fork_project.namespace.to_param, - project_id: fork_project.to_param, + namespace_id: fork_project.namespace, + project_id: fork_project, id: commit.id) expect(response).to be_success @@ -139,8 +139,8 @@ describe Projects::CommitController do commit = project.commit('5937ac0a7beb003549fc5fd26fc247adbce4a52e') get(:branches, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, id: commit.id) expect(assigns(:branches)).to include("master", "feature_conflict") @@ -152,8 +152,8 @@ describe Projects::CommitController do context 'when target branch is not provided' do it 'renders the 404 page' do post(:revert, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, id: commit.id) expect(response).not_to be_success @@ -164,8 +164,8 @@ describe Projects::CommitController do context 'when the revert was successful' do it 'redirects to the commits page' do post(:revert, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, target_branch: 'master', id: commit.id) @@ -177,8 +177,8 @@ describe Projects::CommitController do context 'when the revert failed' do before do post(:revert, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, target_branch: 'master', id: commit.id) end @@ -186,8 +186,8 @@ describe Projects::CommitController do it 'redirects to the commit page' do # Reverting a commit that has been already reverted. post(:revert, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, target_branch: 'master', id: commit.id) @@ -201,8 +201,8 @@ describe Projects::CommitController do context 'when target branch is not provided' do it 'renders the 404 page' do post(:cherry_pick, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, id: master_pickable_commit.id) expect(response).not_to be_success @@ -213,8 +213,8 @@ describe Projects::CommitController do context 'when the cherry-pick was successful' do it 'redirects to the commits page' do post(:cherry_pick, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, target_branch: 'master', id: master_pickable_commit.id) @@ -226,8 +226,8 @@ describe Projects::CommitController do context 'when the cherry_pick failed' do before do post(:cherry_pick, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, target_branch: 'master', id: master_pickable_commit.id) end @@ -235,8 +235,8 @@ describe Projects::CommitController do it 'redirects to the commit page' do # Cherry-picking a commit that has been already cherry-picked. post(:cherry_pick, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, target_branch: 'master', id: master_pickable_commit.id) @@ -249,8 +249,8 @@ describe Projects::CommitController do describe 'GET diff_for_path' do def diff_for_path(extra_params = {}) params = { - namespace_id: project.namespace.to_param, - project_id: project.to_param + namespace_id: project.namespace, + project_id: project } get :diff_for_path, params.merge(extra_params) @@ -313,8 +313,8 @@ describe Projects::CommitController do describe 'GET pipelines' do def get_pipelines(extra_params = {}) params = { - namespace_id: project.namespace.to_param, - project_id: project.to_param + namespace_id: project.namespace, + project_id: project } get :pipelines, params.merge(extra_params) diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb index 54b8d1108a5..e26731fb691 100644 --- a/spec/controllers/projects/commits_controller_spec.rb +++ b/spec/controllers/projects/commits_controller_spec.rb @@ -16,8 +16,8 @@ describe Projects::CommitsController do context "when the ref does not exist with the suffix" do it "renders as atom" do get(:show, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, id: "master.atom") expect(response).to be_success @@ -33,8 +33,8 @@ describe Projects::CommitsController do allow_any_instance_of(Repository).to receive(:commit).with('master.atom').and_return(commit) get(:show, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, id: "master.atom") end diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb index e811c76fb31..15ac4e0925a 100644 --- a/spec/controllers/projects/compare_controller_spec.rb +++ b/spec/controllers/projects/compare_controller_spec.rb @@ -13,8 +13,8 @@ describe Projects::CompareController do it 'compare shows some diffs' do get(:show, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, from: ref_from, to: ref_to) @@ -25,8 +25,8 @@ describe Projects::CompareController do it 'compare shows some diffs with ignore whitespace change option' do get(:show, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, from: '08f22f25', to: '66eceea0', w: 1) @@ -43,8 +43,8 @@ describe Projects::CompareController do describe 'non-existent refs' do it 'uses invalid source ref' do get(:show, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, from: 'non-existent', to: ref_to) @@ -55,8 +55,8 @@ describe Projects::CompareController do it 'uses invalid target ref' do get(:show, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, from: ref_from, to: 'non-existent') @@ -67,8 +67,8 @@ describe Projects::CompareController do it 'redirects back to index when params[:from] is empty and preserves params[:to]' do post(:create, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, from: '', to: 'master') @@ -77,8 +77,8 @@ describe Projects::CompareController do it 'redirects back to index when params[:to] is empty and preserves params[:from]' do post(:create, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, from: 'master', to: '') @@ -87,8 +87,8 @@ describe Projects::CompareController do it 'redirects back to index when params[:from] and params[:to] are empty' do post(:create, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, from: '', to: '') @@ -99,8 +99,8 @@ describe Projects::CompareController do describe 'GET diff_for_path' do def diff_for_path(extra_params = {}) params = { - namespace_id: project.namespace.to_param, - project_id: project.to_param + namespace_id: project.namespace, + project_id: project } get :diff_for_path, params.merge(extra_params) diff --git a/spec/controllers/projects/cycle_analytics_controller_spec.rb b/spec/controllers/projects/cycle_analytics_controller_spec.rb index 6a6d71a16ee..6fae52edbad 100644 --- a/spec/controllers/projects/cycle_analytics_controller_spec.rb +++ b/spec/controllers/projects/cycle_analytics_controller_spec.rb @@ -13,8 +13,8 @@ describe Projects::CycleAnalyticsController do context 'with no data' do it 'is true' do get(:show, - namespace_id: project.namespace.to_param, - project_id: project.to_param) + namespace_id: project.namespace, + project_id: project) expect(response).to be_success expect(assigns(:cycle_analytics_no_data)).to eq(true) @@ -32,8 +32,8 @@ describe Projects::CycleAnalyticsController do it 'is false' do get(:show, - namespace_id: project.namespace.to_param, - project_id: project.to_param) + namespace_id: project.namespace, + project_id: project) expect(response).to be_success expect(assigns(:cycle_analytics_no_data)).to eq(false) diff --git a/spec/controllers/projects/find_file_controller_spec.rb b/spec/controllers/projects/find_file_controller_spec.rb index a4884256c92..6a5433bcc9c 100644 --- a/spec/controllers/projects/find_file_controller_spec.rb +++ b/spec/controllers/projects/find_file_controller_spec.rb @@ -17,8 +17,8 @@ describe Projects::FindFileController do before do get(:show, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, id: id) end @@ -36,8 +36,8 @@ describe Projects::FindFileController do describe "GET #list" do def go(format: 'json') get :list, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, id: id, format: format end diff --git a/spec/controllers/projects/forks_controller_spec.rb b/spec/controllers/projects/forks_controller_spec.rb index a867668d97b..8282d79298f 100644 --- a/spec/controllers/projects/forks_controller_spec.rb +++ b/spec/controllers/projects/forks_controller_spec.rb @@ -9,8 +9,8 @@ describe Projects::ForksController do describe 'GET index' do def get_forks get :index, - namespace_id: project.namespace.to_param, - project_id: project.to_param + namespace_id: project.namespace, + project_id: project end context 'when fork is public' do @@ -71,8 +71,8 @@ describe Projects::ForksController do describe 'GET new' do def get_new get :new, - namespace_id: project.namespace.to_param, - project_id: project.to_param + namespace_id: project.namespace, + project_id: project end context 'when user is signed in' do @@ -99,8 +99,8 @@ describe Projects::ForksController do describe 'POST create' do def post_create post :create, - namespace_id: project.namespace.to_param, - project_id: project.to_param, + namespace_id: project.namespace, + project_id: project, namespace_key: user.namespace.id end diff --git a/spec/controllers/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb index bbe8e4bf6b2..c4a7aa7d63e 100644 --- a/spec/controllers/projects/graphs_controller_spec.rb +++ b/spec/controllers/projects/graphs_controller_spec.rb @@ -34,7 +34,7 @@ describe Projects::GraphsController do end it 'sets the correct colour according to language' do - get(:languages, namespace_id: project.namespace.path, project_id: project.path, id: 'master') + get(:languages, namespace_id: project.namespace, project_id: project, id: 'master') expected_values.each do |val| expect(assigns(:languages)).to include(a_hash_including(val)) diff --git a/spec/controllers/projects/group_links_controller_spec.rb b/spec/controllers/projects/group_links_controller_spec.rb index a976a9c27ab..ca4a8e871c0 100644 --- a/spec/controllers/projects/group_links_controller_spec.rb +++ b/spec/controllers/projects/group_links_controller_spec.rb @@ -14,8 +14,8 @@ describe Projects::GroupLinksController do describe '#create' do shared_context 'link project to group' do before do - post(:create, namespace_id: project.namespace.to_param, - project_id: project.to_param, + post(:create, namespace_id: project.namespace, + project_id: project, link_group_id: group.id, link_group_access: ProjectGroupLink.default_access) end @@ -50,8 +50,8 @@ describe Projects::GroupLinksController do context 'when project group id equal link group id' do before do - post(:create, namespace_id: project.namespace.to_param, - project_id: project.to_param, + post(:create, namespace_id: project.namespace, + project_id: project, link_group_id: group2.id, link_group_access: ProjectGroupLink.default_access) end @@ -69,8 +69,8 @@ describe Projects::GroupLinksController do context 'when link group id is not present' do before do - post(:create, namespace_id: project.namespace.to_param, - project_id: project.to_param, + post(:create, namespace_id: project.namespace, + project_id: project, link_group_access: ProjectGroupLink.default_access) end diff --git a/spec/controllers/projects/imports_controller_spec.rb b/spec/controllers/projects/imports_controller_spec.rb index 2acbba469e3..7c75815f3c4 100644 --- a/spec/controllers/projects/imports_controller_spec.rb +++ b/spec/controllers/projects/imports_controller_spec.rb @@ -13,13 +13,13 @@ describe Projects::ImportsController do end it 'renders template' do - get :show, namespace_id: project.namespace.to_param, project_id: project.to_param + get :show, namespace_id: project.namespace.to_param, project_id: project expect(response).to render_template :show end it 'sets flash.now if params is present' do - get :show, namespace_id: project.namespace.to_param, project_id: project.to_param, continue: { to: '/', notice_now: 'Started' } + get :show, namespace_id: project.namespace.to_param, project_id: project, continue: { to: '/', notice_now: 'Started' } expect(flash.now[:notice]).to eq 'Started' end @@ -39,13 +39,13 @@ describe Projects::ImportsController do end it 'renders template' do - get :show, namespace_id: project.namespace.to_param, project_id: project.to_param + get :show, namespace_id: project.namespace.to_param, project_id: project expect(response).to render_template :show end it 'sets flash.now if params is present' do - get :show, namespace_id: project.namespace.to_param, project_id: project.to_param, continue: { to: '/', notice_now: 'In progress' } + get :show, namespace_id: project.namespace.to_param, project_id: project, continue: { to: '/', notice_now: 'In progress' } expect(flash.now[:notice]).to eq 'In progress' end @@ -57,7 +57,7 @@ describe Projects::ImportsController do end it 'redirects to new_namespace_project_import_path' do - get :show, namespace_id: project.namespace.to_param, project_id: project.to_param + get :show, namespace_id: project.namespace.to_param, project_id: project expect(response).to redirect_to new_namespace_project_import_path(project.namespace, project) end @@ -72,7 +72,7 @@ describe Projects::ImportsController do it 'redirects to namespace_project_path' do allow_any_instance_of(Project).to receive(:forked?).and_return(true) - get :show, namespace_id: project.namespace.to_param, project_id: project.to_param + get :show, namespace_id: project.namespace.to_param, project_id: project expect(flash[:notice]).to eq 'The project was successfully forked.' expect(response).to redirect_to namespace_project_path(project.namespace, project) @@ -81,7 +81,7 @@ describe Projects::ImportsController do context 'when project is external' do it 'redirects to namespace_project_path' do - get :show, namespace_id: project.namespace.to_param, project_id: project.to_param + get :show, namespace_id: project.namespace.to_param, project_id: project expect(flash[:notice]).to eq 'The project was successfully imported.' expect(response).to redirect_to namespace_project_path(project.namespace, project) @@ -97,7 +97,7 @@ describe Projects::ImportsController do end it 'redirects to params[:to]' do - get :show, namespace_id: project.namespace.to_param, project_id: project.to_param, continue: params + get :show, namespace_id: project.namespace.to_param, project_id: project, continue: params expect(flash[:notice]).to eq params[:notice] expect(response).to redirect_to params[:to] @@ -111,7 +111,7 @@ describe Projects::ImportsController do end it 'redirects to namespace_project_path' do - get :show, namespace_id: project.namespace.to_param, project_id: project.to_param + get :show, namespace_id: project.namespace.to_param, project_id: project expect(response).to redirect_to namespace_project_path(project.namespace, project) end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 7871b6a9e10..e493b9396f6 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -12,7 +12,7 @@ describe Projects::IssuesController do allow(project).to receive(:external_issue_tracker).and_return(external) controller.instance_variable_set(:@project, project) - get :index, namespace_id: project.namespace.path, project_id: project + get :index, namespace_id: project.namespace, project_id: project expect(response).to redirect_to('https://example.com/project') end @@ -27,13 +27,13 @@ describe Projects::IssuesController do it_behaves_like "issuables list meta-data", :issue it "returns index" do - get :index, namespace_id: project.namespace.path, project_id: project.path + get :index, namespace_id: project.namespace, project_id: project expect(response).to have_http_status(200) end it "returns 301 if request path doesn't match project path" do - get :index, namespace_id: project.namespace.path, project_id: project.path.upcase + get :index, namespace_id: project.namespace, project_id: project.path.upcase expect(response).to redirect_to(namespace_project_issues_path(project.namespace, project)) end @@ -42,7 +42,7 @@ describe Projects::IssuesController do project.issues_enabled = false project.save - get :index, namespace_id: project.namespace.path, project_id: project.path + get :index, namespace_id: project.namespace, project_id: project expect(response).to have_http_status(404) end @@ -50,7 +50,7 @@ describe Projects::IssuesController do controller.instance_variable_set(:@project, project) allow(project).to receive(:default_issues_tracker?).and_return(false) - get :index, namespace_id: project.namespace.path, project_id: project.path + get :index, namespace_id: project.namespace, project_id: project expect(response).to have_http_status(404) end end @@ -67,8 +67,8 @@ describe Projects::IssuesController do it 'redirects to last_page if page number is larger than number of pages' do get :index, - namespace_id: project.namespace.path.to_param, - project_id: project.path.to_param, + namespace_id: project.namespace.to_param, + project_id: project, page: (last_page + 1).to_param expect(response).to redirect_to(namespace_project_issues_path(page: last_page, state: controller.params[:state], scope: controller.params[:scope])) @@ -76,8 +76,8 @@ describe Projects::IssuesController do it 'redirects to specified page' do get :index, - namespace_id: project.namespace.path.to_param, - project_id: project.path.to_param, + namespace_id: project.namespace.to_param, + project_id: project, page: last_page.to_param expect(assigns(:issues).current_page).to eq(last_page) @@ -94,7 +94,7 @@ describe Projects::IssuesController do end it 'builds a new issue' do - get :new, namespace_id: project.namespace.path, project_id: project + get :new, namespace_id: project.namespace, project_id: project expect(assigns(:issue)).to be_a_new(Issue) end @@ -104,7 +104,7 @@ describe Projects::IssuesController do project_with_repository.team << [user, :developer] mr = create(:merge_request_with_diff_notes, source_project: project_with_repository) - get :new, namespace_id: project_with_repository.namespace.path, project_id: project_with_repository, merge_request_for_resolving_discussions: mr.iid + get :new, namespace_id: project_with_repository.namespace, project_id: project_with_repository, merge_request_for_resolving_discussions: mr.iid expect(assigns(:issue).title).not_to be_empty expect(assigns(:issue).description).not_to be_empty @@ -117,7 +117,7 @@ describe Projects::IssuesController do allow(project).to receive(:external_issue_tracker).and_return(external) controller.instance_variable_set(:@project, project) - get :new, namespace_id: project.namespace.path, project_id: project + get :new, namespace_id: project.namespace, project_id: project expect(response).to redirect_to('https://example.com/issues/new') end @@ -251,7 +251,7 @@ describe Projects::IssuesController do def update_issue(issue_params = {}, additional_params = {}) params = { namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: issue.iid, issue: issue_params }.merge(additional_params) @@ -262,7 +262,7 @@ describe Projects::IssuesController do def move_issue put :update, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: issue.iid, issue: { title: 'New title' }, move_to_project_id: another_project.id @@ -342,7 +342,7 @@ describe Projects::IssuesController do def get_issues get :index, namespace_id: project.namespace.to_param, - project_id: project.to_param + project_id: project end end @@ -405,7 +405,7 @@ describe Projects::IssuesController do def go(id:) get :show, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: id end end @@ -416,7 +416,7 @@ describe Projects::IssuesController do def go(id:) get :edit, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: id end end @@ -427,7 +427,7 @@ describe Projects::IssuesController do def go(id:) put :update, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: id, issue: { title: 'New title' } end @@ -442,7 +442,7 @@ describe Projects::IssuesController do post :create, { namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, issue: { title: 'Title', description: 'Description' }.merge(issue_attrs) }.merge(additional_params) @@ -464,7 +464,7 @@ describe Projects::IssuesController do end def post_issue(issue_params) - post :create, namespace_id: project.namespace.to_param, project_id: project.to_param, issue: issue_params, merge_request_for_resolving_discussions: merge_request.iid + post :create, namespace_id: project.namespace.to_param, project_id: project, issue: issue_params, merge_request_for_resolving_discussions: merge_request.iid end it 'creates an issue for the project' do @@ -607,8 +607,8 @@ describe Projects::IssuesController do project.team << [admin, :master] sign_in(admin) post :mark_as_spam, { - namespace_id: project.namespace.path, - project_id: project.path, + namespace_id: project.namespace, + project_id: project, id: issue.iid } end @@ -624,7 +624,7 @@ describe Projects::IssuesController do context "when the user is a developer" do before { sign_in(user) } it "rejects a developer to destroy an issue" do - delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid + delete :destroy, namespace_id: project.namespace, project_id: project, id: issue.iid expect(response).to have_http_status(404) end end @@ -637,7 +637,7 @@ describe Projects::IssuesController do before { sign_in(owner) } it "deletes the issue" do - delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid + delete :destroy, namespace_id: project.namespace, project_id: project, id: issue.iid expect(response).to have_http_status(302) expect(controller).to set_flash[:notice].to(/The issue was successfully deleted\./).now @@ -646,7 +646,7 @@ describe Projects::IssuesController do it 'delegates the update of the todos count cache to TodoService' do expect_any_instance_of(TodoService).to receive(:destroy_issue).with(issue, owner).once - delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid + delete :destroy, namespace_id: project.namespace, project_id: project, id: issue.iid end end end @@ -659,8 +659,8 @@ describe Projects::IssuesController do it "toggles the award emoji" do expect do - post(:toggle_award_emoji, namespace_id: project.namespace.path, - project_id: project.path, id: issue.iid, name: "thumbsup") + post(:toggle_award_emoji, namespace_id: project.namespace, + project_id: project, id: issue.iid, name: "thumbsup") end.to change { issue.award_emoji.count }.by(1) expect(response).to have_http_status(200) diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb index 3e0326dd47d..6a6e9bf378a 100644 --- a/spec/controllers/projects/labels_controller_spec.rb +++ b/spec/controllers/projects/labels_controller_spec.rb @@ -67,7 +67,7 @@ describe Projects::LabelsController do end def list_labels - get :index, namespace_id: project.namespace.to_param, project_id: project.to_param + get :index, namespace_id: project.namespace.to_param, project_id: project end end @@ -76,7 +76,7 @@ describe Projects::LabelsController do let(:personal_project) { create(:empty_project, namespace: user.namespace) } it 'creates labels' do - post :generate, namespace_id: personal_project.namespace.to_param, project_id: personal_project.to_param + post :generate, namespace_id: personal_project.namespace.to_param, project_id: personal_project expect(response).to have_http_status(302) end @@ -84,7 +84,7 @@ describe Projects::LabelsController do context 'project belonging to a group' do it 'creates labels' do - post :generate, namespace_id: project.namespace.to_param, project_id: project.to_param + post :generate, namespace_id: project.namespace.to_param, project_id: project expect(response).to have_http_status(302) end @@ -109,7 +109,7 @@ describe Projects::LabelsController do end def toggle_subscription(label) - post :toggle_subscription, namespace_id: project.namespace.to_param, project_id: project.to_param, id: label.to_param + post :toggle_subscription, namespace_id: project.namespace.to_param, project_id: project, id: label.to_param end end @@ -119,7 +119,7 @@ describe Projects::LabelsController do context 'not group owner' do it 'denies access' do - post :promote, namespace_id: project.namespace.to_param, project_id: project.to_param, id: label_1.to_param + post :promote, namespace_id: project.namespace.to_param, project_id: project, id: label_1.to_param expect(response).to have_http_status(404) end @@ -131,13 +131,13 @@ describe Projects::LabelsController do end it 'gives access' do - post :promote, namespace_id: project.namespace.to_param, project_id: project.to_param, id: label_1.to_param + post :promote, namespace_id: project.namespace.to_param, project_id: project, id: label_1.to_param expect(response).to redirect_to(namespace_project_labels_path) end it 'promotes the label' do - post :promote, namespace_id: project.namespace.to_param, project_id: project.to_param, id: label_1.to_param + post :promote, namespace_id: project.namespace.to_param, project_id: project, id: label_1.to_param expect(Label.where(id: label_1.id)).to be_empty expect(GroupLabel.find_by(title: promoted_label_name)).not_to be_nil @@ -151,7 +151,7 @@ describe Projects::LabelsController do end it 'returns to label list' do - post :promote, namespace_id: project.namespace.to_param, project_id: project.to_param, id: label_1.to_param + post :promote, namespace_id: project.namespace.to_param, project_id: project, id: label_1.to_param expect(response).to redirect_to(namespace_project_labels_path) end end diff --git a/spec/controllers/projects/mattermosts_controller_spec.rb b/spec/controllers/projects/mattermosts_controller_spec.rb index cae733f0cfb..c5abf11cfa5 100644 --- a/spec/controllers/projects/mattermosts_controller_spec.rb +++ b/spec/controllers/projects/mattermosts_controller_spec.rb @@ -18,7 +18,7 @@ describe Projects::MattermostsController do it 'accepts the request' do get(:new, namespace_id: project.namespace.to_param, - project_id: project.to_param) + project_id: project) expect(response).to have_http_status(200) end @@ -30,7 +30,7 @@ describe Projects::MattermostsController do subject do post(:create, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, mattermost: mattermost_params) end diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index f84f922ba5e..d9cb429132f 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -51,10 +51,11 @@ describe Projects::MergeRequestsController do def submit_new_merge_request(format: :html) get :new, namespace_id: fork_project.namespace.to_param, - project_id: fork_project.to_param, + project_id: fork_project, merge_request: { source_branch: 'remove-submodule', - target_branch: 'master' }, + target_branch: 'master' + }, format: format end end @@ -63,7 +64,7 @@ describe Projects::MergeRequestsController do it "loads labels into the @labels variable" do get action, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: merge_request.iid, format: 'html' expect(assigns(:labels)).not_to be_nil @@ -75,7 +76,7 @@ describe Projects::MergeRequestsController do it "does generally work" do get(:show, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: merge_request.iid, format: format) @@ -89,7 +90,7 @@ describe Projects::MergeRequestsController do get(:show, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: merge_request.iid, format: format) end @@ -97,7 +98,7 @@ describe Projects::MergeRequestsController do it "renders it" do get(:show, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: merge_request.iid, format: format) @@ -110,7 +111,7 @@ describe Projects::MergeRequestsController do get(:show, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: merge_request.iid, format: format) @@ -125,7 +126,7 @@ describe Projects::MergeRequestsController do it "triggers workhorse to serve the request" do get(:show, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: merge_request.iid, format: :diff) @@ -137,7 +138,7 @@ describe Projects::MergeRequestsController do it 'triggers workhorse to serve the request' do get(:show, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: merge_request.iid, format: :patch) @@ -152,7 +153,7 @@ describe Projects::MergeRequestsController do def get_merge_requests(page = nil) get :index, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, state: 'opened', page: page.to_param end @@ -215,8 +216,8 @@ describe Projects::MergeRequestsController do it 'closes MR without errors' do post :update, - namespace_id: project.namespace.path, - project_id: project.path, + namespace_id: project.namespace, + project_id: project, id: merge_request.iid, merge_request: { state_event: 'close' @@ -230,8 +231,8 @@ describe Projects::MergeRequestsController do merge_request.close! put :update, - namespace_id: project.namespace.path, - project_id: project.path, + namespace_id: project.namespace, + project_id: project, id: merge_request.iid, merge_request: { title: 'New title' @@ -245,8 +246,8 @@ describe Projects::MergeRequestsController do merge_request.close! put :update, - namespace_id: project.namespace.path, - project_id: project.path, + namespace_id: project.namespace, + project_id: project, id: merge_request.iid, merge_request: { target_branch: 'new_branch' @@ -260,8 +261,8 @@ describe Projects::MergeRequestsController do describe 'POST merge' do let(:base_params) do { - namespace_id: project.namespace.path, - project_id: project.path, + namespace_id: project.namespace, + project_id: project, id: merge_request.iid, format: 'raw' } @@ -425,7 +426,7 @@ describe Projects::MergeRequestsController do describe "DELETE destroy" do it "denies access to users unless they're admin or project owner" do - delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid + delete :destroy, namespace_id: project.namespace, project_id: project, id: merge_request.iid expect(response).to have_http_status(404) end @@ -438,7 +439,7 @@ describe Projects::MergeRequestsController do before { sign_in owner } it "deletes the merge request" do - delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid + delete :destroy, namespace_id: project.namespace, project_id: project, id: merge_request.iid expect(response).to have_http_status(302) expect(controller).to set_flash[:notice].to(/The merge request was successfully deleted\./).now @@ -447,7 +448,7 @@ describe Projects::MergeRequestsController do it 'delegates the update of the todos count cache to TodoService' do expect_any_instance_of(TodoService).to receive(:destroy_merge_request).with(merge_request, owner).once - delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid + delete :destroy, namespace_id: project.namespace, project_id: project, id: merge_request.iid end end end @@ -456,7 +457,7 @@ describe Projects::MergeRequestsController do def go(extra_params = {}) params = { namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: merge_request.iid } @@ -536,7 +537,7 @@ describe Projects::MergeRequestsController do def diff_for_path(extra_params = {}) params = { namespace_id: project.namespace.to_param, - project_id: project.to_param + project_id: project } get :diff_for_path, params.merge(extra_params) @@ -600,7 +601,7 @@ describe Projects::MergeRequestsController do before do other_project.team << [user, :master] - diff_for_path(id: merge_request.iid, old_path: existing_path, new_path: existing_path, project_id: other_project.to_param) + diff_for_path(id: merge_request.iid, old_path: existing_path, new_path: existing_path, project_id: other_project) end it 'returns a 404' do @@ -666,7 +667,7 @@ describe Projects::MergeRequestsController do def go(format: 'html') get :commits, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: merge_request.iid, format: format end @@ -706,7 +707,7 @@ describe Projects::MergeRequestsController do before do get :pipelines, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: merge_request.iid, format: :json end @@ -725,7 +726,7 @@ describe Projects::MergeRequestsController do get :conflicts, namespace_id: merge_request_with_conflicts.project.namespace.to_param, - project_id: merge_request_with_conflicts.project.to_param, + project_id: merge_request_with_conflicts.project, id: merge_request_with_conflicts.iid, format: 'json' end @@ -743,7 +744,7 @@ describe Projects::MergeRequestsController do before do get :conflicts, namespace_id: merge_request_with_conflicts.project.namespace.to_param, - project_id: merge_request_with_conflicts.project.to_param, + project_id: merge_request_with_conflicts.project, id: merge_request_with_conflicts.iid, format: 'json' end @@ -772,7 +773,7 @@ describe Projects::MergeRequestsController do section['lines'].each do |line| if section['conflict'] - expect(line['type']).to be_in(['old', 'new']) + expect(line['type']).to be_in(%w(old new)) expect(line.values_at('old_line', 'new_line')).to contain_exactly(nil, a_kind_of(Integer)) else if line['type'].nil? @@ -806,7 +807,7 @@ describe Projects::MergeRequestsController do post :remove_wip, namespace_id: merge_request.project.namespace.to_param, - project_id: merge_request.project.to_param, + project_id: merge_request.project, id: merge_request.iid expect(merge_request.reload.title).to eq(merge_request.wipless_title) @@ -817,7 +818,7 @@ describe Projects::MergeRequestsController do def conflict_for_path(path) get :conflict_for_path, namespace_id: merge_request_with_conflicts.project.namespace.to_param, - project_id: merge_request_with_conflicts.project.to_param, + project_id: merge_request_with_conflicts.project, id: merge_request_with_conflicts.iid, old_path: path, new_path: path, @@ -873,7 +874,7 @@ describe Projects::MergeRequestsController do def resolve_conflicts(files) post :resolve_conflicts, namespace_id: merge_request_with_conflicts.project.namespace.to_param, - project_id: merge_request_with_conflicts.project.to_param, + project_id: merge_request_with_conflicts.project, id: merge_request_with_conflicts.iid, format: 'json', files: files, @@ -1024,7 +1025,7 @@ describe Projects::MergeRequestsController do post :assign_related_issues, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: merge_request.iid end @@ -1079,7 +1080,7 @@ describe Projects::MergeRequestsController do get :ci_environments_status, namespace_id: merge_request.project.namespace.to_param, - project_id: merge_request.project.to_param, + project_id: merge_request.project, id: merge_request.iid, format: 'json' end @@ -1092,8 +1093,8 @@ describe Projects::MergeRequestsController do describe 'GET merge_widget_refresh' do let(:params) do { - namespace_id: project.namespace.path, - project_id: project.path, + namespace_id: project.namespace, + project_id: project, id: merge_request.iid, format: :raw } diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index 1ed2ee3ab4a..04bb5cbbd59 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -12,10 +12,13 @@ describe Projects::PipelinesController do describe 'GET index.json' do before do - create_list(:ci_empty_pipeline, 2, project: project) + create(:ci_empty_pipeline, status: 'pending', project: project) + create(:ci_empty_pipeline, status: 'running', project: project) + create(:ci_empty_pipeline, status: 'created', project: project) + create(:ci_empty_pipeline, status: 'success', project: project) - get :index, namespace_id: project.namespace.path, - project_id: project.path, + get :index, namespace_id: project.namespace, + project_id: project, format: :json end @@ -23,9 +26,11 @@ describe Projects::PipelinesController do expect(response).to have_http_status(:ok) expect(json_response).to include('pipelines') - expect(json_response['pipelines'].count).to eq 2 - expect(json_response['count']['all']).to eq 2 - expect(json_response['count']['running_or_pending']).to eq 2 + expect(json_response['pipelines'].count).to eq 4 + expect(json_response['count']['all']).to eq 4 + expect(json_response['count']['running']).to eq 1 + expect(json_response['count']['pending']).to eq 1 + expect(json_response['count']['finished']).to eq 1 end end @@ -57,8 +62,8 @@ describe Projects::PipelinesController do end def get_stage(name) - get :stage, namespace_id: project.namespace.path, - project_id: project.path, + get :stage, namespace_id: project.namespace, + project_id: project, id: pipeline.id, stage: name, format: :json diff --git a/spec/controllers/projects/protected_branches_controller_spec.rb b/spec/controllers/projects/protected_branches_controller_spec.rb index da6112a13f7..e378b5714fe 100644 --- a/spec/controllers/projects/protected_branches_controller_spec.rb +++ b/spec/controllers/projects/protected_branches_controller_spec.rb @@ -4,7 +4,7 @@ describe Projects::ProtectedBranchesController do describe "GET #index" do let(:project) { create(:project_empty_repo, :public) } it "redirects empty repo to projects page" do - get(:index, namespace_id: project.namespace.to_param, project_id: project.to_param) + get(:index, namespace_id: project.namespace.to_param, project_id: project) end end end diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index b23d6e257ba..4cebe3884bf 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -10,7 +10,7 @@ describe Projects::RawController do it 'delivers ASCII file' do get(:show, namespace_id: public_project.namespace.to_param, - project_id: public_project.to_param, + project_id: public_project, id: id) expect(response).to have_http_status(200) @@ -27,7 +27,7 @@ describe Projects::RawController do it 'sets image content type header' do get(:show, namespace_id: public_project.namespace.to_param, - project_id: public_project.to_param, + project_id: public_project, id: id) expect(response).to have_http_status(200) @@ -51,7 +51,7 @@ describe Projects::RawController do expect(controller).to receive(:send_file).with("#{Gitlab.config.shared.path}/lfs-objects/91/ef/f75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897", filename: "lfs_object.iso", disposition: 'attachment') get(:show, namespace_id: public_project.namespace.to_param, - project_id: public_project.to_param, + project_id: public_project, id: id) expect(response).to have_http_status(200) @@ -62,7 +62,7 @@ describe Projects::RawController do it 'does not serve the file' do get(:show, namespace_id: public_project.namespace.to_param, - project_id: public_project.to_param, + project_id: public_project, id: id) expect(response).to have_http_status(404) diff --git a/spec/controllers/projects/refs_controller_spec.rb b/spec/controllers/projects/refs_controller_spec.rb index d8fb4667c67..3a3e7467ef2 100644 --- a/spec/controllers/projects/refs_controller_spec.rb +++ b/spec/controllers/projects/refs_controller_spec.rb @@ -13,7 +13,7 @@ describe Projects::RefsController do def default_get(format = :html) get :logs_tree, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: 'master', path: 'foo/bar/baz.html', format: format @@ -23,7 +23,7 @@ describe Projects::RefsController do xhr :get, :logs_tree, namespace_id: project.namespace.to_param, - project_id: project.to_param, id: 'master', + project_id: project, id: 'master', path: 'foo/bar/baz.html', format: format end diff --git a/spec/controllers/projects/releases_controller_spec.rb b/spec/controllers/projects/releases_controller_spec.rb index 69fcc26c77e..358f26dfb02 100644 --- a/spec/controllers/projects/releases_controller_spec.rb +++ b/spec/controllers/projects/releases_controller_spec.rb @@ -16,7 +16,7 @@ describe Projects::ReleasesController do tag_id = release.tag project.releases.destroy_all - get :edit, namespace_id: project.namespace.path, project_id: project.path, tag_id: tag_id + get :edit, namespace_id: project.namespace, project_id: project, tag_id: tag_id release = assigns(:release) expect(release).not_to be_nil @@ -24,7 +24,7 @@ describe Projects::ReleasesController do end it 'retrieves an existing release' do - get :edit, namespace_id: project.namespace.path, project_id: project.path, tag_id: release.tag + get :edit, namespace_id: project.namespace, project_id: project, tag_id: release.tag release = assigns(:release) expect(release).not_to be_nil @@ -48,7 +48,7 @@ describe Projects::ReleasesController do def update_release(description) put :update, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, tag_id: release.tag, release: { description: description } end diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb index 04e88879fb8..9c55d159fa0 100644 --- a/spec/controllers/projects/repositories_controller_spec.rb +++ b/spec/controllers/projects/repositories_controller_spec.rb @@ -6,7 +6,7 @@ describe Projects::RepositoriesController do describe "GET archive" do context 'as a guest' do it 'responds with redirect in correct format' do - get :archive, namespace_id: project.namespace.path, project_id: project.path, format: "zip" + get :archive, namespace_id: project.namespace, project_id: project, format: "zip" expect(response.header["Content-Type"]).to start_with('text/html') expect(response).to be_redirect @@ -22,7 +22,7 @@ describe Projects::RepositoriesController do end it "uses Gitlab::Workhorse" do - get :archive, namespace_id: project.namespace.path, project_id: project.path, ref: "master", format: "zip" + get :archive, namespace_id: project.namespace, project_id: project, ref: "master", format: "zip" expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-archive:") end @@ -33,7 +33,7 @@ describe Projects::RepositoriesController do end it "renders Not Found" do - get :archive, namespace_id: project.namespace.path, project_id: project.path, ref: "master", format: "zip" + get :archive, namespace_id: project.namespace, project_id: project, ref: "master", format: "zip" expect(response).to have_http_status(404) end diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb index 8bab094a79e..24a59caff4e 100644 --- a/spec/controllers/projects/snippets_controller_spec.rb +++ b/spec/controllers/projects/snippets_controller_spec.rb @@ -17,16 +17,16 @@ describe Projects::SnippetsController do it 'redirects to last_page if page number is larger than number of pages' do get :index, - namespace_id: project.namespace.path, - project_id: project.path, page: (last_page + 1).to_param + namespace_id: project.namespace, + project_id: project, page: (last_page + 1).to_param expect(response).to redirect_to(namespace_project_snippets_path(page: last_page)) end it 'redirects to specified page' do get :index, - namespace_id: project.namespace.path, - project_id: project.path, page: last_page.to_param + namespace_id: project.namespace, + project_id: project, page: last_page.to_param expect(assigns(:snippets).current_page).to eq(last_page) expect(response).to have_http_status(200) @@ -38,7 +38,7 @@ describe Projects::SnippetsController do context 'when anonymous' do it 'does not include the private snippet' do - get :index, namespace_id: project.namespace.path, project_id: project.path + get :index, namespace_id: project.namespace, project_id: project expect(assigns(:snippets)).not_to include(project_snippet) expect(response).to have_http_status(200) @@ -49,7 +49,7 @@ describe Projects::SnippetsController do before { sign_in(user) } it 'renders the snippet' do - get :index, namespace_id: project.namespace.path, project_id: project.path + get :index, namespace_id: project.namespace, project_id: project expect(assigns(:snippets)).to include(project_snippet) expect(response).to have_http_status(200) @@ -60,7 +60,7 @@ describe Projects::SnippetsController do before { sign_in(user2) } it 'renders the snippet' do - get :index, namespace_id: project.namespace.path, project_id: project.path + get :index, namespace_id: project.namespace, project_id: project expect(assigns(:snippets)).to include(project_snippet) expect(response).to have_http_status(200) @@ -77,7 +77,7 @@ describe Projects::SnippetsController do post :create, { namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, project_snippet: { title: 'Title', content: 'Content' }.merge(snippet_params) }.merge(additional_params) end @@ -152,7 +152,7 @@ describe Projects::SnippetsController do put :update, { namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: snippet.id, project_snippet: { title: 'Title', content: 'Content' }.merge(snippet_params) }.merge(additional_params) @@ -281,8 +281,8 @@ describe Projects::SnippetsController do sign_in(admin) post :mark_as_spam, - namespace_id: project.namespace.path, - project_id: project.path, + namespace_id: project.namespace, + project_id: project, id: snippet.id end @@ -300,7 +300,7 @@ describe Projects::SnippetsController do context 'when anonymous' do it 'responds with status 404' do - get action, namespace_id: project.namespace.path, project_id: project.path, id: project_snippet.to_param + get action, namespace_id: project.namespace, project_id: project, id: project_snippet.to_param expect(response).to have_http_status(404) end @@ -310,7 +310,7 @@ describe Projects::SnippetsController do before { sign_in(user) } it 'renders the snippet' do - get action, namespace_id: project.namespace.path, project_id: project.path, id: project_snippet.to_param + get action, namespace_id: project.namespace, project_id: project, id: project_snippet.to_param expect(assigns(:snippet)).to eq(project_snippet) expect(response).to have_http_status(200) @@ -321,7 +321,7 @@ describe Projects::SnippetsController do before { sign_in(user2) } it 'renders the snippet' do - get action, namespace_id: project.namespace.path, project_id: project.path, id: project_snippet.to_param + get action, namespace_id: project.namespace, project_id: project, id: project_snippet.to_param expect(assigns(:snippet)).to eq(project_snippet) expect(response).to have_http_status(200) @@ -332,7 +332,7 @@ describe Projects::SnippetsController do context 'when the project snippet does not exist' do context 'when anonymous' do it 'responds with status 404' do - get action, namespace_id: project.namespace.path, project_id: project.path, id: 42 + get action, namespace_id: project.namespace, project_id: project, id: 42 expect(response).to have_http_status(404) end @@ -342,7 +342,7 @@ describe Projects::SnippetsController do before { sign_in(user) } it 'responds with status 404' do - get action, namespace_id: project.namespace.path, project_id: project.path, id: 42 + get action, namespace_id: project.namespace, project_id: project, id: 42 expect(response).to have_http_status(404) end @@ -364,8 +364,8 @@ describe Projects::SnippetsController do context 'CRLF line ending' do let(:params) do { - namespace_id: project.namespace.path, - project_id: project.path, + namespace_id: project.namespace, + project_id: project, id: project_snippet.to_param } end diff --git a/spec/controllers/projects/tags_controller_spec.rb b/spec/controllers/projects/tags_controller_spec.rb index c36a5fdd66c..fc97bac64cd 100644 --- a/spec/controllers/projects/tags_controller_spec.rb +++ b/spec/controllers/projects/tags_controller_spec.rb @@ -6,7 +6,7 @@ describe Projects::TagsController do let!(:invalid_release) { create(:release, project: project, tag: 'does-not-exist') } describe 'GET index' do - before { get :index, namespace_id: project.namespace.to_param, project_id: project.to_param } + before { get :index, namespace_id: project.namespace.to_param, project_id: project } it 'returns the tags for the page' do expect(assigns(:tags).map(&:name)).to eq(['v1.1.0', 'v1.0.0']) @@ -19,7 +19,7 @@ describe Projects::TagsController do end describe 'GET show' do - before { get :show, namespace_id: project.namespace.to_param, project_id: project.to_param, id: id } + before { get :show, namespace_id: project.namespace.to_param, project_id: project, id: id } context "valid tag" do let(:id) { 'v1.0.0' } diff --git a/spec/controllers/projects/templates_controller_spec.rb b/spec/controllers/projects/templates_controller_spec.rb index 80f84a388ce..70e7f9ca96e 100644 --- a/spec/controllers/projects/templates_controller_spec.rb +++ b/spec/controllers/projects/templates_controller_spec.rb @@ -14,13 +14,13 @@ describe Projects::TemplatesController do before do project.add_user(user, Gitlab::Access::MASTER) - project.repository.commit_file(user, file_path_1, 'something valid', - message: 'test 3', branch_name: 'master', update: false) + project.repository.create_file(user, file_path_1, 'something valid', + message: 'test 3', branch_name: 'master') end describe '#show' do it 'renders template name and content as json' do - get(:show, namespace_id: project.namespace.to_param, template_type: "issue", key: "bug", project_id: project.path, format: :json) + get(:show, namespace_id: project.namespace.to_param, template_type: "issue", key: "bug", project_id: project, format: :json) expect(response.status).to eq(200) expect(body["name"]).to eq("bug") @@ -29,21 +29,21 @@ describe Projects::TemplatesController do it 'renders 404 when unauthorized' do sign_in(user2) - get(:show, namespace_id: project.namespace.to_param, template_type: "issue", key: "bug", project_id: project.path, format: :json) + get(:show, namespace_id: project.namespace.to_param, template_type: "issue", key: "bug", project_id: project, format: :json) expect(response.status).to eq(404) end it 'renders 404 when template type is not found' do sign_in(user) - get(:show, namespace_id: project.namespace.to_param, template_type: "dont_exist", key: "bug", project_id: project.path, format: :json) + get(:show, namespace_id: project.namespace.to_param, template_type: "dont_exist", key: "bug", project_id: project, format: :json) expect(response.status).to eq(404) end it 'renders 404 without errors' do sign_in(user) - expect { get(:show, namespace_id: project.namespace.to_param, template_type: "dont_exist", key: "bug", project_id: project.path, format: :json) }.not_to raise_error + expect { get(:show, namespace_id: project.namespace.to_param, template_type: "dont_exist", key: "bug", project_id: project, format: :json) }.not_to raise_error end end end diff --git a/spec/controllers/projects/todo_controller_spec.rb b/spec/controllers/projects/todo_controller_spec.rb index 415c264e0dd..9a7beeff6fe 100644 --- a/spec/controllers/projects/todo_controller_spec.rb +++ b/spec/controllers/projects/todo_controller_spec.rb @@ -12,8 +12,8 @@ describe Projects::TodosController do describe 'POST create' do def go post :create, - namespace_id: project.namespace.path, - project_id: project.path, + namespace_id: project.namespace, + project_id: project, issuable_id: issue.id, issuable_type: 'issue', format: 'html' @@ -80,8 +80,8 @@ describe Projects::TodosController do describe 'POST create' do def go post :create, - namespace_id: project.namespace.path, - project_id: project.path, + namespace_id: project.namespace, + project_id: project, issuable_id: merge_request.id, issuable_type: 'merge_request', format: 'html' diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb index b81645a3d2d..ab94e292e48 100644 --- a/spec/controllers/projects/tree_controller_spec.rb +++ b/spec/controllers/projects/tree_controller_spec.rb @@ -18,7 +18,7 @@ describe Projects::TreeController do before do get(:show, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: id) end @@ -74,7 +74,7 @@ describe Projects::TreeController do before do get(:show, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: id) end @@ -94,7 +94,7 @@ describe Projects::TreeController do before do post(:create_dir, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: 'master', dir_name: path, target_branch: target_branch, diff --git a/spec/controllers/projects/uploads_controller_spec.rb b/spec/controllers/projects/uploads_controller_spec.rb index 0347e789576..699c6f77cec 100644 --- a/spec/controllers/projects/uploads_controller_spec.rb +++ b/spec/controllers/projects/uploads_controller_spec.rb @@ -16,7 +16,7 @@ describe Projects::UploadsController do it "returns an error" do post :create, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, format: :json expect(response).to have_http_status(422) end @@ -26,7 +26,7 @@ describe Projects::UploadsController do before do post :create, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, file: jpg, format: :json end @@ -41,7 +41,7 @@ describe Projects::UploadsController do before do post :create, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, file: txt, format: :json end @@ -57,7 +57,7 @@ describe Projects::UploadsController do let(:go) do get :show, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, secret: "123456", filename: "image.jpg" end diff --git a/spec/controllers/projects/variables_controller_spec.rb b/spec/controllers/projects/variables_controller_spec.rb index 9fa358f7d62..e3f3b4fe8eb 100644 --- a/spec/controllers/projects/variables_controller_spec.rb +++ b/spec/controllers/projects/variables_controller_spec.rb @@ -12,7 +12,7 @@ describe Projects::VariablesController do describe 'POST #create' do context 'variable is valid' do it 'shows a success flash message' do - post :create, namespace_id: project.namespace.to_param, project_id: project.to_param, + post :create, namespace_id: project.namespace.to_param, project_id: project, variable: { key: "one", value: "two" } expect(flash[:notice]).to include 'Variables were successfully updated.' @@ -22,7 +22,7 @@ describe Projects::VariablesController do context 'variable is invalid' do it 'shows an alert flash message' do - post :create, namespace_id: project.namespace.to_param, project_id: project.to_param, + post :create, namespace_id: project.namespace.to_param, project_id: project, variable: { key: "..one", value: "two" } expect(response).to render_template("projects/variables/show") @@ -40,7 +40,7 @@ describe Projects::VariablesController do end it 'shows a success flash message' do - post :update, namespace_id: project.namespace.to_param, project_id: project.to_param, + post :update, namespace_id: project.namespace.to_param, project_id: project, id: variable.id, variable: { key: variable.key, value: 'two' } expect(flash[:notice]).to include 'Variable was successfully updated.' @@ -48,7 +48,7 @@ describe Projects::VariablesController do end it 'renders the action #show if the variable key is invalid' do - post :update, namespace_id: project.namespace.to_param, project_id: project.to_param, + post :update, namespace_id: project.namespace.to_param, project_id: project, id: variable.id, variable: { key: '?', value: variable.value } expect(response).to have_http_status(200) diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index e7aa8745b99..202759664a0 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -35,7 +35,7 @@ describe ProjectsController do let(:private_project) { create(:empty_project, :private) } it "does not initialize notification setting" do - get :show, namespace_id: private_project.namespace.path, id: private_project.path + get :show, namespace_id: private_project.namespace, id: private_project expect(assigns(:notification_setting)).to be_nil end end @@ -43,7 +43,7 @@ describe ProjectsController do context "user has access to project" do context "and does not have notification setting" do it "initializes notification as disabled" do - get :show, namespace_id: public_project.namespace.path, id: public_project.path + get :show, namespace_id: public_project.namespace, id: public_project expect(assigns(:notification_setting).level).to eq("global") end end @@ -56,7 +56,7 @@ describe ProjectsController do end it "shows current notification setting" do - get :show, namespace_id: public_project.namespace.path, id: public_project.path + get :show, namespace_id: public_project.namespace, id: public_project expect(assigns(:notification_setting).level).to eq("watch") end end @@ -71,7 +71,7 @@ describe ProjectsController do end it 'shows wiki homepage' do - get :show, namespace_id: project.namespace.path, id: project.path + get :show, namespace_id: project.namespace, id: project expect(response).to render_template('projects/_wiki') end @@ -79,7 +79,7 @@ describe ProjectsController do it 'shows issues list page if wiki is disabled' do project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED) - get :show, namespace_id: project.namespace.path, id: project.path + get :show, namespace_id: project.namespace, id: project expect(response).to render_template('projects/issues/_issues') end @@ -88,7 +88,7 @@ describe ProjectsController do project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED) project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED) - get :show, namespace_id: project.namespace.path, id: project.path + get :show, namespace_id: project.namespace, id: project expect(response).to render_template("projects/_customize_workflow") end @@ -96,7 +96,7 @@ describe ProjectsController do it 'shows activity if enabled by user' do user.update_attribute(:project_view, 'activity') - get :show, namespace_id: project.namespace.path, id: project.path + get :show, namespace_id: project.namespace, id: project expect(response).to render_template("projects/_activity") end @@ -113,7 +113,7 @@ describe ProjectsController do before do user.update_attributes(project_view: project_view) - get :show, namespace_id: empty_project.namespace.path, id: empty_project.path + get :show, namespace_id: empty_project.namespace, id: empty_project end it "renders the empty project view" do @@ -133,7 +133,7 @@ describe ProjectsController do before do user.update_attributes(project_view: project_view) - get :show, namespace_id: empty_project.namespace.path, id: empty_project.path + get :show, namespace_id: empty_project.namespace, id: empty_project end it "renders the empty project view" do @@ -154,7 +154,7 @@ describe ProjectsController do allow(controller).to receive(:current_user).and_return(user) allow(user).to receive(:project_view).and_return('activity') - get :show, namespace_id: public_project.namespace.path, id: public_project.path + get :show, namespace_id: public_project.namespace, id: public_project expect(response).to render_template('_activity') end @@ -162,7 +162,7 @@ describe ProjectsController do allow(controller).to receive(:current_user).and_return(user) allow(user).to receive(:project_view).and_return('readme') - get :show, namespace_id: public_project.namespace.path, id: public_project.path + get :show, namespace_id: public_project.namespace, id: public_project expect(response).to render_template('_readme') end @@ -170,7 +170,7 @@ describe ProjectsController do allow(controller).to receive(:current_user).and_return(user) allow(user).to receive(:project_view).and_return('files') - get :show, namespace_id: public_project.namespace.path, id: public_project.path + get :show, namespace_id: public_project.namespace, id: public_project expect(response).to render_template('_files') end end @@ -178,7 +178,7 @@ describe ProjectsController do context "when requested with case sensitive namespace and project path" do context "when there is a match with the same casing" do it "loads the project" do - get :show, namespace_id: public_project.namespace.path, id: public_project.path + get :show, namespace_id: public_project.namespace, id: public_project expect(assigns(:project)).to eq(public_project) expect(response).to have_http_status(200) @@ -187,10 +187,10 @@ describe ProjectsController do context "when there is a match with different casing" do it "redirects to the normalized path" do - get :show, namespace_id: public_project.namespace.path, id: public_project.path.upcase + get :show, namespace_id: public_project.namespace, id: public_project.path.upcase expect(assigns(:project)).to eq(public_project) - expect(response).to redirect_to("/#{public_project.path_with_namespace}") + expect(response).to redirect_to("/#{public_project.full_path}") end end end @@ -208,7 +208,7 @@ describe ProjectsController do project = create(:empty_project, pending_delete: true) sign_in(user) - get :show, namespace_id: project.namespace.path, id: project.path + get :show, namespace_id: project.namespace, id: project expect(response.status).to eq 404 end @@ -218,7 +218,7 @@ describe ProjectsController do it 'redirects to project page (format.html)' do project = create(:project, :public) - get :show, namespace_id: project.namespace.path, id: project.path, format: :git + get :show, namespace_id: project.namespace, id: project, format: :git expect(response).to have_http_status(302) expect(response).to redirect_to(namespace_project_path) @@ -239,7 +239,7 @@ describe ProjectsController do sign_in(admin) put :update, - namespace_id: project.namespace.to_param, + namespace_id: project.namespace, id: project.id, project: project_params @@ -257,7 +257,7 @@ describe ProjectsController do sign_in(admin) orig_id = project.id - delete :destroy, namespace_id: project.namespace.path, id: project.path + delete :destroy, namespace_id: project.namespace, id: project expect { Project.find(orig_id) }.to raise_error(ActiveRecord::RecordNotFound) expect(response).to have_http_status(302) @@ -277,7 +277,7 @@ describe ProjectsController do project.merge_requests << merge_request sign_in(admin) - delete :destroy, namespace_id: fork_project.namespace.path, id: fork_project.path + delete :destroy, namespace_id: fork_project.namespace, id: fork_project expect(merge_request.reload.state).to eq('closed') end @@ -287,8 +287,8 @@ describe ProjectsController do describe 'PUT #new_issue_address' do subject do put :new_issue_address, - namespace_id: project.namespace.to_param, - id: project.to_param + namespace_id: project.namespace, + id: project user.reload end @@ -316,23 +316,23 @@ describe ProjectsController do sign_in(user) expect(user.starred?(public_project)).to be_falsey post(:toggle_star, - namespace_id: public_project.namespace.to_param, - id: public_project.to_param) + namespace_id: public_project.namespace, + id: public_project) expect(user.starred?(public_project)).to be_truthy post(:toggle_star, - namespace_id: public_project.namespace.to_param, - id: public_project.to_param) + namespace_id: public_project.namespace, + id: public_project) expect(user.starred?(public_project)).to be_falsey end it "does nothing if user is not signed in" do post(:toggle_star, - namespace_id: project.namespace.to_param, - id: public_project.to_param) + namespace_id: project.namespace, + id: public_project) expect(user.starred?(public_project)).to be_falsey post(:toggle_star, - namespace_id: project.namespace.to_param, - id: public_project.to_param) + namespace_id: project.namespace, + id: public_project) expect(user.starred?(public_project)).to be_falsey end end @@ -366,8 +366,8 @@ describe ProjectsController do it 'does nothing if project was not forked' do delete(:remove_fork, - namespace_id: unforked_project.namespace.to_param, - id: unforked_project.to_param, format: :js) + namespace_id: unforked_project.namespace, + id: unforked_project, format: :js) expect(flash[:notice]).to be_nil expect(response).to render_template(:remove_fork) @@ -377,8 +377,8 @@ describe ProjectsController do it "does nothing if user is not signed in" do delete(:remove_fork, - namespace_id: project.namespace.to_param, - id: project.to_param, format: :js) + namespace_id: project.namespace, + id: project, format: :js) expect(response).to have_http_status(401) end end @@ -387,7 +387,7 @@ describe ProjectsController do let(:public_project) { create(:project, :public) } it "gets a list of branches and tags" do - get :refs, namespace_id: public_project.namespace.path, id: public_project.path + get :refs, namespace_id: public_project.namespace, id: public_project parsed_body = JSON.parse(response.body) expect(parsed_body["Branches"]).to include("master") @@ -396,7 +396,7 @@ describe ProjectsController do end it "gets a list of branches, tags and commits" do - get :refs, namespace_id: public_project.namespace.path, id: public_project.path, ref: "123456" + get :refs, namespace_id: public_project.namespace, id: public_project, ref: "123456" parsed_body = JSON.parse(response.body) expect(parsed_body["Branches"]).to include("master") diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index c80b09e9b9d..586efdefdb3 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -138,27 +138,24 @@ FactoryGirl.define do project.add_user(args[:user], args[:access]) - project.repository.commit_file( + project.repository.create_file( args[:user], ".gitlab/#{args[:path]}/bug.md", 'something valid', message: 'test 3', - branch_name: 'master', - update: false) - project.repository.commit_file( + branch_name: 'master') + project.repository.create_file( args[:user], ".gitlab/#{args[:path]}/template_test.md", 'template_test', message: 'test 1', - branch_name: 'master', - update: false) - project.repository.commit_file( + branch_name: 'master') + project.repository.create_file( args[:user], ".gitlab/#{args[:path]}/feature_proposal.md", 'feature_proposal', message: 'test 2', - branch_name: 'master', - update: false) + branch_name: 'master') end end end diff --git a/spec/factories/services.rb b/spec/factories/services.rb index a14a46c803e..51335bdcf1d 100644 --- a/spec/factories/services.rb +++ b/spec/factories/services.rb @@ -2,4 +2,14 @@ FactoryGirl.define do factory :service do project factory: :empty_project end + + factory :kubernetes_service do + project factory: :empty_project + active true + properties({ + namespace: 'somepath', + api_url: 'https://kubernetes.example.com', + token: 'a' * 40, + }) + end end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 1732b1a0081..249dabbaae8 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -26,6 +26,11 @@ FactoryGirl.define do two_factor_via_otp end + trait :ghost do + ghost true + after(:build) { |user, _| user.block! } + end + trait :two_factor_via_otp do before(:create) do |user| user.otp_required_for_login = true diff --git a/spec/features/admin/admin_disables_git_access_protocol_spec.rb b/spec/features/admin/admin_disables_git_access_protocol_spec.rb index e8e080ce3e2..273cacd82cd 100644 --- a/spec/features/admin/admin_disables_git_access_protocol_spec.rb +++ b/spec/features/admin/admin_disables_git_access_protocol_spec.rb @@ -32,7 +32,7 @@ feature 'Admin disables Git access protocol', feature: true do scenario 'shows only HTTP url' do visit_project - expect(page).to have_content("git clone #{project.http_url_to_repo}") + expect(page).to have_content("git clone #{project.http_url_to_repo(admin)}") expect(page).not_to have_selector('#clone-dropdown') end end diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb index 21ee6cedbae..a7c22615b89 100644 --- a/spec/features/atom/dashboard_issues_spec.rb +++ b/spec/features/atom/dashboard_issues_spec.rb @@ -23,7 +23,7 @@ describe "Dashboard Issues Feed", feature: true do visit issues_dashboard_path(:atom, private_token: user.private_token, state: 'opened', assignee_id: user.id) link = find('link[type="application/atom+xml"]') - params = CGI::parse(URI.parse(link[:href]).query) + params = CGI.parse(URI.parse(link[:href]).query) expect(params).to include('private_token' => [user.private_token]) expect(params).to include('state' => ['opened']) diff --git a/spec/features/atom/issues_spec.rb b/spec/features/atom/issues_spec.rb index 863412d18eb..a01a050a013 100644 --- a/spec/features/atom/issues_spec.rb +++ b/spec/features/atom/issues_spec.rb @@ -43,7 +43,7 @@ describe 'Issues Feed', feature: true do :atom, private_token: user.private_token, state: 'opened', assignee_id: user.id) link = find('link[type="application/atom+xml"]') - params = CGI::parse(URI.parse(link[:href]).query) + params = CGI.parse(URI.parse(link[:href]).query) expect(params).to include('private_token' => [user.private_token]) expect(params).to include('state' => ['opened']) @@ -54,7 +54,7 @@ describe 'Issues Feed', feature: true do visit issues_group_path(group, :atom, private_token: user.private_token, state: 'opened', assignee_id: user.id) link = find('link[type="application/atom+xml"]') - params = CGI::parse(URI.parse(link[:href]).query) + params = CGI.parse(URI.parse(link[:href]).query) expect(params).to include('private_token' => [user.private_token]) expect(params).to include('state' => ['opened']) diff --git a/spec/features/dashboard/project_member_activity_index_spec.rb b/spec/features/dashboard/project_member_activity_index_spec.rb index ba77093a6d4..49d93db58a9 100644 --- a/spec/features/dashboard/project_member_activity_index_spec.rb +++ b/spec/features/dashboard/project_member_activity_index_spec.rb @@ -12,7 +12,7 @@ feature 'Project member activity', feature: true, js: true do def visit_activities_and_wait_with_event(event_type) Event.create(project: project, author_id: user.id, action: event_type) - visit activity_namespace_project_path(project.namespace.path, project.path) + visit activity_namespace_project_path(project.namespace, project) wait_for_ajax end diff --git a/spec/features/dashboard_issues_spec.rb b/spec/features/dashboard_issues_spec.rb index b898f9bc64f..aa75e1140f6 100644 --- a/spec/features/dashboard_issues_spec.rb +++ b/spec/features/dashboard_issues_spec.rb @@ -49,9 +49,9 @@ describe "Dashboard Issues filtering", feature: true, js: true do visit_issues(milestone_title: '', assignee_id: user.id) link = find('.nav-controls a', text: 'Subscribe') - params = CGI::parse(URI.parse(link[:href]).query) + params = CGI.parse(URI.parse(link[:href]).query) auto_discovery_link = find('link[type="application/atom+xml"]', visible: false) - auto_discovery_params = CGI::parse(URI.parse(auto_discovery_link[:href]).query) + auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query) expect(params).to include('private_token' => [user.private_token]) expect(params).to include('milestone_title' => ['']) diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb index 0bf7977fb02..b90bf6268fd 100644 --- a/spec/features/issuables/issuable_list_spec.rb +++ b/spec/features/issuables/issuable_list_spec.rb @@ -47,11 +47,12 @@ describe 'issuable list', feature: true do def create_issuables(issuable_type) 3.times do - if issuable_type == :issue - issuable = create(:issue, project: project, author: user) - else - issuable = create(:merge_request, title: FFaker::Lorem.sentence, source_project: project, source_branch: FFaker::Name.name) - end + issuable = + if issuable_type == :issue + create(:issue, project: project, author: user) + else + create(:merge_request, title: FFaker::Lorem.sentence, source_project: project, source_branch: FFaker::Name.name) + end 2.times do create(:note_on_issue, noteable: issuable, project: project, note: 'Test note') diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb index 832757b24d4..2f59630b4fb 100644 --- a/spec/features/issues/bulk_assignment_labels_spec.rb +++ b/spec/features/issues/bulk_assignment_labels_spec.rb @@ -55,7 +55,7 @@ feature 'Issues > Labels bulk assignment', feature: true do context 'to all issues' do before do check 'check_all_issues' - open_labels_dropdown ['bug', 'feature'] + open_labels_dropdown %w(bug feature) update_issues end @@ -70,7 +70,7 @@ feature 'Issues > Labels bulk assignment', feature: true do context 'to a issue' do before do check "selected_issue_#{issue1.id}" - open_labels_dropdown ['bug', 'feature'] + open_labels_dropdown %w(bug feature) update_issues end @@ -112,7 +112,7 @@ feature 'Issues > Labels bulk assignment', feature: true do visit namespace_project_issues_path(project.namespace, project) check 'check_all_issues' - unmark_labels_in_dropdown ['bug', 'feature'] + unmark_labels_in_dropdown %w(bug feature) update_issues end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index ed3826bd46e..1e0db4a0499 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -150,7 +150,7 @@ describe 'Issues', feature: true do describe 'Filter issue' do before do - ['foobar', 'barbaz', 'gitlab'].each do |title| + %w(foobar barbaz gitlab).each do |title| create(:issue, author: @user, assignee: @user, diff --git a/spec/features/merge_requests/conflicts_spec.rb b/spec/features/merge_requests/conflicts_spec.rb index d710a780111..18508a44184 100644 --- a/spec/features/merge_requests/conflicts_spec.rb +++ b/spec/features/merge_requests/conflicts_spec.rb @@ -154,7 +154,7 @@ feature 'Merge request conflict resolution', js: true, feature: true do 'conflict-binary-file' => 'when the conflicts contain a binary file', 'conflict-missing-side' => 'when the conflicts contain a file edited in one branch and deleted in another', 'conflict-non-utf8' => 'when the conflicts contain a non-UTF-8 file', - } + }.freeze UNRESOLVABLE_CONFLICTS.each do |source_branch, description| context description do diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb index 7a562b5e03d..406d7cf791c 100644 --- a/spec/features/profile_spec.rb +++ b/spec/features/profile_spec.rb @@ -4,7 +4,7 @@ describe 'Profile account page', feature: true do let(:user) { create(:user) } before do - login_as :user + login_as(user) end describe 'when signup is enabled' do @@ -16,7 +16,7 @@ describe 'Profile account page', feature: true do it { expect(page).to have_content('Remove account') } it 'deletes the account' do - expect { click_link 'Delete account' }.to change { User.count }.by(-1) + expect { click_link 'Delete account' }.to change { User.where(id: user.id).count }.by(-1) expect(current_path).to eq(new_user_session_path) end end diff --git a/spec/features/projects/developer_views_empty_project_instructions_spec.rb b/spec/features/projects/developer_views_empty_project_instructions_spec.rb index 0c51fe72ca4..2352329d58c 100644 --- a/spec/features/projects/developer_views_empty_project_instructions_spec.rb +++ b/spec/features/projects/developer_views_empty_project_instructions_spec.rb @@ -56,8 +56,14 @@ feature 'Developer views empty project instructions', feature: true do end def expect_instructions_for(protocol) - msg = :"#{protocol.downcase}_url_to_repo" - - expect(page).to have_content("git clone #{project.send(msg)}") + url = + case protocol + when 'ssh' + project.ssh_url_to_repo + when 'http' + project.http_url_to_repo(developer) + end + + expect(page).to have_content("git clone #{url}") end end diff --git a/spec/features/projects/files/project_owner_creates_license_file_spec.rb b/spec/features/projects/files/project_owner_creates_license_file_spec.rb index 64094af29c0..ccadc936567 100644 --- a/spec/features/projects/files/project_owner_creates_license_file_spec.rb +++ b/spec/features/projects/files/project_owner_creates_license_file_spec.rb @@ -6,7 +6,7 @@ feature 'project owner creates a license file', feature: true, js: true do let(:project_master) { create(:user) } let(:project) { create(:project) } background do - project.repository.remove_file(project_master, 'LICENSE', + project.repository.delete_file(project_master, 'LICENSE', message: 'Remove LICENSE', branch_name: 'master') project.team << [project_master, :master] login_as(project_master) @@ -25,7 +25,7 @@ feature 'project owner creates a license file', feature: true, js: true do select_template('MIT License') file_content = first('.file-editor') - expect(file_content).to have_content('The MIT License (MIT)') + expect(file_content).to have_content('MIT License') expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}") fill_in :commit_message, with: 'Add a LICENSE file', visible: true @@ -33,7 +33,7 @@ feature 'project owner creates a license file', feature: true, js: true do expect(current_path).to eq( namespace_project_blob_path(project.namespace, project, 'master/LICENSE')) - expect(page).to have_content('The MIT License (MIT)') + expect(page).to have_content('MIT License') expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}") end @@ -49,7 +49,7 @@ feature 'project owner creates a license file', feature: true, js: true do select_template('MIT License') file_content = first('.file-editor') - expect(file_content).to have_content('The MIT License (MIT)') + expect(file_content).to have_content('MIT License') expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}") fill_in :commit_message, with: 'Add a LICENSE file', visible: true @@ -57,7 +57,7 @@ feature 'project owner creates a license file', feature: true, js: true do expect(current_path).to eq( namespace_project_blob_path(project.namespace, project, 'master/LICENSE')) - expect(page).to have_content('The MIT License (MIT)') + expect(page).to have_content('MIT License') expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}") end diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb index 4453b6d485f..420db962318 100644 --- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb +++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb @@ -24,7 +24,7 @@ feature 'project owner sees a link to create a license file in empty project', f select_template('MIT License') file_content = first('.file-editor') - expect(file_content).to have_content('The MIT License (MIT)') + expect(file_content).to have_content('MIT License') expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}") fill_in :commit_message, with: 'Add a LICENSE file', visible: true @@ -34,7 +34,7 @@ feature 'project owner sees a link to create a license file in empty project', f expect(current_path).to eq( namespace_project_blob_path(project.namespace, project, 'master/LICENSE')) - expect(page).to have_content('The MIT License (MIT)') + expect(page).to have_content('MIT License') expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}") end diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb index 16dddb2a86b..40caf89dd54 100644 --- a/spec/features/projects/import_export/export_file_spec.rb +++ b/spec/features/projects/import_export/export_file_spec.rb @@ -9,7 +9,7 @@ feature 'Import/Export - project export integration test', feature: true, js: tr include ExportFileHelper let(:user) { create(:admin) } - let(:export_path) { "#{Dir::tmpdir}/import_file_spec" } + let(:export_path) { "#{Dir.tmpdir}/import_file_spec" } let(:config_hash) { YAML.load_file(Gitlab::ImportExport.config_file).deep_stringify_keys } let(:sensitive_words) { %w[pass secret token key] } diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb index 3015576f6f8..2d1106ea3e8 100644 --- a/spec/features/projects/import_export/import_file_spec.rb +++ b/spec/features/projects/import_export/import_file_spec.rb @@ -4,7 +4,7 @@ feature 'Import/Export - project import integration test', feature: true, js: tr include Select2Helper let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') } - let(:export_path) { "#{Dir::tmpdir}/import_file_spec" } + let(:export_path) { "#{Dir.tmpdir}/import_file_spec" } background do allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) diff --git a/spec/features/projects/import_export/namespace_export_file_spec.rb b/spec/features/projects/import_export/namespace_export_file_spec.rb index d0bafc6168c..cb399ea55df 100644 --- a/spec/features/projects/import_export/namespace_export_file_spec.rb +++ b/spec/features/projects/import_export/namespace_export_file_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' feature 'Import/Export - Namespace export file cleanup', feature: true, js: true do - let(:export_path) { "#{Dir::tmpdir}/import_file_spec" } + let(:export_path) { "#{Dir.tmpdir}/import_file_spec" } let(:config_hash) { YAML.load_file(Gitlab::ImportExport.config_file).deep_stringify_keys } let(:project) { create(:empty_project) } diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb index e90a033b8c4..62d0aedda48 100644 --- a/spec/features/projects/issuable_templates_spec.rb +++ b/spec/features/projects/issuable_templates_spec.rb @@ -18,20 +18,18 @@ feature 'issuable templates', feature: true, js: true do let(:description_addition) { ' appending to description' } background do - project.repository.commit_file( + project.repository.create_file( user, '.gitlab/issue_templates/bug.md', template_content, message: 'added issue template', - branch_name: 'master', - update: false) - project.repository.commit_file( + branch_name: 'master') + project.repository.create_file( user, '.gitlab/issue_templates/test.md', longtemplate_content, message: 'added issue template', - branch_name: 'master', - update: false) + branch_name: 'master') visit edit_namespace_project_issue_path project.namespace, project, issue fill_in :'issue[title]', with: 'test issue title' end @@ -79,13 +77,12 @@ feature 'issuable templates', feature: true, js: true do let(:issue) { create(:issue, author: user, assignee: user, project: project) } background do - project.repository.commit_file( + project.repository.create_file( user, '.gitlab/issue_templates/bug.md', template_content, message: 'added issue template', - branch_name: 'master', - update: false) + branch_name: 'master') visit edit_namespace_project_issue_path project.namespace, project, issue fill_in :'issue[title]', with: 'test issue title' fill_in :'issue[description]', with: prior_description @@ -104,13 +101,12 @@ feature 'issuable templates', feature: true, js: true do let(:merge_request) { create(:merge_request, :with_diffs, source_project: project) } background do - project.repository.commit_file( + project.repository.create_file( user, '.gitlab/merge_request_templates/feature-proposal.md', template_content, message: 'added merge request template', - branch_name: 'master', - update: false) + branch_name: 'master') visit edit_namespace_project_merge_request_path project.namespace, project, merge_request fill_in :'merge_request[title]', with: 'test merge request title' end @@ -135,13 +131,12 @@ feature 'issuable templates', feature: true, js: true do fork_project.team << [fork_user, :master] create(:forked_project_link, forked_to_project: fork_project, forked_from_project: project) login_as fork_user - project.repository.commit_file( + project.repository.create_file( fork_user, '.gitlab/merge_request_templates/feature-proposal.md', template_content, message: 'added merge request template', - branch_name: 'master', - update: false) + branch_name: 'master') visit edit_namespace_project_merge_request_path project.namespace, project, merge_request fill_in :'merge_request[title]', with: 'test merge request title' end diff --git a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb index 81b0c991d4f..7414ce21f59 100644 --- a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb +++ b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb @@ -37,7 +37,7 @@ feature 'Issue prioritization', feature: true do page.within('.issues-holder') do issue_titles = all('.issues-list .issue-title-text').map(&:text) - expect(issue_titles).to eq(['issue_4', 'issue_3', 'issue_5', 'issue_2', 'issue_1']) + expect(issue_titles).to eq(%w(issue_4 issue_3 issue_5 issue_2 issue_1)) end end end @@ -77,7 +77,7 @@ feature 'Issue prioritization', feature: true do expect(issue_titles[0..1]).to contain_exactly('issue_5', 'issue_8') expect(issue_titles[2..4]).to contain_exactly('issue_1', 'issue_3', 'issue_7') - expect(issue_titles[5..-1]).to eq(['issue_2', 'issue_4', 'issue_6']) + expect(issue_titles[5..-1]).to eq(%w(issue_2 issue_4 issue_6)) end end end diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb index d6ebb523f95..c7a32a65e49 100644 --- a/spec/features/projects/members/sorting_spec.rb +++ b/spec/features/projects/members/sorting_spec.rb @@ -85,7 +85,7 @@ feature 'Projects > Members > Sorting', feature: true do end def visit_members_list(sort:) - visit namespace_project_project_members_path(project.namespace.to_param, project.to_param, sort: sort) + visit namespace_project_project_members_path(project.namespace.to_param, project, sort: sort) end def first_member diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 8d1214dedb4..22bf1bfbdf0 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -26,22 +26,66 @@ describe 'Pipelines', :feature, :js do ) end - [:all, :running, :branches].each do |scope| - context "when displaying #{scope}" do - before do - visit_project_pipelines(scope: scope) - end + context 'scope' do + before do + create(:ci_empty_pipeline, status: 'pending', project: project, sha: project.commit.id, ref: 'master') + create(:ci_empty_pipeline, status: 'running', project: project, sha: project.commit.id, ref: 'master') + create(:ci_empty_pipeline, status: 'created', project: project, sha: project.commit.id, ref: 'master') + create(:ci_empty_pipeline, status: 'success', project: project, sha: project.commit.id, ref: 'master') + end - it 'contains pipeline commit short SHA' do - expect(page).to have_content(pipeline.short_sha) - end + [:all, :running, :pending, :finished, :branches].each do |scope| + context "when displaying #{scope}" do + before do + visit_project_pipelines(scope: scope) + end - it 'contains branch name' do - expect(page).to have_content(pipeline.ref) + it 'contains pipeline commit short SHA' do + expect(page).to have_content(pipeline.short_sha) + end + + it 'contains branch name' do + expect(page).to have_content(pipeline.ref) + end end end end + context 'header tabs' do + before do + visit namespace_project_pipelines_path(project.namespace, project) + wait_for_vue_resource + end + + it 'shows a tab for All pipelines and count' do + expect(page.find('.js-pipelines-tab-all a').text).to include('All') + expect(page.find('.js-pipelines-tab-all .badge').text).to include('1') + end + + it 'shows a tab for Pending pipelines and count' do + expect(page.find('.js-pipelines-tab-pending a').text).to include('Pending') + expect(page.find('.js-pipelines-tab-pending .badge').text).to include('0') + end + + it 'shows a tab for Running pipelines and count' do + expect(page.find('.js-pipelines-tab-running a').text).to include('Running') + expect(page.find('.js-pipelines-tab-running .badge').text).to include('1') + end + + it 'shows a tab for Finished pipelines and count' do + expect(page.find('.js-pipelines-tab-finished a').text).to include('Finished') + expect(page.find('.js-pipelines-tab-finished .badge').text).to include('0') + end + + it 'shows a tab for Branches' do + expect(page.find('.js-pipelines-tab-branches a').text).to include('Branches') + end + + it 'shows a tab for Tags' do + expect(page.find('.js-pipelines-tab-tags a').text).to include('Tags') + end + end + context 'when pipeline is cancelable' do let!(:build) do create(:ci_build, pipeline: pipeline, @@ -284,6 +328,18 @@ describe 'Pipelines', :feature, :js do expect(build.reload).to be_canceled end end + + context 'dropdown jobs list' do + it 'should keep the dropdown open when the user ctr/cmd + clicks in the job name' do + find('.js-builds-dropdown-button').trigger('click') + + execute_script('var e = $.Event("keydown", { keyCode: 64 }); $("body").trigger(e);') + + find('.mini-pipeline-graph-dropdown-item').trigger('click') + + expect(page).to have_selector('.js-ci-action-icon') + end + end end context 'with pagination' do @@ -315,8 +371,14 @@ describe 'Pipelines', :feature, :js do visit new_namespace_project_pipeline_path(project.namespace, project) end - context 'for valid commit' do - before { fill_in('pipeline[ref]', with: 'master') } + context 'for valid commit', js: true do + before do + click_button project.default_branch + + page.within '.dropdown-menu' do + click_link 'master' + end + end context 'with gitlab-ci.yml' do before { stub_ci_pipeline_to_return_yaml_file } @@ -333,15 +395,6 @@ describe 'Pipelines', :feature, :js do it { expect(page).to have_content('Missing .gitlab-ci.yml file') } end end - - context 'for invalid commit' do - before do - fill_in('pipeline[ref]', with: 'invalid-reference') - click_on 'Create pipeline' - end - - it { expect(page).to have_content('Reference not found') } - end end describe 'Create pipelines' do @@ -353,18 +406,22 @@ describe 'Pipelines', :feature, :js do describe 'new pipeline page' do it 'has field to add a new pipeline' do - expect(page).to have_field('pipeline[ref]') + expect(page).to have_selector('.js-branch-select') + expect(find('.js-branch-select')).to have_content project.default_branch expect(page).to have_content('Create for') end end describe 'find pipelines' do it 'shows filtered pipelines', js: true do - fill_in('pipeline[ref]', with: 'fix') - find('input#ref').native.send_keys(:keydown) + click_button project.default_branch - within('.ui-autocomplete') do - expect(page).to have_selector('li', text: 'fix') + page.within '.dropdown-menu' do + find('.dropdown-input-field').native.send_keys('fix') + + page.within '.dropdown-content' do + expect(page).to have_content('fix') + end end end end diff --git a/spec/features/user_callout_spec.rb b/spec/features/user_callout_spec.rb new file mode 100644 index 00000000000..336c4092c98 --- /dev/null +++ b/spec/features/user_callout_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe 'User Callouts', js: true do + let(:user) { create(:user) } + let(:project) { create(:empty_project, path: 'gitlab', name: 'sample') } + + before do + login_as(user) + project.team << [user, :master] + end + + it 'takes you to the profile preferences when the link is clicked' do + visit dashboard_projects_path + click_link 'Check it out' + expect(current_path).to eq profile_preferences_path + end + + describe 'user callout should appear in two routes' do + it 'shows up on the user profile' do + visit user_path(user) + expect(find('.user-callout')).to have_content 'Customize your experience' + end + + it 'shows up on the dashboard projects' do + visit dashboard_projects_path + expect(find('.user-callout')).to have_content 'Customize your experience' + end + end + + it 'hides the user callout when click on the dismiss icon' do + visit user_path(user) + within('.user-callout') do + find('.close-user-callout').click + end + expect(page).not_to have_selector('#user-callout') + end +end diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb index f8b05d4e9bc..77a04507be1 100644 --- a/spec/finders/notes_finder_spec.rb +++ b/spec/finders/notes_finder_spec.rb @@ -111,7 +111,7 @@ describe NotesFinder do end it 'raises an exception for an invalid target_type' do - params.merge!(target_type: 'invalid') + params[:target_type] = 'invalid' expect { described_class.new(project, user, params).execute }.to raise_error('invalid target_type') end diff --git a/spec/finders/pipelines_finder_spec.rb b/spec/finders/pipelines_finder_spec.rb index fdc8215aa47..6bada7b3eb9 100644 --- a/spec/finders/pipelines_finder_spec.rb +++ b/spec/finders/pipelines_finder_spec.rb @@ -39,8 +39,8 @@ describe PipelinesFinder do end end - # Scoping to running will speed up the test as it doesn't hit the FS - let(:params) { { scope: 'running' } } + # Scoping to pending will speed up the test as it doesn't hit the FS + let(:params) { { scope: 'pending' } } it 'orders in descending order on ID' do feature_pipeline = create(:ci_pipeline, project: project, ref: 'feature') diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index fd40fe99941..4ffdd530171 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -58,7 +58,7 @@ describe ApplicationHelper do project = create(:empty_project, avatar: File.open(uploaded_image_temp_path)) avatar_url = "http://#{Gitlab.config.gitlab.host}/uploads/project/avatar/#{project.id}/banana_sample.gif" - expect(helper.project_icon("#{project.namespace.to_param}/#{project.to_param}").to_s). + expect(helper.project_icon(project.full_path).to_s). to eq "<img src=\"#{avatar_url}\" alt=\"Banana sample\" />" end @@ -68,7 +68,7 @@ describe ApplicationHelper do allow_any_instance_of(Project).to receive(:avatar_in_git).and_return(true) avatar_url = "http://#{Gitlab.config.gitlab.host}#{namespace_project_avatar_path(project.namespace, project)}" - expect(helper.project_icon("#{project.namespace.to_param}/#{project.to_param}").to_s).to match( + expect(helper.project_icon(project.full_path).to_s).to match( image_tag(avatar_url)) end end diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb index 49ea4fa6d3e..cd3281d6f51 100644 --- a/spec/helpers/auth_helper_spec.rb +++ b/spec/helpers/auth_helper_spec.rb @@ -55,7 +55,7 @@ describe AuthHelper do context 'all the button based providers are disabled via application_setting' do it 'returns false' do stub_application_setting( - disabled_oauth_sign_in_sources: ['github', 'twitter'] + disabled_oauth_sign_in_sources: %w(github twitter) ) expect(helper.button_based_providers_enabled?).to be false diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index df71680e44c..93bb711f29a 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -51,7 +51,7 @@ describe IssuablesHelper do utf8: '✓', author_id: '11', assignee_id: '18', - label_name: ['bug', 'discussion', 'documentation'], + label_name: %w(bug discussion documentation), milestone_title: 'v4.0', sort: 'due_date_asc', namespace_id: 'gitlab-org', diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb index 13fb9c1f1a7..88d853935c7 100644 --- a/spec/helpers/issues_helper_spec.rb +++ b/spec/helpers/issues_helper_spec.rb @@ -55,8 +55,8 @@ describe IssuesHelper do describe "merge_requests_sentence" do subject { merge_requests_sentence(merge_requests)} let(:merge_requests) do - [ build(:merge_request, iid: 1), build(:merge_request, iid: 2), - build(:merge_request, iid: 3)] + [build(:merge_request, iid: 1), build(:merge_request, iid: 2), + build(:merge_request, iid: 3)] end it { is_expected.to eq("!1, !2, or !3") } @@ -113,7 +113,7 @@ describe IssuesHelper do describe "awards_sort" do it "sorts a hash so thumbsup and thumbsdown are always on top" do data = { "thumbsdown" => "some value", "lifter" => "some value", "thumbsup" => "some value" } - expect(awards_sort(data).keys).to eq(["thumbsup", "thumbsdown", "lifter"]) + expect(awards_sort(data).keys).to eq(%w(thumbsup thumbsdown lifter)) end end diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb index 4da1569e59f..28b8def331d 100644 --- a/spec/helpers/submodule_helper_spec.rb +++ b/spec/helpers/submodule_helper_spec.rb @@ -20,97 +20,97 @@ describe SubmoduleHelper do it 'detects ssh on standard port' do allow(Gitlab.config.gitlab_shell).to receive(:ssh_port).and_return(22) # set this just to be sure allow(Gitlab.config.gitlab_shell).to receive(:ssh_path_prefix).and_return(Settings.send(:build_gitlab_shell_ssh_path_prefix)) - stub_url([ config.user, '@', config.host, ':gitlab-org/gitlab-ce.git' ].join('')) - expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ]) + stub_url([config.user, '@', config.host, ':gitlab-org/gitlab-ce.git'].join('')) + expect(submodule_links(submodule_item)).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')]) end it 'detects ssh on non-standard port' do allow(Gitlab.config.gitlab_shell).to receive(:ssh_port).and_return(2222) allow(Gitlab.config.gitlab_shell).to receive(:ssh_path_prefix).and_return(Settings.send(:build_gitlab_shell_ssh_path_prefix)) - stub_url([ 'ssh://', config.user, '@', config.host, ':2222/gitlab-org/gitlab-ce.git' ].join('')) - expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ]) + stub_url(['ssh://', config.user, '@', config.host, ':2222/gitlab-org/gitlab-ce.git'].join('')) + expect(submodule_links(submodule_item)).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')]) end it 'detects http on standard port' do allow(Gitlab.config.gitlab).to receive(:port).and_return(80) allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url)) - stub_url([ 'http://', config.host, '/gitlab-org/gitlab-ce.git' ].join('')) - expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ]) + stub_url(['http://', config.host, '/gitlab-org/gitlab-ce.git'].join('')) + expect(submodule_links(submodule_item)).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')]) end it 'detects http on non-standard port' do allow(Gitlab.config.gitlab).to receive(:port).and_return(3000) allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url)) - stub_url([ 'http://', config.host, ':3000/gitlab-org/gitlab-ce.git' ].join('')) - expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ]) + stub_url(['http://', config.host, ':3000/gitlab-org/gitlab-ce.git'].join('')) + expect(submodule_links(submodule_item)).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')]) end it 'works with relative_url_root' do allow(Gitlab.config.gitlab).to receive(:port).and_return(80) # set this just to be sure allow(Gitlab.config.gitlab).to receive(:relative_url_root).and_return('/gitlab/root') allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url)) - stub_url([ 'http://', config.host, '/gitlab/root/gitlab-org/gitlab-ce.git' ].join('')) - expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ]) + stub_url(['http://', config.host, '/gitlab/root/gitlab-org/gitlab-ce.git'].join('')) + expect(submodule_links(submodule_item)).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')]) end end context 'submodule on github.com' do it 'detects ssh' do stub_url('git@github.com:gitlab-org/gitlab-ce.git') - expect(submodule_links(submodule_item)).to eq([ 'https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash' ]) + expect(submodule_links(submodule_item)).to eq(['https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash']) end it 'detects http' do stub_url('http://github.com/gitlab-org/gitlab-ce.git') - expect(submodule_links(submodule_item)).to eq([ 'https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash' ]) + expect(submodule_links(submodule_item)).to eq(['https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash']) end it 'detects https' do stub_url('https://github.com/gitlab-org/gitlab-ce.git') - expect(submodule_links(submodule_item)).to eq([ 'https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash' ]) + expect(submodule_links(submodule_item)).to eq(['https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash']) end it 'returns original with non-standard url' do stub_url('http://github.com/gitlab-org/gitlab-ce') - expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) + expect(submodule_links(submodule_item)).to eq([repo.submodule_url_for, nil]) stub_url('http://github.com/another/gitlab-org/gitlab-ce.git') - expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) + expect(submodule_links(submodule_item)).to eq([repo.submodule_url_for, nil]) end end context 'submodule on gitlab.com' do it 'detects ssh' do stub_url('git@gitlab.com:gitlab-org/gitlab-ce.git') - expect(submodule_links(submodule_item)).to eq([ 'https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash' ]) + expect(submodule_links(submodule_item)).to eq(['https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash']) end it 'detects http' do stub_url('http://gitlab.com/gitlab-org/gitlab-ce.git') - expect(submodule_links(submodule_item)).to eq([ 'https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash' ]) + expect(submodule_links(submodule_item)).to eq(['https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash']) end it 'detects https' do stub_url('https://gitlab.com/gitlab-org/gitlab-ce.git') - expect(submodule_links(submodule_item)).to eq([ 'https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash' ]) + expect(submodule_links(submodule_item)).to eq(['https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash']) end it 'returns original with non-standard url' do stub_url('http://gitlab.com/gitlab-org/gitlab-ce') - expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) + expect(submodule_links(submodule_item)).to eq([repo.submodule_url_for, nil]) stub_url('http://gitlab.com/another/gitlab-org/gitlab-ce.git') - expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) + expect(submodule_links(submodule_item)).to eq([repo.submodule_url_for, nil]) end end context 'submodule on unsupported' do it 'returns original' do stub_url('http://mygitserver.com/gitlab-org/gitlab-ce') - expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) + expect(submodule_links(submodule_item)).to eq([repo.submodule_url_for, nil]) stub_url('http://mygitserver.com/gitlab-org/gitlab-ce.git') - expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) + expect(submodule_links(submodule_item)).to eq([repo.submodule_url_for, nil]) end end diff --git a/spec/initializers/trusted_proxies_spec.rb b/spec/initializers/trusted_proxies_spec.rb index 290e47763eb..ff8b8daa347 100644 --- a/spec/initializers/trusted_proxies_spec.rb +++ b/spec/initializers/trusted_proxies_spec.rb @@ -27,7 +27,7 @@ describe 'trusted_proxies', lib: true do context 'with private IP ranges added' do before do - set_trusted_proxies([ "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16" ]) + set_trusted_proxies(["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]) end it 'filters out private and local IPs' do @@ -39,7 +39,7 @@ describe 'trusted_proxies', lib: true do context 'with proxy IP added' do before do - set_trusted_proxies([ "60.98.25.47" ]) + set_trusted_proxies(["60.98.25.47"]) end it 'filters out proxy IP' do diff --git a/spec/javascripts/ajax_loading_spinner_spec.js b/spec/javascripts/ajax_loading_spinner_spec.js new file mode 100644 index 00000000000..a68bccb16f4 --- /dev/null +++ b/spec/javascripts/ajax_loading_spinner_spec.js @@ -0,0 +1,58 @@ +require('~/extensions/array'); +require('jquery'); +require('jquery-ujs'); +require('~/ajax_loading_spinner'); + +describe('Ajax Loading Spinner', () => { + const fixtureTemplate = 'static/ajax_loading_spinner.html.raw'; + preloadFixtures(fixtureTemplate); + + beforeEach(() => { + loadFixtures(fixtureTemplate); + gl.AjaxLoadingSpinner.init(); + }); + + it('change current icon with spinner icon and disable link while waiting ajax response', (done) => { + spyOn(jQuery, 'ajax').and.callFake((req) => { + const xhr = new XMLHttpRequest(); + const ajaxLoadingSpinner = document.querySelector('.js-ajax-loading-spinner'); + const icon = ajaxLoadingSpinner.querySelector('i'); + + req.beforeSend(xhr, { dataType: 'text/html' }); + + expect(icon).not.toHaveClass('fa-trash-o'); + expect(icon).toHaveClass('fa-spinner'); + expect(icon).toHaveClass('fa-spin'); + expect(icon.dataset.icon).toEqual('fa-trash-o'); + expect(ajaxLoadingSpinner.getAttribute('disabled')).toEqual(''); + + req.complete({}); + + done(); + const deferred = $.Deferred(); + return deferred.promise(); + }); + document.querySelector('.js-ajax-loading-spinner').click(); + }); + + it('use original icon again and enabled the link after complete the ajax request', (done) => { + spyOn(jQuery, 'ajax').and.callFake((req) => { + const xhr = new XMLHttpRequest(); + const ajaxLoadingSpinner = document.querySelector('.js-ajax-loading-spinner'); + + req.beforeSend(xhr, { dataType: 'text/html' }); + req.complete({}); + + const icon = ajaxLoadingSpinner.querySelector('i'); + expect(icon).toHaveClass('fa-trash-o'); + expect(icon).not.toHaveClass('fa-spinner'); + expect(icon).not.toHaveClass('fa-spin'); + expect(ajaxLoadingSpinner.getAttribute('disabled')).toEqual(null); + + done(); + const deferred = $.Deferred(); + return deferred.promise(); + }); + document.querySelector('.js-ajax-loading-spinner').click(); + }); +}); diff --git a/spec/javascripts/boards/board_card_spec.js b/spec/javascripts/boards/board_card_spec.js new file mode 100644 index 00000000000..192916fbc6a --- /dev/null +++ b/spec/javascripts/boards/board_card_spec.js @@ -0,0 +1,168 @@ +/* global Vue */ +/* global List */ +/* global ListLabel */ +/* global listObj */ +/* global boardsMockInterceptor */ +/* global BoardService */ + +require('~/boards/models/list'); +require('~/boards/models/label'); +require('~/boards/stores/boards_store'); +const boardCard = require('~/boards/components/board_card'); +require('./mock_data'); + +describe('Issue card', () => { + let vm; + + beforeEach((done) => { + Vue.http.interceptors.push(boardsMockInterceptor); + + gl.boardService = new BoardService('/test/issue-boards/board', '', '1'); + gl.issueBoards.BoardsStore.create(); + gl.issueBoards.BoardsStore.detail.issue = {}; + + const BoardCardComp = Vue.extend(boardCard); + const list = new List(listObj); + const label1 = new ListLabel({ + id: 3, + title: 'testing 123', + color: 'blue', + text_color: 'white', + description: 'test', + }); + + setTimeout(() => { + list.issues[0].labels.push(label1); + + vm = new BoardCardComp({ + propsData: { + list, + issue: list.issues[0], + issueLinkBase: '/', + disabled: false, + index: 0, + rootPath: '/', + }, + }).$mount(); + done(); + }, 0); + }); + + afterEach(() => { + Vue.http.interceptors = _.without(Vue.http.interceptors, boardsMockInterceptor); + }); + + it('returns false when detailIssue is empty', () => { + expect(vm.issueDetailVisible).toBe(false); + }); + + it('returns true when detailIssue is equal to card issue', () => { + gl.issueBoards.BoardsStore.detail.issue = vm.issue; + + expect(vm.issueDetailVisible).toBe(true); + }); + + it('adds user-can-drag class if not disabled', () => { + expect(vm.$el.classList.contains('user-can-drag')).toBe(true); + }); + + it('does not add user-can-drag class disabled', (done) => { + vm.disabled = true; + + setTimeout(() => { + expect(vm.$el.classList.contains('user-can-drag')).toBe(false); + done(); + }, 0); + }); + + it('does not add disabled class', () => { + expect(vm.$el.classList.contains('is-disabled')).toBe(false); + }); + + it('adds disabled class is disabled is true', (done) => { + vm.disabled = true; + + setTimeout(() => { + expect(vm.$el.classList.contains('is-disabled')).toBe(true); + done(); + }, 0); + }); + + describe('mouse events', () => { + const triggerEvent = (eventName, el = vm.$el) => { + const event = document.createEvent('MouseEvents'); + event.initMouseEvent(eventName, true, true, window, 1, 0, 0, 0, 0, false, false, + false, false, 0, null); + + el.dispatchEvent(event); + }; + + it('sets showDetail to true on mousedown', () => { + triggerEvent('mousedown'); + + expect(vm.showDetail).toBe(true); + }); + + it('sets showDetail to false on mousemove', () => { + triggerEvent('mousedown'); + + expect(vm.showDetail).toBe(true); + + triggerEvent('mousemove'); + + expect(vm.showDetail).toBe(false); + }); + + it('does not set detail issue if showDetail is false', () => { + expect(gl.issueBoards.BoardsStore.detail.issue).toEqual({}); + }); + + it('does not set detail issue if link is clicked', () => { + triggerEvent('mouseup', vm.$el.querySelector('a')); + + expect(gl.issueBoards.BoardsStore.detail.issue).toEqual({}); + }); + + it('does not set detail issue if button is clicked', () => { + triggerEvent('mouseup', vm.$el.querySelector('button')); + + expect(gl.issueBoards.BoardsStore.detail.issue).toEqual({}); + }); + + it('does not set detail issue if showDetail is false after mouseup', () => { + triggerEvent('mouseup'); + + expect(gl.issueBoards.BoardsStore.detail.issue).toEqual({}); + }); + + it('sets detail issue to card issue on mouse up', () => { + triggerEvent('mousedown'); + triggerEvent('mouseup'); + + expect(gl.issueBoards.BoardsStore.detail.issue).toEqual(vm.issue); + expect(gl.issueBoards.BoardsStore.detail.list).toEqual(vm.list); + }); + + it('adds active class if detail issue is set', (done) => { + triggerEvent('mousedown'); + triggerEvent('mouseup'); + + setTimeout(() => { + expect(vm.$el.classList.contains('is-active')).toBe(true); + done(); + }, 0); + }); + + it('resets detail issue to empty if already set', () => { + triggerEvent('mousedown'); + triggerEvent('mouseup'); + + expect(gl.issueBoards.BoardsStore.detail.issue).toEqual(vm.issue); + + triggerEvent('mousedown'); + triggerEvent('mouseup'); + + expect(gl.issueBoards.BoardsStore.detail.issue).toEqual({}); + }); + }); +}); diff --git a/spec/javascripts/boards/list_spec.js.es6 b/spec/javascripts/boards/list_spec.js.es6 index 4397a32fedc..c8a18af7198 100644 --- a/spec/javascripts/boards/list_spec.js.es6 +++ b/spec/javascripts/boards/list_spec.js.es6 @@ -3,7 +3,9 @@ /* global boardsMockInterceptor */ /* global BoardService */ /* global List */ +/* global ListIssue */ /* global listObj */ +/* global listObjDuplicate */ require('~/lib/utils/url_utility'); require('~/boards/models/issue'); @@ -84,4 +86,23 @@ describe('List model', () => { done(); }, 0); }); + + it('sends service request to update issue label', () => { + const listDup = new List(listObjDuplicate); + const issue = new ListIssue({ + title: 'Testing', + iid: 1, + confidential: false, + labels: [list.label, listDup.label] + }); + + list.issues.push(issue); + listDup.issues.push(issue); + + spyOn(gl.boardService, 'moveIssue').and.callThrough(); + + listDup.updateIssueLabel(list, issue); + + expect(gl.boardService.moveIssue).toHaveBeenCalledWith(issue.id, list.id, listDup.id); + }); }); diff --git a/spec/javascripts/fixtures/ajax_loading_spinner.html.haml b/spec/javascripts/fixtures/ajax_loading_spinner.html.haml new file mode 100644 index 00000000000..09d8c9df3b2 --- /dev/null +++ b/spec/javascripts/fixtures/ajax_loading_spinner.html.haml @@ -0,0 +1,2 @@ +%a.js-ajax-loading-spinner{href: "http://goesnowhere.nothing/whereami", data: {remote: true}} + %i.fa.fa-trash-o diff --git a/spec/javascripts/fixtures/branches.rb b/spec/javascripts/fixtures/branches.rb index 0e7c2351b66..a059818145b 100644 --- a/spec/javascripts/fixtures/branches.rb +++ b/spec/javascripts/fixtures/branches.rb @@ -20,7 +20,7 @@ describe Projects::BranchesController, '(JavaScript fixtures)', type: :controlle it 'branches/new_branch.html.raw' do |example| get :new, namespace_id: project.namespace.to_param, - project_id: project.to_param + project_id: project expect(response).to be_success store_frontend_fixture(response, example.description) diff --git a/spec/javascripts/fixtures/builds.rb b/spec/javascripts/fixtures/builds.rb index 978e25a1c32..320de791b08 100644 --- a/spec/javascripts/fixtures/builds.rb +++ b/spec/javascripts/fixtures/builds.rb @@ -24,7 +24,7 @@ describe Projects::BuildsController, '(JavaScript fixtures)', type: :controller it 'builds/build-with-artifacts.html.raw' do |example| get :show, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: build_with_artifacts.to_param expect(response).to be_success diff --git a/spec/javascripts/fixtures/issues.rb b/spec/javascripts/fixtures/issues.rb index 06f708f9e15..88e3f860809 100644 --- a/spec/javascripts/fixtures/issues.rb +++ b/spec/javascripts/fixtures/issues.rb @@ -41,7 +41,7 @@ describe Projects::IssuesController, '(JavaScript fixtures)', type: :controller def render_issue(fixture_file_name, issue) get :show, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: issue.to_param expect(response).to be_success diff --git a/spec/javascripts/fixtures/merge_requests.rb b/spec/javascripts/fixtures/merge_requests.rb index 62984097099..ee893b76c84 100644 --- a/spec/javascripts/fixtures/merge_requests.rb +++ b/spec/javascripts/fixtures/merge_requests.rb @@ -27,7 +27,7 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont def render_merge_request(fixture_file_name, merge_request) get :show, namespace_id: project.namespace.to_param, - project_id: project.to_param, + project_id: project, id: merge_request.to_param expect(response).to be_success diff --git a/spec/javascripts/fixtures/projects.rb b/spec/javascripts/fixtures/projects.rb index 56513219e1e..6c33b240e5c 100644 --- a/spec/javascripts/fixtures/projects.rb +++ b/spec/javascripts/fixtures/projects.rb @@ -20,7 +20,7 @@ describe ProjectsController, '(JavaScript fixtures)', type: :controller do it 'projects/dashboard.html.raw' do |example| get :show, namespace_id: project.namespace.to_param, - id: project.to_param + id: project expect(response).to be_success store_frontend_fixture(response, example.description) diff --git a/spec/javascripts/fixtures/todos.rb b/spec/javascripts/fixtures/todos.rb index 2c08b06ea9e..a81ef8c5492 100644 --- a/spec/javascripts/fixtures/todos.rb +++ b/spec/javascripts/fixtures/todos.rb @@ -39,8 +39,8 @@ describe 'Todos (JavaScript fixtures)' do it 'todos/todos.json' do |example| post :create, - namespace_id: namespace.path, - project_id: project.path, + namespace_id: namespace, + project_id: project, issuable_type: 'issue', issuable_id: issue_2.id, format: 'json' diff --git a/spec/javascripts/fixtures/user_callout.html.haml b/spec/javascripts/fixtures/user_callout.html.haml new file mode 100644 index 00000000000..275359bde0a --- /dev/null +++ b/spec/javascripts/fixtures/user_callout.html.haml @@ -0,0 +1,2 @@ +.user-callout{ 'callout-svg' => custom_icon('icon_customization') } + diff --git a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js index a954bb60560..861f26e162f 100644 --- a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js +++ b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js @@ -1,9 +1,7 @@ -/* eslint-disable quotes, jasmine/no-suite-dupes, vars-on-top, no-var, max-len */ -/* global d3 */ -/* global ContributorsGraph */ -/* global ContributorsMasterGraph */ +/* eslint-disable quotes, jasmine/no-suite-dupes, vars-on-top, no-var */ -require('~/graphs/stat_graph_contributors_graph'); +import d3 from 'd3'; +import { ContributorsGraph, ContributorsMasterGraph } from '~/graphs/stat_graph_contributors_graph'; describe("ContributorsGraph", function () { describe("#set_x_domain", function () { diff --git a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js index b15764abe8c..9b47ab62181 100644 --- a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js +++ b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js @@ -1,7 +1,6 @@ /* eslint-disable quotes, no-var, camelcase, object-property-newline, comma-dangle, max-len, vars-on-top, quote-props */ -/* global ContributorsStatGraphUtil */ -require('~/graphs/stat_graph_contributors_util'); +import ContributorsStatGraphUtil from '~/graphs/stat_graph_contributors_util'; describe("ContributorsStatGraphUtil", function () { describe("#parse_log", function () { diff --git a/spec/javascripts/graphs/stat_graph_spec.js b/spec/javascripts/graphs/stat_graph_spec.js deleted file mode 100644 index 876c23361bc..00000000000 --- a/spec/javascripts/graphs/stat_graph_spec.js +++ /dev/null @@ -1,20 +0,0 @@ -/* eslint-disable quotes */ -/* global StatGraph */ - -require('~/graphs/stat_graph'); - -describe("StatGraph", function () { - describe("#get_log", function () { - it("returns log", function () { - StatGraph.log = "test"; - expect(StatGraph.get_log()).toBe("test"); - }); - }); - - describe("#set_log", function () { - it("sets the log", function () { - StatGraph.set_log("test"); - expect(StatGraph.log).toBe("test"); - }); - }); -}); diff --git a/spec/javascripts/user_callout_spec.js.es6 b/spec/javascripts/user_callout_spec.js.es6 new file mode 100644 index 00000000000..6ee63f56a26 --- /dev/null +++ b/spec/javascripts/user_callout_spec.js.es6 @@ -0,0 +1,37 @@ +const UserCallout = require('~/user_callout'); + +const USER_CALLOUT_COOKIE = 'user_callout_dismissed'; +const Cookie = window.Cookies; + +describe('UserCallout', () => { + const fixtureName = 'static/user_callout.html.raw'; + preloadFixtures(fixtureName); + + beforeEach(function () { + loadFixtures(fixtureName); + this.userCallout = new UserCallout(); + this.closeButton = $('.close-user-callout'); + this.userCalloutBtn = $('.user-callout-btn'); + this.userCalloutContainer = $('.user-callout'); + Cookie.set(USER_CALLOUT_COOKIE, 'false'); + }); + + afterEach(function () { + Cookie.set(USER_CALLOUT_COOKIE, 'false'); + }); + + it('shows when cookie is set to false', function () { + expect(Cookie.get(USER_CALLOUT_COOKIE)).toBeDefined(); + expect(this.userCalloutContainer.is(':visible')).toBe(true); + }); + + it('hides when user clicks on the dismiss-icon', function () { + this.closeButton.click(); + expect(Cookie.get(USER_CALLOUT_COOKIE)).toBe('true'); + }); + + it('hides when user clicks on the "check it out" button', function () { + this.userCalloutBtn.click(); + expect(Cookie.get(USER_CALLOUT_COOKIE)).toBe('true'); + }); +}); diff --git a/spec/lib/bitbucket/collection_spec.rb b/spec/lib/bitbucket/collection_spec.rb index 015a7f80e03..9008cb3e870 100644 --- a/spec/lib/bitbucket/collection_spec.rb +++ b/spec/lib/bitbucket/collection_spec.rb @@ -19,6 +19,6 @@ describe Bitbucket::Collection do it "iterates paginator" do collection = described_class.new(TestPaginator.new) - expect(collection.to_a).to match(["result_1_page_1", "result_2_page_1", "result_1_page_2", "result_2_page_2"]) + expect(collection.to_a).to match(%w(result_1_page_1 result_2_page_1 result_1_page_2 result_2_page_2)) end end diff --git a/spec/lib/bitbucket/representation/repo_spec.rb b/spec/lib/bitbucket/representation/repo_spec.rb index adcd978e1b3..405265cc669 100644 --- a/spec/lib/bitbucket/representation/repo_spec.rb +++ b/spec/lib/bitbucket/representation/repo_spec.rb @@ -29,7 +29,7 @@ describe Bitbucket::Representation::Repo do end describe '#owner_and_slug' do - it { expect(described_class.new({ 'full_name' => 'ben/test' }).owner_and_slug).to eq(['ben', 'test']) } + it { expect(described_class.new({ 'full_name' => 'ben/test' }).owner_and_slug).to eq(%w(ben test)) } end describe '#owner' do @@ -42,7 +42,7 @@ describe Bitbucket::Representation::Repo do describe '#clone_url' do it 'builds url' do - data = { 'links' => { 'clone' => [ { 'name' => 'https', 'href' => 'https://bibucket.org/test/test.git' }] } } + data = { 'links' => { 'clone' => [{ 'name' => 'https', 'href' => 'https://bibucket.org/test/test.git' }] } } expect(described_class.new(data).clone_url('abc')).to eq('https://x-token-auth:abc@bibucket.org/test/test.git') end end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 68ad429608d..7145f0da1d3 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -96,7 +96,7 @@ module Ci it "returns builds if only has a list of branches including specified" do config = YAML.dump({ before_script: ["pwd"], - rspec: { script: "rspec", type: type, only: ["master", "deploy"] } + rspec: { script: "rspec", type: type, only: %w(master deploy) } }) config_processor = GitlabCiYamlProcessor.new(config, path) @@ -173,8 +173,8 @@ module Ci it "returns build only for specified type" do config = YAML.dump({ before_script: ["pwd"], - rspec: { script: "rspec", type: "test", only: ["master", "deploy"] }, - staging: { script: "deploy", type: "deploy", only: ["master", "deploy"] }, + rspec: { script: "rspec", type: "test", only: %w(master deploy) }, + staging: { script: "deploy", type: "deploy", only: %w(master deploy) }, production: { script: "deploy", type: "deploy", only: ["master@path", "deploy"] }, }) @@ -252,7 +252,7 @@ module Ci it "does not return builds if except has a list of branches including specified" do config = YAML.dump({ before_script: ["pwd"], - rspec: { script: "rspec", type: type, except: ["master", "deploy"] } + rspec: { script: "rspec", type: type, except: %w(master deploy) } }) config_processor = GitlabCiYamlProcessor.new(config, path) @@ -580,7 +580,7 @@ module Ci context 'when syntax is incorrect' do context 'when variables defined but invalid' do let(:variables) do - [ 'VAR1', 'value1', 'VAR2', 'value2' ] + %w(VAR1 value1 VAR2 value2) end it 'raises error' do @@ -909,7 +909,7 @@ module Ci end context 'dependencies to builds' do - let(:dependencies) { ['build1', 'build2'] } + let(:dependencies) { %w(build1 build2) } it { expect { subject }.not_to raise_error } end @@ -1215,7 +1215,7 @@ EOT end it "returns errors if job stage is not a defined stage" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", type: "acceptance" } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", type: "acceptance" } }) expect do GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test") @@ -1257,42 +1257,42 @@ EOT end it "returns errors if job artifacts:name is not an a string" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { name: 1 } } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", artifacts: { name: 1 } } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts name should be a string") end it "returns errors if job artifacts:when is not an a predefined value" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { when: 1 } } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", artifacts: { when: 1 } } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts when should be on_success, on_failure or always") end it "returns errors if job artifacts:expire_in is not an a string" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { expire_in: 1 } } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", artifacts: { expire_in: 1 } } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts expire in should be a duration") end it "returns errors if job artifacts:expire_in is not an a valid duration" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { expire_in: "7 elephants" } } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", artifacts: { expire_in: "7 elephants" } } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts expire in should be a duration") end it "returns errors if job artifacts:untracked is not an array of strings" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { untracked: "string" } } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", artifacts: { untracked: "string" } } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts untracked should be a boolean value") end it "returns errors if job artifacts:paths is not an array of strings" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { paths: "string" } } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", artifacts: { paths: "string" } } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts paths should be an array of strings") @@ -1320,28 +1320,28 @@ EOT end it "returns errors if job cache:key is not an a string" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", cache: { key: 1 } } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", cache: { key: 1 } } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:cache:key config should be a string or symbol") end it "returns errors if job cache:untracked is not an array of strings" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", cache: { untracked: "string" } } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", cache: { untracked: "string" } } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:cache:untracked config should be a boolean value") end it "returns errors if job cache:paths is not an array of strings" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", cache: { paths: "string" } } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", cache: { paths: "string" } } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:cache:paths config should be an array of strings") end it "returns errors if job dependencies is not an array of strings" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", dependencies: "string" } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", dependencies: "string" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec dependencies should be an array of strings") diff --git a/spec/lib/constraints/project_url_constrainer_spec.rb b/spec/lib/constraints/project_url_constrainer_spec.rb index a5251e9a8c2..4f25ad88960 100644 --- a/spec/lib/constraints/project_url_constrainer_spec.rb +++ b/spec/lib/constraints/project_url_constrainer_spec.rb @@ -6,7 +6,7 @@ describe ProjectUrlConstrainer, lib: true do describe '#matches?' do context 'valid request' do - let(:request) { build_request(namespace.path, project.path) } + let(:request) { build_request(namespace.full_path, project.path) } it { expect(subject.matches?(request)).to be_truthy } end @@ -19,7 +19,7 @@ describe ProjectUrlConstrainer, lib: true do end context "project id ending with .git" do - let(:request) { build_request(namespace.path, project.path + '.git') } + let(:request) { build_request(namespace.full_path, project.path + '.git') } it { expect(subject.matches?(request)).to be_falsey } end diff --git a/spec/lib/expand_variables_spec.rb b/spec/lib/expand_variables_spec.rb index 90bc7dad379..730ca1f7c0a 100644 --- a/spec/lib/expand_variables_spec.rb +++ b/spec/lib/expand_variables_spec.rb @@ -7,58 +7,49 @@ describe ExpandVariables do tests = [ { value: 'key', result: 'key', - variables: [] - }, + variables: [] }, { value: 'key$variable', result: 'key', - variables: [] - }, + variables: [] }, { value: 'key$variable', result: 'keyvalue', variables: [ { key: 'variable', value: 'value' } - ] - }, + ] }, { value: 'key${variable}', result: 'keyvalue', variables: [ { key: 'variable', value: 'value' } - ] - }, + ] }, { value: 'key$variable$variable2', result: 'keyvalueresult', variables: [ { key: 'variable', value: 'value' }, { key: 'variable2', value: 'result' }, - ] - }, + ] }, { value: 'key${variable}${variable2}', result: 'keyvalueresult', variables: [ { key: 'variable', value: 'value' }, { key: 'variable2', value: 'result' } - ] - }, + ] }, { value: 'key$variable2$variable', result: 'keyresultvalue', variables: [ { key: 'variable', value: 'value' }, { key: 'variable2', value: 'result' }, - ] - }, + ] }, { value: 'key${variable2}${variable}', result: 'keyresultvalue', variables: [ { key: 'variable', value: 'value' }, { key: 'variable2', value: 'result' } - ] - }, + ] }, { value: 'review/$CI_BUILD_REF_NAME', result: 'review/feature/add-review-apps', variables: [ { key: 'CI_BUILD_REF_NAME', value: 'feature/add-review-apps' } - ] - }, + ] }, ] tests.each do |test| diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb index 29c07655ae8..33ab005667a 100644 --- a/spec/lib/extracts_path_spec.rb +++ b/spec/lib/extracts_path_spec.rb @@ -177,12 +177,12 @@ describe ExtractsPath, lib: true do it "extracts a valid commit SHA" do expect(extract_ref('f4b14494ef6abf3d144c28e4af0c20143383e062/CHANGELOG')).to eq( - ['f4b14494ef6abf3d144c28e4af0c20143383e062', 'CHANGELOG'] + %w(f4b14494ef6abf3d144c28e4af0c20143383e062 CHANGELOG) ) end it "falls back to a primitive split for an invalid ref" do - expect(extract_ref('stable/CHANGELOG')).to eq(['stable', 'CHANGELOG']) + expect(extract_ref('stable/CHANGELOG')).to eq(%w(stable CHANGELOG)) end end end diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb index 0a2fe5af2c3..a7ee7f53a6b 100644 --- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb @@ -87,10 +87,10 @@ describe Gitlab::BitbucketImport::Importer, lib: true do body: issues_statuses_sample_data.to_json) stub_request(:get, "https://api.bitbucket.org/2.0/repositories/namespace/repo?pagelen=50&sort=created_on"). - with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer', 'User-Agent' => 'Faraday v0.9.2' }). - to_return(status: 200, - body: "", - headers: {}) + with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer', 'User-Agent' => 'Faraday v0.9.2' }). + to_return(status: 200, + body: "", + headers: {}) sample_issues_statuses.each_with_index do |issue, index| stub_request( diff --git a/spec/lib/gitlab/ci/config/entry/commands_spec.rb b/spec/lib/gitlab/ci/config/entry/commands_spec.rb index b8b0825a1c7..afa4a089418 100644 --- a/spec/lib/gitlab/ci/config/entry/commands_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/commands_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::Ci::Config::Entry::Commands do let(:entry) { described_class.new(config) } context 'when entry config value is an array' do - let(:config) { ['ls', 'pwd'] } + let(:config) { %w(ls pwd) } describe '#value' do it 'returns array of strings' do diff --git a/spec/lib/gitlab/ci/config/entry/factory_spec.rb b/spec/lib/gitlab/ci/config/entry/factory_spec.rb index 00dad5d9591..3395b3c645b 100644 --- a/spec/lib/gitlab/ci/config/entry/factory_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/factory_spec.rb @@ -8,20 +8,20 @@ describe Gitlab::Ci::Config::Entry::Factory do context 'when setting a concrete value' do it 'creates entry with valid value' do entry = factory - .value(['ls', 'pwd']) + .value(%w(ls pwd)) .create! - expect(entry.value).to eq ['ls', 'pwd'] + expect(entry.value).to eq %w(ls pwd) end context 'when setting description' do it 'creates entry with description' do entry = factory - .value(['ls', 'pwd']) + .value(%w(ls pwd)) .with(description: 'test description') .create! - expect(entry.value).to eq ['ls', 'pwd'] + expect(entry.value).to eq %w(ls pwd) expect(entry.description).to eq 'test description' end end @@ -29,7 +29,7 @@ describe Gitlab::Ci::Config::Entry::Factory do context 'when setting key' do it 'creates entry with custom key' do entry = factory - .value(['ls', 'pwd']) + .value(%w(ls pwd)) .with(key: 'test key') .create! diff --git a/spec/lib/gitlab/ci/config/entry/global_spec.rb b/spec/lib/gitlab/ci/config/entry/global_spec.rb index 432a99dce33..ebd80ac5e1d 100644 --- a/spec/lib/gitlab/ci/config/entry/global_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/global_spec.rb @@ -21,12 +21,12 @@ describe Gitlab::Ci::Config::Entry::Global do context 'when configuration is valid' do context 'when some entries defined' do let(:hash) do - { before_script: ['ls', 'pwd'], + { before_script: %w(ls pwd), image: 'ruby:2.2', services: ['postgres:9.1', 'mysql:5.5'], variables: { VAR: 'value' }, after_script: ['make clean'], - stages: ['build', 'pages'], + stages: %w(build pages), cache: { key: 'k', untracked: true, paths: ['public/'] }, rspec: { script: %w[rspec ls] }, spinach: { before_script: [], variables: {}, script: 'spinach' } } @@ -89,7 +89,7 @@ describe Gitlab::Ci::Config::Entry::Global do describe '#before_script_value' do it 'returns correct script' do - expect(global.before_script_value).to eq ['ls', 'pwd'] + expect(global.before_script_value).to eq %w(ls pwd) end end @@ -126,7 +126,7 @@ describe Gitlab::Ci::Config::Entry::Global do context 'when deprecated types key defined' do let(:hash) do - { types: ['test', 'deploy'], + { types: %w(test deploy), rspec: { script: 'rspec' } } end @@ -148,7 +148,7 @@ describe Gitlab::Ci::Config::Entry::Global do expect(global.jobs_value).to eq( rspec: { name: :rspec, script: %w[rspec ls], - before_script: ['ls', 'pwd'], + before_script: %w(ls pwd), commands: "ls\npwd\nrspec\nls", image: 'ruby:2.2', services: ['postgres:9.1', 'mysql:5.5'], diff --git a/spec/lib/gitlab/ci/config/entry/key_spec.rb b/spec/lib/gitlab/ci/config/entry/key_spec.rb index a55e5b4b8ac..0dd36fe1f44 100644 --- a/spec/lib/gitlab/ci/config/entry/key_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/key_spec.rb @@ -21,7 +21,7 @@ describe Gitlab::Ci::Config::Entry::Key do end context 'when entry value is not correct' do - let(:config) { [ 'incorrect' ] } + let(:config) { ['incorrect'] } describe '#errors' do it 'saves errors' do diff --git a/spec/lib/gitlab/ci/config/entry/paths_spec.rb b/spec/lib/gitlab/ci/config/entry/paths_spec.rb index e60c9aaf661..1d9c5ddee9b 100644 --- a/spec/lib/gitlab/ci/config/entry/paths_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/paths_spec.rb @@ -21,7 +21,7 @@ describe Gitlab::Ci::Config::Entry::Paths do end context 'when entry value is not valid' do - let(:config) { [ 1 ] } + let(:config) { [1] } describe '#errors' do it 'saves errors' do diff --git a/spec/lib/gitlab/ci/config/entry/script_spec.rb b/spec/lib/gitlab/ci/config/entry/script_spec.rb index aa99cee2690..069eaa26422 100644 --- a/spec/lib/gitlab/ci/config/entry/script_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/script_spec.rb @@ -5,7 +5,7 @@ describe Gitlab::Ci::Config::Entry::Script do describe 'validations' do context 'when entry config value is correct' do - let(:config) { ['ls', 'pwd'] } + let(:config) { %w(ls pwd) } describe '#value' do it 'returns array of strings' do diff --git a/spec/lib/gitlab/ci/config/entry/variables_spec.rb b/spec/lib/gitlab/ci/config/entry/variables_spec.rb index 58327d08904..f15f02f403e 100644 --- a/spec/lib/gitlab/ci/config/entry/variables_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/variables_spec.rb @@ -29,7 +29,7 @@ describe Gitlab::Ci::Config::Entry::Variables do end context 'when entry value is not correct' do - let(:config) { [ :VAR, 'test' ] } + let(:config) { [:VAR, 'test'] } describe '#errors' do it 'saves errors' do diff --git a/spec/lib/gitlab/conflict/file_spec.rb b/spec/lib/gitlab/conflict/file_spec.rb index fbf679c5215..780ac0ad97e 100644 --- a/spec/lib/gitlab/conflict/file_spec.rb +++ b/spec/lib/gitlab/conflict/file_spec.rb @@ -44,7 +44,7 @@ describe Gitlab::Conflict::File, lib: true do it 'returns a file containing only the chosen parts of the resolved sections' do expect(resolved_lines.chunk { |line| line.type || 'both' }.map(&:first)). - to eq(['both', 'new', 'both', 'old', 'both', 'new', 'both']) + to eq(%w(both new both old both new both)) end end @@ -123,7 +123,7 @@ describe Gitlab::Conflict::File, lib: true do it 'sets conflict to true for sections with only changed lines' do conflict_file.sections.select { |section| section[:conflict] }.each do |section| section[:lines].each do |line| - expect(line.type).to be_in(['new', 'old']) + expect(line.type).to be_in(%w(new old)) end end end @@ -251,7 +251,7 @@ FILE describe '#as_json' do it 'includes the blob path for the file' do expect(conflict_file.as_json[:blob_path]). - to eq("/#{project.namespace.to_param}/#{merge_request.project.to_param}/blob/#{our_commit.oid}/files/ruby/regex.rb") + to eq("/#{project.full_path}/blob/#{our_commit.oid}/files/ruby/regex.rb") end it 'includes the blob icon for the file' do diff --git a/spec/lib/gitlab/diff/highlight_spec.rb b/spec/lib/gitlab/diff/highlight_spec.rb index 5893485634d..0e9309d278e 100644 --- a/spec/lib/gitlab/diff/highlight_spec.rb +++ b/spec/lib/gitlab/diff/highlight_spec.rb @@ -53,21 +53,21 @@ describe Gitlab::Diff::Highlight, lib: true do end it 'marks unchanged lines' do - code = %Q{ def popen(cmd, path=nil)} + code = %q{ def popen(cmd, path=nil)} expect(subject[2].text).to eq(code) expect(subject[2].text).not_to be_html_safe end it 'marks removed lines' do - code = %Q{- raise "System commands must be given as an array of strings"} + code = %q{- raise "System commands must be given as an array of strings"} expect(subject[4].text).to eq(code) expect(subject[4].text).not_to be_html_safe end it 'marks added lines' do - code = %Q{+ raise <span class='idiff left right'>RuntimeError, </span>"System commands must be given as an array of strings"} + code = %q{+ raise <span class='idiff left right'>RuntimeError, </span>"System commands must be given as an array of strings"} expect(subject[5].text).to eq(code) expect(subject[5].text).to be_html_safe diff --git a/spec/lib/gitlab/git/blob_snippet_spec.rb b/spec/lib/gitlab/git/blob_snippet_spec.rb index 79b1311ac91..17d6be470ac 100644 --- a/spec/lib/gitlab/git/blob_snippet_spec.rb +++ b/spec/lib/gitlab/git/blob_snippet_spec.rb @@ -11,7 +11,7 @@ describe Gitlab::Git::BlobSnippet, seed_helper: true do end context 'present lines' do - let(:snippet) { Gitlab::Git::BlobSnippet.new('master', ['wow', 'much'], 1, 'wow.rb') } + let(:snippet) { Gitlab::Git::BlobSnippet.new('master', %w(wow much), 1, 'wow.rb') } it { expect(snippet.data).to eq("wow\nmuch") } end diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb index 84f79ec2391..8049e2c120d 100644 --- a/spec/lib/gitlab/git/blob_spec.rb +++ b/spec/lib/gitlab/git/blob_spec.rb @@ -222,191 +222,6 @@ describe Gitlab::Git::Blob, seed_helper: true do end end - describe :commit do - let(:repository) { Gitlab::Git::Repository.new(TEST_REPO_PATH) } - - let(:commit_options) do - { - file: { - content: 'Lorem ipsum...', - path: 'documents/story.txt' - }, - author: { - email: 'user@example.com', - name: 'Test User', - time: Time.now - }, - committer: { - email: 'user@example.com', - name: 'Test User', - time: Time.now - }, - commit: { - message: 'Wow such commit', - branch: 'fix-mode' - } - } - end - - let(:commit_sha) { Gitlab::Git::Blob.commit(repository, commit_options) } - let(:commit) { repository.lookup(commit_sha) } - - it 'should add file with commit' do - # Commit message valid - expect(commit.message).to eq('Wow such commit') - - tree = commit.tree.to_a.find { |tree| tree[:name] == 'documents' } - - # Directory was created - expect(tree[:type]).to eq(:tree) - - # File was created - expect(repository.lookup(tree[:oid]).first[:name]).to eq('story.txt') - end - - describe "ref updating" do - it 'creates a commit but does not udate a ref' do - commit_opts = commit_options.tap{ |opts| opts[:commit][:update_ref] = false} - commit_sha = Gitlab::Git::Blob.commit(repository, commit_opts) - commit = repository.lookup(commit_sha) - - # Commit message valid - expect(commit.message).to eq('Wow such commit') - - # Does not update any related ref - expect(repository.lookup("fix-mode").oid).not_to eq(commit.oid) - expect(repository.lookup("HEAD").oid).not_to eq(commit.oid) - end - end - - describe 'reject updates' do - it 'should reject updates' do - commit_options[:file][:update] = false - commit_options[:file][:path] = 'files/executables/ls' - - expect{ commit_sha }.to raise_error('Filename already exists; update not allowed') - end - end - - describe 'file modes' do - it 'should preserve file modes with commit' do - commit_options[:file][:path] = 'files/executables/ls' - - entry = Gitlab::Git::Blob::find_entry_by_path(repository, commit.tree.oid, commit_options[:file][:path]) - expect(entry[:filemode]).to eq(0100755) - end - end - end - - describe :rename do - let(:repository) { Gitlab::Git::Repository.new(TEST_NORMAL_REPO_PATH) } - let(:rename_options) do - { - file: { - path: 'NEWCONTRIBUTING.md', - previous_path: 'CONTRIBUTING.md', - content: 'Lorem ipsum...', - update: true - }, - author: { - email: 'user@example.com', - name: 'Test User', - time: Time.now - }, - committer: { - email: 'user@example.com', - name: 'Test User', - time: Time.now - }, - commit: { - message: 'Rename readme', - branch: 'master' - } - } - end - - let(:rename_options2) do - { - file: { - content: 'Lorem ipsum...', - path: 'bin/new_executable', - previous_path: 'bin/executable', - }, - author: { - email: 'user@example.com', - name: 'Test User', - time: Time.now - }, - committer: { - email: 'user@example.com', - name: 'Test User', - time: Time.now - }, - commit: { - message: 'Updates toberenamed.txt', - branch: 'master', - update_ref: false - } - } - end - - it 'maintains file permissions when renaming' do - mode = 0o100755 - - Gitlab::Git::Blob.rename(repository, rename_options2) - - expect(repository.rugged.index.get(rename_options2[:file][:path])[:mode]).to eq(mode) - end - - it 'renames the file with commit and not change file permissions' do - ref = rename_options[:commit][:branch] - - expect(repository.rugged.index.get('CONTRIBUTING.md')).not_to be_nil - expect { Gitlab::Git::Blob.rename(repository, rename_options) }.to change { repository.commit_count(ref) }.by(1) - - expect(repository.rugged.index.get('CONTRIBUTING.md')).to be_nil - expect(repository.rugged.index.get('NEWCONTRIBUTING.md')).not_to be_nil - end - end - - describe :remove do - let(:repository) { Gitlab::Git::Repository.new(TEST_REPO_PATH) } - - let(:commit_options) do - { - file: { - path: 'README.md' - }, - author: { - email: 'user@example.com', - name: 'Test User', - time: Time.now - }, - committer: { - email: 'user@example.com', - name: 'Test User', - time: Time.now - }, - commit: { - message: 'Remove readme', - branch: 'feature' - } - } - end - - let(:commit_sha) { Gitlab::Git::Blob.remove(repository, commit_options) } - let(:commit) { repository.lookup(commit_sha) } - let(:blob) { Gitlab::Git::Blob.find(repository, commit_sha, "README.md") } - - it 'should remove file with commit' do - # Commit message valid - expect(commit.message).to eq('Remove readme') - - # File was removed - expect(blob).to be_nil - end - end - describe :lfs_pointers do context 'file a valid lfs pointer' do let(:blob) do diff --git a/spec/lib/gitlab/git/diff_collection_spec.rb b/spec/lib/gitlab/git/diff_collection_spec.rb index 4fa72c565ae..47bdd7310d5 100644 --- a/spec/lib/gitlab/git/diff_collection_spec.rb +++ b/spec/lib/gitlab/git/diff_collection_spec.rb @@ -365,7 +365,7 @@ describe Gitlab::Git::DiffCollection, seed_helper: true do end context 'when go over safe limits on files' do - let(:iterator) { [ fake_diff(1, 1) ] * 4 } + let(:iterator) { [fake_diff(1, 1)] * 4 } before(:each) do stub_const('Gitlab::Git::DiffCollection::DEFAULT_LIMITS', { max_files: 2, max_lines: max_lines }) diff --git a/spec/lib/gitlab/git/index_spec.rb b/spec/lib/gitlab/git/index_spec.rb new file mode 100644 index 00000000000..d0c7ca60ddc --- /dev/null +++ b/spec/lib/gitlab/git/index_spec.rb @@ -0,0 +1,220 @@ +require 'spec_helper' + +describe Gitlab::Git::Index, seed_helper: true do + let(:repository) { Gitlab::Git::Repository.new(TEST_REPO_PATH) } + let(:index) { described_class.new(repository) } + + before do + index.read_tree(repository.lookup('master').tree) + end + + describe '#create' do + let(:options) do + { + content: 'Lorem ipsum...', + file_path: 'documents/story.txt' + } + end + + context 'when no file at that path exists' do + it 'creates the file in the index' do + index.create(options) + + entry = index.get(options[:file_path]) + + expect(entry).not_to be_nil + expect(repository.lookup(entry[:oid]).content).to eq(options[:content]) + end + end + + context 'when a file at that path exists' do + before do + options[:file_path] = 'files/executables/ls' + end + + it 'raises an error' do + expect { index.create(options) }.to raise_error('Filename already exists') + end + end + + context 'when content is in base64' do + before do + options[:content] = Base64.encode64(options[:content]) + options[:encoding] = 'base64' + end + + it 'decodes base64' do + index.create(options) + + entry = index.get(options[:file_path]) + expect(repository.lookup(entry[:oid]).content).to eq(Base64.decode64(options[:content])) + end + end + + context 'when content contains CRLF' do + before do + repository.autocrlf = :input + options[:content] = "Hello,\r\nWorld" + end + + it 'converts to LF' do + index.create(options) + + entry = index.get(options[:file_path]) + expect(repository.lookup(entry[:oid]).content).to eq("Hello,\nWorld") + end + end + end + + describe '#create_dir' do + let(:options) do + { + file_path: 'newdir' + } + end + + context 'when no file or dir at that path exists' do + it 'creates the dir in the index' do + index.create_dir(options) + + entry = index.get(options[:file_path] + '/.gitkeep') + + expect(entry).not_to be_nil + end + end + + context 'when a file at that path exists' do + before do + options[:file_path] = 'files/executables/ls' + end + + it 'raises an error' do + expect { index.create_dir(options) }.to raise_error('Directory already exists as a file') + end + end + + context 'when a directory at that path exists' do + before do + options[:file_path] = 'files/executables' + end + + it 'raises an error' do + expect { index.create_dir(options) }.to raise_error('Directory already exists') + end + end + end + + describe '#update' do + let(:options) do + { + content: 'Lorem ipsum...', + file_path: 'README.md' + } + end + + context 'when no file at that path exists' do + before do + options[:file_path] = 'documents/story.txt' + end + + it 'raises an error' do + expect { index.update(options) }.to raise_error("File doesn't exist") + end + end + + context 'when a file at that path exists' do + it 'updates the file in the index' do + index.update(options) + + entry = index.get(options[:file_path]) + + expect(repository.lookup(entry[:oid]).content).to eq(options[:content]) + end + + it 'preserves file mode' do + options[:file_path] = 'files/executables/ls' + + index.update(options) + + entry = index.get(options[:file_path]) + + expect(entry[:mode]).to eq(0100755) + end + end + end + + describe '#move' do + let(:options) do + { + content: 'Lorem ipsum...', + previous_path: 'README.md', + file_path: 'NEWREADME.md' + } + end + + context 'when no file at that path exists' do + it 'raises an error' do + options[:previous_path] = 'documents/story.txt' + + expect { index.move(options) }.to raise_error("File doesn't exist") + end + end + + context 'when a file at that path exists' do + it 'removes the old file in the index' do + index.move(options) + + entry = index.get(options[:previous_path]) + + expect(entry).to be_nil + end + + it 'creates the new file in the index' do + index.move(options) + + entry = index.get(options[:file_path]) + + expect(entry).not_to be_nil + expect(repository.lookup(entry[:oid]).content).to eq(options[:content]) + end + + it 'preserves file mode' do + options[:previous_path] = 'files/executables/ls' + + index.move(options) + + entry = index.get(options[:file_path]) + + expect(entry[:mode]).to eq(0100755) + end + end + end + + describe '#delete' do + let(:options) do + { + file_path: 'README.md' + } + end + + context 'when no file at that path exists' do + before do + options[:file_path] = 'documents/story.txt' + end + + it 'raises an error' do + expect { index.delete(options) }.to raise_error("File doesn't exist") + end + end + + context 'when a file at that path exists' do + it 'removes the file in the index' do + index.delete(options) + + entry = index.get(options[:file_path]) + + expect(entry).to be_nil + end + end + end +end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 2a915bf426f..1919542ca25 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -529,7 +529,7 @@ describe Gitlab::Git::Repository, seed_helper: true do commit_with_new_name = nil rename_commit = nil - before(:all) do + before(:context) do # Add new commits so that there's a renamed file in the commit history repo = Gitlab::Git::Repository.new(TEST_REPO_PATH).rugged @@ -538,49 +538,119 @@ describe Gitlab::Git::Repository, seed_helper: true do commit_with_new_name = new_commit_edit_new_file(repo) end + after(:context) do + # Erase our commits so other tests get the original repo + repo = Gitlab::Git::Repository.new(TEST_REPO_PATH).rugged + repo.references.update("refs/heads/master", SeedRepo::LastCommit::ID) + end + context "where 'follow' == true" do - options = { ref: "master", follow: true } + let(:options) { { ref: "master", follow: true } } context "and 'path' is a directory" do - let(:log_commits) do - repository.log(options.merge(path: "encoding")) - end + it "does not follow renames" do + log_commits = repository.log(options.merge(path: "encoding")) - it "should not follow renames" do - expect(log_commits).to include(commit_with_new_name) - expect(log_commits).to include(rename_commit) - expect(log_commits).not_to include(commit_with_old_name) + aggregate_failures do + expect(log_commits).to include(commit_with_new_name) + expect(log_commits).to include(rename_commit) + expect(log_commits).not_to include(commit_with_old_name) + end end end context "and 'path' is a file that matches the new filename" do - let(:log_commits) do - repository.log(options.merge(path: "encoding/CHANGELOG")) + context 'without offset' do + it "follows renames" do + log_commits = repository.log(options.merge(path: "encoding/CHANGELOG")) + + aggregate_failures do + expect(log_commits).to include(commit_with_new_name) + expect(log_commits).to include(rename_commit) + expect(log_commits).to include(commit_with_old_name) + end + end end - it "should follow renames" do - expect(log_commits).to include(commit_with_new_name) - expect(log_commits).to include(rename_commit) - expect(log_commits).to include(commit_with_old_name) + context 'with offset=1' do + it "follows renames and skip the latest commit" do + log_commits = repository.log(options.merge(path: "encoding/CHANGELOG", offset: 1)) + + aggregate_failures do + expect(log_commits).not_to include(commit_with_new_name) + expect(log_commits).to include(rename_commit) + expect(log_commits).to include(commit_with_old_name) + end + end + end + + context 'with offset=1', 'and limit=1' do + it "follows renames, skip the latest commit and return only one commit" do + log_commits = repository.log(options.merge(path: "encoding/CHANGELOG", offset: 1, limit: 1)) + + expect(log_commits).to contain_exactly(rename_commit) + end + end + + context 'with offset=1', 'and limit=2' do + it "follows renames, skip the latest commit and return only two commits" do + log_commits = repository.log(options.merge(path: "encoding/CHANGELOG", offset: 1, limit: 2)) + + aggregate_failures do + expect(log_commits).to contain_exactly(rename_commit, commit_with_old_name) + end + end + end + + context 'with offset=2' do + it "follows renames and skip the latest commit" do + log_commits = repository.log(options.merge(path: "encoding/CHANGELOG", offset: 2)) + + aggregate_failures do + expect(log_commits).not_to include(commit_with_new_name) + expect(log_commits).not_to include(rename_commit) + expect(log_commits).to include(commit_with_old_name) + end + end + end + + context 'with offset=2', 'and limit=1' do + it "follows renames, skip the two latest commit and return only one commit" do + log_commits = repository.log(options.merge(path: "encoding/CHANGELOG", offset: 2, limit: 1)) + + expect(log_commits).to contain_exactly(commit_with_old_name) + end + end + + context 'with offset=2', 'and limit=2' do + it "follows renames, skip the two latest commit and return only one commit" do + log_commits = repository.log(options.merge(path: "encoding/CHANGELOG", offset: 2, limit: 2)) + + aggregate_failures do + expect(log_commits).not_to include(commit_with_new_name) + expect(log_commits).not_to include(rename_commit) + expect(log_commits).to include(commit_with_old_name) + end + end end end context "and 'path' is a file that matches the old filename" do - let(:log_commits) do - repository.log(options.merge(path: "CHANGELOG")) - end + it "does not follow renames" do + log_commits = repository.log(options.merge(path: "CHANGELOG")) - it "should not follow renames" do - expect(log_commits).to include(commit_with_old_name) - expect(log_commits).to include(rename_commit) - expect(log_commits).not_to include(commit_with_new_name) + aggregate_failures do + expect(log_commits).not_to include(commit_with_new_name) + expect(log_commits).to include(rename_commit) + expect(log_commits).to include(commit_with_old_name) + end end end context "unknown ref" do - let(:log_commits) { repository.log(options.merge(ref: 'unknown')) } + it "returns an empty array" do + log_commits = repository.log(options.merge(ref: 'unknown')) - it "should return empty" do expect(log_commits).to eq([]) end end @@ -699,12 +769,6 @@ describe Gitlab::Git::Repository, seed_helper: true do end end end - - after(:all) do - # Erase our commits so other tests get the original repo - repo = Gitlab::Git::Repository.new(TEST_REPO_PATH).rugged - repo.references.update("refs/heads/master", SeedRepo::LastCommit::ID) - end end describe "#commits_between" do @@ -844,81 +908,6 @@ describe Gitlab::Git::Repository, seed_helper: true do end end - describe '#mkdir' do - let(:commit_options) do - { - author: { - email: 'user@example.com', - name: 'Test User', - time: Time.now - }, - committer: { - email: 'user@example.com', - name: 'Test User', - time: Time.now - }, - commit: { - message: 'Test message', - branch: 'refs/heads/fix', - } - } - end - - def generate_diff_for_path(path) - "diff --git a/#{path}/.gitkeep b/#{path}/.gitkeep -new file mode 100644 -index 0000000..e69de29 ---- /dev/null -+++ b/#{path}/.gitkeep\n" - end - - shared_examples 'mkdir diff check' do |path, expected_path| - it 'creates a directory' do - result = repository.mkdir(path, commit_options) - expect(result).not_to eq(nil) - - # Verify another mkdir doesn't create a directory that already exists - expect{ repository.mkdir(path, commit_options) }.to raise_error('Directory already exists') - end - end - - describe 'creates a directory in root directory' do - it_should_behave_like 'mkdir diff check', 'new_dir', 'new_dir' - end - - describe 'creates a directory in subdirectory' do - it_should_behave_like 'mkdir diff check', 'files/ruby/test', 'files/ruby/test' - end - - describe 'creates a directory in subdirectory with a slash' do - it_should_behave_like 'mkdir diff check', '/files/ruby/test2', 'files/ruby/test2' - end - - describe 'creates a directory in subdirectory with multiple slashes' do - it_should_behave_like 'mkdir diff check', '//files/ruby/test3', 'files/ruby/test3' - end - - describe 'handles relative paths' do - it_should_behave_like 'mkdir diff check', 'files/ruby/../test_relative', 'files/test_relative' - end - - describe 'creates nested directories' do - it_should_behave_like 'mkdir diff check', 'files/missing/test', 'files/missing/test' - end - - it 'does not attempt to create a directory with invalid relative path' do - expect{ repository.mkdir('../files/missing/test', commit_options) }.to raise_error('Invalid path') - end - - it 'does not attempt to overwrite a file' do - expect{ repository.mkdir('README.md', commit_options) }.to raise_error('Directory already exists as a file') - end - - it 'does not attempt to overwrite a directory' do - expect{ repository.mkdir('files', commit_options) }.to raise_error('Directory already exists') - end - end - describe "#ls_files" do let(:master_file_paths) { repository.ls_files("master") } let(:not_existed_branch) { repository.ls_files("not_existed_branch") } diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index a55bd4387e0..48f7754bed8 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -199,7 +199,9 @@ describe Gitlab::GitAccess, lib: true do def stub_git_hooks # Running the `pre-receive` hook is expensive, and not necessary for this test. - allow_any_instance_of(GitHooksService).to receive(:execute).and_yield + allow_any_instance_of(GitHooksService).to receive(:execute) do |service, &block| + block.call(service) + end end def merge_into_protected_branch @@ -207,13 +209,12 @@ describe Gitlab::GitAccess, lib: true do stub_git_hooks project.repository.add_branch(user, unprotected_branch, 'feature') target_branch = project.repository.lookup('feature') - source_branch = project.repository.commit_file( + source_branch = project.repository.create_file( user, FFaker::InternetSE.login_user_name, FFaker::HipsterIpsum.paragraph, message: FFaker::HipsterIpsum.sentence, - branch_name: unprotected_branch, - update: false) + branch_name: unprotected_branch) rugged = project.repository.rugged author = { email: "email@example.com", time: Time.now, name: "Example Git User" } @@ -232,11 +233,18 @@ describe Gitlab::GitAccess, lib: true do else project.team << [user, role] end + end + + permissions_matrix[role].each do |action, allowed| + context action do + subject { access.send(:check_push_access!, changes[action]) } - permissions_matrix[role].each do |action, allowed| - context action do - subject { access.send(:check_push_access!, changes[action]) } - it { expect(subject.allowed?).to allowed ? be_truthy : be_falsey } + it do + if allowed + expect { subject }.not_to raise_error + else + expect { subject }.to raise_error(Gitlab::GitAccess::UnauthorizedError) + end end end end @@ -301,7 +309,7 @@ describe Gitlab::GitAccess, lib: true do } } - [['feature', 'exact'], ['feat*', 'wildcard']].each do |protected_branch_name, protected_branch_type| + [%w(feature exact), ['feat*', 'wildcard']].each do |protected_branch_name, protected_branch_type| context do before { create(:protected_branch, name: protected_branch_name, project: project) } diff --git a/spec/lib/gitlab/git_spec.rb b/spec/lib/gitlab/git_spec.rb index 219198eff60..8eaf7aac264 100644 --- a/spec/lib/gitlab/git_spec.rb +++ b/spec/lib/gitlab/git_spec.rb @@ -19,7 +19,7 @@ describe Gitlab::Git, lib: true do describe 'committer_hash' do it "returns a hash containing the given email and name" do - committer_hash = Gitlab::Git::committer_hash(email: committer_email, name: committer_name) + committer_hash = Gitlab::Git.committer_hash(email: committer_email, name: committer_name) expect(committer_hash[:email]).to eq(committer_email) expect(committer_hash[:name]).to eq(committer_name) @@ -28,7 +28,7 @@ describe Gitlab::Git, lib: true do context 'when email is nil' do it "returns nil" do - committer_hash = Gitlab::Git::committer_hash(email: nil, name: committer_name) + committer_hash = Gitlab::Git.committer_hash(email: nil, name: committer_name) expect(committer_hash).to be_nil end @@ -36,7 +36,7 @@ describe Gitlab::Git, lib: true do context 'when name is nil' do it "returns nil" do - committer_hash = Gitlab::Git::committer_hash(email: committer_email, name: nil) + committer_hash = Gitlab::Git.committer_hash(email: committer_email, name: nil) expect(committer_hash).to be_nil end diff --git a/spec/lib/gitlab/import_export/attribute_configuration_spec.rb b/spec/lib/gitlab/import_export/attribute_configuration_spec.rb index ea65a5dfed1..e24d070706a 100644 --- a/spec/lib/gitlab/import_export/attribute_configuration_spec.rb +++ b/spec/lib/gitlab/import_export/attribute_configuration_spec.rb @@ -17,7 +17,7 @@ describe 'Import/Export attribute configuration', lib: true do # Remove duplicated or add missing models # - project is not part of the tree, so it has to be added manually. # - milestone, labels have both singular and plural versions in the tree, so remove the duplicates. - names.flatten.uniq - ['milestones', 'labels'] + ['project'] + names.flatten.uniq - %w(milestones labels) + ['project'] end let(:safe_attributes_file) { 'spec/lib/gitlab/import_export/safe_model_attributes.yml' } diff --git a/spec/lib/gitlab/import_export/avatar_saver_spec.rb b/spec/lib/gitlab/import_export/avatar_saver_spec.rb index d6ee94442cb..579a31ead58 100644 --- a/spec/lib/gitlab/import_export/avatar_saver_spec.rb +++ b/spec/lib/gitlab/import_export/avatar_saver_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::ImportExport::AvatarSaver, lib: true do let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: 'test') } - let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" } + let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } let(:project_with_avatar) { create(:empty_project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } let(:project) { create(:empty_project) } diff --git a/spec/lib/gitlab/import_export/file_importer_spec.rb b/spec/lib/gitlab/import_export/file_importer_spec.rb index a88ddd17aca..b88b9c18c15 100644 --- a/spec/lib/gitlab/import_export/file_importer_spec.rb +++ b/spec/lib/gitlab/import_export/file_importer_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::ImportExport::FileImporter, lib: true do let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: 'test') } - let(:export_path) { "#{Dir::tmpdir}/file_importer_spec" } + let(:export_path) { "#{Dir.tmpdir}/file_importer_spec" } let(:valid_file) { "#{shared.export_path}/valid.json" } let(:symlink_file) { "#{shared.export_path}/invalid.json" } let(:subfolder_symlink_file) { "#{shared.export_path}/subfolder/invalid.json" } diff --git a/spec/lib/gitlab/import_export/import_export_spec.rb b/spec/lib/gitlab/import_export/import_export_spec.rb index 20743811dab..f3fd0d82875 100644 --- a/spec/lib/gitlab/import_export/import_export_spec.rb +++ b/spec/lib/gitlab/import_export/import_export_spec.rb @@ -10,7 +10,7 @@ describe Gitlab::ImportExport, services: true do end it 'contains the namespace path' do - expect(described_class.export_filename(project: project)).to include(project.namespace.full_path) + expect(described_class.export_filename(project: project)).to include(project.namespace.full_path.tr('/', '_')) end it 'does not go over a certain length' do diff --git a/spec/lib/gitlab/import_export/model_configuration_spec.rb b/spec/lib/gitlab/import_export/model_configuration_spec.rb index 9b492d1b9c7..2ede5cdd2ad 100644 --- a/spec/lib/gitlab/import_export/model_configuration_spec.rb +++ b/spec/lib/gitlab/import_export/model_configuration_spec.rb @@ -14,7 +14,7 @@ describe 'Import/Export model configuration', lib: true do # - project is not part of the tree, so it has to be added manually. # - milestone, labels have both singular and plural versions in the tree, so remove the duplicates. # - User, Author... Models we do not care about for checking models - names.flatten.uniq - ['milestones', 'labels', 'user', 'author'] + ['project'] + names.flatten.uniq - %w(milestones labels user author) + ['project'] end let(:all_models_yml) { 'spec/lib/gitlab/import_export/all_models.yml' } diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb index 3628adefc0c..012c22ec5ad 100644 --- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do describe 'saves the project tree into a json object' do let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } let(:project_tree_saver) { described_class.new(project: project, current_user: user, shared: shared) } - let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" } + let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } let(:user) { create(:user) } let(:project) { setup_project } @@ -114,7 +114,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do it 'has project and group labels' do label_types = saved_project_json['issues'].first['label_links'].map { |link| link['label']['type'] } - expect(label_types).to match_array(['ProjectLabel', 'GroupLabel']) + expect(label_types).to match_array(%w(ProjectLabel GroupLabel)) end it 'has priorities associated to labels' do diff --git a/spec/lib/gitlab/import_export/repo_bundler_spec.rb b/spec/lib/gitlab/import_export/repo_bundler_spec.rb index d39ea60ff7f..a7f4e11271e 100644 --- a/spec/lib/gitlab/import_export/repo_bundler_spec.rb +++ b/spec/lib/gitlab/import_export/repo_bundler_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::ImportExport::RepoSaver, services: true do describe 'bundle a project Git repo' do let(:user) { create(:user) } let!(:project) { create(:empty_project, :public, name: 'searchable_project') } - let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" } + let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } let(:bundler) { described_class.new(project: project, shared: shared) } diff --git a/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb b/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb index 47d5d2fc150..071e5fac3f0 100644 --- a/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb +++ b/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::ImportExport::WikiRepoSaver, services: true do describe 'bundle a wiki Git repo' do let(:user) { create(:user) } let!(:project) { create(:empty_project, :public, name: 'searchable_project') } - let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" } + let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } let(:wiki_bundler) { described_class.new(project: project, shared: shared) } let!(:project_wiki) { ProjectWiki.new(project, user) } diff --git a/spec/lib/gitlab/import_sources_spec.rb b/spec/lib/gitlab/import_sources_spec.rb index 8cea38e9ff8..b3b5e5e7e33 100644 --- a/spec/lib/gitlab/import_sources_spec.rb +++ b/spec/lib/gitlab/import_sources_spec.rb @@ -22,16 +22,16 @@ describe Gitlab::ImportSources do describe '.values' do it 'returns an array' do expected = - [ - 'github', - 'bitbucket', - 'gitlab', - 'google_code', - 'fogbugz', - 'git', - 'gitlab_project', - 'gitea' - ] + %w( + github + bitbucket + gitlab + google_code + fogbugz + git + gitlab_project + gitea + ) expect(described_class.values).to eq(expected) end @@ -40,15 +40,15 @@ describe Gitlab::ImportSources do describe '.importer_names' do it 'returns an array of importer names' do expected = - [ - 'github', - 'bitbucket', - 'gitlab', - 'google_code', - 'fogbugz', - 'gitlab_project', - 'gitea' - ] + %w( + github + bitbucket + gitlab + google_code + fogbugz + gitlab_project + gitea + ) expect(described_class.importer_names).to eq(expected) end diff --git a/spec/lib/gitlab/kubernetes_spec.rb b/spec/lib/gitlab/kubernetes_spec.rb index c9bd52a3b8f..91f9d06b85a 100644 --- a/spec/lib/gitlab/kubernetes_spec.rb +++ b/spec/lib/gitlab/kubernetes_spec.rb @@ -9,7 +9,7 @@ describe Gitlab::Kubernetes do let(:pod_name) { 'pod1' } let(:container_name) { 'container1' } - subject(:result) { URI::parse(container_exec_url(api_url, namespace, pod_name, container_name)) } + subject(:result) { URI.parse(container_exec_url(api_url, namespace, pod_name, container_name)) } it { expect(result.scheme).to eq('wss') } it { expect(result.host).to eq('example.com') } diff --git a/spec/lib/gitlab/ldap/auth_hash_spec.rb b/spec/lib/gitlab/ldap/auth_hash_spec.rb index 69c49051156..7a2f774b948 100644 --- a/spec/lib/gitlab/ldap/auth_hash_spec.rb +++ b/spec/lib/gitlab/ldap/auth_hash_spec.rb @@ -44,7 +44,7 @@ describe Gitlab::LDAP::AuthHash, lib: true do context "with overridden attributes" do let(:attributes) do { - 'username' => ['mail', 'email'], + 'username' => %w(mail email), 'name' => 'fullName' } end diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb index 89790c9e1af..2f3bd4393b7 100644 --- a/spec/lib/gitlab/ldap/user_spec.rb +++ b/spec/lib/gitlab/ldap/user_spec.rb @@ -95,10 +95,10 @@ describe Gitlab::LDAP::User, lib: true do it 'maintains an identity per provider' do existing_user = create(:omniauth_user, email: 'john@example.com', provider: 'twitter') - expect(existing_user.identities.count).to eql(1) + expect(existing_user.identities.count).to be(1) ldap_user.save - expect(ldap_user.gl_user.identities.count).to eql(2) + expect(ldap_user.gl_user.identities.count).to be(2) # Expect that find_by provider only returns a single instance of an identity and not an Enumerable expect(ldap_user.gl_user.identities.find_by(provider: 'twitter')).to be_instance_of Identity diff --git a/spec/lib/gitlab/metrics/instrumentation_spec.rb b/spec/lib/gitlab/metrics/instrumentation_spec.rb index d88bcae41fb..a986cb520fb 100644 --- a/spec/lib/gitlab/metrics/instrumentation_spec.rb +++ b/spec/lib/gitlab/metrics/instrumentation_spec.rb @@ -197,11 +197,13 @@ describe Gitlab::Metrics::Instrumentation do @child1 = Class.new(@dummy) do def self.child1_foo; end + def child1_bar; end end @child2 = Class.new(@child1) do def self.child2_foo; end + def child2_bar; end end end diff --git a/spec/lib/gitlab/metrics/method_call_spec.rb b/spec/lib/gitlab/metrics/method_call_spec.rb index 8d05081eecb..a247f03b2da 100644 --- a/spec/lib/gitlab/metrics/method_call_spec.rb +++ b/spec/lib/gitlab/metrics/method_call_spec.rb @@ -23,7 +23,7 @@ describe Gitlab::Metrics::MethodCall do expect(metric.values[:duration]).to be_a_kind_of(Numeric) expect(metric.values[:cpu_duration]).to be_a_kind_of(Numeric) - expect(metric.values[:call_count]).to an_instance_of(Fixnum) + expect(metric.values[:call_count]).to be_an(Integer) expect(metric.tags).to eq({ method: 'Foo#bar' }) end diff --git a/spec/lib/gitlab/metrics/metric_spec.rb b/spec/lib/gitlab/metrics/metric_spec.rb index f26fca52c50..d240b8a01fd 100644 --- a/spec/lib/gitlab/metrics/metric_spec.rb +++ b/spec/lib/gitlab/metrics/metric_spec.rb @@ -62,7 +62,7 @@ describe Gitlab::Metrics::Metric do end it 'includes the timestamp' do - expect(hash[:timestamp]).to be_an_instance_of(Fixnum) + expect(hash[:timestamp]).to be_an(Integer) end end end diff --git a/spec/lib/gitlab/metrics/system_spec.rb b/spec/lib/gitlab/metrics/system_spec.rb index 9e2ea89a712..4d94d8705fb 100644 --- a/spec/lib/gitlab/metrics/system_spec.rb +++ b/spec/lib/gitlab/metrics/system_spec.rb @@ -29,19 +29,19 @@ describe Gitlab::Metrics::System do describe '.cpu_time' do it 'returns a Fixnum' do - expect(described_class.cpu_time).to be_an_instance_of(Fixnum) + expect(described_class.cpu_time).to be_an(Integer) end end describe '.real_time' do it 'returns a Fixnum' do - expect(described_class.real_time).to be_an_instance_of(Fixnum) + expect(described_class.real_time).to be_an(Integer) end end describe '.monotonic_time' do it 'returns a Fixnum' do - expect(described_class.monotonic_time).to be_an_instance_of(Fixnum) + expect(described_class.monotonic_time).to be_an(Integer) end end end diff --git a/spec/lib/gitlab/metrics/transaction_spec.rb b/spec/lib/gitlab/metrics/transaction_spec.rb index 3887c04c832..0c5a6246d85 100644 --- a/spec/lib/gitlab/metrics/transaction_spec.rb +++ b/spec/lib/gitlab/metrics/transaction_spec.rb @@ -134,7 +134,7 @@ describe Gitlab::Metrics::Transaction do series: 'rails_transactions', tags: { action: 'Foo#bar' }, values: { duration: 0.0, allocated_memory: a_kind_of(Numeric) }, - timestamp: an_instance_of(Fixnum) + timestamp: a_kind_of(Integer) } expect(Gitlab::Metrics).to receive(:submit_metrics). @@ -151,7 +151,7 @@ describe Gitlab::Metrics::Transaction do series: 'events', tags: { event: :meow }, values: { count: 1 }, - timestamp: an_instance_of(Fixnum) + timestamp: a_kind_of(Integer) } expect(Gitlab::Metrics).to receive(:submit_metrics). diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb index fc9e1cb430a..6c84a4c8b73 100644 --- a/spec/lib/gitlab/o_auth/user_spec.rb +++ b/spec/lib/gitlab/o_auth/user_spec.rb @@ -148,12 +148,14 @@ describe Gitlab::OAuth::User, lib: true do expect(gl_user).to be_valid expect(gl_user.username).to eql uid expect(gl_user.email).to eql 'johndoe@example.com' - expect(gl_user.identities.length).to eql 2 + expect(gl_user.identities.length).to be 2 identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array( - [ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + [ + { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, { provider: 'twitter', extern_uid: uid } - ]) + ] + ) end end @@ -167,12 +169,14 @@ describe Gitlab::OAuth::User, lib: true do expect(gl_user).to be_valid expect(gl_user.username).to eql 'john' expect(gl_user.email).to eql 'john@example.com' - expect(gl_user.identities.length).to eql 2 + expect(gl_user.identities.length).to be 2 identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array( - [ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + [ + { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, { provider: 'twitter', extern_uid: uid } - ]) + ] + ) end end diff --git a/spec/lib/gitlab/optimistic_locking_spec.rb b/spec/lib/gitlab/optimistic_locking_spec.rb index 498dc514c8c..acce2be93f2 100644 --- a/spec/lib/gitlab/optimistic_locking_spec.rb +++ b/spec/lib/gitlab/optimistic_locking_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' describe Gitlab::OptimisticLocking, lib: true do - describe '#retry_lock' do - let!(:pipeline) { create(:ci_pipeline) } - let!(:pipeline2) { Ci::Pipeline.find(pipeline.id) } + let!(:pipeline) { create(:ci_pipeline) } + let!(:pipeline2) { Ci::Pipeline.find(pipeline.id) } + describe '#retry_lock' do it 'does not reload object if state changes' do expect(pipeline).not_to receive(:reload) expect(pipeline).to receive(:succeed).and_call_original @@ -36,4 +36,17 @@ describe Gitlab::OptimisticLocking, lib: true do end.to raise_error(ActiveRecord::StaleObjectError) end end + + describe '#retry_optimistic_lock' do + context 'when locking module is mixed in' do + let(:unlockable) do + Class.new.include(described_class).new + end + + it 'is an alias for retry_lock' do + expect(unlockable.method(:retry_optimistic_lock)) + .to eq unlockable.method(:retry_lock) + end + end + end end diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb index 089ec4e2737..ba45e2d758c 100644 --- a/spec/lib/gitlab/regex_spec.rb +++ b/spec/lib/gitlab/regex_spec.rb @@ -51,8 +51,8 @@ describe Gitlab::Regex, lib: true do it { is_expected.not_to match('foo-') } end - describe 'NAMESPACE_REF_REGEX_STR' do - subject { %r{\A#{Gitlab::Regex::NAMESPACE_REF_REGEX_STR}\z} } + describe 'FULL_NAMESPACE_REGEX_STR' do + subject { %r{\A#{Gitlab::Regex::FULL_NAMESPACE_REGEX_STR}\z} } it { is_expected.to match('gitlab.org') } it { is_expected.to match('gitlab.org/gitlab-git') } diff --git a/spec/lib/gitlab/saml/user_spec.rb b/spec/lib/gitlab/saml/user_spec.rb index 02c139f1a0d..4f6ef3c10fc 100644 --- a/spec/lib/gitlab/saml/user_spec.rb +++ b/spec/lib/gitlab/saml/user_spec.rb @@ -155,11 +155,10 @@ describe Gitlab::Saml::User, lib: true do expect(gl_user).to be_valid expect(gl_user.username).to eql uid expect(gl_user.email).to eql 'john@mail.com' - expect(gl_user.identities.length).to eql 2 + expect(gl_user.identities.length).to be 2 identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } - expect(identities_as_hash).to match_array([ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, - { provider: 'saml', extern_uid: uid } - ]) + expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + { provider: 'saml', extern_uid: uid }]) end end @@ -178,11 +177,10 @@ describe Gitlab::Saml::User, lib: true do expect(gl_user).to be_valid expect(gl_user.username).to eql 'john' expect(gl_user.email).to eql 'john@mail.com' - expect(gl_user.identities.length).to eql 2 + expect(gl_user.identities.length).to be 2 identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } - expect(identities_as_hash).to match_array([ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, - { provider: 'saml', extern_uid: uid } - ]) + expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + { provider: 'saml', extern_uid: uid }]) end it 'saves successfully on subsequent tries, when both identities are present' do @@ -204,11 +202,10 @@ describe Gitlab::Saml::User, lib: true do local_gl_user = local_saml_user.gl_user expect(local_gl_user).to be_valid - expect(local_gl_user.identities.length).to eql 2 + expect(local_gl_user.identities.length).to be 2 identities_as_hash = local_gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } - expect(identities_as_hash).to match_array([ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, - { provider: 'saml', extern_uid: 'uid=user1,ou=People,dc=example' } - ]) + expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + { provider: 'saml', extern_uid: 'uid=user1,ou=People,dc=example' }]) end end end diff --git a/spec/lib/gitlab/serializer/ci/variables_spec.rb b/spec/lib/gitlab/serializer/ci/variables_spec.rb index b810c68ea03..c4b7fda5dbb 100644 --- a/spec/lib/gitlab/serializer/ci/variables_spec.rb +++ b/spec/lib/gitlab/serializer/ci/variables_spec.rb @@ -13,6 +13,7 @@ describe Gitlab::Serializer::Ci::Variables do it 'converts keys into strings' do is_expected.to eq([ { key: 'key', value: 'value', public: true }, - { key: 'wee', value: 1, public: false }]) + { key: 'wee', value: 1, public: false } + ]) end end diff --git a/spec/lib/gitlab/template/issue_template_spec.rb b/spec/lib/gitlab/template/issue_template_spec.rb index 1335a2b8f35..9213ced7b19 100644 --- a/spec/lib/gitlab/template/issue_template_spec.rb +++ b/spec/lib/gitlab/template/issue_template_spec.rb @@ -11,7 +11,8 @@ describe Gitlab::Template::IssueTemplate do create_template: { user: user, access: Gitlab::Access::MASTER, - path: 'issue_templates' }) + path: 'issue_templates' + }) end describe '.all' do diff --git a/spec/lib/gitlab/template/merge_request_template_spec.rb b/spec/lib/gitlab/template/merge_request_template_spec.rb index 320b870309a..77dd3079e22 100644 --- a/spec/lib/gitlab/template/merge_request_template_spec.rb +++ b/spec/lib/gitlab/template/merge_request_template_spec.rb @@ -11,7 +11,8 @@ describe Gitlab::Template::MergeRequestTemplate do create_template: { user: user, access: Gitlab::Access::MASTER, - path: 'merge_request_templates' }) + path: 'merge_request_templates' + }) end describe '.all' do diff --git a/spec/lib/gitlab/upgrader_spec.rb b/spec/lib/gitlab/upgrader_spec.rb index edadab043d7..fcfd8d58b70 100644 --- a/spec/lib/gitlab/upgrader_spec.rb +++ b/spec/lib/gitlab/upgrader_spec.rb @@ -32,7 +32,8 @@ describe Gitlab::Upgrader, lib: true do '43af3e65a486a9237f29f56d96c3b3da59c24ae0 refs/tags/v7.11.2', 'dac18e7728013a77410e926a1e64225703754a2d refs/tags/v7.11.2^{}', '0bf21fd4b46c980c26fd8c90a14b86a4d90cc950 refs/tags/v7.9.4', - 'b10de29edbaff7219547dc506cb1468ee35065c3 refs/tags/v7.9.4^{}']) + 'b10de29edbaff7219547dc506cb1468ee35065c3 refs/tags/v7.9.4^{}' + ]) expect(upgrader.latest_version_raw).to eq("v7.11.2") end end diff --git a/spec/lib/gitlab/utils_spec.rb b/spec/lib/gitlab/utils_spec.rb index d5d87310874..56772409989 100644 --- a/spec/lib/gitlab/utils_spec.rb +++ b/spec/lib/gitlab/utils_spec.rb @@ -1,7 +1,5 @@ describe Gitlab::Utils, lib: true do - def to_boolean(value) - described_class.to_boolean(value) - end + delegate :to_boolean, to: :described_class describe '.to_boolean' do it 'accepts booleans' do diff --git a/spec/lib/mattermost/command_spec.rb b/spec/lib/mattermost/command_spec.rb index 5ccf1100898..4b5938edeb9 100644 --- a/spec/lib/mattermost/command_spec.rb +++ b/spec/lib/mattermost/command_spec.rb @@ -13,8 +13,7 @@ describe Mattermost::Command do describe '#create' do let(:params) do { team_id: 'abc', - trigger: 'gitlab' - } + trigger: 'gitlab' } end subject { described_class.new(nil).create(params) } @@ -24,7 +23,8 @@ describe Mattermost::Command do stub_request(:post, 'http://mattermost.example.com/api/v3/teams/abc/commands/create'). with(body: { team_id: 'abc', - trigger: 'gitlab' }.to_json). + trigger: 'gitlab' + }.to_json). to_return( status: 200, headers: { 'Content-Type' => 'application/json' }, diff --git a/spec/lib/mattermost/team_spec.rb b/spec/lib/mattermost/team_spec.rb index 2d14be6bcc2..6a9c7cebfff 100644 --- a/spec/lib/mattermost/team_spec.rb +++ b/spec/lib/mattermost/team_spec.rb @@ -14,18 +14,19 @@ describe Mattermost::Team do context 'for valid request' do let(:response) do [{ - "id" => "xiyro8huptfhdndadpz8r3wnbo", - "create_at" => 1482174222155, - "update_at" => 1482174222155, - "delete_at" => 0, - "display_name" => "chatops", - "name" => "chatops", - "email" => "admin@example.com", - "type" => "O", - "company_name" => "", - "allowed_domains" => "", - "invite_id" => "o4utakb9jtb7imctdfzbf9r5ro", - "allow_open_invite" => false }] + "id" => "xiyro8huptfhdndadpz8r3wnbo", + "create_at" => 1482174222155, + "update_at" => 1482174222155, + "delete_at" => 0, + "display_name" => "chatops", + "name" => "chatops", + "email" => "admin@example.com", + "type" => "O", + "company_name" => "", + "allowed_domains" => "", + "invite_id" => "o4utakb9jtb7imctdfzbf9r5ro", + "allow_open_invite" => false + }] end before do diff --git a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb index 6a93deb5412..b6d678bac18 100644 --- a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb +++ b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb @@ -62,7 +62,7 @@ describe MigrateProcessCommitWorkerJobs do end def pop_job - JSON.load(Sidekiq.redis { |r| r.lpop('queue:process_commit') }) + JSON.parse(Sidekiq.redis { |r| r.lpop('queue:process_commit') }) end before do @@ -198,7 +198,7 @@ describe MigrateProcessCommitWorkerJobs do let(:job) do migration.down - JSON.load(Sidekiq.redis { |r| r.lpop('queue:process_commit') }) + JSON.parse(Sidekiq.redis { |r| r.lpop('queue:process_commit') }) end it 'includes the project ID' do diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb index c4486a32082..4e71597521d 100644 --- a/spec/models/abuse_report_spec.rb +++ b/spec/models/abuse_report_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' RSpec.describe AbuseReport, type: :model do subject { create(:abuse_report) } - let(:user) { create(:user) } + let(:user) { create(:admin) } it { expect(subject).to be_valid } diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index b950fcdd81a..01ca1584ed2 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -29,6 +29,40 @@ describe ApplicationSetting, models: true do it { is_expected.not_to allow_value(['test']).for(:disabled_oauth_sign_in_sources) } end + describe 'default_artifacts_expire_in' do + it 'sets an error if it cannot parse' do + setting.update(default_artifacts_expire_in: 'a') + + expect_invalid + end + + it 'sets an error if it is blank' do + setting.update(default_artifacts_expire_in: ' ') + + expect_invalid + end + + it 'sets the value if it is valid' do + setting.update(default_artifacts_expire_in: '30 days') + + expect(setting).to be_valid + expect(setting.default_artifacts_expire_in).to eq('30 days') + end + + it 'sets the value if it is 0' do + setting.update(default_artifacts_expire_in: '0') + + expect(setting).to be_valid + expect(setting.default_artifacts_expire_in).to eq('0') + end + + def expect_invalid + expect(setting).to be_invalid + expect(setting.errors.messages) + .to have_key(:default_artifacts_expire_in) + end + end + it { is_expected.to validate_presence_of(:max_attachment_size) } it do @@ -62,9 +96,9 @@ describe ApplicationSetting, models: true do describe 'inclusion' do it { is_expected.to allow_value('custom1').for(:repository_storages) } - it { is_expected.to allow_value(['custom2', 'custom3']).for(:repository_storages) } + it { is_expected.to allow_value(%w(custom2 custom3)).for(:repository_storages) } it { is_expected.not_to allow_value('alternative').for(:repository_storages) } - it { is_expected.not_to allow_value(['alternative', 'custom1']).for(:repository_storages) } + it { is_expected.not_to allow_value(%w(alternative custom1)).for(:repository_storages) } end describe 'presence' do @@ -83,7 +117,7 @@ describe ApplicationSetting, models: true do describe '#repository_storage' do it 'returns the first storage' do - setting.repository_storages = ['good', 'bad'] + setting.repository_storages = %w(good bad) expect(setting.repository_storage).to eq('good') end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 2dfca8bcfce..b963ca4e542 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -162,11 +162,17 @@ describe Ci::Build, :models do is_expected.to be_nil end - it 'when resseting value' do + it 'when resetting value' do build.artifacts_expire_in = nil is_expected.to be_nil end + + it 'when setting to 0' do + build.artifacts_expire_in = '0' + + is_expected.to be_nil + end end describe '#commit' do @@ -1239,8 +1245,8 @@ describe Ci::Build, :models do { key: 'CI_SERVER_REVISION', value: Gitlab::REVISION, public: true }, { key: 'CI_PROJECT_ID', value: project.id.to_s, public: true }, { key: 'CI_PROJECT_NAME', value: project.path, public: true }, - { key: 'CI_PROJECT_PATH', value: project.path_with_namespace, public: true }, - { key: 'CI_PROJECT_NAMESPACE', value: project.namespace.path, public: true }, + { key: 'CI_PROJECT_PATH', value: project.full_path, public: true }, + { key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true }, { key: 'CI_PROJECT_URL', value: project.web_url, public: true }, { key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true } ] @@ -1262,8 +1268,8 @@ describe Ci::Build, :models do context 'when build has user' do let(:user_variables) do - [ { key: 'GITLAB_USER_ID', value: user.id.to_s, public: true }, - { key: 'GITLAB_USER_EMAIL', value: user.email, public: true } ] + [{ key: 'GITLAB_USER_ID', value: user.id.to_s, public: true }, + { key: 'GITLAB_USER_EMAIL', value: user.email, public: true }] end before do @@ -1420,7 +1426,7 @@ describe Ci::Build, :models do end context 'when runner is assigned to build' do - let(:runner) { create(:ci_runner, description: 'description', tag_list: ['docker', 'linux']) } + let(:runner) { create(:ci_runner, description: 'description', tag_list: %w(docker linux)) } before do build.update(runner: runner) diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 10c2bfbb400..c2fc8c02bb3 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -168,9 +168,9 @@ describe Ci::Pipeline, models: true do end it 'returns list of stages with correct statuses' do - expect(statuses).to eq([['build', 'failed'], - ['test', 'success'], - ['deploy', 'running']]) + expect(statuses).to eq([%w(build failed), + %w(test success), + %w(deploy running)]) end context 'when commit status is retried' do @@ -183,9 +183,9 @@ describe Ci::Pipeline, models: true do end it 'ignores the previous state' do - expect(statuses).to eq([['build', 'success'], - ['test', 'success'], - ['deploy', 'running']]) + expect(statuses).to eq([%w(build success), + %w(test success), + %w(deploy running)]) end end end @@ -199,7 +199,7 @@ describe Ci::Pipeline, models: true do describe '#stages_name' do it 'returns a valid names of stages' do - expect(pipeline.stages_name).to eq(['build', 'test', 'deploy']) + expect(pipeline.stages_name).to eq(%w(build test deploy)) end end end @@ -767,7 +767,7 @@ describe Ci::Pipeline, models: true do end it 'cancels created builds' do - expect(latest_status).to eq ['canceled', 'canceled'] + expect(latest_status).to eq %w(canceled canceled) end end end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index f8513ac8b1c..76ce558eea0 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -113,7 +113,7 @@ describe Ci::Runner, models: true do context 'when runner has tags' do before do - runner.tag_list = ['bb', 'cc'] + runner.tag_list = %w(bb cc) end shared_examples 'tagged build picker' do @@ -169,7 +169,7 @@ describe Ci::Runner, models: true do context 'when having runner tags' do before do - runner.tag_list = ['bb', 'cc'] + runner.tag_list = %w(bb cc) end it 'cannot handle it for builds without matching tags' do @@ -189,7 +189,7 @@ describe Ci::Runner, models: true do context 'when having runner tags' do before do - runner.tag_list = ['bb', 'cc'] + runner.tag_list = %w(bb cc) build.tag_list = ['bb'] end @@ -212,7 +212,7 @@ describe Ci::Runner, models: true do context 'when having runner tags' do before do - runner.tag_list = ['bb', 'cc'] + runner.tag_list = %w(bb cc) build.tag_list = ['bb'] end diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb index 2e3702f7520..6151d53cd91 100644 --- a/spec/models/concerns/cache_markdown_field_spec.rb +++ b/spec/models/concerns/cache_markdown_field_spec.rb @@ -1,7 +1,8 @@ require 'spec_helper' describe CacheMarkdownField do - CacheMarkdownField::CACHING_CLASSES << "ThingWithMarkdownFields" + caching_classes = CacheMarkdownField::CACHING_CLASSES + CacheMarkdownField::CACHING_CLASSES = ["ThingWithMarkdownFields"].freeze # The minimum necessary ActiveModel to test this concern class ThingWithMarkdownFields @@ -54,7 +55,7 @@ describe CacheMarkdownField do end end - CacheMarkdownField::CACHING_CLASSES.delete("ThingWithMarkdownFields") + CacheMarkdownField::CACHING_CLASSES = caching_classes def thing_subclass(new_attr) Class.new(ThingWithMarkdownFields) { add_attr(new_attr) } diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb index e008ec28fa4..677e60e1282 100644 --- a/spec/models/concerns/routable_spec.rb +++ b/spec/models/concerns/routable_spec.rb @@ -86,7 +86,7 @@ describe Group, 'Routable' do let(:nested_group) { create(:group, parent: group) } it { expect(group.full_path).to eq(group.path) } - it { expect(nested_group.full_path).to eq("#{group.path}/#{nested_group.path}") } + it { expect(nested_group.full_path).to eq("#{group.full_path}/#{nested_group.path}") } end describe '#full_name' do @@ -102,7 +102,7 @@ describe Project, 'Routable' do describe '#full_path' do let(:project) { build_stubbed(:empty_project) } - it { expect(project.full_path).to eq "#{project.namespace.path}/#{project.path}" } + it { expect(project.full_path).to eq "#{project.namespace.full_path}/#{project.path}" } end describe '#full_name' do diff --git a/spec/models/concerns/uniquify_spec.rb b/spec/models/concerns/uniquify_spec.rb new file mode 100644 index 00000000000..83187d732e4 --- /dev/null +++ b/spec/models/concerns/uniquify_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe Uniquify, models: true do + let(:uniquify) { described_class.new } + + describe "#string" do + it 'returns the given string if it does not exist' do + result = uniquify.string('test_string') { |s| false } + + expect(result).to eq('test_string') + end + + it 'returns the given string with a counter attached if the string exists' do + result = uniquify.string('test_string') { |s| s == 'test_string' } + + expect(result).to eq('test_string1') + end + + it 'increments the counter for each candidate string that also exists' do + result = uniquify.string('test_string') { |s| s == 'test_string' || s == 'test_string1' } + + expect(result).to eq('test_string2') + end + + it 'allows passing in a base function that defines the location of the counter' do + result = uniquify.string(-> (counter) { "test_#{counter}_string" }) do |s| + s == 'test__string' + end + + expect(result).to eq('test_1_string') + end + end +end diff --git a/spec/models/cycle_analytics/production_spec.rb b/spec/models/cycle_analytics/production_spec.rb index b9fe492fe2c..e6a826a9418 100644 --- a/spec/models/cycle_analytics/production_spec.rb +++ b/spec/models/cycle_analytics/production_spec.rb @@ -21,13 +21,12 @@ describe 'CycleAnalytics#production', feature: true do ["production deploy happens after merge request is merged (along with other changes)", lambda do |context, data| # Make other changes on master - sha = context.project.repository.commit_file( + sha = context.project.repository.create_file( context.user, context.random_git_name, 'content', message: 'commit message', - branch_name: 'master', - update: false) + branch_name: 'master') context.project.repository.commit(sha) context.deploy_master diff --git a/spec/models/cycle_analytics/staging_spec.rb b/spec/models/cycle_analytics/staging_spec.rb index 9a024d533a1..3a02ed81adb 100644 --- a/spec/models/cycle_analytics/staging_spec.rb +++ b/spec/models/cycle_analytics/staging_spec.rb @@ -18,7 +18,7 @@ describe 'CycleAnalytics#staging', feature: true do start_time_conditions: [["merge request that closes issue is merged", -> (context, data) do context.merge_merge_requests_closing_issue(data[:issue]) - end ]], + end]], end_time_conditions: [["merge request that closes issue is deployed to production", -> (context, data) do context.deploy_master @@ -26,13 +26,12 @@ describe 'CycleAnalytics#staging', feature: true do ["production deploy happens after merge request is merged (along with other changes)", lambda do |context, data| # Make other changes on master - sha = context.project.repository.commit_file( + sha = context.project.repository.create_file( context.user, context.random_git_name, 'content', message: 'commit message', - branch_name: 'master', - update: false) + branch_name: 'master') context.project.repository.commit(sha) context.deploy_master diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index f0ed0c679d5..dce18f008f8 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -76,7 +76,8 @@ describe Environment, models: true do end describe '#update_merge_request_metrics?' do - { 'production' => true, + { + 'production' => true, 'production/eu' => true, 'production/www.gitlab.com' => true, 'productioneu' => false, @@ -311,7 +312,7 @@ describe Environment, models: true do end describe '#generate_slug' do - SUFFIX = "-[a-z0-9]{6}" + SUFFIX = "-[a-z0-9]{6}".freeze { "staging-12345678901234567" => "staging-123456789" + SUFFIX, "9-staging-123456789012345" => "env-9-staging-123" + SUFFIX, diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb index e4be0aba7a6..87ea2e70680 100644 --- a/spec/models/members/project_member_spec.rb +++ b/spec/models/members/project_member_spec.rb @@ -89,8 +89,8 @@ describe ProjectMember, models: true do @user_1 = create :user @user_2 = create :user - @project_1.team << [ @user_1, :developer ] - @project_2.team << [ @user_2, :reporter ] + @project_1.team << [@user_1, :developer] + @project_2.team << [@user_2, :reporter] @status = @project_2.team.import(@project_1) end @@ -137,8 +137,8 @@ describe ProjectMember, models: true do @user_1 = create :user @user_2 = create :user - @project_1.team << [ @user_1, :developer] - @project_2.team << [ @user_2, :reporter] + @project_1.team << [@user_1, :developer] + @project_2.team << [@user_2, :reporter] ProjectMember.truncate_teams([@project_1.id, @project_2.id]) end diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb index 6d599e148a2..0a10ee01506 100644 --- a/spec/models/merge_request_diff_spec.rb +++ b/spec/models/merge_request_diff_spec.rb @@ -109,7 +109,7 @@ describe MergeRequestDiff, models: true do { id: 'sha2' } ] - expect(subject.commits_sha).to eq(['sha1', 'sha2']) + expect(subject.commits_sha).to eq(%w(sha1 sha2)) end end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index a01741a9971..fa1b0396bcf 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -209,6 +209,50 @@ describe MergeRequest, models: true do end end + describe '#diff_size' do + let(:merge_request) do + build(:merge_request, source_branch: 'expand-collapse-files', target_branch: 'master') + end + + context 'when there are MR diffs' do + before do + merge_request.save + end + + it 'returns the correct count' do + expect(merge_request.diff_size).to eq(105) + end + + it 'does not perform highlighting' do + expect(Gitlab::Diff::Highlight).not_to receive(:new) + + merge_request.diff_size + end + end + + context 'when there are no MR diffs' do + before do + merge_request.compare = CompareService.new( + merge_request.source_project, + merge_request.source_branch + ).execute( + merge_request.target_project, + merge_request.target_branch + ) + end + + it 'returns the correct count' do + expect(merge_request.diff_size).to eq(105) + end + + it 'does not perform highlighting' do + expect(Gitlab::Diff::Highlight).not_to receive(:new) + + merge_request.diff_size + end + end + end + describe "#related_notes" do let!(:merge_request) { create(:merge_request) } diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 35d932f1c64..3f9c4289de9 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -36,7 +36,7 @@ describe Namespace, models: true do end describe '#to_param' do - it { expect(namespace.to_param).to eq(namespace.path) } + it { expect(namespace.to_param).to eq(namespace.full_path) } end describe '#human_name' do @@ -151,7 +151,7 @@ describe Namespace, models: true do describe :rm_dir do let!(:project) { create(:empty_project, namespace: namespace) } - let!(:path) { File.join(Gitlab.config.repositories.storages.default, namespace.path) } + let!(:path) { File.join(Gitlab.config.repositories.storages.default, namespace.full_path) } it "removes its dirs when deleted" do namespace.destroy diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb index 497a626a418..4014d6129ee 100644 --- a/spec/models/project_services/bamboo_service_spec.rb +++ b/spec/models/project_services/bamboo_service_spec.rb @@ -181,7 +181,7 @@ describe BambooService, models: true, caching: true do end it 'sets commit status to "pending" when response has no results' do - stub_request(body: %Q({"results":{"results":{"size":"0"}}})) + stub_request(body: %q({"results":{"results":{"size":"0"}}})) is_expected.to eq('pending') end diff --git a/spec/models/project_services/buildkite_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb index dbd23ff5491..05b602d8106 100644 --- a/spec/models/project_services/buildkite_service_spec.rb +++ b/spec/models/project_services/buildkite_service_spec.rb @@ -92,7 +92,7 @@ describe BuildkiteService, models: true, caching: true do end it 'passes through build status untouched when status is 200' do - stub_request(body: %Q({"status":"Great Success"})) + stub_request(body: %q({"status":"Great Success"})) is_expected.to eq('Great Success') end @@ -101,7 +101,7 @@ describe BuildkiteService, models: true, caching: true do end def stub_request(status: 200, body: nil) - body ||= %Q({"status":"success"}) + body ||= %q({"status":"success"}) buildkite_full_url = 'https://gitlab.buildkite.com/status/secret-sauce-status-token.json?commit=123' WebMock.stub_request(:get, buildkite_full_url).to_return( diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb index f9307d6de7b..044737c6026 100644 --- a/spec/models/project_services/drone_ci_service_spec.rb +++ b/spec/models/project_services/drone_ci_service_spec.rb @@ -28,7 +28,7 @@ describe DroneCiService, models: true, caching: true do shared_context :drone_ci_service do let(:drone) { DroneCiService.new } let(:project) { create(:project, :repository, name: 'project') } - let(:path) { "#{project.namespace.path}/#{project.path}" } + let(:path) { project.full_path } let(:drone_url) { 'http://drone.example.com' } let(:sha) { '2ab7834c' } let(:branch) { 'dev' } @@ -50,7 +50,7 @@ describe DroneCiService, models: true, caching: true do end def stub_request(status: 200, body: nil) - body ||= %Q({"status":"success"}) + body ||= %q({"status":"success"}) WebMock.stub_request(:get, commit_status_path).to_return( status: status, @@ -95,12 +95,12 @@ describe DroneCiService, models: true, caching: true do is_expected.to eq(:error) end - { "killed" => :canceled, + { + "killed" => :canceled, "failure" => :failed, "error" => :failed, - "success" => "success", + "success" => "success" }.each do |drone_status, our_status| - it "sets commit status to #{our_status.inspect} when returned status is #{drone_status.inspect}" do stub_request(body: %Q({"status":"#{drone_status}"})) diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/project_services/irker_service_spec.rb index b9fb6f3f6f4..d5a16226d9d 100644 --- a/spec/models/project_services/irker_service_spec.rb +++ b/spec/models/project_services/irker_service_spec.rb @@ -59,8 +59,8 @@ describe IrkerService, models: true do conn = @irker_server.accept conn.readlines.each do |line| - msg = JSON.load(line.chomp("\n")) - expect(msg.keys).to match_array(['to', 'privmsg']) + msg = JSON.parse(line.chomp("\n")) + expect(msg.keys).to match_array(%w(to privmsg)) expect(msg['to']).to match_array(["irc://chat.freenode.net/#commits", "irc://test.net/#test"]) end diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb index 9052479d35e..24356447fed 100644 --- a/spec/models/project_services/kubernetes_service_spec.rb +++ b/spec/models/project_services/kubernetes_service_spec.rb @@ -171,7 +171,7 @@ describe KubernetesService, models: true, caching: true do context 'with invalid pods' do it 'returns no terminals' do - stub_reactive_cache(service, pods: [ { "bad" => "pod" } ]) + stub_reactive_cache(service, pods: [{ "bad" => "pod" }]) is_expected.to be_empty end @@ -184,7 +184,7 @@ describe KubernetesService, models: true, caching: true do before do stub_reactive_cache( service, - pods: [ pod, pod, kube_pod(app: "should-be-filtered-out") ] + pods: [pod, pod, kube_pod(app: "should-be-filtered-out")] ) end diff --git a/spec/models/project_services/mattermost_slash_commands_service_spec.rb b/spec/models/project_services/mattermost_slash_commands_service_spec.rb index 98f3d420c8a..598ff232c82 100644 --- a/spec/models/project_services/mattermost_slash_commands_service_spec.rb +++ b/spec/models/project_services/mattermost_slash_commands_service_spec.rb @@ -36,7 +36,8 @@ describe MattermostSlashCommandsService, :models do description: "Perform common operations on: #{project.name_with_namespace}", display_name: "GitLab / #{project.name_with_namespace}", method: 'P', - username: 'GitLab' }.to_json). + username: 'GitLab' + }.to_json). to_return( status: 200, headers: { 'Content-Type' => 'application/json' }, diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb index a1edd083aa1..77b18e1c7d0 100644 --- a/spec/models/project_services/teamcity_service_spec.rb +++ b/spec/models/project_services/teamcity_service_spec.rb @@ -143,7 +143,7 @@ describe TeamcityService, models: true, caching: true do end it 'returns a build URL when teamcity_url has no trailing slash' do - stub_request(body: %Q({"build":{"id":"666"}})) + stub_request(body: %q({"build":{"id":"666"}})) is_expected.to eq('http://gitlab.com/teamcity/viewLog.html?buildId=666&buildTypeId=foo') end @@ -152,7 +152,7 @@ describe TeamcityService, models: true, caching: true do let(:teamcity_url) { 'http://gitlab.com/teamcity/' } it 'returns a build URL' do - stub_request(body: %Q({"build":{"id":"666"}})) + stub_request(body: %q({"build":{"id":"666"}})) is_expected.to eq('http://gitlab.com/teamcity/viewLog.html?buildId=666&buildTypeId=foo') end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index b0087a9e15d..ee4f4092062 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -402,7 +402,7 @@ describe Project, models: true do let(:project) { create(:empty_project, path: "somewhere") } it 'returns the full web URL for this repo' do - expect(project.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.namespace.path}/somewhere") + expect(project.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.namespace.full_path}/somewhere") end end @@ -803,7 +803,7 @@ describe Project, models: true do end let(:avatar_path) do - "/#{project.namespace.name}/#{project.path}/avatar" + "/#{project.full_path}/avatar" end it { should eq "http://#{Gitlab.config.gitlab.host}#{avatar_path}" } @@ -1148,16 +1148,14 @@ describe Project, models: true do end it 'renames a repository' do - ns = project.namespace_dir - expect(gitlab_shell).to receive(:mv_repository). ordered. - with(project.repository_storage_path, "#{ns}/foo", "#{ns}/#{project.path}"). + with(project.repository_storage_path, "#{project.namespace.full_path}/foo", "#{project.full_path}"). and_return(true) expect(gitlab_shell).to receive(:mv_repository). ordered. - with(project.repository_storage_path, "#{ns}/foo.wiki", "#{ns}/#{project.path}.wiki"). + with(project.repository_storage_path, "#{project.namespace.full_path}/foo.wiki", "#{project.full_path}.wiki"). and_return(true) expect_any_instance_of(SystemHooksService). @@ -1166,7 +1164,7 @@ describe Project, models: true do expect_any_instance_of(Gitlab::UploadsTransfer). to receive(:rename_project). - with('foo', project.path, ns) + with('foo', project.path, project.namespace.full_path) expect(project).to receive(:expire_caches_before_rename) @@ -1538,7 +1536,7 @@ describe Project, models: true do it 'schedules a RepositoryForkWorker job' do expect(RepositoryForkWorker).to receive(:perform_async). with(project.id, forked_from_project.repository_storage_path, - forked_from_project.path_with_namespace, project.namespace.path) + forked_from_project.path_with_namespace, project.namespace.full_path) project.add_import_job end @@ -1752,7 +1750,7 @@ describe Project, models: true do describe 'inside_path' do let!(:project1) { create(:empty_project) } let!(:project2) { create(:empty_project) } - let!(:path) { project1.namespace.path } + let!(:path) { project1.namespace.full_path } it { expect(Project.inside_path(path)).to eq([project1]) } end @@ -1767,7 +1765,7 @@ describe Project, models: true do end before do - project.repository.commit_file(User.last, '.gitlab/route-map.yml', route_map, message: 'Add .gitlab/route-map.yml', branch_name: 'master', update: false) + project.repository.create_file(User.last, '.gitlab/route-map.yml', route_map, message: 'Add .gitlab/route-map.yml', branch_name: 'master') end context 'when there is a .gitlab/route-map.yml at the commit' do @@ -1896,4 +1894,25 @@ describe Project, models: true do end end end + + describe '#http_url_to_repo' do + let(:project) { create :empty_project } + + context 'when no user is given' do + it 'returns the url to the repo without a username' do + url = project.http_url_to_repo + + expect(url).to eq(project.http_url_to_repo) + expect(url).not_to include('@') + end + end + + context 'when user is given' do + it 'returns the url to the repo with the username' do + user = build_stubbed(:user) + + expect(project.http_url_to_repo(user)).to match(%r{https?:\/\/#{user.username}@}) + end + end + end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 838fd3754b2..ae203fada12 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -291,10 +291,10 @@ describe Repository, models: true do end end - describe "#commit_dir" do + describe "#create_dir" do it "commits a change that creates a new directory" do expect do - repository.commit_dir(user, 'newdir', + repository.create_dir(user, 'newdir', message: 'Create newdir', branch_name: 'master') end.to change { repository.commits('master').count }.by(1) @@ -307,7 +307,7 @@ describe Repository, models: true do it "creates a fork and commit to the forked project" do expect do - repository.commit_dir(user, 'newdir', + repository.create_dir(user, 'newdir', message: 'Create newdir', branch_name: 'patch', start_branch_name: 'master', start_project: forked_project) end.to change { repository.commits('master').count }.by(0) @@ -323,7 +323,7 @@ describe Repository, models: true do context "when an author is specified" do it "uses the given email/name to set the commit's author" do expect do - repository.commit_dir(user, 'newdir', + repository.create_dir(user, 'newdir', message: 'Add newdir', branch_name: 'master', author_email: author_email, author_name: author_name) @@ -337,25 +337,23 @@ describe Repository, models: true do end end - describe "#commit_file" do - it 'commits change to a file successfully' do + describe "#create_file" do + it 'commits new file successfully' do expect do - repository.commit_file(user, 'CHANGELOG', 'Changelog!', - message: 'Updates file content', - branch_name: 'master', - update: true) + repository.create_file(user, 'NEWCHANGELOG', 'Changelog!', + message: 'Create changelog', + branch_name: 'master') end.to change { repository.commits('master').count }.by(1) - blob = repository.blob_at('master', 'CHANGELOG') + blob = repository.blob_at('master', 'NEWCHANGELOG') expect(blob.data).to eq('Changelog!') end it 'respects the autocrlf setting' do - repository.commit_file(user, 'hello.txt', "Hello,\r\nWorld", + repository.create_file(user, 'hello.txt', "Hello,\r\nWorld", message: 'Add hello world', - branch_name: 'master', - update: true) + branch_name: 'master') blob = repository.blob_at('master', 'hello.txt') @@ -365,10 +363,9 @@ describe Repository, models: true do context "when an author is specified" do it "uses the given email/name to set the commit's author" do expect do - repository.commit_file(user, 'README', 'README!', + repository.create_file(user, 'NEWREADME', 'README!', message: 'Add README', branch_name: 'master', - update: true, author_email: author_email, author_name: author_name) end.to change { repository.commits('master').count }.by(1) @@ -382,6 +379,18 @@ describe Repository, models: true do end describe "#update_file" do + it 'updates file successfully' do + expect do + repository.update_file(user, 'CHANGELOG', 'Changelog!', + message: 'Update changelog', + branch_name: 'master') + end.to change { repository.commits('master').count }.by(1) + + blob = repository.blob_at('master', 'CHANGELOG') + + expect(blob.data).to eq('Changelog!') + end + it 'updates filename successfully' do expect do repository.update_file(user, 'NEWLICENSE', 'Copyright!', @@ -398,9 +407,6 @@ describe Repository, models: true do context "when an author is specified" do it "uses the given email/name to set the commit's author" do - repository.commit_file(user, 'README', 'README!', - message: 'Add README', branch_name: 'master', update: true) - expect do repository.update_file(user, 'README', 'Updated README!', branch_name: 'master', @@ -418,13 +424,10 @@ describe Repository, models: true do end end - describe "#remove_file" do + describe "#delete_file" do it 'removes file successfully' do - repository.commit_file(user, 'README', 'README!', - message: 'Add README', branch_name: 'master', update: true) - expect do - repository.remove_file(user, 'README', + repository.delete_file(user, 'README', message: 'Remove README', branch_name: 'master') end.to change { repository.commits('master').count }.by(1) @@ -433,11 +436,8 @@ describe Repository, models: true do context "when an author is specified" do it "uses the given email/name to set the commit's author" do - repository.commit_file(user, 'README', 'README!', - message: 'Add README', branch_name: 'master', update: true) - expect do - repository.remove_file(user, 'README', + repository.delete_file(user, 'README', message: 'Remove README', branch_name: 'master', author_email: author_email, author_name: author_name) end.to change { repository.commits('master').count }.by(1) @@ -587,14 +587,14 @@ describe Repository, models: true do describe "#license_blob", caching: true do before do - repository.remove_file( + repository.delete_file( user, 'LICENSE', message: 'Remove LICENSE', branch_name: 'master') end it 'handles when HEAD points to non-existent ref' do - repository.commit_file( + repository.create_file( user, 'LICENSE', 'Copyright!', - message: 'Add LICENSE', branch_name: 'master', update: false) + message: 'Add LICENSE', branch_name: 'master') allow(repository).to receive(:file_on_head). and_raise(Rugged::ReferenceError) @@ -603,27 +603,27 @@ describe Repository, models: true do end it 'looks in the root_ref only' do - repository.remove_file(user, 'LICENSE', + repository.delete_file(user, 'LICENSE', message: 'Remove LICENSE', branch_name: 'markdown') - repository.commit_file(user, 'LICENSE', + repository.create_file(user, 'LICENSE', Licensee::License.new('mit').content, - message: 'Add LICENSE', branch_name: 'markdown', update: false) + message: 'Add LICENSE', branch_name: 'markdown') expect(repository.license_blob).to be_nil end it 'detects license file with no recognizable open-source license content' do - repository.commit_file(user, 'LICENSE', 'Copyright!', - message: 'Add LICENSE', branch_name: 'master', update: false) + repository.create_file(user, 'LICENSE', 'Copyright!', + message: 'Add LICENSE', branch_name: 'master') expect(repository.license_blob.name).to eq('LICENSE') end %w[LICENSE LICENCE LiCensE LICENSE.md LICENSE.foo COPYING COPYING.md].each do |filename| it "detects '#{filename}'" do - repository.commit_file(user, filename, + repository.create_file(user, filename, Licensee::License.new('mit').content, - message: "Add #{filename}", branch_name: 'master', update: false) + message: "Add #{filename}", branch_name: 'master') expect(repository.license_blob.name).to eq(filename) end @@ -632,7 +632,7 @@ describe Repository, models: true do describe '#license_key', caching: true do before do - repository.remove_file(user, 'LICENSE', + repository.delete_file(user, 'LICENSE', message: 'Remove LICENSE', branch_name: 'master') end @@ -647,16 +647,16 @@ describe Repository, models: true do end it 'detects license file with no recognizable open-source license content' do - repository.commit_file(user, 'LICENSE', 'Copyright!', - message: 'Add LICENSE', branch_name: 'master', update: false) + repository.create_file(user, 'LICENSE', 'Copyright!', + message: 'Add LICENSE', branch_name: 'master') expect(repository.license_key).to be_nil end it 'returns the license key' do - repository.commit_file(user, 'LICENSE', + repository.create_file(user, 'LICENSE', Licensee::License.new('mit').content, - message: 'Add LICENSE', branch_name: 'master', update: false) + message: 'Add LICENSE', branch_name: 'master') expect(repository.license_key).to eq('mit') end @@ -913,10 +913,9 @@ describe Repository, models: true do expect(empty_repository).to receive(:expire_emptiness_caches) expect(empty_repository).to receive(:expire_branches_cache) - empty_repository.commit_file(user, 'CHANGELOG', 'Changelog!', + empty_repository.create_file(user, 'CHANGELOG', 'Changelog!', message: 'Updates file content', - branch_name: 'master', - update: false) + branch_name: 'master') end end end @@ -1382,13 +1381,13 @@ describe Repository, models: true do describe '#branch_count' do it 'returns the number of branches' do - expect(repository.branch_count).to be_an_instance_of(Fixnum) + expect(repository.branch_count).to be_an(Integer) end end describe '#tag_count' do it 'returns the number of tags' do - expect(repository.tag_count).to be_an_instance_of(Fixnum) + expect(repository.tag_count).to be_an(Integer) end end @@ -1738,7 +1737,7 @@ describe Repository, models: true do context 'with an existing repository' do it 'returns the commit count' do - expect(repository.commit_count).to be_an_instance_of(Fixnum) + expect(repository.commit_count).to be_an(Integer) end end end @@ -1796,7 +1795,7 @@ describe Repository, models: true do describe '#gitlab_ci_yml_for' do before do - repository.commit_file(User.last, '.gitlab-ci.yml', 'CONTENT', message: 'Add .gitlab-ci.yml', branch_name: 'master', update: false) + repository.create_file(User.last, '.gitlab-ci.yml', 'CONTENT', message: 'Add .gitlab-ci.yml', branch_name: 'master') end context 'when there is a .gitlab-ci.yml at the commit' do @@ -1814,7 +1813,7 @@ describe Repository, models: true do describe '#route_map_for' do before do - repository.commit_file(User.last, '.gitlab/route-map.yml', 'CONTENT', message: 'Add .gitlab/route-map.yml', branch_name: 'master', update: false) + repository.create_file(User.last, '.gitlab/route-map.yml', 'CONTENT', message: 'Add .gitlab/route-map.yml', branch_name: 'master') end context 'when there is a .gitlab/route-map.yml at the commit' do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 584a4facd94..6356f8b6c92 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -22,7 +22,7 @@ describe User, models: true do it { is_expected.to have_many(:deploy_keys).dependent(:destroy) } it { is_expected.to have_many(:events).dependent(:destroy) } it { is_expected.to have_many(:recent_events).class_name('Event') } - it { is_expected.to have_many(:issues).dependent(:destroy) } + it { is_expected.to have_many(:issues).dependent(:restrict_with_exception) } it { is_expected.to have_many(:notes).dependent(:destroy) } it { is_expected.to have_many(:assigned_issues).dependent(:nullify) } it { is_expected.to have_many(:merge_requests).dependent(:destroy) } @@ -208,6 +208,22 @@ describe User, models: true do end end end + + describe 'ghost users' do + it 'does not allow a non-blocked ghost user' do + user = build(:user, :ghost) + user.state = 'active' + + expect(user).to be_invalid + end + + it 'allows a blocked ghost user' do + user = build(:user, :ghost) + user.state = 'blocked' + + expect(user).to be_valid + end + end end describe "scopes" do @@ -693,9 +709,7 @@ describe User, models: true do end describe '.search_with_secondary_emails' do - def search_with_secondary_emails(query) - described_class.search_with_secondary_emails(query) - end + delegate :search_with_secondary_emails, to: :described_class let!(:user) { create(:user) } let!(:email) { create(:email) } @@ -1492,4 +1506,41 @@ describe User, models: true do expect(user.admin).to be true end end + + describe '.ghost' do + it "creates a ghost user if one isn't already present" do + ghost = User.ghost + + expect(ghost).to be_ghost + expect(ghost).to be_persisted + end + + it "does not create a second ghost user if one is already present" do + expect do + User.ghost + User.ghost + end.to change { User.count }.by(1) + expect(User.ghost).to eq(User.ghost) + end + + context "when a regular user exists with the username 'ghost'" do + it "creates a ghost user with a non-conflicting username" do + create(:user, username: 'ghost') + ghost = User.ghost + + expect(ghost).to be_persisted + expect(ghost.username).to eq('ghost1') + end + end + + context "when a regular user exists with the email 'ghost@example.com'" do + it "creates a ghost user with a non-conflicting email" do + create(:user, email: 'ghost@example.com') + ghost = User.ghost + + expect(ghost).to be_persisted + expect(ghost.email).to eq('ghost1@example.com') + end + end + end end diff --git a/spec/policies/user_policy_spec.rb b/spec/policies/user_policy_spec.rb new file mode 100644 index 00000000000..d5761390d39 --- /dev/null +++ b/spec/policies/user_policy_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe UserPolicy, models: true do + let(:current_user) { create(:user) } + let(:user) { create(:user) } + + subject { described_class.abilities(current_user, user).to_set } + + describe "reading a user's information" do + it { is_expected.to include(:read_user) } + end + + describe "destroying a user" do + context "when a regular user tries to destroy another regular user" do + it { is_expected.not_to include(:destroy_user) } + end + + context "when a regular user tries to destroy themselves" do + let(:current_user) { user } + + it { is_expected.to include(:destroy_user) } + end + + context "when an admin user tries to destroy a regular user" do + let(:current_user) { create(:user, :admin) } + + it { is_expected.to include(:destroy_user) } + end + + context "when an admin user tries to destroy a ghost user" do + let(:current_user) { create(:user, :admin) } + let(:user) { create(:user, :ghost) } + + it { is_expected.not_to include(:destroy_user) } + end + end +end diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index 5571f6cc107..cacdb21c692 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -360,9 +360,11 @@ describe API::Branches, api: true do allow_any_instance_of(Repository).to receive(:rm_branch).and_return(true) end - it 'returns 200' do + it 'returns 202 with json body' do delete api("/projects/#{project.id}/repository/merged_branches", user) - expect(response).to have_http_status(200) + + expect(response).to have_http_status(202) + expect(json_response['message']).to eql('202 Accepted') end it 'returns a 403 error if guest' do diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb index 38aef7f2767..76a10a2374c 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/builds_spec.rb @@ -16,6 +16,8 @@ describe API::Builds, api: true do let(:query) { '' } before do + create(:ci_build, :skipped, pipeline: pipeline) + get api("/projects/#{project.id}/builds?#{query}", api_user) end @@ -49,6 +51,18 @@ describe API::Builds, api: true do end end + context 'filter project with scope skipped' do + let(:query) { 'scope=skipped' } + let(:json_build) { json_response.first } + + it 'return builds with status skipped' do + expect(response).to have_http_status 200 + expect(json_response).to be_an Array + expect(json_response.length).to eq 1 + expect(json_build['status']).to eq 'skipped' + end + end + context 'filter project with array of scope elements' do let(:query) { 'scope[0]=pending&scope[1]=running' } diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 8b3dfedc5a9..5190fcca2d1 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -148,7 +148,7 @@ describe API::Commits, api: true do end context 'with project path in URL' do - let(:url) { "/projects/#{project.namespace.path}%2F#{project.path}/repository/commits" } + let(:url) { "/projects/#{project.full_path.gsub('/', '%2F')}/repository/commits" } it 'a new file in project repo' do post api(url, user), valid_c_params diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index a8ce0430401..29d67b5259e 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -127,7 +127,7 @@ describe API::Files, api: true do end it "returns a 400 if editor fails to create file" do - allow_any_instance_of(Repository).to receive(:commit_file). + allow_any_instance_of(Repository).to receive(:create_file). and_return(false) post api("/projects/#{project.id}/repository/files", user), valid_params @@ -215,7 +215,7 @@ describe API::Files, api: true do end it "returns a 400 if fails to create file" do - allow_any_instance_of(Repository).to receive(:remove_file).and_return(false) + allow_any_instance_of(Repository).to receive(:delete_file).and_return(false) delete api("/projects/#{project.id}/repository/files", user), valid_params diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index a59112579e5..fb3dc1b074e 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -150,20 +150,10 @@ describe API::Groups, api: true do expect(response_groups).to eq([group1.name, group3.name]) end end - end - - describe 'GET /groups/owned' do - context 'when unauthenticated' do - it 'returns authentication error' do - get api('/groups/owned') - - expect(response).to have_http_status(401) - end - end - context 'when authenticated as group owner' do + context 'when using owned in the request' do it 'returns an array of groups the user owns' do - get api('/groups/owned', user2) + get api('/groups', user2), owned: true expect(response).to have_http_status(200) expect(response).to include_pagination_headers @@ -303,7 +293,7 @@ describe API::Groups, api: true do expect(response).to have_http_status(200) expect(response).to include_pagination_headers expect(json_response.length).to eq(2) - project_names = json_response.map { |proj| proj['name' ] } + project_names = json_response.map { |proj| proj['name'] } expect(project_names).to match_array([project1.name, project3.name]) expect(json_response.first['visibility_level']).to be_present end @@ -314,7 +304,7 @@ describe API::Groups, api: true do expect(response).to have_http_status(200) expect(response).to include_pagination_headers expect(json_response.length).to eq(2) - project_names = json_response.map { |proj| proj['name' ] } + project_names = json_response.map { |proj| proj['name'] } expect(project_names).to match_array([project1.name, project3.name]) expect(json_response.first['visibility_level']).not_to be_present end @@ -398,7 +388,7 @@ describe API::Groups, api: true do expect(response).to have_http_status(200) expect(response).to include_pagination_headers - project_names = json_response.map { |proj| proj['name' ] } + project_names = json_response.map { |proj| proj['name'] } expect(project_names).to match_array([project1.name, project3.name]) end @@ -519,7 +509,7 @@ describe API::Groups, api: true do describe "POST /groups/:id/projects/:project_id" do let(:project) { create(:empty_project) } - let(:project_path) { "#{project.namespace.path}%2F#{project.path}" } + let(:project_path) { project.full_path.gsub('/', '%2F') } before(:each) do allow_any_instance_of(Projects::TransferService). diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 56ca4c04e7d..7cb75310204 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -775,7 +775,7 @@ describe API::Issues, api: true do expect(response).to have_http_status(201) expect(json_response['title']).to eq('new issue') expect(json_response['description']).to be_nil - expect(json_response['labels']).to eq(['label', 'label2']) + expect(json_response['labels']).to eq(%w(label label2)) expect(json_response['confidential']).to be_falsy end diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index 566d11bba57..af271dbd4f5 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -21,11 +21,11 @@ describe API::Labels, api: true do create(:labeled_issue, project: project, labels: [label1], author: user, state: :closed) create(:labeled_merge_request, labels: [priority_label], author: user, source_project: project ) - expected_keys = [ - 'id', 'name', 'color', 'description', - 'open_issues_count', 'closed_issues_count', 'open_merge_requests_count', - 'subscribed', 'priority' - ] + expected_keys = %w( + id name color description + open_issues_count closed_issues_count open_merge_requests_count + subscribed priority + ) get api("/projects/#{project.id}/labels", user) diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb index 31166b50033..127498ed109 100644 --- a/spec/requests/api/members_spec.rb +++ b/spec/requests/api/members_spec.rb @@ -173,11 +173,11 @@ describe API::Members, api: true do expect(response).to have_http_status(400) end - it 'returns 422 when access_level is not valid' do + it 'returns 400 when access_level is not valid' do post api("/#{source_type.pluralize}/#{source.id}/members", master), user_id: stranger.id, access_level: 1234 - expect(response).to have_http_status(422) + expect(response).to have_http_status(400) end end end @@ -230,11 +230,11 @@ describe API::Members, api: true do expect(response).to have_http_status(400) end - it 'returns 422 when access level is not valid' do + it 'returns 400 when access level is not valid' do put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master), access_level: 1234 - expect(response).to have_http_status(422) + expect(response).to have_http_status(400) end end end @@ -342,7 +342,7 @@ describe API::Members, api: true do post api("/projects/#{project.id}/members", master), user_id: stranger.id, access_level: Member::OWNER - expect(response).to have_http_status(422) + expect(response).to have_http_status(400) end.to change { project.members.count }.by(0) end end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index c125df8b90b..b87d0cd7de9 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -250,7 +250,7 @@ describe API::MergeRequests, api: true do expect(response).to have_http_status(201) expect(json_response['title']).to eq('Test merge_request') - expect(json_response['labels']).to eq(['label', 'label2']) + expect(json_response['labels']).to eq(%w(label label2)) expect(json_response['milestone']['id']).to eq(milestone.id) expect(json_response['force_remove_source_branch']).to be_truthy end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 2f1181b2e8c..5de4426f3bd 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -121,7 +121,7 @@ describe API::Projects, api: true do context 'and with simple=true' do it 'returns a simplified version of all the projects' do - expected_keys = ["id", "http_url_to_repo", "web_url", "name", "name_with_namespace", "path", "path_with_namespace"] + expected_keys = %w(id http_url_to_repo web_url name name_with_namespace path path_with_namespace) get api('/projects?simple=true', user) @@ -1263,7 +1263,9 @@ describe API::Projects, api: true do context 'when authenticated as user' do it 'removes project' do delete api("/projects/#{project.id}", user) - expect(response).to have_http_status(200) + + expect(response).to have_http_status(202) + expect(json_response['message']).to eql('202 Accepted') end it 'does not remove a project if not an owner' do @@ -1287,7 +1289,9 @@ describe API::Projects, api: true do context 'when authenticated as admin' do it 'removes any existing project' do delete api("/projects/#{project.id}", admin) - expect(response).to have_http_status(200) + + expect(response).to have_http_status(202) + expect(json_response['message']).to eql('202 Accepted') end it 'does not remove a non existing project' do diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb new file mode 100644 index 00000000000..73e82647ca0 --- /dev/null +++ b/spec/requests/api/runner_spec.rb @@ -0,0 +1,148 @@ +require 'spec_helper' + +describe API::Runner do + include ApiHelpers + include StubGitlabCalls + + let(:registration_token) { 'abcdefg123456' } + + before do + stub_gitlab_calls + stub_application_setting(runners_registration_token: registration_token) + end + + describe '/api/v4/runners' do + describe 'POST /api/v4/runners' do + context 'when no token is provided' do + it 'returns 400 error' do + post api('/runners') + expect(response).to have_http_status 400 + end + end + + context 'when invalid token is provided' do + it 'returns 403 error' do + post api('/runners'), token: 'invalid' + expect(response).to have_http_status 403 + end + end + + context 'when valid token is provided' do + it 'creates runner with default values' do + post api('/runners'), token: registration_token + + runner = Ci::Runner.first + + expect(response).to have_http_status 201 + expect(json_response['id']).to eq(runner.id) + expect(json_response['token']).to eq(runner.token) + expect(runner.run_untagged).to be true + end + + context 'when project token is used' do + let(:project) { create(:empty_project) } + + it 'creates runner' do + post api('/runners'), token: project.runners_token + + expect(response).to have_http_status 201 + expect(project.runners.size).to eq(1) + end + end + end + + context 'when runner description is provided' do + it 'creates runner' do + post api('/runners'), token: registration_token, + description: 'server.hostname' + + expect(response).to have_http_status 201 + expect(Ci::Runner.first.description).to eq('server.hostname') + end + end + + context 'when runner tags are provided' do + it 'creates runner' do + post api('/runners'), token: registration_token, + tag_list: 'tag1, tag2' + + expect(response).to have_http_status 201 + expect(Ci::Runner.first.tag_list.sort).to eq(%w(tag1 tag2)) + end + end + + context 'when option for running untagged jobs is provided' do + context 'when tags are provided' do + it 'creates runner' do + post api('/runners'), token: registration_token, + run_untagged: false, + tag_list: ['tag'] + + expect(response).to have_http_status 201 + expect(Ci::Runner.first.run_untagged).to be false + expect(Ci::Runner.first.tag_list.sort).to eq(['tag']) + end + end + + context 'when tags are not provided' do + it 'returns 404 error' do + post api('/runners'), token: registration_token, + run_untagged: false + + expect(response).to have_http_status 404 + end + end + end + + context 'when option for locking Runner is provided' do + it 'creates runner' do + post api('/runners'), token: registration_token, + locked: true + + expect(response).to have_http_status 201 + expect(Ci::Runner.first.locked).to be true + end + end + + %w(name version revision platform architecture).each do |param| + context "when info parameter '#{param}' info is present" do + let(:value) { "#{param}_value" } + + it %q(updates provided Runner's parameter) do + post api('/runners'), token: registration_token, + info: { param => value } + + expect(response).to have_http_status 201 + expect(Ci::Runner.first.read_attribute(param.to_sym)).to eq(value) + end + end + end + end + + describe 'DELETE /api/v4/runners' do + context 'when no token is provided' do + it 'returns 400 error' do + delete api('/runners') + expect(response).to have_http_status 400 + end + end + + context 'when invalid token is provided' do + it 'returns 403 error' do + delete api('/runners'), token: 'invalid' + expect(response).to have_http_status 403 + end + end + + context 'when valid token is provided' do + let(:runner) { create(:ci_runner) } + + it 'deletes Runner' do + delete api('/runners'), token: runner.token + expect(response).to have_http_status 200 + expect(Ci::Runner.count).to eq(0) + end + end + end + end +end diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index 91e3c333a02..411905edb49 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -30,8 +30,14 @@ describe API::Settings, 'Settings', api: true do it "updates application settings" do put api("/application/settings", admin), - default_projects_limit: 3, signin_enabled: false, repository_storage: 'custom', koding_enabled: true, koding_url: 'http://koding.example.com', - plantuml_enabled: true, plantuml_url: 'http://plantuml.example.com' + default_projects_limit: 3, + signin_enabled: false, + repository_storage: 'custom', + koding_enabled: true, + koding_url: 'http://koding.example.com', + plantuml_enabled: true, + plantuml_url: 'http://plantuml.example.com', + default_artifacts_expire_in: '2 days' expect(response).to have_http_status(200) expect(json_response['default_projects_limit']).to eq(3) expect(json_response['signin_enabled']).to be_falsey @@ -41,6 +47,7 @@ describe API::Settings, 'Settings', api: true do expect(json_response['koding_url']).to eq('http://koding.example.com') expect(json_response['plantuml_enabled']).to be_truthy expect(json_response['plantuml_url']).to eq('http://plantuml.example.com') + expect(json_response['default_artifacts_expire_in']).to eq('2 days') end end diff --git a/spec/requests/api/templates_spec.rb b/spec/requests/api/templates_spec.rb index 8506e8fccde..2c83e119065 100644 --- a/spec/requests/api/templates_spec.rb +++ b/spec/requests/api/templates_spec.rb @@ -58,11 +58,11 @@ describe API::Templates, api: true do expect(json_response['popular']).to be true expect(json_response['html_url']).to eq('http://choosealicense.com/licenses/mit/') expect(json_response['source_url']).to eq('https://opensource.org/licenses/MIT') - expect(json_response['description']).to include('A permissive license that is short and to the point.') + expect(json_response['description']).to include('A short and simple permissive license with conditions') expect(json_response['conditions']).to eq(%w[include-copyright]) expect(json_response['permissions']).to eq(%w[commercial-use modifications distribution private-use]) expect(json_response['limitations']).to eq(%w[no-liability]) - expect(json_response['content']).to include('The MIT License (MIT)') + expect(json_response['content']).to include('MIT License') end end @@ -73,7 +73,7 @@ describe API::Templates, api: true do expect(response).to have_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.size).to eq(15) + expect(json_response.size).to eq(12) expect(json_response.map { |l| l['key'] }).to include('agpl-3.0') end @@ -102,7 +102,7 @@ describe API::Templates, api: true do let(:license_type) { 'mit' } it 'returns the license text' do - expect(json_response['content']).to include('The MIT License (MIT)') + expect(json_response['content']).to include('MIT License') end it 'replaces placeholder values' do diff --git a/spec/requests/api/v3/branches_spec.rb b/spec/requests/api/v3/branches_spec.rb index 0e4c6bc3bc6..a3e1581fcc5 100644 --- a/spec/requests/api/v3/branches_spec.rb +++ b/spec/requests/api/v3/branches_spec.rb @@ -20,4 +20,25 @@ describe API::V3::Branches, api: true do expect(branch_names).to match_array(project.repository.branch_names) end end + + describe "DELETE /projects/:id/repository/merged_branches" do + before do + allow_any_instance_of(Repository).to receive(:rm_branch).and_return(true) + end + + it 'returns 200' do + delete v3_api("/projects/#{project.id}/repository/merged_branches", user) + + expect(response).to have_http_status(200) + end + + it 'returns a 403 error if guest' do + user_b = create :user + create(:project_member, :guest, user: user_b, project: project) + + delete v3_api("/projects/#{project.id}/repository/merged_branches", user_b) + + expect(response).to have_http_status(403) + end + end end diff --git a/spec/requests/api/v3/commits_spec.rb b/spec/requests/api/v3/commits_spec.rb index 2d7584c3e59..e298ef055e1 100644 --- a/spec/requests/api/v3/commits_spec.rb +++ b/spec/requests/api/v3/commits_spec.rb @@ -148,7 +148,7 @@ describe API::V3::Commits, api: true do end context 'with project path in URL' do - let(:url) { "/projects/#{project.namespace.path}%2F#{project.path}/repository/commits" } + let(:url) { "/projects/#{project.full_path.gsub('/', '%2F')}/repository/commits" } it 'a new file in project repo' do post v3_api(url, user), valid_c_params diff --git a/spec/requests/api/v3/files_spec.rb b/spec/requests/api/v3/files_spec.rb index 4af05605ec6..52fd908af7d 100644 --- a/spec/requests/api/v3/files_spec.rb +++ b/spec/requests/api/v3/files_spec.rb @@ -127,7 +127,7 @@ describe API::V3::Files, api: true do end it "returns a 400 if editor fails to create file" do - allow_any_instance_of(Repository).to receive(:commit_file). + allow_any_instance_of(Repository).to receive(:create_file). and_return(false) post v3_api("/projects/#{project.id}/repository/files", user), valid_params @@ -215,7 +215,7 @@ describe API::V3::Files, api: true do end it "returns a 400 if fails to create file" do - allow_any_instance_of(Repository).to receive(:remove_file).and_return(false) + allow_any_instance_of(Repository).to receive(:delete_file).and_return(false) delete v3_api("/projects/#{project.id}/repository/files", user), valid_params diff --git a/spec/requests/api/v3/groups_spec.rb b/spec/requests/api/v3/groups_spec.rb new file mode 100644 index 00000000000..8b29ad03737 --- /dev/null +++ b/spec/requests/api/v3/groups_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe API::V3::Groups, api: true do + include ApiHelpers + include UploadHelpers + + let(:user2) { create(:user) } + let!(:group2) { create(:group, :private) } + let!(:project2) { create(:empty_project, namespace: group2) } + + before do + group2.add_owner(user2) + end + + describe 'GET /groups/owned' do + context 'when unauthenticated' do + it 'returns authentication error' do + get v3_api('/groups/owned') + + expect(response).to have_http_status(401) + end + end + + context 'when authenticated as group owner' do + it 'returns an array of groups the user owns' do + get v3_api('/groups/owned', user2) + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.first['name']).to eq(group2.name) + end + end + end +end diff --git a/spec/requests/api/v3/issues_spec.rb b/spec/requests/api/v3/issues_spec.rb index 8e6732fe23e..803acd55470 100644 --- a/spec/requests/api/v3/issues_spec.rb +++ b/spec/requests/api/v3/issues_spec.rb @@ -722,7 +722,7 @@ describe API::V3::Issues, api: true do expect(response).to have_http_status(201) expect(json_response['title']).to eq('new issue') expect(json_response['description']).to be_nil - expect(json_response['labels']).to eq(['label', 'label2']) + expect(json_response['labels']).to eq(%w(label label2)) expect(json_response['confidential']).to be_falsy end diff --git a/spec/requests/api/v3/labels_spec.rb b/spec/requests/api/v3/labels_spec.rb index bcb0c6b9449..f44403374e9 100644 --- a/spec/requests/api/v3/labels_spec.rb +++ b/spec/requests/api/v3/labels_spec.rb @@ -21,11 +21,11 @@ describe API::V3::Labels, api: true do create(:labeled_issue, project: project, labels: [label1], author: user, state: :closed) create(:labeled_merge_request, labels: [priority_label], author: user, source_project: project ) - expected_keys = [ - 'id', 'name', 'color', 'description', - 'open_issues_count', 'closed_issues_count', 'open_merge_requests_count', - 'subscribed', 'priority' - ] + expected_keys = %w( + id name color description + open_issues_count closed_issues_count open_merge_requests_count + subscribed priority + ) get v3_api("/projects/#{project.id}/labels", user) diff --git a/spec/requests/api/v3/merge_requests_spec.rb b/spec/requests/api/v3/merge_requests_spec.rb index b94e1ef4ced..51764d1000e 100644 --- a/spec/requests/api/v3/merge_requests_spec.rb +++ b/spec/requests/api/v3/merge_requests_spec.rb @@ -237,7 +237,7 @@ describe API::MergeRequests, api: true do expect(response).to have_http_status(201) expect(json_response['title']).to eq('Test merge_request') - expect(json_response['labels']).to eq(['label', 'label2']) + expect(json_response['labels']).to eq(%w(label label2)) expect(json_response['milestone']['id']).to eq(milestone.id) expect(json_response['force_remove_source_branch']).to be_truthy end diff --git a/spec/requests/api/v3/notes_spec.rb b/spec/requests/api/v3/notes_spec.rb new file mode 100644 index 00000000000..b51cb3055d5 --- /dev/null +++ b/spec/requests/api/v3/notes_spec.rb @@ -0,0 +1,432 @@ +require 'spec_helper' + +describe API::V3::Notes, api: true do + include ApiHelpers + let(:user) { create(:user) } + let!(:project) { create(:empty_project, :public, namespace: user.namespace) } + let!(:issue) { create(:issue, project: project, author: user) } + let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) } + let!(:snippet) { create(:project_snippet, project: project, author: user) } + let!(:issue_note) { create(:note, noteable: issue, project: project, author: user) } + let!(:merge_request_note) { create(:note, noteable: merge_request, project: project, author: user) } + let!(:snippet_note) { create(:note, noteable: snippet, project: project, author: user) } + + # For testing the cross-reference of a private issue in a public issue + let(:private_user) { create(:user) } + let(:private_project) do + create(:empty_project, namespace: private_user.namespace). + tap { |p| p.team << [private_user, :master] } + end + let(:private_issue) { create(:issue, project: private_project) } + + let(:ext_proj) { create(:empty_project, :public) } + let(:ext_issue) { create(:issue, project: ext_proj) } + + let!(:cross_reference_note) do + create :note, + noteable: ext_issue, project: ext_proj, + note: "mentioned in issue #{private_issue.to_reference(ext_proj)}", + system: true + end + + before { project.team << [user, :reporter] } + + describe "GET /projects/:id/noteable/:noteable_id/notes" do + context "when noteable is an Issue" do + it "returns an array of issue notes" do + get v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user) + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.first['body']).to eq(issue_note.note) + expect(json_response.first['upvote']).to be_falsey + expect(json_response.first['downvote']).to be_falsey + end + + it "returns a 404 error when issue id not found" do + get v3_api("/projects/#{project.id}/issues/12345/notes", user) + + expect(response).to have_http_status(404) + end + + context "and current user cannot view the notes" do + it "returns an empty array" do + get v3_api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user) + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response).to be_empty + end + + context "and issue is confidential" do + before { ext_issue.update_attributes(confidential: true) } + + it "returns 404" do + get v3_api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user) + + expect(response).to have_http_status(404) + end + end + + context "and current user can view the note" do + it "returns an empty array" do + get v3_api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", private_user) + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.first['body']).to eq(cross_reference_note.note) + end + end + end + end + + context "when noteable is a Snippet" do + it "returns an array of snippet notes" do + get v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user) + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.first['body']).to eq(snippet_note.note) + end + + it "returns a 404 error when snippet id not found" do + get v3_api("/projects/#{project.id}/snippets/42/notes", user) + + expect(response).to have_http_status(404) + end + + it "returns 404 when not authorized" do + get v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes", private_user) + + expect(response).to have_http_status(404) + end + end + + context "when noteable is a Merge Request" do + it "returns an array of merge_requests notes" do + get v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/notes", user) + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.first['body']).to eq(merge_request_note.note) + end + + it "returns a 404 error if merge request id not found" do + get v3_api("/projects/#{project.id}/merge_requests/4444/notes", user) + + expect(response).to have_http_status(404) + end + + it "returns 404 when not authorized" do + get v3_api("/projects/#{project.id}/merge_requests/4444/notes", private_user) + + expect(response).to have_http_status(404) + end + end + end + + describe "GET /projects/:id/noteable/:noteable_id/notes/:note_id" do + context "when noteable is an Issue" do + it "returns an issue note by id" do + get v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", user) + + expect(response).to have_http_status(200) + expect(json_response['body']).to eq(issue_note.note) + end + + it "returns a 404 error if issue note not found" do + get v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user) + + expect(response).to have_http_status(404) + end + + context "and current user cannot view the note" do + it "returns a 404 error" do + get v3_api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", user) + + expect(response).to have_http_status(404) + end + + context "when issue is confidential" do + before { issue.update_attributes(confidential: true) } + + it "returns 404" do + get v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", private_user) + + expect(response).to have_http_status(404) + end + end + + context "and current user can view the note" do + it "returns an issue note by id" do + get v3_api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", private_user) + + expect(response).to have_http_status(200) + expect(json_response['body']).to eq(cross_reference_note.note) + end + end + end + end + + context "when noteable is a Snippet" do + it "returns a snippet note by id" do + get v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes/#{snippet_note.id}", user) + + expect(response).to have_http_status(200) + expect(json_response['body']).to eq(snippet_note.note) + end + + it "returns a 404 error if snippet note not found" do + get v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes/12345", user) + + expect(response).to have_http_status(404) + end + end + end + + describe "POST /projects/:id/noteable/:noteable_id/notes" do + context "when noteable is an Issue" do + it "creates a new issue note" do + post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!' + + expect(response).to have_http_status(201) + expect(json_response['body']).to eq('hi!') + expect(json_response['author']['username']).to eq(user.username) + end + + it "returns a 400 bad request error if body not given" do + post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user) + + expect(response).to have_http_status(400) + end + + it "returns a 401 unauthorized error if user not authenticated" do + post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes"), body: 'hi!' + + expect(response).to have_http_status(401) + end + + context 'when an admin or owner makes the request' do + it 'accepts the creation date to be set' do + creation_time = 2.weeks.ago + post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user), + body: 'hi!', created_at: creation_time + + expect(response).to have_http_status(201) + expect(json_response['body']).to eq('hi!') + expect(json_response['author']['username']).to eq(user.username) + expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time) + end + end + + context 'when the user is posting an award emoji on an issue created by someone else' do + let(:issue2) { create(:issue, project: project) } + + it 'returns an award emoji' do + post v3_api("/projects/#{project.id}/issues/#{issue2.id}/notes", user), body: ':+1:' + + expect(response).to have_http_status(201) + expect(json_response['awardable_id']).to eq issue2.id + end + end + + context 'when the user is posting an award emoji on his/her own issue' do + it 'creates a new issue note' do + post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: ':+1:' + + expect(response).to have_http_status(201) + expect(json_response['body']).to eq(':+1:') + end + end + end + + context "when noteable is a Snippet" do + it "creates a new snippet note" do + post v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user), body: 'hi!' + + expect(response).to have_http_status(201) + expect(json_response['body']).to eq('hi!') + expect(json_response['author']['username']).to eq(user.username) + end + + it "returns a 400 bad request error if body not given" do + post v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user) + + expect(response).to have_http_status(400) + end + + it "returns a 401 unauthorized error if user not authenticated" do + post v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes"), body: 'hi!' + + expect(response).to have_http_status(401) + end + end + + context 'when user does not have access to read the noteable' do + it 'responds with 404' do + project = create(:empty_project, :private) { |p| p.add_guest(user) } + issue = create(:issue, :confidential, project: project) + + post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user), + body: 'Foo' + + expect(response).to have_http_status(404) + end + end + + context 'when user does not have access to create noteable' do + let(:private_issue) { create(:issue, project: create(:empty_project, :private)) } + + ## + # We are posting to project user has access to, but we use issue id + # from a different project, see #15577 + # + before do + post v3_api("/projects/#{project.id}/issues/#{private_issue.id}/notes", user), + body: 'Hi!' + end + + it 'responds with resource not found error' do + expect(response.status).to eq 404 + end + + it 'does not create new note' do + expect(private_issue.notes.reload).to be_empty + end + end + end + + describe "POST /projects/:id/noteable/:noteable_id/notes to test observer on create" do + it "creates an activity event when an issue note is created" do + expect(Event).to receive(:create) + + post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!' + end + end + + describe 'PUT /projects/:id/noteable/:noteable_id/notes/:note_id' do + context 'when noteable is an Issue' do + it 'returns modified note' do + put v3_api("/projects/#{project.id}/issues/#{issue.id}/"\ + "notes/#{issue_note.id}", user), body: 'Hello!' + + expect(response).to have_http_status(200) + expect(json_response['body']).to eq('Hello!') + end + + it 'returns a 404 error when note id not found' do + put v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user), + body: 'Hello!' + + expect(response).to have_http_status(404) + end + + it 'returns a 400 bad request error if body not given' do + put v3_api("/projects/#{project.id}/issues/#{issue.id}/"\ + "notes/#{issue_note.id}", user) + + expect(response).to have_http_status(400) + end + end + + context 'when noteable is a Snippet' do + it 'returns modified note' do + put v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\ + "notes/#{snippet_note.id}", user), body: 'Hello!' + + expect(response).to have_http_status(200) + expect(json_response['body']).to eq('Hello!') + end + + it 'returns a 404 error when note id not found' do + put v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\ + "notes/12345", user), body: "Hello!" + + expect(response).to have_http_status(404) + end + end + + context 'when noteable is a Merge Request' do + it 'returns modified note' do + put v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\ + "notes/#{merge_request_note.id}", user), body: 'Hello!' + + expect(response).to have_http_status(200) + expect(json_response['body']).to eq('Hello!') + end + + it 'returns a 404 error when note id not found' do + put v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\ + "notes/12345", user), body: "Hello!" + + expect(response).to have_http_status(404) + end + end + end + + describe 'DELETE /projects/:id/noteable/:noteable_id/notes/:note_id' do + context 'when noteable is an Issue' do + it 'deletes a note' do + delete v3_api("/projects/#{project.id}/issues/#{issue.id}/"\ + "notes/#{issue_note.id}", user) + + expect(response).to have_http_status(200) + # Check if note is really deleted + delete v3_api("/projects/#{project.id}/issues/#{issue.id}/"\ + "notes/#{issue_note.id}", user) + expect(response).to have_http_status(404) + end + + it 'returns a 404 error when note id not found' do + delete v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user) + + expect(response).to have_http_status(404) + end + end + + context 'when noteable is a Snippet' do + it 'deletes a note' do + delete v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\ + "notes/#{snippet_note.id}", user) + + expect(response).to have_http_status(200) + # Check if note is really deleted + delete v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\ + "notes/#{snippet_note.id}", user) + expect(response).to have_http_status(404) + end + + it 'returns a 404 error when note id not found' do + delete v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\ + "notes/12345", user) + + expect(response).to have_http_status(404) + end + end + + context 'when noteable is a Merge Request' do + it 'deletes a note' do + delete v3_api("/projects/#{project.id}/merge_requests/"\ + "#{merge_request.id}/notes/#{merge_request_note.id}", user) + + expect(response).to have_http_status(200) + # Check if note is really deleted + delete v3_api("/projects/#{project.id}/merge_requests/"\ + "#{merge_request.id}/notes/#{merge_request_note.id}", user) + expect(response).to have_http_status(404) + end + + it 'returns a 404 error when note id not found' do + delete v3_api("/projects/#{project.id}/merge_requests/"\ + "#{merge_request.id}/notes/12345", user) + + expect(response).to have_http_status(404) + end + end + end +end diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb index 36d99d80e79..662be3f3531 100644 --- a/spec/requests/api/v3/projects_spec.rb +++ b/spec/requests/api/v3/projects_spec.rb @@ -84,7 +84,7 @@ describe API::V3::Projects, api: true do context 'GET /projects?simple=true' do it 'returns a simplified version of all the projects' do - expected_keys = ["id", "http_url_to_repo", "web_url", "name", "name_with_namespace", "path", "path_with_namespace"] + expected_keys = %w(id http_url_to_repo web_url name name_with_namespace path path_with_namespace) get v3_api('/projects?simple=true', user) diff --git a/spec/requests/api/v3/templates_spec.rb b/spec/requests/api/v3/templates_spec.rb index 4fd4e70bedd..f1e554b98cc 100644 --- a/spec/requests/api/v3/templates_spec.rb +++ b/spec/requests/api/v3/templates_spec.rb @@ -56,11 +56,11 @@ describe API::V3::Templates, api: true do expect(json_response['popular']).to be true expect(json_response['html_url']).to eq('http://choosealicense.com/licenses/mit/') expect(json_response['source_url']).to eq('https://opensource.org/licenses/MIT') - expect(json_response['description']).to include('A permissive license that is short and to the point.') + expect(json_response['description']).to include('A short and simple permissive license with conditions') expect(json_response['conditions']).to eq(%w[include-copyright]) expect(json_response['permissions']).to eq(%w[commercial-use modifications distribution private-use]) expect(json_response['limitations']).to eq(%w[no-liability]) - expect(json_response['content']).to include('The MIT License (MIT)') + expect(json_response['content']).to include('MIT License') end end @@ -70,7 +70,7 @@ describe API::V3::Templates, api: true do expect(response).to have_http_status(200) expect(json_response).to be_an Array - expect(json_response.size).to eq(15) + expect(json_response.size).to eq(12) expect(json_response.map { |l| l['key'] }).to include('agpl-3.0') end @@ -98,7 +98,7 @@ describe API::V3::Templates, api: true do let(:license_type) { 'mit' } it 'returns the license text' do - expect(json_response['content']).to include('The MIT License (MIT)') + expect(json_response['content']).to include('MIT License') end it 'replaces placeholder values' do diff --git a/spec/requests/api/v3/users_spec.rb b/spec/requests/api/v3/users_spec.rb index 5020ef18a3a..17bbb0b53c1 100644 --- a/spec/requests/api/v3/users_spec.rb +++ b/spec/requests/api/v3/users_spec.rb @@ -186,4 +186,81 @@ describe API::V3::Users, api: true do expect(response).to have_http_status(404) end end + + describe 'GET /users/:id/events' do + let(:user) { create(:user) } + let(:project) { create(:empty_project) } + let(:note) { create(:note_on_issue, note: 'What an awesome day!', project: project) } + + before do + project.add_user(user, :developer) + EventCreateService.new.leave_note(note, user) + end + + context "as a user than cannot see the event's project" do + it 'returns no events' do + other_user = create(:user) + + get api("/users/#{user.id}/events", other_user) + + expect(response).to have_http_status(200) + expect(json_response).to be_empty + end + end + + context "as a user than can see the event's project" do + context 'joined event' do + it 'returns the "joined" event' do + get v3_api("/users/#{user.id}/events", user) + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + + comment_event = json_response.find { |e| e['action_name'] == 'commented on' } + + expect(comment_event['project_id'].to_i).to eq(project.id) + expect(comment_event['author_username']).to eq(user.username) + expect(comment_event['note']['id']).to eq(note.id) + expect(comment_event['note']['body']).to eq('What an awesome day!') + + joined_event = json_response.find { |e| e['action_name'] == 'joined' } + + expect(joined_event['project_id'].to_i).to eq(project.id) + expect(joined_event['author_username']).to eq(user.username) + expect(joined_event['author']['name']).to eq(user.name) + end + end + + context 'when there are multiple events from different projects' do + let(:second_note) { create(:note_on_issue, project: create(:empty_project)) } + let(:third_note) { create(:note_on_issue, project: project) } + + before do + second_note.project.add_user(user, :developer) + + [second_note, third_note].each do |note| + EventCreateService.new.leave_note(note, user) + end + end + + it 'returns events in the correct order (from newest to oldest)' do + get v3_api("/users/#{user.id}/events", user) + + comment_events = json_response.select { |e| e['action_name'] == 'commented on' } + + expect(comment_events[0]['target_id']).to eq(third_note.id) + expect(comment_events[1]['target_id']).to eq(second_note.id) + expect(comment_events[2]['target_id']).to eq(note.id) + end + end + end + + it 'returns a 404 error if not found' do + get v3_api('/users/42/events', user) + + expect(response).to have_http_status(404) + expect(json_response['message']).to eq('404 User Not Found') + end + end end diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index d85afdeab42..9948d1a9ea0 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Ci::API::Builds do include ApiHelpers - let(:runner) { FactoryGirl.create(:ci_runner, tag_list: ["mysql", "ruby"]) } + let(:runner) { FactoryGirl.create(:ci_runner, tag_list: %w(mysql ruby)) } let(:project) { FactoryGirl.create(:empty_project, shared_runners_enabled: false) } let(:last_update) { nil } @@ -630,6 +630,7 @@ describe Ci::API::Builds do context 'with an expire date' do let!(:artifacts) { file_upload } + let(:default_artifacts_expire_in) {} let(:post_data) do { 'file.path' => artifacts.path, @@ -638,6 +639,9 @@ describe Ci::API::Builds do end before do + stub_application_setting( + default_artifacts_expire_in: default_artifacts_expire_in) + post(post_url, post_data, headers_with_token) end @@ -648,7 +652,8 @@ describe Ci::API::Builds do build.reload expect(response).to have_http_status(201) expect(json_response['artifacts_expire_at']).not_to be_empty - expect(build.artifacts_expire_at).to be_within(5.minutes).of(Time.now + 7.days) + expect(build.artifacts_expire_at). + to be_within(5.minutes).of(7.days.from_now) end end @@ -661,6 +666,32 @@ describe Ci::API::Builds do expect(json_response['artifacts_expire_at']).to be_nil expect(build.artifacts_expire_at).to be_nil end + + context 'with application default' do + context 'default to 5 days' do + let(:default_artifacts_expire_in) { '5 days' } + + it 'sets to application default' do + build.reload + expect(response).to have_http_status(201) + expect(json_response['artifacts_expire_at']) + .not_to be_empty + expect(build.artifacts_expire_at) + .to be_within(5.minutes).of(5.days.from_now) + end + end + + context 'default to 0' do + let(:default_artifacts_expire_in) { '0' } + + it 'does not set expire_in' do + build.reload + expect(response).to have_http_status(201) + expect(json_response['artifacts_expire_at']).to be_nil + expect(build.artifacts_expire_at).to be_nil + end + end + end end end diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb index bd55934d0c8..8719313783e 100644 --- a/spec/requests/ci/api/runners_spec.rb +++ b/spec/requests/ci/api/runners_spec.rb @@ -41,7 +41,7 @@ describe Ci::API::Runners do it 'creates runner' do expect(response).to have_http_status 201 - expect(Ci::Runner.first.tag_list.sort).to eq(["tag1", "tag2"]) + expect(Ci::Runner.first.tag_list.sort).to eq(%w(tag1 tag2)) end end diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb index c0e7bab8199..5d495bc9e7d 100644 --- a/spec/requests/lfs_http_spec.rb +++ b/spec/requests/lfs_http_spec.rb @@ -25,11 +25,9 @@ describe 'Git LFS API and storage' do { 'objects' => [ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }, + 'size' => 1575078 }, { 'oid' => sample_oid, - 'size' => sample_size - } + 'size' => sample_size } ], 'operation' => 'upload' } @@ -53,11 +51,9 @@ describe 'Git LFS API and storage' do { 'objects' => [ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }, + 'size' => 1575078 }, { 'oid' => sample_oid, - 'size' => sample_size - } + 'size' => sample_size } ], 'operation' => 'upload' } @@ -374,11 +370,12 @@ describe 'Git LFS API and storage' do describe 'download' do let(:project) { create(:empty_project) } let(:body) do - { 'operation' => 'download', + { + 'operation' => 'download', 'objects' => [ { 'oid' => sample_oid, - 'size' => sample_size - }] + 'size' => sample_size } + ] } end @@ -393,16 +390,20 @@ describe 'Git LFS API and storage' do end it 'with href to download' do - expect(json_response).to eq('objects' => [ - { 'oid' => sample_oid, - 'size' => sample_size, - 'actions' => { - 'download' => { - 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", - 'header' => { 'Authorization' => authorization } + expect(json_response).to eq({ + 'objects' => [ + { + 'oid' => sample_oid, + 'size' => sample_size, + 'actions' => { + 'download' => { + 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", + 'header' => { 'Authorization' => authorization } + } } } - }]) + ] + }) end end @@ -417,24 +418,29 @@ describe 'Git LFS API and storage' do end it 'with href to download' do - expect(json_response).to eq('objects' => [ - { 'oid' => sample_oid, - 'size' => sample_size, - 'error' => { - 'code' => 404, - 'message' => "Object does not exist on the server or you don't have permissions to access it", + expect(json_response).to eq({ + 'objects' => [ + { + 'oid' => sample_oid, + 'size' => sample_size, + 'error' => { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } } - }]) + ] + }) end end context 'when downloading a lfs object that does not exist' do let(:body) do - { 'operation' => 'download', + { + 'operation' => 'download', 'objects' => [ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }] + 'size' => 1575078 } + ] } end @@ -443,27 +449,30 @@ describe 'Git LFS API and storage' do end it 'with an 404 for specific object' do - expect(json_response).to eq('objects' => [ - { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078, - 'error' => { - 'code' => 404, - 'message' => "Object does not exist on the server or you don't have permissions to access it", + expect(json_response).to eq({ + 'objects' => [ + { + 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078, + 'error' => { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } } - }]) + ] + }) end end context 'when downloading one new and one existing lfs object' do let(:body) do - { 'operation' => 'download', + { + 'operation' => 'download', 'objects' => [ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }, + 'size' => 1575078 }, { 'oid' => sample_oid, - 'size' => sample_size - } + 'size' => sample_size } ] } end @@ -477,23 +486,28 @@ describe 'Git LFS API and storage' do end it 'responds with upload hypermedia link for the new object' do - expect(json_response).to eq('objects' => [ - { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078, - 'error' => { - 'code' => 404, - 'message' => "Object does not exist on the server or you don't have permissions to access it", - } - }, - { 'oid' => sample_oid, - 'size' => sample_size, - 'actions' => { - 'download' => { - 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", - 'header' => { 'Authorization' => authorization } + expect(json_response).to eq({ + 'objects' => [ + { + 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078, + 'error' => { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } + }, + { + 'oid' => sample_oid, + 'size' => sample_size, + 'actions' => { + 'download' => { + 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", + 'header' => { 'Authorization' => authorization } + } } } - }]) + ] + }) end end end @@ -597,17 +611,21 @@ describe 'Git LFS API and storage' do end it 'responds with status 200 and href to download' do - expect(json_response).to eq('objects' => [ - { 'oid' => sample_oid, - 'size' => sample_size, - 'authenticated' => true, - 'actions' => { - 'download' => { - 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", - 'header' => {} + expect(json_response).to eq({ + 'objects' => [ + { + 'oid' => sample_oid, + 'size' => sample_size, + 'authenticated' => true, + 'actions' => { + 'download' => { + 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", + 'header' => {} + } } } - }]) + ] + }) end end @@ -626,11 +644,12 @@ describe 'Git LFS API and storage' do describe 'upload' do let(:project) { create(:project, :public) } let(:body) do - { 'operation' => 'upload', + { + 'operation' => 'upload', 'objects' => [ { 'oid' => sample_oid, - 'size' => sample_size - }] + 'size' => sample_size } + ] } end @@ -665,11 +684,12 @@ describe 'Git LFS API and storage' do context 'when pushing a lfs object that does not exist' do let(:body) do - { 'operation' => 'upload', + { + 'operation' => 'upload', 'objects' => [ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }] + 'size' => 1575078 } + ] } end @@ -688,14 +708,13 @@ describe 'Git LFS API and storage' do context 'when pushing one new and one existing lfs object' do let(:body) do - { 'operation' => 'upload', + { + 'operation' => 'upload', 'objects' => [ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }, + 'size' => 1575078 }, { 'oid' => sample_oid, - 'size' => sample_size - } + 'size' => sample_size } ] } end @@ -789,11 +808,12 @@ describe 'Git LFS API and storage' do let(:project) { create(:empty_project) } let(:authorization) { authorize_user } let(:body) do - { 'operation' => 'other', + { + 'operation' => 'other', 'objects' => [ { 'oid' => sample_oid, - 'size' => sample_size - }] + 'size' => sample_size } + ] } end diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb index bb26513103d..b91234ddb1e 100644 --- a/spec/services/auth/container_registry_authentication_service_spec.rb +++ b/spec/services/auth/container_registry_authentication_service_spec.rb @@ -72,7 +72,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do shared_examples 'a pullable and pushable' do it_behaves_like 'a accessible' do - let(:actions) { ['pull', 'push'] } + let(:actions) { %w(pull push) } end end diff --git a/spec/services/ci/process_pipeline_service_spec.rb b/spec/services/ci/process_pipeline_service_spec.rb index ef2ddc4b1d7..b818dfdd50c 100644 --- a/spec/services/ci/process_pipeline_service_spec.rb +++ b/spec/services/ci/process_pipeline_service_spec.rb @@ -377,9 +377,7 @@ describe Ci::ProcessPipelineService, :services do builds.pending.update_all(status: 'success') end - def manual_actions - pipeline.manual_actions - end + delegate :manual_actions, to: :pipeline def create_build(name, stage_idx, when_value = nil) create(:ci_build, diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb index 93147870afe..d03f7505eac 100644 --- a/spec/services/ci/retry_build_service_spec.rb +++ b/spec/services/ci/retry_build_service_spec.rb @@ -12,7 +12,7 @@ describe Ci::RetryBuildService, :services do shared_examples 'build duplication' do let(:build) do - create(:ci_build, :failed, :artifacts, :erased, :trace, + create(:ci_build, :failed, :artifacts_expired, :erased, :trace, :queued, :coverage, pipeline: pipeline) end @@ -38,7 +38,7 @@ describe Ci::RetryBuildService, :services do described_class::IGNORE_ATTRIBUTES + described_class::REJECT_ATTRIBUTES - expect(attributes.size).to eq build.attributes.size + expect(build.attributes.size).to eq(attributes.size) end end diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb index c0af8b8450a..8b1ed6470e4 100644 --- a/spec/services/ci/retry_pipeline_service_spec.rb +++ b/spec/services/ci/retry_pipeline_service_spec.rb @@ -69,6 +69,25 @@ describe Ci::RetryPipelineService, '#execute', :services do end end + context 'when the last stage was skipepd' do + before do + create_build('build 1', :success, 0) + create_build('test 2', :failed, 1) + create_build('report 3', :skipped, 2) + create_build('report 4', :skipped, 2) + end + + it 'retries builds only in the first stage' do + service.execute(pipeline) + + expect(build('build 1')).to be_success + expect(build('test 2')).to be_pending + expect(build('report 3')).to be_created + expect(build('report 4')).to be_created + expect(pipeline.reload).to be_running + end + end + context 'when pipeline contains manual actions' do context 'when there is a canceled manual action in first stage' do before do @@ -90,14 +109,16 @@ describe Ci::RetryPipelineService, '#execute', :services do context 'when there is a skipped manual action in last stage' do before do create_build('rspec 1', :canceled, 0) + create_build('rspec 2', :skipped, 0, :manual) create_build('staging', :skipped, 1, :manual) end - it 'retries canceled job and skips manual action' do + it 'retries canceled job and reprocesses manual actions' do service.execute(pipeline) expect(build('rspec 1')).to be_pending - expect(build('staging')).to be_skipped + expect(build('rspec 2')).to be_skipped + expect(build('staging')).to be_created expect(pipeline.reload).to be_running end end diff --git a/spec/services/create_deployment_service_spec.rb b/spec/services/create_deployment_service_spec.rb index 6fb4d517115..18b964e2453 100644 --- a/spec/services/create_deployment_service_spec.rb +++ b/spec/services/create_deployment_service_spec.rb @@ -9,7 +9,8 @@ describe CreateDeploymentService, services: true do describe '#execute' do let(:options) { nil } let(:params) do - { environment: 'production', + { + environment: 'production', ref: 'master', tag: false, sha: '97de212e80737a608d939f648d959671fb0a0142', @@ -83,10 +84,11 @@ describe CreateDeploymentService, services: true do context 'for environment with invalid name' do let(:params) do - { environment: 'name,with,commas', + { + environment: 'name,with,commas', ref: 'master', tag: false, - sha: '97de212e80737a608d939f648d959671fb0a0142', + sha: '97de212e80737a608d939f648d959671fb0a0142' } end @@ -101,7 +103,8 @@ describe CreateDeploymentService, services: true do context 'when variables are used' do let(:params) do - { environment: 'review-apps/$CI_BUILD_REF_NAME', + { + environment: 'review-apps/$CI_BUILD_REF_NAME', ref: 'master', tag: false, sha: '97de212e80737a608d939f648d959671fb0a0142', diff --git a/spec/services/merge_requests/resolve_service_spec.rb b/spec/services/merge_requests/resolve_service_spec.rb index a0e51681725..d33535d22af 100644 --- a/spec/services/merge_requests/resolve_service_spec.rb +++ b/spec/services/merge_requests/resolve_service_spec.rb @@ -59,20 +59,19 @@ describe MergeRequests::ResolveService do it 'creates a commit with the correct parents' do expect(merge_request.source_branch_head.parents.map(&:id)). - to eq(['1450cd639e0bc6721eb02800169e464f212cde06', - '824be604a34828eb682305f0d963056cfac87b2d']) + to eq(%w(1450cd639e0bc6721eb02800169e464f212cde06 + 824be604a34828eb682305f0d963056cfac87b2d)) end end context 'when the source project is a fork and does not contain the HEAD of the target branch' do let!(:target_head) do - project.repository.commit_file( + project.repository.create_file( user, 'new-file-in-target', '', message: 'Add new file in target', - branch_name: 'conflict-start', - update: false) + branch_name: 'conflict-start') end before do @@ -125,8 +124,8 @@ describe MergeRequests::ResolveService do it 'creates a commit with the correct parents' do expect(merge_request.source_branch_head.parents.map(&:id)). - to eq(['1450cd639e0bc6721eb02800169e464f212cde06', - '824be604a34828eb682305f0d963056cfac87b2d']) + to eq(%w(1450cd639e0bc6721eb02800169e464f212cde06 + 824be604a34828eb682305f0d963056cfac87b2d)) end it 'sets the content to the content given' do diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index af515ad2e0e..62f21049b0b 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -50,7 +50,7 @@ describe Projects::CreateService, '#execute', services: true do context 'error handling' do it 'handles invalid options' do - opts.merge!({ default_branch: 'master' } ) + opts[:default_branch] = 'master' expect(create_project(user, opts)).to eq(nil) end end @@ -67,7 +67,7 @@ describe Projects::CreateService, '#execute', services: true do context 'wiki_enabled false does not create wiki repository directory' do it do - opts.merge!(wiki_enabled: false) + opts[:wiki_enabled] = false project = create_project(user, opts) path = ProjectWiki.new(project, user).send(:path_to_repo) diff --git a/spec/services/protected_branches/create_service_spec.rb b/spec/services/protected_branches/create_service_spec.rb index 7d4eff3b6ef..6ea8f309981 100644 --- a/spec/services/protected_branches/create_service_spec.rb +++ b/spec/services/protected_branches/create_service_spec.rb @@ -6,8 +6,8 @@ describe ProtectedBranches::CreateService, services: true do let(:params) do { name: 'master', - merge_access_levels_attributes: [ { access_level: Gitlab::Access::MASTER } ], - push_access_levels_attributes: [ { access_level: Gitlab::Access::MASTER } ] + merge_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }], + push_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }] } end diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 7f027ae02a2..eca5a418f2a 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -631,7 +631,7 @@ describe SystemNoteService, services: true do jira_service_settings end - noteable_types = ["merge_requests", "commit"] + noteable_types = %w(merge_requests commit) noteable_types.each do |type| context "when noteable is a #{type}" do diff --git a/spec/services/users/destroy_spec.rb b/spec/services/users/destroy_spec.rb index c0bf27c698c..922e82445d0 100644 --- a/spec/services/users/destroy_spec.rb +++ b/spec/services/users/destroy_spec.rb @@ -24,6 +24,54 @@ describe Users::DestroyService, services: true do end end + context "a deleted user's issues" do + let(:project) { create :project } + + before do + project.add_developer(user) + end + + context "for an issue the user has created" do + let!(:issue) { create(:issue, project: project, author: user) } + + before do + service.execute(user) + end + + it 'does not delete the issue' do + expect(Issue.find_by_id(issue.id)).to be_present + end + + it 'migrates the issue so that the "Ghost User" is the issue owner' do + migrated_issue = Issue.find_by_id(issue.id) + + expect(migrated_issue.author).to eq(User.ghost) + end + + it 'blocks the user before migrating issues to the "Ghost User' do + expect(user).to be_blocked + end + end + + context "for an issue the user was assigned to" do + let!(:issue) { create(:issue, project: project, assignee: user) } + + before do + service.execute(user) + end + + it 'does not delete issues the user is assigned to' do + expect(Issue.find_by_id(issue.id)).to be_present + end + + it 'migrates the issue so that it is "Unassigned"' do + migrated_issue = Issue.find_by_id(issue.id) + + expect(migrated_issue.assignee).to be_nil + end + end + end + context "solo owned groups present" do let(:solo_owned) { create(:group) } let(:member) { create(:group_member) } diff --git a/spec/support/cycle_analytics_helpers.rb b/spec/support/cycle_analytics_helpers.rb index 6ed55289ed9..c864a705ca4 100644 --- a/spec/support/cycle_analytics_helpers.rb +++ b/spec/support/cycle_analytics_helpers.rb @@ -9,14 +9,7 @@ module CycleAnalyticsHelpers commit_shas = Array.new(count) do |index| filename = random_git_name - options = { - committer: project.repository.user_to_committer(user), - author: project.repository.user_to_committer(user), - commit: { message: message, branch: branch_name, update_ref: true }, - file: { content: "content", path: filename, update: false } - } - - commit_sha = Gitlab::Git::Blob.commit(project.repository, options) + commit_sha = project.repository.create_file(user, filename, "content", message: message, branch_name: branch_name) project.repository.commit(commit_sha) commit_sha @@ -35,13 +28,12 @@ module CycleAnalyticsHelpers project.repository.add_branch(user, source_branch, 'master') end - sha = project.repository.commit_file( + sha = project.repository.create_file( user, random_git_name, 'content', message: 'commit message', - branch_name: source_branch, - update: false) + branch_name: source_branch) project.repository.commit(sha) opts = { diff --git a/spec/support/issuables_list_metadata_shared_examples.rb b/spec/support/issuables_list_metadata_shared_examples.rb index dac94dfc31e..4c0f556e736 100644 --- a/spec/support/issuables_list_metadata_shared_examples.rb +++ b/spec/support/issuables_list_metadata_shared_examples.rb @@ -3,11 +3,12 @@ shared_examples 'issuables list meta-data' do |issuable_type, action = nil| @issuable_ids = [] 2.times do - if issuable_type == :issue - issuable = create(issuable_type, project: project) - else - issuable = create(issuable_type, title: FFaker::Lorem.sentence, source_project: project, source_branch: FFaker::Name.name) - end + issuable = + if issuable_type == :issue + create(issuable_type, project: project) + else + create(issuable_type, title: FFaker::Lorem.sentence, source_project: project, source_branch: FFaker::Name.name) + end @issuable_ids << issuable.id @@ -21,7 +22,7 @@ shared_examples 'issuables list meta-data' do |issuable_type, action = nil| if action get action else - get :index, namespace_id: project.namespace.path, project_id: project.path + get :index, namespace_id: project.namespace, project_id: project end meta_data = assigns(:issuable_meta_data) diff --git a/spec/support/javascript_fixtures_helpers.rb b/spec/support/javascript_fixtures_helpers.rb index 0b8729db0f9..a982b159b48 100644 --- a/spec/support/javascript_fixtures_helpers.rb +++ b/spec/support/javascript_fixtures_helpers.rb @@ -5,7 +5,7 @@ require 'gitlab/popen' module JavaScriptFixturesHelpers include Gitlab::Popen - FIXTURE_PATH = 'spec/javascripts/fixtures' + FIXTURE_PATH = 'spec/javascripts/fixtures'.freeze # Public: Removes all fixture files from given directory # diff --git a/spec/support/jira_service_helper.rb b/spec/support/jira_service_helper.rb index 929fc0c5182..97ae0b6afc5 100644 --- a/spec/support/jira_service_helper.rb +++ b/spec/support/jira_service_helper.rb @@ -1,5 +1,5 @@ module JiraServiceHelper - JIRA_URL = "http://jira.example.net" + JIRA_URL = "http://jira.example.net".freeze JIRA_API = JIRA_URL + "/rest/api/2" def jira_service_settings diff --git a/spec/support/kubernetes_helpers.rb b/spec/support/kubernetes_helpers.rb index 444612cf871..b5ed71ba3be 100644 --- a/spec/support/kubernetes_helpers.rb +++ b/spec/support/kubernetes_helpers.rb @@ -2,23 +2,24 @@ module KubernetesHelpers include Gitlab::Kubernetes def kube_discovery_body - { "kind" => "APIResourceList", + { + "kind" => "APIResourceList", "resources" => [ { "name" => "pods", "namespaced" => true, "kind" => "Pod" }, - ], + ] } end def kube_pods_body(*pods) { "kind" => "PodList", - "items" => [ kube_pod ], - } + "items" => [kube_pod] } end # This is a partial response, it will have many more elements in reality but # these are the ones we care about at the moment def kube_pod(app: "valid-pod-label") - { "metadata" => { + { + "metadata" => { "name" => "kube-pod", "creationTimestamp" => "2016-11-25T19:55:19Z", "labels" => { "app" => app }, @@ -29,7 +30,7 @@ module KubernetesHelpers { "name" => "container-1" }, ], }, - "status" => { "phase" => "Running" }, + "status" => { "phase" => "Running" } } end diff --git a/spec/support/login_helpers.rb b/spec/support/login_helpers.rb index ad1eed5b369..9ffb00be0b8 100644 --- a/spec/support/login_helpers.rb +++ b/spec/support/login_helpers.rb @@ -15,11 +15,12 @@ module LoginHelpers # user = create(:user) # login_as(user) def login_as(user_or_role) - if user_or_role.kind_of?(User) - @user = user_or_role - else - @user = create(user_or_role) - end + @user = + if user_or_role.is_a?(User) + user_or_role + else + create(user_or_role) + end login_with(@user) end diff --git a/spec/support/markdown_feature.rb b/spec/support/markdown_feature.rb index a79386b5db9..dea0015f105 100644 --- a/spec/support/markdown_feature.rb +++ b/spec/support/markdown_feature.rb @@ -79,8 +79,8 @@ class MarkdownFeature def xproject @xproject ||= begin - namespace = create(:namespace, name: 'cross-reference') - create(:project, namespace: namespace) do |project| + group = create(:group, :nested) + create(:project, namespace: group) do |project| project.team << [user, :developer] end end diff --git a/spec/support/matchers/access_matchers.rb b/spec/support/matchers/access_matchers.rb index ceddb656596..7d238850520 100644 --- a/spec/support/matchers/access_matchers.rb +++ b/spec/support/matchers/access_matchers.rb @@ -38,7 +38,7 @@ module AccessMatchers end def description_for(user, type) - if user.kind_of?(User) + if user.is_a?(User) # User#inspect displays too much information for RSpec's descriptions "be #{type} for the specified user" else diff --git a/spec/support/repo_helpers.rb b/spec/support/repo_helpers.rb index 73f375c481b..e9d5c7b12ae 100644 --- a/spec/support/repo_helpers.rb +++ b/spec/support/repo_helpers.rb @@ -42,7 +42,7 @@ Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> eos ) end - + def another_sample_commit OpenStruct.new( id: "e56497bb5f03a90a51293fc6d516788730953899", @@ -100,13 +100,13 @@ eos } ] - commits = [ - '5937ac0a7beb003549fc5fd26fc247adbce4a52e', - '570e7b2abdd848b95f2f578043fc23bd6f6fd24d', - '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9', - 'd14d6c0abdd253381df51a723d58691b2ee1ab08', - 'c1acaa58bbcbc3eafe538cb8274ba387047b69f8', - ].reverse # last commit is recent one + commits = %w( + 5937ac0a7beb003549fc5fd26fc247adbce4a52e + 570e7b2abdd848b95f2f578043fc23bd6f6fd24d + 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9 + d14d6c0abdd253381df51a723d58691b2ee1ab08 + c1acaa58bbcbc3eafe538cb8274ba387047b69f8 + ).reverse # last commit is recent one OpenStruct.new( source_branch: 'master', diff --git a/spec/support/seed_helper.rb b/spec/support/seed_helper.rb index 03fa0a66b9a..07f81e9c4f3 100644 --- a/spec/support/seed_helper.rb +++ b/spec/support/seed_helper.rb @@ -7,7 +7,7 @@ TEST_MUTABLE_REPO_PATH = File.join(SEED_REPOSITORY_PATH, "mutable-repo.git") TEST_BROKEN_REPO_PATH = File.join(SEED_REPOSITORY_PATH, "broken-repo.git") module SeedHelper - GITLAB_URL = "https://gitlab.com/gitlab-org/gitlab-git-test.git" + GITLAB_URL = "https://gitlab.com/gitlab-org/gitlab-git-test.git".freeze def ensure_seeds if File.exist?(SEED_REPOSITORY_PATH) diff --git a/spec/support/seed_repo.rb b/spec/support/seed_repo.rb index 9f2cd7c67c5..99a500bbbb1 100644 --- a/spec/support/seed_repo.rb +++ b/spec/support/seed_repo.rb @@ -25,64 +25,64 @@ module SeedRepo module BigCommit - ID = "913c66a37b4a45b9769037c55c2d238bd0942d2e" - PARENT_ID = "cfe32cf61b73a0d5e9f13e774abde7ff789b1660" - MESSAGE = "Files, encoding and much more" - AUTHOR_FULL_NAME = "Dmitriy Zaporozhets" + ID = "913c66a37b4a45b9769037c55c2d238bd0942d2e".freeze + PARENT_ID = "cfe32cf61b73a0d5e9f13e774abde7ff789b1660".freeze + MESSAGE = "Files, encoding and much more".freeze + AUTHOR_FULL_NAME = "Dmitriy Zaporozhets".freeze FILES_COUNT = 2 end module Commit - ID = "570e7b2abdd848b95f2f578043fc23bd6f6fd24d" - PARENT_ID = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9" - MESSAGE = "Change some files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n" - AUTHOR_FULL_NAME = "Dmitriy Zaporozhets" - FILES = ["files/ruby/popen.rb", "files/ruby/regex.rb"] + ID = "570e7b2abdd848b95f2f578043fc23bd6f6fd24d".freeze + PARENT_ID = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9".freeze + MESSAGE = "Change some files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n".freeze + AUTHOR_FULL_NAME = "Dmitriy Zaporozhets".freeze + FILES = ["files/ruby/popen.rb", "files/ruby/regex.rb"].freeze FILES_COUNT = 2 - C_FILE_PATH = "files/ruby" - C_FILES = ["popen.rb", "regex.rb", "version_info.rb"] - BLOB_FILE = %{%h3= @key.title\n%hr\n%pre= @key.key\n.actions\n = link_to 'Remove', @key, :confirm => 'Are you sure?', :method => :delete, :class => \"btn danger delete-key\"\n\n\n} - BLOB_FILE_PATH = "app/views/keys/show.html.haml" + C_FILE_PATH = "files/ruby".freeze + C_FILES = ["popen.rb", "regex.rb", "version_info.rb"].freeze + BLOB_FILE = %{%h3= @key.title\n%hr\n%pre= @key.key\n.actions\n = link_to 'Remove', @key, :confirm => 'Are you sure?', :method => :delete, :class => \"btn danger delete-key\"\n\n\n}.freeze + BLOB_FILE_PATH = "app/views/keys/show.html.haml".freeze end module EmptyCommit - ID = "b0e52af38d7ea43cf41d8a6f2471351ac036d6c9" - PARENT_ID = "40f4a7a617393735a95a0bb67b08385bc1e7c66d" - MESSAGE = "Empty commit" - AUTHOR_FULL_NAME = "Rémy Coutable" - FILES = [] + ID = "b0e52af38d7ea43cf41d8a6f2471351ac036d6c9".freeze + PARENT_ID = "40f4a7a617393735a95a0bb67b08385bc1e7c66d".freeze + MESSAGE = "Empty commit".freeze + AUTHOR_FULL_NAME = "Rémy Coutable".freeze + FILES = [].freeze FILES_COUNT = FILES.count end module EncodingCommit - ID = "40f4a7a617393735a95a0bb67b08385bc1e7c66d" - PARENT_ID = "66028349a123e695b589e09a36634d976edcc5e8" - MESSAGE = "Add ISO-8859-encoded file" - AUTHOR_FULL_NAME = "Stan Hu" - FILES = ["encoding/iso8859.txt"] + ID = "40f4a7a617393735a95a0bb67b08385bc1e7c66d".freeze + PARENT_ID = "66028349a123e695b589e09a36634d976edcc5e8".freeze + MESSAGE = "Add ISO-8859-encoded file".freeze + AUTHOR_FULL_NAME = "Stan Hu".freeze + FILES = ["encoding/iso8859.txt"].freeze FILES_COUNT = FILES.count end module FirstCommit - ID = "1a0b36b3cdad1d2ee32457c102a8c0b7056fa863" + ID = "1a0b36b3cdad1d2ee32457c102a8c0b7056fa863".freeze PARENT_ID = nil - MESSAGE = "Initial commit" - AUTHOR_FULL_NAME = "Dmitriy Zaporozhets" - FILES = ["LICENSE", ".gitignore", "README.md"] + MESSAGE = "Initial commit".freeze + AUTHOR_FULL_NAME = "Dmitriy Zaporozhets".freeze + FILES = ["LICENSE", ".gitignore", "README.md"].freeze FILES_COUNT = 3 end module LastCommit - ID = "4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6" - PARENT_ID = "0e1b353b348f8477bdbec1ef47087171c5032cd9" - MESSAGE = "Merge branch 'master' into 'master'" - AUTHOR_FULL_NAME = "Stan Hu" - FILES = ["bin/executable"] + ID = "4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6".freeze + PARENT_ID = "0e1b353b348f8477bdbec1ef47087171c5032cd9".freeze + MESSAGE = "Merge branch 'master' into 'master'".freeze + AUTHOR_FULL_NAME = "Stan Hu".freeze + FILES = ["bin/executable"].freeze FILES_COUNT = FILES.count end module Repo - HEAD = "master" + HEAD = "master".freeze BRANCHES = %w[ feature fix @@ -93,14 +93,14 @@ module SeedRepo gitattributes-updated master merge-test - ] - TAGS = %w[v1.0.0 v1.1.0 v1.2.0 v1.2.1] + ].freeze + TAGS = %w[v1.0.0 v1.1.0 v1.2.0 v1.2.1].freeze end module RubyBlob - ID = "7e3e39ebb9b2bf433b4ad17313770fbe4051649c" - NAME = "popen.rb" - CONTENT = <<-eos + ID = "7e3e39ebb9b2bf433b4ad17313770fbe4051649c".freeze + NAME = "popen.rb".freeze + CONTENT = <<-eos.freeze require 'fileutils' require 'open3' diff --git a/spec/support/select2_helper.rb b/spec/support/select2_helper.rb index d30cc8ff9f2..0d526045012 100644 --- a/spec/support/select2_helper.rb +++ b/spec/support/select2_helper.rb @@ -12,7 +12,7 @@ module Select2Helper def select2(value, options = {}) - raise ArgumentError, 'options must be a Hash' unless options.kind_of?(Hash) + raise ArgumentError, 'options must be a Hash' unless options.is_a?(Hash) selector = options.fetch(:from) diff --git a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb index c64574679b6..81d06dc2a3d 100644 --- a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb +++ b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb @@ -11,7 +11,7 @@ shared_examples 'new issuable record that supports slash commands' do let(:params) { base_params.merge(defined?(default_params) ? default_params : {}).merge(example_params) } let(:issuable) { described_class.new(project, user, params).execute } - before { project.team << [assignee, :master ] } + before { project.team << [assignee, :master] } context 'with labels in command only' do let(:example_params) do diff --git a/spec/support/stub_gitlab_calls.rb b/spec/support/stub_gitlab_calls.rb index 93f96cacc00..a01ef576234 100644 --- a/spec/support/stub_gitlab_calls.rb +++ b/spec/support/stub_gitlab_calls.rb @@ -35,7 +35,7 @@ module StubGitlabCalls { "tags" => tags } ) allow_any_instance_of(ContainerRegistry::Client).to receive(:repository_manifest).and_return( - JSON.load(File.read(Rails.root + 'spec/fixtures/container_registry/tag_manifest.json')) + JSON.parse(File.read(Rails.root + 'spec/fixtures/container_registry/tag_manifest.json')) ) allow_any_instance_of(ContainerRegistry::Client).to receive(:blob).and_return( File.read(Rails.root + 'spec/fixtures/container_registry/config_blob.json') diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index b87232a350b..c3aa3ef44c2 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -38,7 +38,7 @@ module TestEnv 'deleted-image-test' => '6c17798', 'wip' => 'b9238ee', 'csv' => '3dd0896' - } + }.freeze # gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily # need to keep all the branches in sync. @@ -48,7 +48,7 @@ module TestEnv 'master' => '5937ac0', 'remove-submodule' => '2a33e0c', 'conflict-resolvable-fork' => '404fa3f' - } + }.freeze # Test environment # @@ -135,7 +135,7 @@ module TestEnv def copy_repo(project) base_repo_path = File.expand_path(factory_repo_path_bare) - target_repo_path = File.expand_path(project.repository_storage_path + "/#{project.namespace.path}/#{project.path}.git") + target_repo_path = File.expand_path(project.repository_storage_path + "/#{project.full_path}.git") FileUtils.mkdir_p(target_repo_path) FileUtils.cp_r("#{base_repo_path}/.", target_repo_path) FileUtils.chmod_R 0755, target_repo_path @@ -152,7 +152,7 @@ module TestEnv def copy_forked_repo_with_submodules(project) base_repo_path = File.expand_path(forked_repo_path_bare) - target_repo_path = File.expand_path(project.repository_storage_path + "/#{project.namespace.path}/#{project.path}.git") + target_repo_path = File.expand_path(project.repository_storage_path + "/#{project.full_path}.git") FileUtils.mkdir_p(target_repo_path) FileUtils.cp_r("#{base_repo_path}/.", target_repo_path) FileUtils.chmod_R 0755, target_repo_path diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb index 5ef8cf1105b..a60af574a08 100644 --- a/spec/workers/git_garbage_collect_worker_spec.rb +++ b/spec/workers/git_garbage_collect_worker_spec.rb @@ -102,8 +102,8 @@ describe GitGarbageCollectWorker do new_commit_sha = Rugged::Commit.create( rugged, message: "hello world #{SecureRandom.hex(6)}", - author: Gitlab::Git::committer_hash(email: 'foo@bar', name: 'baz'), - committer: Gitlab::Git::committer_hash(email: 'foo@bar', name: 'baz'), + author: Gitlab::Git.committer_hash(email: 'foo@bar', name: 'baz'), + committer: Gitlab::Git.committer_hash(email: 'foo@bar', name: 'baz'), tree: old_commit.tree, parents: [old_commit], ) diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb index 60605460adb..87521ae408e 100644 --- a/spec/workers/repository_fork_worker_spec.rb +++ b/spec/workers/repository_fork_worker_spec.rb @@ -15,24 +15,24 @@ describe RepositoryForkWorker do it "creates a new repository from a fork" do expect(shell).to receive(:fork_repository).with( '/test/path', - project.path_with_namespace, + project.full_path, project.repository_storage_path, - fork_project.namespace.path + fork_project.namespace.full_path ).and_return(true) subject.perform( project.id, '/test/path', - project.path_with_namespace, - fork_project.namespace.path) + project.full_path, + fork_project.namespace.full_path) end it 'flushes various caches' do expect(shell).to receive(:fork_repository).with( '/test/path', - project.path_with_namespace, + project.full_path, project.repository_storage_path, - fork_project.namespace.path + fork_project.namespace.full_path ).and_return(true) expect_any_instance_of(Repository).to receive(:expire_emptiness_caches). @@ -41,8 +41,8 @@ describe RepositoryForkWorker do expect_any_instance_of(Repository).to receive(:expire_exists_cache). and_call_original - subject.perform(project.id, '/test/path', project.path_with_namespace, - fork_project.namespace.path) + subject.perform(project.id, '/test/path', project.full_path, + fork_project.namespace.full_path) end it "handles bad fork" do @@ -53,8 +53,8 @@ describe RepositoryForkWorker do subject.perform( project.id, '/test/path', - project.path_with_namespace, - fork_project.namespace.path) + project.full_path, + fork_project.namespace.full_path) end end end diff --git a/spec/workers/repository_import_worker_spec.rb b/spec/workers/repository_import_worker_spec.rb index f1b1574abf4..c42f3147b7a 100644 --- a/spec/workers/repository_import_worker_spec.rb +++ b/spec/workers/repository_import_worker_spec.rb @@ -20,7 +20,7 @@ describe RepositoryImportWorker do context 'when the import has failed' do it 'hide the credentials that were used in the import URL' do - error = %Q{remote: Not Found fatal: repository 'https://user:pass@test.com/root/repoC.git/' not found } + error = %q{remote: Not Found fatal: repository 'https://user:pass@test.com/root/repoC.git/' not found } expect_any_instance_of(Projects::ImportService).to receive(:execute). and_return({ status: :error, message: error }) diff --git a/yarn.lock b/yarn.lock index ad4b5223d60..cb4ef36119f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -33,6 +33,10 @@ acorn@^3.0.4: version "3.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" +acorn@^4.0.11: + version "4.0.11" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.11.tgz#edcda3bd937e7556410d42ed5860f67399c794c0" + after@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" @@ -1408,6 +1412,10 @@ dropzone@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/dropzone/-/dropzone-4.2.0.tgz#fbe7acbb9918e0706489072ef663effeef8a79f3" +duplexer@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + ecc-jsbn@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" @@ -1418,6 +1426,10 @@ ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" +ejs@^2.5.5: + version "2.5.6" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.6.tgz#479636bfa3fe3b1debd52087f0acb204b4f19c88" + elliptic@^6.0.0: version "6.3.3" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.3.3.tgz#5482d9646d54bcb89fd7d994fc9e2e9568876e3f" @@ -1792,7 +1804,7 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" -express@^4.13.3: +express@^4.13.3, express@^4.14.1: version "4.14.1" resolved "https://registry.yarnpkg.com/express/-/express-4.14.1.tgz#646c237f766f148c2120aff073817b9e4d7e0d33" dependencies: @@ -1893,6 +1905,10 @@ fileset@^2.0.2: glob "^7.0.3" minimatch "^3.0.3" +filesize@^3.5.4: + version "3.5.4" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.5.4.tgz#742fc7fb6aef4ee3878682600c22f840731e1fda" + fill-range@^2.1.0: version "2.2.3" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" @@ -2118,6 +2134,12 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" +gzip-size@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-3.0.0.tgz#546188e9bdc337f673772f81660464b389dce520" + dependencies: + duplexer "^0.1.1" + handle-thing@^1.2.4: version "1.2.5" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" @@ -2627,7 +2649,7 @@ jquery-ujs@^1.2.1: dependencies: jquery ">=1.8.0" -jquery@^2.2.1, jquery@>=1.8.0: +jquery@>=1.8.0, jquery@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.2.1.tgz#3c3e16854ad3d2ac44ac65021b17426d22ad803f" @@ -2824,7 +2846,7 @@ loader-runner@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" -loader-utils@0.2.x, loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0.2.5: +loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0.2.5: version "0.2.16" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.16.tgz#f08632066ed8282835dff88dfb52704765adee6d" dependencies: @@ -2903,7 +2925,7 @@ lodash@^3.8.0: version "3.10.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" -lodash@^4.0.0, lodash@^4.0.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0: +lodash@^4.0.0, lodash@^4.0.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -3216,6 +3238,10 @@ onetime@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" +opener@^1.4.2: + version "1.4.3" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8" + opn@4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95" @@ -3964,7 +3990,7 @@ source-map-support@^0.4.2: dependencies: source-map "^0.5.3" -source-map@0.1.x, source-map@^0.1.41: +source-map@^0.1.41: version "0.1.43" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" dependencies: @@ -4384,9 +4410,9 @@ vue-resource@^0.9.3: version "0.9.3" resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-0.9.3.tgz#ab46e1c44ea219142dcc28ae4043b3b04c80959d" -vue@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/vue/-/vue-2.0.3.tgz#3f7698f83d6ad1f0e35955447901672876c63fde" +vue@^2.1.10: + version "2.1.10" + resolved "https://registry.yarnpkg.com/vue/-/vue-2.1.10.tgz#c9235ca48c7925137be5807832ac4e3ac180427b" watchpack@^1.2.0: version "1.2.1" @@ -4402,6 +4428,21 @@ wbuf@^1.1.0, wbuf@^1.4.0: dependencies: minimalistic-assert "^1.0.0" +webpack-bundle-analyzer@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.3.0.tgz#0d05e96a43033f7cc57f6855b725782ba61e93a4" + dependencies: + acorn "^4.0.11" + chalk "^1.1.3" + commander "^2.9.0" + ejs "^2.5.5" + express "^4.14.1" + filesize "^3.5.4" + gzip-size "^3.0.0" + lodash "^4.17.4" + mkdirp "^0.5.1" + opener "^1.4.2" + webpack-dev-middleware@^1.0.11, webpack-dev-middleware@^1.9.0: version "1.10.0" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.10.0.tgz#7d5be2651e692fddfafd8aaed177c16ff51f0eb8" |