diff options
author | Filipa Lacerda <filipa@gitlab.com> | 2017-08-01 17:45:20 +0100 |
---|---|---|
committer | Filipa Lacerda <filipa@gitlab.com> | 2017-08-01 17:45:20 +0100 |
commit | 335d4a535f737f19ef4959ec4027681d97d1002b (patch) | |
tree | 0d80d58b8f66c2770698d842866ac497d56f9756 | |
parent | 95f9d6d83bcb7a9f45601f828c34301e536fc195 (diff) | |
parent | 4c77c30fbfb3734fd893f7cfe540fa595ea6539c (diff) | |
download | gitlab-ce-335d4a535f737f19ef4959ec4027681d97d1002b.tar.gz |
Merge branch 'master' into issue-discussions-refactor
* master: (76 commits)
Add code review guidelines related to Build [CI skip].
Make time span dropdown style on cycle analytics page consistent
Add 204. Remove duplicated method.
Make sure we didn't commit conflicts
Fix bug in blob test
Always fetch branches before finding the merge base, otherwise we could find an outdated merge base
Fixes dropdown margin in sidebar
Docs add blog articles
Inline script cleanup globals and easy
Add option to use CommitLanguages RPC
CI fixes for gitaly-ruby
fix
Allow logged in users to read user list under public restriction
Small refactor in LegacyNamespace and moved back send_update_instructions
Rename ensure_dir_exist -> ensure_storage_path_exist
Added some extra TODOs for the Legacy Storage refactor
Make disk_path keyword argument and optional
Rename more path_with_namespace -> full_path or disk_path
Rename path_with_namespace -> disk_path when dealing with the filesystem
Rename many path_with_namespace -> full_path
...
272 files changed, 3154 insertions, 1596 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index adde3400107..27fdf6ca0b5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -96,6 +96,7 @@ stages: - export KNAPSACK_GENERATE_REPORT=true - export CACHE_CLASSES=true - cp ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH} + - scripts/gitaly-test-spawn - knapsack rspec "--color --format documentation" artifacts: expire_in: 31d @@ -221,6 +222,7 @@ setup-test-env: - bundle exec rake gettext:po_to_json - bundle exec rake gitlab:assets:compile - bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init' + - scripts/gitaly-test-build # Do not use 'bundle exec' here artifacts: expire_in: 7d paths: @@ -486,6 +488,7 @@ karma: BABEL_ENV: "coverage" CHROME_LOG_FILE: "chrome_debug.log" script: + - scripts/gitaly-test-spawn - bundle exec rake gettext:po_to_json - bundle exec rake karma coverage: '/^Statements *: (\d+\.\d+%)/' diff --git a/.rubocop.yml b/.rubocop.yml index f661a29d9d1..3e4275c271d 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -6,6 +6,7 @@ inherit_from: .rubocop_todo.yml AllCops: TargetRubyVersion: 2.3 + TargetRailsVersion: 4.2 # Cop names are not d§splayed in offense messages by default. Change behavior # by overriding DisplayCopNames, or by giving the -D/--display-cop-names # option. @@ -29,34 +30,230 @@ AllCops: Bundler/OrderedGems: Enabled: false -# Style ####################################################################### +# Layout ###################################################################### # Check indentation of private/protected visibility modifiers. -Style/AccessModifierIndentation: - Enabled: true - -# Check the naming of accessor methods for get_/set_. -Style/AccessorMethodName: - Enabled: false - -# Use alias_method instead of alias. -Style/Alias: - EnforcedStyle: prefer_alias_method +Layout/AccessModifierIndentation: Enabled: true # Align the elements of an array literal if they span more than one line. -Style/AlignArray: +Layout/AlignArray: Enabled: true # Align the elements of a hash literal if they span more than one line. -Style/AlignHash: +Layout/AlignHash: Enabled: true # Here we check if the parameters on a multi-line method call or # definition are aligned. -Style/AlignParameters: +Layout/AlignParameters: + Enabled: false + +# Put end statement of multiline block on its own line. +Layout/BlockEndNewline: + Enabled: true + +# Indentation of when in a case/when/[else/]end. +Layout/CaseIndentation: + Enabled: true + +# Indentation of comments. +Layout/CommentIndentation: + Enabled: true + +# Multi-line method chaining should be done with leading dots. +Layout/DotPosition: + Enabled: true + EnforcedStyle: leading + +# Align elses and elsifs correctly. +Layout/ElseAlignment: + Enabled: true + +# Add an empty line after magic comments to separate them from code. +Layout/EmptyLineAfterMagicComment: + Enabled: false + +# Use empty lines between defs. +Layout/EmptyLineBetweenDefs: + Enabled: true + +# Don't use several empty lines in a row. +Layout/EmptyLines: + Enabled: true + +# Keep blank lines around access modifiers. +Layout/EmptyLinesAroundAccessModifier: + Enabled: true + +# Keeps track of empty lines around block bodies. +Layout/EmptyLinesAroundBlockBody: + Enabled: true + +# Keeps track of empty lines around class bodies. +Layout/EmptyLinesAroundClassBody: + Enabled: true + +# Keeps track of empty lines around exception handling keywords. +Layout/EmptyLinesAroundExceptionHandlingKeywords: + Enabled: false + +# Keeps track of empty lines around method bodies. +Layout/EmptyLinesAroundMethodBody: + Enabled: true + +# Keeps track of empty lines around module bodies. +Layout/EmptyLinesAroundModuleBody: + Enabled: true + +# Use Unix-style line endings. +Layout/EndOfLine: + Enabled: true + +# Checks for a line break before the first parameter in a multi-line method +# parameter definition. +Layout/FirstMethodParameterLineBreak: + Enabled: true + +# Keep indentation straight. +Layout/IndentationConsistency: + Enabled: true + +# Use 2 spaces for indentation. +Layout/IndentationWidth: + Enabled: true + +# Checks the indentation of the first line of the right-hand-side of a +# multi-line assignment. +Layout/IndentAssignment: + Enabled: true + +# This cops checks the indentation of the here document bodies. +Layout/IndentHeredoc: + Enabled: false + +# Comments should start with a space. +Layout/LeadingCommentSpace: + Enabled: true + +# Checks that the closing brace in an array literal is either on the same line +# as the last array element, or a new line. +Layout/MultilineArrayBraceLayout: + Enabled: true + EnforcedStyle: symmetrical + +# Ensures newlines after multiline block do statements. +Layout/MultilineBlockLayout: + Enabled: true + +# Checks that the closing brace in a hash literal is either on the same line as +# the last hash element, or a new line. +Layout/MultilineHashBraceLayout: + Enabled: true + EnforcedStyle: symmetrical + +# Checks that the closing brace in a method call is either on the same line as +# the last method argument, or a new line. +Layout/MultilineMethodCallBraceLayout: + Enabled: false + EnforcedStyle: symmetrical + +# Checks indentation of method calls with the dot operator that span more than +# one line. +Layout/MultilineMethodCallIndentation: + Enabled: false + +# Checks that the closing brace in a method definition is symmetrical with +# respect to the opening brace and the method parameters. +Layout/MultilineMethodDefinitionBraceLayout: + Enabled: false + +# Checks indentation of binary operations that span more than one line. +Layout/MultilineOperationIndentation: + Enabled: true + EnforcedStyle: indented + +# Use spaces after colons. +Layout/SpaceAfterColon: + Enabled: true + +# Use spaces after commas. +Layout/SpaceAfterComma: + Enabled: true + +# Do not put a space between a method name and the opening parenthesis in a +# method definition. +Layout/SpaceAfterMethodName: + Enabled: true + +# Tracks redundant space after the ! operator. +Layout/SpaceAfterNot: + Enabled: true + +# Use spaces after semicolons. +Layout/SpaceAfterSemicolon: + Enabled: true + +# Use space around equals in parameter default +Layout/SpaceAroundEqualsInParameterDefault: + Enabled: true + +# Use a space around keywords if appropriate. +Layout/SpaceAroundKeyword: + Enabled: true + +# Use a single space around operators. +Layout/SpaceAroundOperators: + Enabled: true + +# No spaces before commas. +Layout/SpaceBeforeComma: + Enabled: true + +# Checks for missing space between code and a comment on the same line. +Layout/SpaceBeforeComment: + Enabled: true + +# No spaces before semicolons. +Layout/SpaceBeforeSemicolon: + Enabled: true + +# Checks for spaces inside square brackets. +Layout/SpaceInsideBrackets: + Enabled: true + +# Use spaces inside hash literal braces - or don't. +Layout/SpaceInsideHashLiteralBraces: + Enabled: true + +# No spaces inside range literals. +Layout/SpaceInsideRangeLiteral: + Enabled: true + +# Checks for padding/surrounding spaces inside string interpolation. +Layout/SpaceInsideStringInterpolation: + EnforcedStyle: no_space + Enabled: true + +# No hard tabs. +Layout/Tab: + Enabled: true + +# Checks trailing blank lines and final newline. +Layout/TrailingBlankLines: + Enabled: true + +# Style ####################################################################### + +# Check the naming of accessor methods for get_/set_. +Style/AccessorMethodName: Enabled: false +# Use alias_method instead of alias. +Style/Alias: + EnforcedStyle: prefer_alias_method + Enabled: true + # Whether `and` and `or` are banned only in conditionals (conditionals) # or completely (always). Style/AndOr: @@ -91,10 +288,6 @@ Style/BlockComments: 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: @@ -104,10 +297,6 @@ Style/BracesAroundHashParameters: Style/CaseEquality: Enabled: false -# Indentation of when in a case/when/[else/]end. -Style/CaseIndentation: - Enabled: true - # Checks for uses of character literals. Style/CharacterLiteral: Enabled: true @@ -142,10 +331,6 @@ Style/ColonMethodCall: 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. @@ -164,57 +349,16 @@ Style/DefWithParentheses: Style/Documentation: Enabled: false -# Multi-line method chaining should be done with leading dots. -Style/DotPosition: - Enabled: true - EnforcedStyle: leading - # 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: true - -# Don't use several empty lines in a row. -Style/EmptyLines: - Enabled: true - -# Keep blank lines around access modifiers. -Style/EmptyLinesAroundAccessModifier: - Enabled: true - -# Keeps track of empty lines around block bodies. -Style/EmptyLinesAroundBlockBody: - Enabled: true - -# Keeps track of empty lines around class bodies. -Style/EmptyLinesAroundClassBody: - 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 -# Use Unix-style line endings. -Style/EndOfLine: - Enabled: true - # Favor the use of Fixnum#even? && Fixnum#odd? Style/EvenOdd: Enabled: true @@ -223,11 +367,6 @@ Style/EvenOdd: Style/FileName: Enabled: true -# Checks for a line break before the first parameter in a multi-line method -# parameter definition. -Style/FirstMethodParameterLineBreak: - Enabled: true - # Checks for flip flops. Style/FlipFlop: Enabled: true @@ -236,6 +375,10 @@ Style/FlipFlop: Style/For: Enabled: true +# Use a consistent style for format string tokens. +Style/FormatStringToken: + Enabled: false + # Checks if there is a magic comment to enforce string literals Style/FrozenStringLiteralComment: Enabled: false @@ -261,31 +404,19 @@ Style/IdenticalConditionalBranches: Style/IfWithSemicolon: Enabled: true -# Checks the indentation of the first line of the right-hand-side of a -# multi-line assignment. -Style/IndentAssignment: - Enabled: true - -# Keep indentation straight. -Style/IndentationConsistency: - Enabled: true - -# Use 2 spaces for indentation. -Style/IndentationWidth: - Enabled: true - # Use Kernel#loop for infinite loops. Style/InfiniteLoop: Enabled: true +# Use the inverse method instead of `!.method` +# if an inverse method is defined. +Style/InverseMethods: + Enabled: false + # Use lambda.call(...) instead of lambda.(...). Style/LambdaCall: Enabled: true -# Comments should start with a space. -Style/LeadingCommentSpace: - Enabled: true - # Checks if the method definitions have or don't have parentheses. Style/MethodDefParentheses: Enabled: true @@ -298,55 +429,23 @@ Style/MethodName: Style/ModuleFunction: Enabled: false -# 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: true - EnforcedStyle: symmetrical - # Avoid multi-line chains of blocks. Style/MultilineBlockChain: Enabled: true -# Ensures newlines after multiline block do statements. -Style/MultilineBlockLayout: - Enabled: true - -# 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: true - EnforcedStyle: symmetrical - # Do not use then for multi-line if/unless. Style/MultilineIfThen: Enabled: true -# Checks that the closing brace in a method call is either on the same line as -# the last method argument, or a new line. -Style/MultilineMethodCallBraceLayout: - Enabled: false - EnforcedStyle: symmetrical - -# Checks indentation of method calls with the dot operator that span more than -# one line. -Style/MultilineMethodCallIndentation: - Enabled: false - -# Checks that the closing brace in a method definition is symmetrical with -# respect to the opening brace and the method parameters. -Style/MultilineMethodDefinitionBraceLayout: - Enabled: false - -# Checks indentation of binary operations that span more than one line. -Style/MultilineOperationIndentation: - Enabled: true - EnforcedStyle: indented - # Avoid multi-line `? :` (the ternary operator), use if/unless instead. Style/MultilineTernaryOperator: Enabled: true +# Avoid comparing a variable with multiple items in a conditional, +# use Array#include? instead. +Style/MultipleComparison: + Enabled: false + # This cop checks whether some constant value isn't a # mutable literal (e.g. array or hash). Style/MutableConstant: @@ -421,68 +520,6 @@ Style/SignalException: EnforcedStyle: only_raise Enabled: true -# Use spaces after colons. -Style/SpaceAfterColon: - Enabled: true - -# Use spaces after commas. -Style/SpaceAfterComma: - Enabled: true - -# Do not put a space between a method name and the opening parenthesis in a -# method definition. -Style/SpaceAfterMethodName: - Enabled: true - -# Tracks redundant space after the ! operator. -Style/SpaceAfterNot: - Enabled: true - -# Use spaces after semicolons. -Style/SpaceAfterSemicolon: - Enabled: true - -# Use space around equals in parameter default -Style/SpaceAroundEqualsInParameterDefault: - Enabled: true - -# Use a space around keywords if appropriate. -Style/SpaceAroundKeyword: - Enabled: true - -# Use a single space around operators. -Style/SpaceAroundOperators: - Enabled: true - -# No spaces before commas. -Style/SpaceBeforeComma: - Enabled: true - -# Checks for missing space between code and a comment on the same line. -Style/SpaceBeforeComment: - Enabled: true - -# No spaces before semicolons. -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 - -# No spaces inside range literals. -Style/SpaceInsideRangeLiteral: - Enabled: true - -# Checks for padding/surrounding spaces inside string interpolation. -Style/SpaceInsideStringInterpolation: - EnforcedStyle: no_space - Enabled: true - # Check for the usage of parentheses around stabby lambda arguments. Style/StabbyLambdaParentheses: EnforcedStyle: require_parentheses @@ -498,13 +535,9 @@ Style/StringMethods: intern: to_sym Enabled: true -# No hard tabs. -Style/Tab: - Enabled: true - -# Checks trailing blank lines and final newline. -Style/TrailingBlankLines: - Enabled: true +# Use %i or %I for arrays of symbols. +Style/SymbolArray: + Enabled: false # This cop checks for trailing comma in array and hash literals. Style/TrailingCommaInLiteral: @@ -553,6 +586,10 @@ Style/WhileUntilModifier: Style/WordArray: Enabled: true +# Do not use literals as the first operand of a comparison. +Style/YodaCondition: + Enabled: false + # Use `proc` instead of `Proc.new`. Style/Proc: Enabled: true @@ -608,6 +645,11 @@ Metrics/PerceivedComplexity: # Lint ######################################################################## +# Checks for ambiguous block association with method when param passed without +# parentheses. +Lint/AmbiguousBlockAssociation: + Enabled: false + # Checks for ambiguous operators in the first argument of a method invocation # without parentheses. Lint/AmbiguousOperator: @@ -809,6 +851,10 @@ Lint/Void: # Performance ################################################################# +# Use `caller(n..n)` instead of `caller`. +Performance/Caller: + Enabled: false + # Use `casecmp` rather than `downcase ==`. Performance/Casecmp: Enabled: true @@ -883,6 +929,14 @@ Rails/ActionFilter: Enabled: true EnforcedStyle: action +# Check that models subclass ApplicationRecord. +Rails/ApplicationRecord: + Enabled: false + +# Enforce using `blank?` and `present?`. +Rails/Blank: + Enabled: false + # Checks the correct usage of date aware methods, such as `Date.today`, # `Date.current`, etc. Rails/Date: @@ -939,10 +993,18 @@ Rails/OutputSafety: Rails/PluralizationGrammar: Enabled: true +# Enforce using `blank?` and `present?`. +Rails/Present: + Enabled: false + # Checks for `read_attribute(:attr)` and `write_attribute(:attr, val)`. Rails/ReadWriteAttribute: Enabled: false +# Do not assign relative date to constants. +Rails/RelativeDateConstant: + Enabled: false + # Checks the arguments of ActiveRecord scopes. Rails/ScopeArgs: Enabled: true diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 2ec558e274f..9caef3bde08 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,26 +1,89 @@ # This configuration was generated by # `rubocop --auto-gen-config --exclude-limit 0` -# on 2017-04-07 20:17:35 -0400 using RuboCop version 0.47.1. +# on 2017-07-10 01:48:30 +0900 using RuboCop version 0.49.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: 233 +# Offense count: 181 +# Cop supports --auto-correct. +# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment. +Layout/ExtraSpacing: + Enabled: false + +# Offense count: 119 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. +# SupportedStyles: special_inside_parentheses, consistent, align_brackets +Layout/IndentArray: + Enabled: false + +# Offense count: 208 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. +# SupportedStyles: special_inside_parentheses, consistent, align_braces +Layout/IndentHash: + Enabled: false + +# Offense count: 174 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: space, no_space +Layout/SpaceBeforeBlockBraces: + Enabled: false + +# Offense count: 8 +# Cop supports --auto-correct. +# Configuration parameters: AllowForAlignment. +Layout/SpaceBeforeFirstArg: + Enabled: false + +# Offense count: 64 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: require_no_space, require_space +Layout/SpaceInLambdaLiteral: + Enabled: false + +# Offense count: 256 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters. +# SupportedStyles: space, no_space +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceInsideBlockBraces: + Enabled: false + +# Offense count: 135 +# Cop supports --auto-correct. +Layout/SpaceInsideParens: + Enabled: false + +# Offense count: 14 +# Cop supports --auto-correct. +Layout/SpaceInsidePercentLiteralDelimiters: + Enabled: false + +# Offense count: 89 +# Cop supports --auto-correct. +Layout/TrailingWhitespace: + Enabled: false + +# Offense count: 272 RSpec/EmptyLineAfterFinalLet: Enabled: false -# Offense count: 167 +# Offense count: 181 RSpec/EmptyLineAfterSubject: Enabled: false -# Offense count: 72 +# Offense count: 78 # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: implicit, each, example RSpec/HookArgument: Enabled: false -# Offense count: 11 +# Offense count: 9 # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: it_behaves_like, it_should_behave_like RSpec/ItBehavesLike: @@ -30,19 +93,19 @@ RSpec/ItBehavesLike: RSpec/IteratedExpectation: Enabled: false -# Offense count: 3 +# Offense count: 2 RSpec/OverwritingSetup: Enabled: false -# Offense count: 34 +# Offense count: 36 RSpec/RepeatedExample: Enabled: false -# Offense count: 43 +# Offense count: 86 RSpec/ScatteredLet: Enabled: false -# Offense count: 32 +# Offense count: 20 RSpec/ScatteredSetup: Enabled: false @@ -50,7 +113,7 @@ RSpec/ScatteredSetup: RSpec/SharedContext: Enabled: false -# Offense count: 150 +# Offense count: 115 Rails/FilePath: Enabled: false @@ -60,90 +123,71 @@ Rails/FilePath: Rails/ReversibleMigration: Enabled: false -# Offense count: 302 +# Offense count: 336 # 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: 7 +# Offense count: 11 # Cop supports --auto-correct. Security/YAMLLoad: Enabled: false -# Offense count: 59 +# Offense count: 58 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: percent_q, bare_percent Style/BarePercentLiterals: Enabled: false -# Offense count: 5 +# Offense count: 6 # Cop supports --auto-correct. Style/EachWithObject: Enabled: false -# Offense count: 28 +# Offense count: 31 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: empty, nil, both Style/EmptyElse: Enabled: false -# Offense count: 4 +# Offense count: 9 # Cop supports --auto-correct. Style/EmptyLiteral: Enabled: false -# Offense count: 59 +# Offense count: 78 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: compact, expanded Style/EmptyMethod: Enabled: false -# Offense count: 214 +# Offense count: 23 # Cop supports --auto-correct. -# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment. -Style/ExtraSpacing: - Enabled: false - -# Offense count: 9 # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: format, sprintf, percent Style/FormatString: Enabled: false -# Offense count: 285 +# Offense count: 301 # Configuration parameters: MinBodyLength. Style/GuardClause: Enabled: false -# Offense count: 16 +# Offense count: 18 Style/IfInsideElse: Enabled: false -# Offense count: 186 +# Offense count: 182 # Cop supports --auto-correct. # Configuration parameters: MaxLineLength. Style/IfUnlessModifier: Enabled: false -# Offense count: 99 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. -# SupportedStyles: special_inside_parentheses, consistent, align_brackets -Style/IndentArray: - Enabled: false - -# Offense count: 160 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. -# SupportedStyles: special_inside_parentheses, consistent, align_braces -Style/IndentHash: - Enabled: false - -# Offense count: 50 +# Offense count: 52 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: line_count_dependent, lambda, literal @@ -155,63 +199,63 @@ Style/Lambda: Style/LineEndConcatenation: Enabled: false -# Offense count: 34 +# Offense count: 40 # Cop supports --auto-correct. Style/MethodCallWithoutArgsParentheses: Enabled: false -# Offense count: 10 +# Offense count: 13 Style/MethodMissing: Enabled: false -# Offense count: 3 +# Offense count: 6 # Cop supports --auto-correct. Style/MultilineIfModifier: Enabled: false -# Offense count: 24 +# Offense count: 26 # Cop supports --auto-correct. Style/NestedParenthesizedCalls: Enabled: false -# Offense count: 18 +# Offense count: 20 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles. # SupportedStyles: skip_modifier_ifs, always Style/Next: Enabled: false -# Offense count: 37 +# Offense count: 45 # Cop supports --auto-correct. # Configuration parameters: EnforcedOctalStyle, SupportedOctalStyles. # SupportedOctalStyles: zero_with_o, zero_only Style/NumericLiteralPrefix: Enabled: false -# Offense count: 88 +# Offense count: 98 # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles. # SupportedStyles: predicate, comparison Style/NumericPredicate: Enabled: false -# Offense count: 36 +# Offense count: 42 # Cop supports --auto-correct. Style/ParallelAssignment: Enabled: false -# Offense count: 570 +# Offense count: 800 # Cop supports --auto-correct. # Configuration parameters: PreferredDelimiters. Style/PercentLiteralDelimiters: Enabled: false -# Offense count: 14 +# Offense count: 15 # Cop supports --auto-correct. Style/PerlBackrefs: Enabled: false -# Offense count: 83 +# Offense count: 105 # Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist. # NamePrefix: is_, has_, have_ # NamePrefixBlacklist: is_, has_, have_ @@ -219,47 +263,47 @@ Style/PerlBackrefs: Style/PredicateName: Enabled: false -# Offense count: 65 +# Offense count: 58 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: compact, exploded Style/RaiseArgs: Enabled: false -# Offense count: 5 +# Offense count: 6 # Cop supports --auto-correct. Style/RedundantBegin: Enabled: false -# Offense count: 32 +# Offense count: 37 # Cop supports --auto-correct. Style/RedundantFreeze: Enabled: false -# Offense count: 15 +# Offense count: 14 # Cop supports --auto-correct. # Configuration parameters: AllowMultipleReturnValues. Style/RedundantReturn: Enabled: false -# Offense count: 382 +# Offense count: 406 # Cop supports --auto-correct. Style/RedundantSelf: Enabled: false -# Offense count: 111 +# Offense count: 115 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes. # SupportedStyles: slashes, percent_r, mixed Style/RegexpLiteral: Enabled: false -# Offense count: 24 +# Offense count: 29 # Cop supports --auto-correct. Style/RescueModifier: Enabled: false -# Offense count: 7 +# Offense count: 8 # Cop supports --auto-correct. Style/SelfAssignment: Enabled: false @@ -270,101 +314,58 @@ Style/SelfAssignment: Style/SingleLineMethods: Enabled: false -# Offense count: 168 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: space, no_space -Style/SpaceBeforeBlockBraces: - Enabled: false - -# Offense count: 8 -# Cop supports --auto-correct. -# Configuration parameters: AllowForAlignment. -Style/SpaceBeforeFirstArg: - Enabled: false - -# Offense count: 46 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: require_no_space, require_space -Style/SpaceInLambdaLiteral: - Enabled: false - -# Offense count: 229 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters. -# SupportedStyles: space, no_space -# SupportedStylesForEmptyBraces: space, no_space -Style/SpaceInsideBlockBraces: - Enabled: false - -# Offense count: 116 -# Cop supports --auto-correct. -Style/SpaceInsideParens: - Enabled: false - -# Offense count: 12 -# Cop supports --auto-correct. -Style/SpaceInsidePercentLiteralDelimiters: - Enabled: false - -# Offense count: 57 +# Offense count: 64 # Cop supports --auto-correct. # Configuration parameters: SupportedStyles. # SupportedStyles: use_perl_names, use_english_names Style/SpecialGlobalVars: EnforcedStyle: use_perl_names -# Offense count: 42 +# Offense count: 44 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: single_quotes, double_quotes Style/StringLiteralsInInterpolation: Enabled: false -# Offense count: 64 +# Offense count: 84 # Cop supports --auto-correct. # Configuration parameters: IgnoredMethods. # IgnoredMethods: respond_to, define_method Style/SymbolProc: Enabled: false -# Offense count: 6 +# Offense count: 8 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, AllowSafeAssignment. # SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex Style/TernaryParentheses: Enabled: false -# Offense count: 18 +# Offense count: 17 # Cop supports --auto-correct. # Configuration parameters: AllowNamedUnderscoreVariables. Style/TrailingUnderscoreVariable: Enabled: false -# Offense count: 78 -# Cop supports --auto-correct. -Style/TrailingWhitespace: - Enabled: false - -# Offense count: 3 +# Offense count: 4 # Cop supports --auto-correct. # Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, Whitelist. # Whitelist: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym Style/TrivialAccessors: Enabled: false -# Offense count: 6 +# Offense count: 5 # Cop supports --auto-correct. Style/UnlessElse: Enabled: false -# Offense count: 24 +# Offense count: 28 # Cop supports --auto-correct. Style/UnneededInterpolation: Enabled: false -# Offense count: 8 +# Offense count: 11 # Cop supports --auto-correct. Style/ZeroLengthPredicate: Enabled: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 412cd86bad6..706823ed693 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 9.4.3 (2017-07-31) + +- Fix Prometheus client PID reuse bug. !13130 +- Improve deploy environment chatops slash command. !13150 +- Fix asynchronous javascript paths when GitLab is installed under a relative URL. !13165 +- Fix LDAP authentication to Git repository or container registry. +- Fixed new navigation breadcrumb title on help pages. +- Ensure filesystem metrics test files are deleted. +- Properly affixes nav bar in job view in microsoft edge. + ## 9.4.2 (2017-07-28) - Fix job merge request link to a forked source project. !12965 diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index d21d277be51..4e8f395fa5e 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0.25.0 +0.26.0 @@ -339,8 +339,8 @@ group :development, :test do gem 'spring-commands-rspec', '~> 1.0.4' gem 'spring-commands-spinach', '~> 1.1.0' - gem 'rubocop', '~> 0.47.1', require: false - gem 'rubocop-rspec', '~> 1.15.0', require: false + gem 'rubocop', '~> 0.49.1', require: false + gem 'rubocop-rspec', '~> 1.15.1', require: false gem 'scss_lint', '~> 0.54.0', require: false gem 'haml_lint', '~> 0.21.0', require: false gem 'simplecov', '~> 0.14.0', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 627750e2c1d..5a327a14c4a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -323,7 +323,7 @@ GEM multi_json (~> 1.10) retriable (~> 1.4) signet (~> 0.6) - google-protobuf (3.2.0.2) + google-protobuf (3.3.0) googleauth (0.5.1) faraday (~> 0.9) jwt (~> 1.4) @@ -545,6 +545,7 @@ GEM rubypants (~> 0.2) orm_adapter (0.5.0) os (0.9.6) + parallel (1.11.2) paranoia (2.3.1) activerecord (>= 4.0, < 5.2) parser (2.4.0.0) @@ -730,13 +731,14 @@ GEM pg rails sqlite3 - rubocop (0.47.1) + rubocop (0.49.1) + parallel (~> 1.10) 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.15.0) + rubocop-rspec (1.15.1) rubocop (>= 0.42.0) ruby-fogbugz (0.2.1) crack (~> 0.4) @@ -868,7 +870,7 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.7.2) - unicode-display_width (1.1.3) + unicode-display_width (1.3.0) unicorn (5.1.0) kgio (~> 2.6) raindrops (~> 0.7) @@ -1078,8 +1080,8 @@ DEPENDENCIES rspec-retry (~> 0.4.5) rspec-set (~> 0.1.3) rspec_profiling (~> 0.0.5) - rubocop (~> 0.47.1) - rubocop-rspec (~> 1.15.0) + rubocop (~> 0.49.1) + rubocop-rspec (~> 1.15.1) ruby-fogbugz (~> 0.2.1) ruby-prof (~> 0.16.2) ruby_parser (~> 3.8) diff --git a/PROCESS.md b/PROCESS.md index 3b97a4e8c75..2b3d142bf77 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -128,7 +128,7 @@ information, see ### After the 7th -Once the stable branch is frozen, only fixes for regressions (bugs introduced in that same release) +Once the stable branch is frozen, only fixes for [regressions](#regressions) and security issues will be cherry-picked into the stable branch. Any merge requests cherry-picked into the stable branch for a previous release will also be picked into the latest stable branch. These fixes will be shipped in the next RC for that release if it is before the 22nd. @@ -158,6 +158,24 @@ release should have the correct milestone assigned _and_ have the label Merge requests without a milestone and this label will not be merged into any stable branches. +### Regressions + +A regression for a particular monthly release is a bug that exists in that +release, but wasn't present in the release before. This includes bugs in +features that were only added in that monthly release. Every regression **must** +have the milestone of the release it was introduced in - if a regression doesn't +have a milestone, it might be 'just' a bug! + +For instance, if 10.5.0 adds a feature, and that feature doesn't work correctly, +then this is a regression in 10.5. If 10.5.1 then fixes that, but 10.5.3 somehow +reintroduces the bug, then this bug is still a regression in 10.5. + +Because GitLab.com runs release candidates of new releases, a regression can be +reported in a release before its 'official' release date on the 22nd of the +month. When we say 'the most recent monthly release', this can refer to either +the version currently running on GitLab.com, or the most recent version +available in the package repositories. + ## Release retrospective and kickoff ### Retrospective diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js index 1c64ccf536f..b5500ac116f 100644 --- a/app/assets/javascripts/blob_edit/blob_bundle.js +++ b/app/assets/javascripts/blob_edit/blob_bundle.js @@ -8,6 +8,7 @@ import BlobFileDropzone from '../blob/blob_file_dropzone'; $(() => { const editBlobForm = $('.js-edit-blob-form'); const uploadBlobForm = $('.js-upload-blob-form'); + const deleteBlobForm = $('.js-delete-blob-form'); if (editBlobForm.length) { const urlRoot = editBlobForm.data('relative-url-root'); @@ -30,4 +31,8 @@ $(() => { '.btn-upload-file', ); } + + if (deleteBlobForm.length) { + new NewCommitForm(deleteBlobForm); + } }); diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js index 1dfa064acfd..b3d3bbcf84f 100644 --- a/app/assets/javascripts/build.js +++ b/app/assets/javascripts/build.js @@ -64,7 +64,7 @@ window.Build = (function () { $(window) .off('scroll') .on('scroll', () => { - const contentHeight = this.$buildTraceOutput.prop('scrollHeight'); + const contentHeight = this.$buildTraceOutput.height(); if (contentHeight > this.windowSize) { // means the user did not scroll, the content was updated. this.windowSize = contentHeight; @@ -105,16 +105,17 @@ window.Build = (function () { }; Build.prototype.canScroll = function () { - return document.body.scrollHeight > window.innerHeight; + return $(document).height() > $(window).height(); }; Build.prototype.toggleScroll = function () { - const currentPosition = document.body.scrollTop; - const windowHeight = window.innerHeight; + const currentPosition = $(document).scrollTop(); + const scrollHeight = $(document).height(); + const windowHeight = $(window).height(); if (this.canScroll()) { if (currentPosition > 0 && - (document.body.scrollHeight - currentPosition !== windowHeight)) { + (scrollHeight - currentPosition !== windowHeight)) { // User is in the middle of the log this.toggleDisableButton(this.$scrollTopBtn, false); @@ -124,7 +125,7 @@ window.Build = (function () { this.toggleDisableButton(this.$scrollTopBtn, true); this.toggleDisableButton(this.$scrollBottomBtn, false); - } else if (document.body.scrollHeight - currentPosition === windowHeight) { + } else if (scrollHeight - currentPosition === windowHeight) { // User is at the bottom of the build log. this.toggleDisableButton(this.$scrollTopBtn, false); @@ -137,7 +138,7 @@ window.Build = (function () { }; Build.prototype.scrollDown = function () { - document.body.scrollTop = document.body.scrollHeight; + $(document).scrollTop($(document).height()); }; Build.prototype.scrollToBottom = function () { @@ -147,7 +148,7 @@ window.Build = (function () { }; Build.prototype.scrollToTop = function () { - document.body.scrollTop = 0; + $(document).scrollTop(0); this.hasBeenScrolled = true; this.toggleScroll(); }; @@ -178,7 +179,7 @@ window.Build = (function () { this.state = log.state; } - this.windowSize = this.$buildTraceOutput.prop('scrollHeight'); + this.windowSize = this.$buildTraceOutput.height(); if (log.append) { this.$buildTraceOutput.append(log.html); diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 9d706b5ba59..a2664c0301e 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -8,6 +8,8 @@ /* global LabelsSelect */ /* global MilestoneSelect */ /* global Commit */ +/* global CommitsList */ +/* global NewBranchForm */ /* global NotificationsForm */ /* global NotificationsDropdown */ /* global GroupAvatar */ @@ -18,15 +20,20 @@ /* global Search */ /* global Admin */ /* global NamespaceSelects */ +/* global NewCommitForm */ +/* global NewBranchForm */ /* global Project */ /* global ProjectAvatar */ /* global MergeRequest */ /* global Compare */ /* global CompareAutocomplete */ +/* global ProjectFindFile */ /* global ProjectNew */ /* global ProjectShow */ +/* global ProjectImport */ /* global Labels */ /* global Shortcuts */ +/* global ShortcutsFindFile */ /* global Sidebar */ /* global ShortcutsWiki */ @@ -194,7 +201,6 @@ import GpgBadges from './gpg_badges'; break; case 'explore:groups:index': new GroupsList(); - const landingElement = document.querySelector('.js-explore-groups-landing'); if (!landingElement) break; const exploreGroupsLanding = new Landing( @@ -217,6 +223,10 @@ import GpgBadges from './gpg_badges'; case 'projects:compare:show': new gl.Diff(); break; + case 'projects:branches:new': + case 'projects:branches:create': + new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML)); + break; case 'projects:branches:index': gl.AjaxLoadingSpinner.init(); new DeleteModal(); @@ -258,7 +268,7 @@ import GpgBadges from './gpg_badges'; case 'projects:tags:new': new ZenMode(); new gl.GLForm($('.tag-form'), true); - new RefSelectDropdown($('.js-branch-select'), window.gl.availableRefs); + new RefSelectDropdown($('.js-branch-select')); break; case 'projects:snippets:show': initNotes(); @@ -304,18 +314,23 @@ import GpgBadges from './gpg_badges'; container: '.js-commit-pipeline-graph', }).bindEvents(); initNotes(); + $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); break; case 'projects:commit:pipelines': new MiniPipelineGraph({ container: '.js-commit-pipeline-graph', }).bindEvents(); + $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); break; - case 'projects:commits:show': + case 'projects:activity': + new gl.Activities(); shortcut_handler = new ShortcutsNavigation(); - GpgBadges.fetch(); break; - case 'projects:activity': + case 'projects:commits:show': + CommitsList.init(document.querySelector('.js-project-commits-show').dataset.commitsLimit); + new gl.Activities(); shortcut_handler = new ShortcutsNavigation(); + GpgBadges.fetch(); break; case 'projects:show': shortcut_handler = new ShortcutsNavigation(); @@ -330,6 +345,12 @@ import GpgBadges from './gpg_badges'; case 'projects:edit': setupProjectEdit(); break; + case 'projects:imports:show': + new ProjectImport(); + break; + case 'projects:pipelines:new': + new NewBranchForm($('.js-new-pipeline-form')); + break; case 'projects:pipelines:builds': case 'projects:pipelines:failures': case 'projects:pipelines:show': @@ -383,8 +404,19 @@ import GpgBadges from './gpg_badges'; shortcut_handler = new ShortcutsNavigation(); new TreeView(); new BlobViewer(); + new NewCommitForm($('.js-create-dir-form')); + $('#tree-slider').waitForImages(function() { + gl.utils.ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath); + }); break; case 'projects:find_file:show': + const findElement = document.querySelector('.js-file-finder'); + const projectFindFile = new ProjectFindFile($(".file-finder-holder"), { + url: findElement.dataset.fileFindUrl, + treeUrl: findElement.dataset.findTreeUrl, + blobUrlTemplate: findElement.dataset.blobUrlTemplate, + }); + new ShortcutsFindFile(projectFindFile); shortcut_handler = true; break; case 'projects:blob:show': @@ -540,6 +572,7 @@ import GpgBadges from './gpg_badges'; shortcut_handler = new ShortcutsWiki(); new ZenMode(); new gl.GLForm($('.wiki-form'), true); + new Sidebar(); break; case 'snippets': shortcut_handler = new ShortcutsNavigation(); diff --git a/app/assets/javascripts/graphs/graphs_bundle.js b/app/assets/javascripts/graphs/graphs_bundle.js index a433c7ba8f0..534bc535bb6 100644 --- a/app/assets/javascripts/graphs/graphs_bundle.js +++ b/app/assets/javascripts/graphs/graphs_bundle.js @@ -1,6 +1,4 @@ import Chart from 'vendor/Chart'; -import ContributorsStatGraph from './stat_graph_contributors'; // export to global scope window.Chart = Chart; -window.ContributorsStatGraph = ContributorsStatGraph; diff --git a/app/assets/javascripts/graphs/graphs_charts.js b/app/assets/javascripts/graphs/graphs_charts.js new file mode 100644 index 00000000000..279ffef770f --- /dev/null +++ b/app/assets/javascripts/graphs/graphs_charts.js @@ -0,0 +1,63 @@ +import Chart from 'vendor/Chart'; + +document.addEventListener('DOMContentLoaded', () => { + const projectChartData = JSON.parse(document.getElementById('projectChartData').innerHTML); + + const responsiveChart = (selector, data) => { + const options = { + scaleOverlay: true, + responsive: true, + pointHitDetectionRadius: 2, + maintainAspectRatio: false, + }; + // get selector by context + const ctx = selector.get(0).getContext('2d'); + // pointing parent container to make chart.js inherit its width + const container = $(selector).parent(); + const generateChart = () => { + selector.attr('width', $(container).width()); + if (window.innerWidth < 768) { + // Scale fonts if window width lower than 768px (iPad portrait) + options.scaleFontSize = 8; + } + return new Chart(ctx).Bar(data, options); + }; + // enabling auto-resizing + $(window).resize(generateChart); + return generateChart(); + }; + + const chartData = (keys, values) => { + const data = { + labels: keys, + datasets: [{ + fillColor: 'rgba(220,220,220,0.5)', + strokeColor: 'rgba(220,220,220,1)', + barStrokeWidth: 1, + barValueSpacing: 1, + barDatasetSpacing: 1, + data: values, + }], + }; + return data; + }; + + const hourData = chartData(projectChartData.hour.keys, projectChartData.hour.values); + responsiveChart($('#hour-chart'), hourData); + + const dayData = chartData(projectChartData.weekDays.keys, projectChartData.weekDays.values); + responsiveChart($('#weekday-chart'), dayData); + + const monthData = chartData(projectChartData.month.keys, projectChartData.month.values); + responsiveChart($('#month-chart'), monthData); + + const data = projectChartData.languages; + const ctx = $('#languages-chart').get(0).getContext('2d'); + const options = { + scaleOverlay: true, + responsive: true, + maintainAspectRatio: false, + }; + + new Chart(ctx).Pie(data, options); +}); diff --git a/app/assets/javascripts/graphs/graphs_show.js b/app/assets/javascripts/graphs/graphs_show.js new file mode 100644 index 00000000000..36bad6db3e1 --- /dev/null +++ b/app/assets/javascripts/graphs/graphs_show.js @@ -0,0 +1,21 @@ +import ContributorsStatGraph from './stat_graph_contributors'; + +document.addEventListener('DOMContentLoaded', () => { + $.ajax({ + type: 'GET', + url: document.querySelector('.js-graphs-show').dataset.projectGraphPath, + dataType: 'json', + success(data) { + const graph = new ContributorsStatGraph(); + graph.init(data); + + $('#brush_change').change(() => { + graph.change_date_header(); + graph.redraw_authors(); + }); + + $('.stat-graph').fadeIn(); + $('.loading-graph').hide(); + }, + }); +}); diff --git a/app/assets/javascripts/pipelines/pipelines_charts.js b/app/assets/javascripts/pipelines/pipelines_charts.js new file mode 100644 index 00000000000..001faf4be33 --- /dev/null +++ b/app/assets/javascripts/pipelines/pipelines_charts.js @@ -0,0 +1,38 @@ +import Chart from 'vendor/Chart'; + +document.addEventListener('DOMContentLoaded', () => { + const chartData = JSON.parse(document.getElementById('pipelinesChartsData').innerHTML); + const buildChart = (chartScope) => { + const data = { + labels: chartScope.labels, + datasets: [{ + fillColor: '#7f8fa4', + strokeColor: '#7f8fa4', + pointColor: '#7f8fa4', + pointStrokeColor: '#EEE', + data: chartScope.totalValues, + }, + { + fillColor: '#44aa22', + strokeColor: '#44aa22', + pointColor: '#44aa22', + pointStrokeColor: '#fff', + data: chartScope.successValues, + }, + ], + }; + const ctx = $(`#${chartScope.scope}Chart`).get(0).getContext('2d'); + const options = { + scaleOverlay: true, + responsive: true, + maintainAspectRatio: false, + }; + if (window.innerWidth < 768) { + // Scale fonts if window width lower than 768px (iPad portrait) + options.scaleFontSize = 8; + } + new Chart(ctx).Line(data, options); + }; + + chartData.forEach(scope => buildChart(scope)); +}); diff --git a/app/assets/javascripts/pipelines/pipelines_times.js b/app/assets/javascripts/pipelines/pipelines_times.js new file mode 100644 index 00000000000..b5e7a0e53d9 --- /dev/null +++ b/app/assets/javascripts/pipelines/pipelines_times.js @@ -0,0 +1,27 @@ +import Chart from 'vendor/Chart'; + +document.addEventListener('DOMContentLoaded', () => { + const chartData = JSON.parse(document.getElementById('pipelinesTimesChartsData').innerHTML); + const data = { + labels: chartData.labels, + datasets: [{ + fillColor: 'rgba(220,220,220,0.5)', + strokeColor: 'rgba(220,220,220,1)', + barStrokeWidth: 1, + barValueSpacing: 1, + barDatasetSpacing: 1, + data: chartData.values, + }], + }; + const ctx = $('#build_timesChart').get(0).getContext('2d'); + const options = { + scaleOverlay: true, + responsive: true, + maintainAspectRatio: false, + }; + if (window.innerWidth < 768) { + // Scale fonts if window width lower than 768px (iPad portrait) + options.scaleFontSize = 8; + } + new Chart(ctx).Bar(data, options); +}); diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js new file mode 100644 index 00000000000..2091b275c3d --- /dev/null +++ b/app/assets/javascripts/projects/project_new.js @@ -0,0 +1,43 @@ +document.addEventListener('DOMContentLoaded', () => { + const importBtnTooltip = 'Please enter a valid project name.'; + const $importBtnWrapper = $('.import_gitlab_project'); + + $('.how_to_import_link').on('click', (e) => { + e.preventDefault(); + $('.how_to_import_link').next('.modal').show(); + }); + + $('.modal-header .close').on('click', () => { + $('.modal').hide(); + }); + + $('.btn_import_gitlab_project').on('click', () => { + const importHref = $('a.btn_import_gitlab_project').attr('href'); + $('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$('#project_path').val()}`); + }); + + $('.btn_import_gitlab_project').attr('disabled', !$('#project_path').val().trim().length); + $importBtnWrapper.attr('title', importBtnTooltip); + + $('#new_project').on('submit', () => { + const $path = $('#project_path'); + $path.val($path.val().trim()); + }); + + $('#project_path').on('keyup', () => { + if ($('#project_path').val().trim().length) { + $('.btn_import_gitlab_project').attr('disabled', false); + $importBtnWrapper.attr('title', ''); + $importBtnWrapper.removeClass('has-tooltip'); + } else { + $('.btn_import_gitlab_project').attr('disabled', true); + $importBtnWrapper.addClass('has-tooltip'); + } + }); + + $('#project_import_url').disable(); + $('.import_git').on('click', () => { + const $projectImportUrl = $('#project_import_url'); + $projectImportUrl.attr('disabled', !$projectImportUrl.attr('disabled')); + }); +}); diff --git a/app/assets/javascripts/ref_select_dropdown.js b/app/assets/javascripts/ref_select_dropdown.js index 215cd6fbdfd..65e4101352c 100644 --- a/app/assets/javascripts/ref_select_dropdown.js +++ b/app/assets/javascripts/ref_select_dropdown.js @@ -1,7 +1,8 @@ class RefSelectDropdown { constructor($dropdownButton, availableRefs) { + const availableRefsValue = availableRefs || JSON.parse(document.getElementById('availableRefs').innerHTML); $dropdownButton.glDropdown({ - data: availableRefs, + data: availableRefsValue, filterable: true, filterByText: true, remote: false, diff --git a/app/assets/javascripts/two_factor_auth.js b/app/assets/javascripts/two_factor_auth.js new file mode 100644 index 00000000000..d26f61562a5 --- /dev/null +++ b/app/assets/javascripts/two_factor_auth.js @@ -0,0 +1,13 @@ +/* global U2FRegister */ +document.addEventListener('DOMContentLoaded', () => { + const twoFactorNode = document.querySelector('.js-two-factor-auth'); + const skippable = twoFactorNode.dataset.twoFactorSkippable === 'true'; + if (skippable) { + const button = `<a class="btn btn-xs btn-warning pull-right" data-method="patch" href="${twoFactorNode.dataset.two_factor_skip_url}">Configure it later</a>`; + const flashAlert = document.querySelector('.flash-alert .container-fluid'); + if (flashAlert) flashAlert.insertAdjacentHTML('beforeend', button); + } + + const u2fRegister = new U2FRegister($('#js-register-u2f'), gon.u2f); + u2fRegister.start(); +}); diff --git a/app/assets/javascripts/ui_development_kit.js b/app/assets/javascripts/ui_development_kit.js new file mode 100644 index 00000000000..f503076715c --- /dev/null +++ b/app/assets/javascripts/ui_development_kit.js @@ -0,0 +1,22 @@ +import Api from './api'; + +document.addEventListener('DOMContentLoaded', () => { + $('#js-project-dropdown').glDropdown({ + data: (term, callback) => { + Api.projects(term, { + order_by: 'last_activity_at', + }, (data) => { + callback(data); + }); + }, + text: project => (project.name_with_namespace || project.name), + selectable: true, + fieldName: 'author_id', + filterable: true, + search: { + fields: ['name_with_namespace'], + }, + id: data => data.id, + isSelected: data => (data.id === 2), + }); +}); diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss index ab2abaca33a..ec13a86ccf7 100644 --- a/app/assets/stylesheets/framework/filters.scss +++ b/app/assets/stylesheets/framework/filters.scss @@ -414,13 +414,16 @@ background-color: $dropdown-hover-color; color: $white-light; text-decoration: none; + outline: 0; .avatar { border-color: $white-light; } } -.filter-dropdown-item { +.droplab-dropdown .dropdown-menu .filter-dropdown-item { + padding: 0; + .btn { border: none; width: 100%; @@ -455,14 +458,11 @@ } .dropdown-user { - display: -webkit-flex; display: flex; } .dropdown-user-details { - display: -webkit-flex; display: flex; - -webkit-flex-direction: column; flex-direction: column; > span { diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 605f4284bb5..1c4238bc564 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -313,6 +313,29 @@ header { .impersonation i { color: $red-500; } + + // TODO: fallback to global style + .dropdown-menu, + .dropdown-menu-nav { + li { + padding: 0 1px; + + a { + border-radius: 0; + padding: 8px 16px; + + &:hover, + &:active, + &:focus { + background-color: $gray-darker; + } + } + } + } +} + +.with-performance-bar header.navbar-gitlab { + top: $performance-bar-height; } .navbar-nav { diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss index 4a9d41b4fda..67c3287ed74 100644 --- a/app/assets/stylesheets/framework/layout.scss +++ b/app/assets/stylesheets/framework/layout.scss @@ -120,3 +120,7 @@ of the body element here, we negate cascading side effects but allow momentum sc .page-with-sidebar { -webkit-overflow-scrolling: auto; } + +.with-performance-bar .page-with-sidebar { + margin-top: $header-height + $performance-bar-height; +} diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index 35b4d77a5ab..88e7ba117d5 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -347,6 +347,10 @@ } } +.with-performance-bar .layout-nav { + margin-top: $header-height + $performance-bar-height; +} + .scrolling-tabs-container { position: relative; @@ -441,6 +445,22 @@ } } +.with-performance-bar .page-with-layout-nav { + .right-sidebar { + top: ($header-height + 1) * 2 + $performance-bar-height; + } + + &.page-with-sub-nav { + .right-sidebar { + top: ($header-height + 1) * 3 + $performance-bar-height; + + &.affix { + top: $header-height + $performance-bar-height; + } + } + } +} + .nav-block { &.activities { border-bottom: 1px solid $border-color; diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 49b2f0e43a4..09b60ad1676 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -89,6 +89,10 @@ } } +.with-performance-bar .right-sidebar.affix { + top: $header-height + $performance-bar-height; +} + @mixin maintain-sidebar-dimensions { display: block; width: $gutter-width; diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index cf0a1ad57d0..0df6f24bfe6 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -204,6 +204,7 @@ $divergence-graph-separator-bg: #ccc; $general-hover-transition-duration: 100ms; $general-hover-transition-curve: linear; $highlight-changes-color: rgb(235, 255, 232); +$performance-bar-height: 35px; /* diff --git a/app/assets/stylesheets/new_nav.scss b/app/assets/stylesheets/new_nav.scss index 360ffda8d71..1c4a84de7ec 100644 --- a/app/assets/stylesheets/new_nav.scss +++ b/app/assets/stylesheets/new_nav.scss @@ -309,6 +309,25 @@ header.navbar-gitlab-new { outline: 0; } } + + // TODO: fallback to global style + .dropdown-menu { + li { + padding: 0 1px; + + a { + border-radius: 0; + padding: 8px 16px; + + &.is-focused, + &:hover, + &:active, + &:focus { + background-color: $gray-darker; + } + } + } + } } .breadcrumbs-container { @@ -325,6 +344,7 @@ header.navbar-gitlab-new { .breadcrumbs-links { flex: 1; + min-width: 0; align-self: center; color: $gl-text-color-quaternary; @@ -343,7 +363,7 @@ header.navbar-gitlab-new { } .title { - white-space: nowrap; + display: inline-block; > a { &:last-of-type:not(:first-child) { diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index ae43197a1a6..54f3e8d882c 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -118,7 +118,7 @@ $new-sidebar-width: 220px; z-index: 400; width: $new-sidebar-width; transition: left $sidebar-transition-duration; - top: 50px; + top: $header-height; bottom: 0; left: 0; overflow: auto; @@ -163,6 +163,10 @@ $new-sidebar-width: 220px; } } +.with-performance-bar .nav-sidebar { + top: $header-height + $performance-bar-height; +} + .sidebar-sub-level-items { display: none; padding-bottom: 8px; @@ -260,7 +264,7 @@ $new-sidebar-width: 220px; // Make issue boards full-height now that sub-nav is gone .boards-list { - height: calc(100vh - 50px); + height: calc(100vh - #{$header-height}); @media (min-width: $screen-sm-min) { height: 475px; // Needed for PhantomJS @@ -270,6 +274,10 @@ $new-sidebar-width: 220px; } } +.with-performance-bar .boards-list { + height: calc(100vh - #{$header-height} - #{$performance-bar-height}); +} + // Change color of all horizontal tabs to match the new indigo color .nav-links li.active a { diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index b6fc628c02b..28c99d8e57c 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -64,10 +64,10 @@ color: $gl-text-color; position: sticky; position: -webkit-sticky; - top: 50px; + top: $header-height; &.affix { - top: 50px; + top: $header-height; } // with sidebar @@ -86,6 +86,7 @@ position: absolute; right: 0; left: 0; + top: 0; } .truncated-info { @@ -171,6 +172,16 @@ } } +.with-performance-bar .build-page { + .top-bar { + top: $header-height + $performance-bar-height; + + &.affix { + top: $header-height + $performance-bar-height; + } + } +} + .build-header { .ci-header-container, .header-action-buttons { @@ -300,9 +311,7 @@ } .dropdown-menu { - right: $gl-padding; - left: $gl-padding; - width: auto; + margin-top: -$gl-padding; } svg { diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss index eeb90759f10..87b50c7687e 100644 --- a/app/assets/stylesheets/pages/cycle_analytics.scss +++ b/app/assets/stylesheets/pages/cycle_analytics.scss @@ -110,6 +110,10 @@ .js-ca-dropdown { top: $gl-padding-top; + + .dropdown-menu-align-right { + margin-top: 2px; + } } .content-list { @@ -442,6 +446,24 @@ margin-bottom: 20px; } } + + // TODO: fallback to global style + .dropdown-menu { + li { + padding: 0 1px; + + a { + border-radius: 0; + padding: 8px 16px; + + &:hover, + &:active, + &:focus { + background-color: $gray-darker; + } + } + } + } } .cycle-analytics-overview { diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 9391e1d70e6..55cfa645f79 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -445,6 +445,14 @@ } } +.with-performance-bar .right-sidebar { + top: $header-height + $performance-bar-height; + + .issuable-sidebar { + height: calc(100% - #{$header-height} - #{$performance-bar-height}); + } +} + .detail-page-description { padding: 16px 0; diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 2db967547dd..4693b2434c7 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -759,6 +759,10 @@ } } +.with-performance-bar .merge-request-tabs-holder { + top: $header-height + $performance-bar-height; +} + .merge-request-tabs { display: flex; margin-bottom: 0; diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index dc88cf3e699..e0f46172769 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -202,6 +202,28 @@ } } } + + // TODO: fallback to global style + .dropdown-menu:not(.dropdown-menu-selectable) { + li { + padding: 0 1px; + + &.dropdown-header { + padding: 8px 16px; + } + + a { + border-radius: 0; + padding: 8px 16px; + + &:hover, + &:active, + &:focus { + background-color: $gray-darker; + } + } + } + } } .blob-commit-info { diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss index 2890b6b1e49..6e539e39ca1 100644 --- a/app/assets/stylesheets/performance_bar.scss +++ b/app/assets/stylesheets/performance_bar.scss @@ -3,9 +3,16 @@ @import "peek/views/rblineprof"; #peek { - height: 35px; + position: fixed; + left: 0; + top: 0; + width: 100%; + z-index: 2000; + overflow-x: hidden; + + height: $performance-bar-height; background: $black; - line-height: 35px; + line-height: $performance-bar-height; color: $perf-bar-text; &.disabled { @@ -25,7 +32,8 @@ } .wrapper { - width: 1000px; + width: 80%; + height: $performance-bar-height; margin: 0 auto; } diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index 57372f9e79d..475d4c86294 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -43,23 +43,7 @@ class Projects::GraphsController < Projects::ApplicationController end def get_languages - @languages = Linguist::Repository.new(@repository.rugged, @repository.rugged.head.target_id).languages - total = @languages.map(&:last).sum - - @languages = @languages.map do |language| - name, share = language - color = Linguist::Language[name].color || "##{Digest::SHA256.hexdigest(name)[0...6]}" - { - value: (share.to_f * 100 / total).round(2), - label: name, - color: color, - highlight: color - } - end - - @languages.sort! do |x, y| - y[:value] <=> x[:value] - end + @languages = @project.repository.languages end def fetch_graph diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 1c165700b19..14dc9bd9d62 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -264,7 +264,11 @@ module ApplicationHelper end def page_class - "issue-boards-page" if current_controller?(:boards) + class_names = [] + class_names << 'issue-boards-page' if current_controller?(:boards) + class_names << 'with-performance-bar' if performance_bar_enabled? + + class_names end # Returns active css class when condition returns true diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index 0517a699ae0..1f7db9b2eb8 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -48,7 +48,11 @@ module GitlabRoutingHelper end def milestone_path(entity, *args) - project_milestone_path(entity.project, entity, *args) + if entity.is_group_milestone? + group_milestone_path(entity.group, entity, *args) + elsif entity.is_project_milestone? + project_milestone_path(entity.project, entity, *args) + end end def issue_url(entity, *args) @@ -63,6 +67,14 @@ module GitlabRoutingHelper project_pipeline_url(pipeline.project, pipeline.id, *args) end + def milestone_url(entity, *args) + if entity.is_group_milestone? + group_milestone_url(entity.group, entity, *args) + elsif entity.is_project_milestone? + project_milestone_url(entity.project, entity, *args) + end + end + def pipeline_job_url(pipeline, build, *args) project_job_url(pipeline.project, build.id, *args) end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 78cf7b26a31..c31023f2d9a 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -40,7 +40,7 @@ module MergeRequestsHelper def merge_path_description(merge_request, separator) if merge_request.for_fork? - "Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}" + "Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.full_path}:#{@merge_request.target_branch}" else "Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}" end diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index b769462abc2..b1205b8529b 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -1,38 +1,49 @@ module NavHelper + def page_with_sidebar_class + class_name = page_gutter_class + class_name << 'page-with-new-sidebar' if defined?(@new_sidebar) && @new_sidebar + + class_name + end + def page_gutter_class if current_path?('merge_requests#show') || current_path?('projects/merge_requests/conflicts#show') || current_path?('issues#show') || current_path?('milestones#show') if cookies[:collapsed_gutter] == 'true' - "page-gutter right-sidebar-collapsed" + %w[page-gutter right-sidebar-collapsed] else - "page-gutter right-sidebar-expanded" + %w[page-gutter right-sidebar-expanded] end elsif current_path?('jobs#show') - "page-gutter build-sidebar right-sidebar-expanded" + %w[page-gutter build-sidebar right-sidebar-expanded] elsif current_path?('wikis#show') || current_path?('wikis#edit') || current_path?('wikis#update') || current_path?('wikis#history') || current_path?('wikis#git_access') - "page-gutter wiki-sidebar right-sidebar-expanded" + %w[page-gutter wiki-sidebar right-sidebar-expanded] + else + [] end end def nav_header_class - class_name = '' - class_name << " with-horizontal-nav" if defined?(nav) && nav + class_names = [] + class_names << 'with-horizontal-nav' if defined?(nav) && nav - class_name + class_names end def layout_nav_class - class_name = '' - class_name << " page-with-layout-nav" if defined?(nav) && nav - class_name << " page-with-sub-nav" if content_for?(:sub_nav) + return [] if show_new_nav? - class_name + class_names = [] + class_names << 'page-with-layout-nav' if defined?(nav) && nav + class_names << 'page-with-sub-nav' if content_for?(:sub_nav) + + class_names end def nav_control_class diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 9a8d296d514..34ff6107eab 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -398,7 +398,7 @@ module ProjectsHelper if project import_path = "/Home/Stacks/import" - repo = project.path_with_namespace + repo = project.full_path branch ||= project.default_branch sha ||= project.commit.short_id @@ -458,7 +458,7 @@ module ProjectsHelper def readme_cache_key sha = @project.commit.try(:sha) || 'nil' - [@project.path_with_namespace, sha, "readme"].join('-') + [@project.full_path, sha, "readme"].join('-') end def current_ref diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index eaac6fcb548..9efabe3f44e 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -165,7 +165,7 @@ class Notify < BaseMailer headers['X-GitLab-Project'] = @project.name headers['X-GitLab-Project-Id'] = @project.id - headers['X-GitLab-Project-Path'] = @project.path_with_namespace + headers['X-GitLab-Project-Path'] = @project.full_path end def add_unsubscription_headers_and_links diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index d2abcf30034..ea7331cb27f 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -317,7 +317,7 @@ module Ci return @config_processor if defined?(@config_processor) @config_processor ||= begin - Ci::GitlabCiYamlProcessor.new(ci_yaml_file, project.path_with_namespace) + Ci::GitlabCiYamlProcessor.new(ci_yaml_file, project.full_path) rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e self.yaml_errors = e.message nil diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb new file mode 100644 index 00000000000..5ab5c80a2f5 --- /dev/null +++ b/app/models/concerns/storage/legacy_namespace.rb @@ -0,0 +1,102 @@ +module Storage + module LegacyNamespace + extend ActiveSupport::Concern + + def move_dir + if any_project_has_container_registry_tags? + raise Gitlab::UpdatePathError.new('Namespace cannot be moved, because at least one project has tags in container registry') + end + + # Move the namespace directory in all storage paths used by member projects + repository_storage_paths.each do |repository_storage_path| + # Ensure old directory exists before moving it + gitlab_shell.add_namespace(repository_storage_path, full_path_was) + + unless gitlab_shell.mv_namespace(repository_storage_path, full_path_was, full_path) + Rails.logger.error "Exception moving path #{repository_storage_path} from #{full_path_was} to #{full_path}" + + # if we cannot move namespace directory we should rollback + # db changes in order to prevent out of sync between db and fs + raise Gitlab::UpdatePathError.new('namespace directory cannot be moved') + end + end + + Gitlab::UploadsTransfer.new.rename_namespace(full_path_was, full_path) + Gitlab::PagesTransfer.new.rename_namespace(full_path_was, full_path) + + remove_exports! + + # If repositories moved successfully we need to + # send update instructions to users. + # However we cannot allow rollback since we moved namespace dir + # So we basically we mute exceptions in next actions + begin + send_update_instructions + true + rescue + # Returning false does not rollback after_* transaction but gives + # us information about failing some of tasks + false + end + end + + # Hooks + + # Save the storage paths before the projects are destroyed to use them on after destroy + def prepare_for_destroy + old_repository_storage_paths + end + + private + + def old_repository_storage_paths + @old_repository_storage_paths ||= repository_storage_paths + end + + def repository_storage_paths + # We need to get the storage paths for all the projects, even the ones that are + # pending delete. Unscoping also get rids of the default order, which causes + # problems with SELECT DISTINCT. + Project.unscoped do + all_projects.select('distinct(repository_storage)').to_a.map(&:repository_storage_path) + end + end + + def rm_dir + # Remove the namespace directory in all storages paths used by member projects + old_repository_storage_paths.each do |repository_storage_path| + # Move namespace directory into trash. + # We will remove it later async + new_path = "#{full_path}+#{id}+deleted" + + if gitlab_shell.mv_namespace(repository_storage_path, full_path, new_path) + Gitlab::AppLogger.info %Q(Namespace directory "#{full_path}" moved to "#{new_path}") + + # Remove namespace directroy async with delay so + # GitLab has time to remove all projects first + run_after_commit do + GitlabShellWorker.perform_in(5.minutes, :rm_namespace, repository_storage_path, new_path) + end + end + end + + remove_exports! + end + + def remove_exports! + Gitlab::Popen.popen(%W(find #{export_path} -not -path #{export_path} -delete)) + end + + def export_path + File.join(Gitlab::ImportExport.storage_path, full_path_was) + end + + def full_path_was + if parent + parent.full_path + '/' + path_was + else + path_was + end + end + end +end diff --git a/app/models/concerns/storage/legacy_project.rb b/app/models/concerns/storage/legacy_project.rb new file mode 100644 index 00000000000..815db712285 --- /dev/null +++ b/app/models/concerns/storage/legacy_project.rb @@ -0,0 +1,76 @@ +module Storage + module LegacyProject + extend ActiveSupport::Concern + + def disk_path + full_path + end + + def ensure_storage_path_exist + gitlab_shell.add_namespace(repository_storage_path, namespace.full_path) + end + + def rename_repo + path_was = previous_changes['path'].first + 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}" + + if has_container_registry_tags? + Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present!" + + # we currently doesn't support renaming repository if it contains images in container registry + raise StandardError.new('Project cannot be renamed, because images are present in its container registry') + end + + expire_caches_before_rename(old_path_with_namespace) + + if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace) + # If repository moved successfully we need to send update instructions to users. + # However we cannot allow rollback since we moved repository + # So we basically we mute exceptions in next actions + begin + gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki") + send_move_instructions(old_path_with_namespace) + expires_full_path_cache + + @old_path_with_namespace = old_path_with_namespace + + SystemHooksService.new.execute_hooks_for(self, :rename) + + @repository = nil + rescue => e + Rails.logger.error "Exception renaming #{old_path_with_namespace} -> #{new_path_with_namespace}: #{e}" + # Returning false does not rollback after_* transaction but gives + # us information about failing some of tasks + false + end + else + Rails.logger.error "Repository could not be renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}" + + # if we cannot move namespace directory we should rollback + # db changes in order to prevent out of sync between db and fs + raise StandardError.new('repository cannot be renamed') + end + + Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}" + + Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.full_path) + Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.full_path) + end + + def create_repository(force: false) + # Forked import is handled asynchronously + return if forked? && !force + + if gitlab_shell.add_repository(repository_storage_path, path_with_namespace) + repository.after_create + true + else + errors.add(:base, 'Failed to create repository via gitlab-shell') + false + end + end + end +end diff --git a/app/models/concerns/storage/legacy_project_wiki.rb b/app/models/concerns/storage/legacy_project_wiki.rb new file mode 100644 index 00000000000..ff82cb0ffa9 --- /dev/null +++ b/app/models/concerns/storage/legacy_project_wiki.rb @@ -0,0 +1,9 @@ +module Storage + module LegacyProjectWiki + extend ActiveSupport::Concern + + def disk_path + project.disk_path + '.wiki' + end + end +end diff --git a/app/models/concerns/storage/legacy_repository.rb b/app/models/concerns/storage/legacy_repository.rb new file mode 100644 index 00000000000..593749bf019 --- /dev/null +++ b/app/models/concerns/storage/legacy_repository.rb @@ -0,0 +1,7 @@ +module Storage + module LegacyRepository + extend ActiveSupport::Concern + + delegate :disk_path, to: :project + end +end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 81e0776e79c..8ca850b6d96 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -630,7 +630,7 @@ class MergeRequest < ActiveRecord::Base def target_project_path if target_project - target_project.path_with_namespace + target_project.full_path else "(removed)" end @@ -638,7 +638,7 @@ class MergeRequest < ActiveRecord::Base def source_project_path if source_project - source_project.path_with_namespace + source_project.full_path else "(removed)" end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 0bb04194bdb..6073fb94a3f 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -8,6 +8,7 @@ class Namespace < ActiveRecord::Base include Gitlab::VisibilityLevel include Routable include AfterCommitQueue + include Storage::LegacyNamespace # Prevent users from creating unreasonably deep level of nesting. # The number 20 was taken based on maximum nesting level of @@ -41,10 +42,11 @@ class Namespace < ActiveRecord::Base delegate :name, to: :owner, allow_nil: true, prefix: true - after_update :move_dir, if: :path_changed? after_commit :refresh_access_of_projects_invited_groups, on: :update, if: -> { previous_changes.key?('share_with_group_lock') } - # Save the storage paths before the projects are destroyed to use them on after destroy + # Legacy Storage specific hooks + + after_update :move_dir, if: :path_changed? before_destroy(prepend: true) { prepare_for_destroy } after_destroy :rm_dir @@ -118,43 +120,6 @@ class Namespace < ActiveRecord::Base owner_name end - def move_dir - if any_project_has_container_registry_tags? - raise Gitlab::UpdatePathError.new('Namespace cannot be moved, because at least one project has tags in container registry') - end - - # Move the namespace directory in all storages paths used by member projects - repository_storage_paths.each do |repository_storage_path| - # Ensure old directory exists before moving it - gitlab_shell.add_namespace(repository_storage_path, full_path_was) - - unless gitlab_shell.mv_namespace(repository_storage_path, full_path_was, full_path) - Rails.logger.error "Exception moving path #{repository_storage_path} from #{full_path_was} to #{full_path}" - - # if we cannot move namespace directory we should rollback - # db changes in order to prevent out of sync between db and fs - raise Gitlab::UpdatePathError.new('namespace directory cannot be moved') - end - end - - Gitlab::UploadsTransfer.new.rename_namespace(full_path_was, full_path) - Gitlab::PagesTransfer.new.rename_namespace(full_path_was, full_path) - - remove_exports! - - # If repositories moved successfully we need to - # send update instructions to users. - # However we cannot allow rollback since we moved namespace dir - # So we basically we mute exceptions in next actions - begin - send_update_instructions - rescue - # Returning false does not rollback after_* transaction but gives - # us information about failing some of tasks - false - end - end - def any_project_has_container_registry_tags? all_projects.any?(&:has_container_registry_tags?) end @@ -206,14 +171,6 @@ class Namespace < ActiveRecord::Base parent_id_changed? end - def prepare_for_destroy - old_repository_storage_paths - end - - def old_repository_storage_paths - @old_repository_storage_paths ||= repository_storage_paths - end - # Includes projects from this namespace and projects from all subgroups # that belongs to this namespace def all_projects @@ -232,37 +189,6 @@ class Namespace < ActiveRecord::Base private - def repository_storage_paths - # We need to get the storage paths for all the projects, even the ones that are - # pending delete. Unscoping also get rids of the default order, which causes - # problems with SELECT DISTINCT. - Project.unscoped do - all_projects.select('distinct(repository_storage)').to_a.map(&:repository_storage_path) - end - end - - def rm_dir - # Remove the namespace directory in all storages paths used by member projects - old_repository_storage_paths.each do |repository_storage_path| - # Move namespace directory into trash. - # We will remove it later async - new_path = "#{full_path}+#{id}+deleted" - - if gitlab_shell.mv_namespace(repository_storage_path, full_path, new_path) - message = "Namespace directory \"#{full_path}\" moved to \"#{new_path}\"" - Gitlab::AppLogger.info message - - # Remove namespace directroy async with delay so - # GitLab has time to remove all projects first - run_after_commit do - GitlabShellWorker.perform_in(5.minutes, :rm_namespace, repository_storage_path, new_path) - end - end - end - - remove_exports! - end - def refresh_access_of_projects_invited_groups Group .joins(project_group_links: :project) @@ -270,22 +196,6 @@ class Namespace < ActiveRecord::Base .find_each(&:refresh_members_authorized_projects) end - def remove_exports! - Gitlab::Popen.popen(%W(find #{export_path} -not -path #{export_path} -delete)) - end - - def export_path - File.join(Gitlab::ImportExport.storage_path, full_path_was) - end - - def full_path_was - if parent - parent.full_path + '/' + path_was - else - path_was - end - end - def nesting_level_allowed if ancestors.count > Group::NUMBER_OF_ANCESTORS_ALLOWED errors.add(:parent_id, "has too deep level of nesting") diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb index 81844b1e2ca..9b1cac64c44 100644 --- a/app/models/notification_setting.rb +++ b/app/models/notification_setting.rb @@ -1,4 +1,8 @@ class NotificationSetting < ActiveRecord::Base + include IgnorableColumn + + ignore_column :events + enum level: { global: 3, watch: 2, mention: 4, participating: 1, disabled: 0, custom: 5 } default_value_for :level, NotificationSetting.levels[:global] @@ -41,9 +45,6 @@ class NotificationSetting < ActiveRecord::Base :success_pipeline ].freeze - store :events, coder: JSON - before_save :convert_events - def self.find_or_create_for(source) setting = find_or_initialize_by(source: source) @@ -54,42 +55,17 @@ class NotificationSetting < ActiveRecord::Base setting end - # 1. Check if this event has a value stored in its database column. - # 2. If it does, return that value. - # 3. If it doesn't (the value is nil), return the value from the serialized - # JSON hash in `events`. - (EMAIL_EVENTS - [:failed_pipeline]).each do |event| - define_method(event) do - bool = super() - - bool.nil? ? !!events[event] : bool - end - - alias_method :"#{event}?", event - end - # Allow people to receive failed pipeline notifications if they already have # custom notifications enabled, as these are more like mentions than the other # custom settings. def failed_pipeline bool = super - bool = events[:failed_pipeline] if bool.nil? bool.nil? || bool end alias_method :failed_pipeline?, :failed_pipeline def event_enabled?(event) - respond_to?(event) && public_send(event) - end - - def convert_events - return if events_before_type_cast.nil? - - EMAIL_EVENTS.each do |event| - write_attribute(event, public_send(event)) - end - - write_attribute(:events, nil) + respond_to?(event) && !!public_send(event) end end diff --git a/app/models/project.rb b/app/models/project.rb index d827bfaa806..d85782782aa 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -17,6 +17,7 @@ class Project < ActiveRecord::Base include ProjectFeaturesCompatibility include SelectForProjectAuthorization include Routable + include Storage::LegacyProject extend Gitlab::ConfigHelper @@ -43,9 +44,8 @@ class Project < ActiveRecord::Base default_value_for :snippets_enabled, gitlab_config_features.snippets default_value_for :only_allow_merge_if_all_discussions_are_resolved, false - after_create :ensure_dir_exist + after_create :ensure_storage_path_exist after_create :create_project_feature, unless: :project_feature - after_save :ensure_dir_exist, if: :namespace_id_changed? after_save :update_project_statistics, if: :namespace_id_changed? # set last_activity_at to the same as created_at @@ -67,6 +67,10 @@ class Project < ActiveRecord::Base after_validation :check_pending_delete + # Legacy Storage specific hooks + + after_save :ensure_storage_path_exist, if: :namespace_id_changed? + acts_as_taggable attr_accessor :new_default_branch @@ -375,7 +379,7 @@ class Project < ActiveRecord::Base begin Projects::HousekeepingService.new(project).execute rescue Projects::HousekeepingService::LeaseTaken => e - Rails.logger.info("Could not perform housekeeping for project #{project.path_with_namespace} (#{project.id}): #{e}") + Rails.logger.info("Could not perform housekeeping for project #{project.full_path} (#{project.id}): #{e}") end end end @@ -476,12 +480,12 @@ class Project < ActiveRecord::Base end def repository - @repository ||= Repository.new(path_with_namespace, self) + @repository ||= Repository.new(full_path, self, disk_path: disk_path) end def container_registry_url if Gitlab.config.registry.enabled - "#{Gitlab.config.registry.host_port}/#{path_with_namespace.downcase}" + "#{Gitlab.config.registry.host_port}/#{full_path.downcase}" end end @@ -520,16 +524,16 @@ class Project < ActiveRecord::Base job_id = if forked? RepositoryForkWorker.perform_async(id, forked_from_project.repository_storage_path, - forked_from_project.path_with_namespace, + forked_from_project.full_path, 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}" + Rails.logger.info "Import job started for #{full_path} with job ID #{job_id}" else - Rails.logger.error "Import job failed to start for #{path_with_namespace}" + Rails.logger.error "Import job failed to start for #{full_path}" end end @@ -690,7 +694,7 @@ class Project < ActiveRecord::Base # `from` argument can be a Namespace or Project. def to_reference(from = nil, full: false) if full || cross_namespace_reference?(from) - path_with_namespace + full_path elsif cross_project_reference?(from) path end @@ -714,7 +718,7 @@ class Project < ActiveRecord::Base author.ensure_incoming_email_token! Gitlab::IncomingEmail.reply_address( - "#{path_with_namespace}+#{author.incoming_email_token}") + "#{full_path}+#{author.incoming_email_token}") end def build_commit_note(commit) @@ -941,7 +945,7 @@ class Project < ActiveRecord::Base end def url_to_repo - gitlab_shell.url_to_repo(path_with_namespace) + gitlab_shell.url_to_repo(full_path) end def repo_exists? @@ -974,56 +978,6 @@ class Project < ActiveRecord::Base !group end - def rename_repo - path_was = previous_changes['path'].first - 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}" - - if has_container_registry_tags? - Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present!" - - # we currently doesn't support renaming repository if it contains images in container registry - raise StandardError.new('Project cannot be renamed, because images are present in its container registry') - end - - expire_caches_before_rename(old_path_with_namespace) - - if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace) - # If repository moved successfully we need to send update instructions to users. - # However we cannot allow rollback since we moved repository - # So we basically we mute exceptions in next actions - begin - gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki") - send_move_instructions(old_path_with_namespace) - expires_full_path_cache - - @old_path_with_namespace = old_path_with_namespace - - SystemHooksService.new.execute_hooks_for(self, :rename) - - @repository = nil - rescue => e - Rails.logger.error "Exception renaming #{old_path_with_namespace} -> #{new_path_with_namespace}: #{e}" - # Returning false does not rollback after_* transaction but gives - # us information about failing some of tasks - false - end - else - Rails.logger.error "Repository could not be renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}" - - # if we cannot move namespace directory we should rollback - # db changes in order to prevent out of sync between db and fs - raise StandardError.new('repository cannot be renamed') - end - - Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}" - - Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.full_path) - Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.full_path) - end - # Expires various caches before a project is renamed. def expire_caches_before_rename(old_path) repo = Repository.new(old_path, self) @@ -1048,7 +1002,7 @@ class Project < ActiveRecord::Base git_http_url: http_url_to_repo, namespace: namespace.name, visibility_level: visibility_level, - path_with_namespace: path_with_namespace, + path_with_namespace: full_path, default_branch: default_branch, ci_config_path: ci_config_path } @@ -1109,19 +1063,6 @@ class Project < ActiveRecord::Base merge_requests.where(source_project_id: self.id) end - def create_repository(force: false) - # Forked import is handled asynchronously - return if forked? && !force - - if gitlab_shell.add_repository(repository_storage_path, path_with_namespace) - repository.after_create - true - else - errors.add(:base, 'Failed to create repository via gitlab-shell') - false - end - end - def ensure_repository create_repository(force: true) unless repository_exists? end @@ -1257,7 +1198,7 @@ class Project < ActiveRecord::Base end def pages_path - File.join(Settings.pages.path, path_with_namespace) + File.join(Settings.pages.path, disk_path) end def public_pages_path @@ -1265,9 +1206,21 @@ class Project < ActiveRecord::Base end def remove_private_deploy_keys - deploy_keys.where(public: false).delete_all + exclude_keys_linked_to_other_projects = <<-SQL + NOT EXISTS ( + SELECT 1 + FROM deploy_keys_projects dkp2 + WHERE dkp2.deploy_key_id = deploy_keys_projects.deploy_key_id + AND dkp2.project_id != deploy_keys_projects.project_id + ) + SQL + + deploy_keys.where(public: false) + .where(exclude_keys_linked_to_other_projects) + .delete_all end + # TODO: what to do here when not using Legacy Storage? Do we still need to rename and delay removal? def remove_pages ::Projects::UpdatePagesConfigurationService.new(self).execute @@ -1315,7 +1268,7 @@ class Project < ActiveRecord::Base end def export_path - File.join(Gitlab::ImportExport.storage_path, path_with_namespace) + File.join(Gitlab::ImportExport.storage_path, disk_path) end def export_project_path @@ -1327,16 +1280,12 @@ class Project < ActiveRecord::Base status.zero? end - def ensure_dir_exist - gitlab_shell.add_namespace(repository_storage_path, namespace.full_path) - end - def predefined_variables [ { key: 'CI_PROJECT_ID', value: id.to_s, public: true }, { key: 'CI_PROJECT_NAME', value: path, public: true }, - { key: 'CI_PROJECT_PATH', value: path_with_namespace, public: true }, - { key: 'CI_PROJECT_PATH_SLUG', value: path_with_namespace.parameterize, public: true }, + { key: 'CI_PROJECT_PATH', value: full_path, public: true }, + { key: 'CI_PROJECT_PATH_SLUG', value: full_path.parameterize, public: true }, { key: 'CI_PROJECT_NAMESPACE', value: namespace.full_path, public: true }, { key: 'CI_PROJECT_URL', value: web_url, public: true } ] @@ -1441,6 +1390,7 @@ class Project < ActiveRecord::Base alias_method :name_with_namespace, :full_name alias_method :human_name, :full_name + # @deprecated cannot remove yet because it has an index with its name in elasticsearch alias_method :path_with_namespace, :full_path private @@ -1495,7 +1445,7 @@ class Project < ActiveRecord::Base def pending_delete_twin return false unless path - Project.pending_delete.find_by_full_path(path_with_namespace) + Project.pending_delete.find_by_full_path(full_path) end ## diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb index 2db95b9aaa3..4d23a17a545 100644 --- a/app/models/project_services/flowdock_service.rb +++ b/app/models/project_services/flowdock_service.rb @@ -35,9 +35,9 @@ class FlowdockService < Service data[:after], token: token, repo: project.repository.path_to_repo, - repo_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}", - commit_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/%s", - diff_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/compare/%s...%s" + repo_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}", + commit_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/%s", + diff_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/compare/%s...%s" ) end end diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 2aa19443198..c2414885368 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -140,7 +140,7 @@ class JiraService < IssueTrackerService url: resource_url(user_path(author)) }, project: { - name: project.path_with_namespace, + name: project.full_path, url: resource_url(namespace_project_path(project.namespace, project)) # rubocop:disable Cop/ProjectPathHelper }, entity: { diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index dfca0031af8..e8929a35836 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -1,5 +1,6 @@ class ProjectWiki include Gitlab::ShellAdapter + include Storage::LegacyProjectWiki MARKUPS = { 'Markdown' => :markdown, @@ -26,16 +27,19 @@ class ProjectWiki @project.path + '.wiki' end - def path_with_namespace - @project.path_with_namespace + ".wiki" + def full_path + @project.full_path + '.wiki' end + # @deprecated use full_path when you need it for an URL route or disk_path when you want to point to the filesystem + alias_method :path_with_namespace, :full_path + def web_url Gitlab::Routing.url_helpers.project_wiki_url(@project, :home) end def url_to_repo - gitlab_shell.url_to_repo(path_with_namespace) + gitlab_shell.url_to_repo(full_path) end def ssh_url_to_repo @@ -43,11 +47,11 @@ class ProjectWiki end def http_url_to_repo - "#{Gitlab.config.gitlab.url}/#{path_with_namespace}.git" + "#{Gitlab.config.gitlab.url}/#{full_path}.git" end def wiki_base_path - [Gitlab.config.gitlab.relative_url_root, "/", @project.path_with_namespace, "/wikis"].join('') + [Gitlab.config.gitlab.relative_url_root, '/', @project.full_path, '/wikis'].join('') end # Returns the Gollum::Wiki object. @@ -134,7 +138,7 @@ class ProjectWiki end def repository - @repository ||= Repository.new(path_with_namespace, @project) + @repository ||= Repository.new(full_path, @project, disk_path: disk_path) end def default_branch @@ -142,7 +146,7 @@ class ProjectWiki end def create_repo! - if init_repo(path_with_namespace) + if init_repo(disk_path) wiki = Gollum::Wiki.new(path_to_repo) else raise CouldNotCreateWikiError @@ -162,15 +166,15 @@ class ProjectWiki web_url: web_url, git_ssh_url: ssh_url_to_repo, git_http_url: http_url_to_repo, - path_with_namespace: path_with_namespace, + path_with_namespace: full_path, default_branch: default_branch } end private - def init_repo(path_with_namespace) - gitlab_shell.add_repository(project.repository_storage_path, path_with_namespace) + def init_repo(disk_path) + gitlab_shell.add_repository(project.repository_storage_path, disk_path) end def commit_details(action, message = nil, title = nil) @@ -184,7 +188,7 @@ class ProjectWiki end def path_to_repo - @path_to_repo ||= File.join(project.repository_storage_path, "#{path_with_namespace}.git") + @path_to_repo ||= File.join(project.repository_storage_path, "#{disk_path}.git") end def update_project_activity diff --git a/app/models/repository.rb b/app/models/repository.rb index 50b7a477904..7ea9f1459a0 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -4,7 +4,7 @@ class Repository include Gitlab::ShellAdapter include RepositoryMirroring - attr_accessor :path_with_namespace, :project + attr_accessor :full_path, :disk_path, :project delegate :ref_name_for_sha, to: :raw_repository @@ -52,13 +52,14 @@ class Repository end end - def initialize(path_with_namespace, project) - @path_with_namespace = path_with_namespace + def initialize(full_path, project, disk_path: nil) + @full_path = full_path + @disk_path = disk_path || full_path @project = project end def raw_repository - return nil unless path_with_namespace + return nil unless full_path @raw_repository ||= initialize_raw_repository end @@ -66,7 +67,7 @@ class Repository # Return absolute path to repository def path_to_repo @path_to_repo ||= File.expand_path( - File.join(repository_storage_path, path_with_namespace + ".git") + File.join(repository_storage_path, disk_path + '.git') ) end @@ -469,7 +470,7 @@ class Repository # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/314 def exists? - return false unless path_with_namespace + return false unless full_path Gitlab::GitalyClient.migrate(:repository_exists) do |enabled| if enabled @@ -1005,7 +1006,7 @@ class Repository end def fetch_remote(remote, forced: false, no_tags: false) - gitlab_shell.fetch_remote(repository_storage_path, path_with_namespace, remote, forced: forced, no_tags: no_tags) + gitlab_shell.fetch_remote(repository_storage_path, disk_path, remote, forced: forced, no_tags: no_tags) end def fetch_ref(source_path, source_ref, target_ref) @@ -1104,7 +1105,8 @@ class Repository end def cache - @cache ||= RepositoryCache.new(path_with_namespace, @project.id) + # TODO: should we use UUIDs here? We could move repositories without clearing this cache + @cache ||= RepositoryCache.new(full_path, @project.id) end def tags_sorted_by_committed_date @@ -1127,7 +1129,7 @@ class Repository end def repository_event(event, tags = {}) - Gitlab::Metrics.add_event(event, { path: path_with_namespace }.merge(tags)) + Gitlab::Metrics.add_event(event, { path: full_path }.merge(tags)) end def create_commit(params = {}) @@ -1141,6 +1143,6 @@ class Repository end def initialize_raw_repository - Gitlab::Git::Repository.new(project.repository_storage, path_with_namespace + '.git') + Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git') end end diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb index 1c91425f589..1be7bbe9953 100644 --- a/app/policies/global_policy.rb +++ b/app/policies/global_policy.rb @@ -44,7 +44,7 @@ class GlobalPolicy < BasePolicy prevent :log_in end - rule { admin | ~restricted_public_level }.policy do + rule { ~(anonymous & restricted_public_level) }.policy do enable :read_users_list end end diff --git a/app/services/git_operation_service.rb b/app/services/git_operation_service.rb index 32925e9c1f2..545ca0742e4 100644 --- a/app/services/git_operation_service.rb +++ b/app/services/git_operation_service.rb @@ -60,7 +60,7 @@ class GitOperationService start_branch_name = nil if start_repository.empty_repo? if start_branch_name && !start_repository.branch_exists?(start_branch_name) - raise ArgumentError, "Cannot find branch #{start_branch_name} in #{start_repository.path_with_namespace}" + raise ArgumentError, "Cannot find branch #{start_branch_name} in #{start_repository.full_path}" end update_branch_with_hooks(branch_name) do diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index f6e8b6655f2..11ad4838471 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -9,7 +9,7 @@ module Projects def async_execute project.update_attribute(:pending_delete, true) job_id = ProjectDestroyWorker.perform_async(project.id, current_user.id, params) - Rails.logger.info("User #{current_user.id} scheduled destruction of project #{project.path_with_namespace} with job ID #{job_id}") + Rails.logger.info("User #{current_user.id} scheduled destruction of project #{project.full_path} with job ID #{job_id}") end def execute @@ -40,7 +40,7 @@ module Projects private def repo_path - project.path_with_namespace + project.disk_path end def wiki_path @@ -127,7 +127,7 @@ module Projects def flush_caches(project) project.repository.before_delete - Repository.new(wiki_path, project).before_delete + Repository.new(wiki_path, project, disk_path: repo_path).before_delete end end end diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb index 535da706159..fe4e8ea10bf 100644 --- a/app/services/projects/import_export/export_service.rb +++ b/app/services/projects/import_export/export_service.rb @@ -2,7 +2,7 @@ module Projects module ImportExport class ExportService < BaseService def execute(_options = {}) - @shared = Gitlab::ImportExport::Shared.new(relative_path: File.join(project.path_with_namespace, 'work')) + @shared = Gitlab::ImportExport::Shared.new(relative_path: File.join(project.disk_path, 'work')) save_all end diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb index eea17e24903..50ec3651515 100644 --- a/app/services/projects/import_service.rb +++ b/app/services/projects/import_service.rb @@ -11,7 +11,7 @@ module Projects success rescue => e - error("Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}") + error("Error importing repository #{project.import_url} into #{project.full_path} - #{e.message}") end private @@ -51,7 +51,7 @@ module Projects end def clone_repository - gitlab_shell.import_repository(project.repository_storage_path, project.path_with_namespace, project.import_url) + gitlab_shell.import_repository(project.repository_storage_path, project.disk_path, project.import_url) end def fetch_repository diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index 4bb98e5cb4e..5957f612e84 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -34,7 +34,7 @@ module Projects private def transfer(project) - @old_path = project.path_with_namespace + @old_path = project.full_path @old_group = project.group @new_path = File.join(@new_namespace.try(:full_path) || '', project.path) @old_namespace = project.namespace @@ -61,11 +61,13 @@ module Projects project.send_move_instructions(@old_path) # Move main repository + # TODO: check storage type and NOOP when not using Legacy unless move_repo_folder(@old_path, @new_path) raise TransferError.new('Cannot move project') end # Move wiki repo also if present + # TODO: check storage type and NOOP when not using Legacy move_repo_folder("#{@old_path}.wiki", "#{@new_path}.wiki") # Move missing group labels to project diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb index 5dc1b91d2c0..c22bf7498bb 100644 --- a/app/services/quick_actions/interpret_service.rb +++ b/app/services/quick_actions/interpret_service.rb @@ -4,6 +4,9 @@ module QuickActions attr_reader :issuable + SHRUG = '¯\\_(ツ)_/¯'.freeze + TABLEFLIP = '(╯°□°)╯︵ ┻━┻'.freeze + # Takes a text and interprets the commands that are extracted from it. # Returns the content without commands, and hash of changes to be applied to a record. def execute(content, issuable) @@ -14,6 +17,7 @@ module QuickActions content, commands = extractor.extract_commands(content, context) extract_updates(commands, context) + [content, @updates] end @@ -423,6 +427,18 @@ module QuickActions @updates[:spend_time] = { duration: :reset, user: current_user } end + desc "Append the comment with #{SHRUG}" + params '<Comment>' + substitution :shrug do |comment| + "#{comment} #{SHRUG}" + end + + desc "Append the comment with #{TABLEFLIP}" + params '<Comment>' + substitution :tableflip do |comment| + "#{comment} #{TABLEFLIP}" + end + # This is a dummy command, so that it appears in the autocomplete commands desc 'CC' params '@user' diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index bd58a54592f..cbcd4478af6 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -24,7 +24,7 @@ class SystemHooksService key: model.key, id: model.id ) - + if model.user data[:username] = model.user.username end @@ -56,7 +56,7 @@ class SystemHooksService when GroupMember data.merge!(group_member_data(model)) end - + data end @@ -79,7 +79,7 @@ class SystemHooksService { name: model.name, path: model.path, - path_with_namespace: model.path_with_namespace, + path_with_namespace: model.full_path, project_id: model.id, owner_name: owner.name, owner_email: owner.respond_to?(:email) ? owner.email : "", @@ -93,7 +93,7 @@ class SystemHooksService { project_name: project.name, project_path: project.path, - project_path_with_namespace: project.path_with_namespace, + project_path_with_namespace: project.full_path, project_id: project.id, user_username: model.user.username, user_name: model.user.name, diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb index 652277e3b78..7027ac4b5db 100644 --- a/app/uploaders/file_uploader.rb +++ b/app/uploaders/file_uploader.rb @@ -30,7 +30,7 @@ class FileUploader < GitlabUploader # # Returns a String without a trailing slash def self.dynamic_path_segment(model) - File.join(CarrierWave.root, base_dir, model.path_with_namespace) + File.join(CarrierWave.root, base_dir, model.full_path) end attr_accessor :model diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index 843c71af466..2aadc071c75 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -70,7 +70,7 @@ %span.badge = storage_counter(project.statistics.storage_size) %span.pull-right.light - %span.monospace= project.path_with_namespace + ".git" + %span.monospace= project.full_path + '.git' .panel-footer = paginate @projects, param_name: 'projects_page', theme: 'gitlab' @@ -88,7 +88,7 @@ %span.badge = storage_counter(project.statistics.storage_size) %span.pull-right.light - %span.monospace= project.path_with_namespace + ".git" + %span.monospace= project.full_path + '.git' .col-md-6 - if can?(current_user, :admin_group_member, @group) diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml index 48edbb8c16f..f18c3a74120 100644 --- a/app/views/help/ui.html.haml +++ b/app/views/help/ui.html.haml @@ -1,5 +1,7 @@ - page_title "UI Development Kit", "Help" - lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed fermentum nisi sapien, non consequat lectus aliquam ultrices. Suspendisse sodales est euismod nunc condimentum, a consectetur diam ornare." +- content_for :page_specific_javascripts do + = webpack_bundle_tag('ui_development_kit') .gitlab-ui-dev-kit %h1 GitLab UI development kit @@ -407,29 +409,6 @@ .dropdown-content .dropdown-loading = icon('spinner spin') - :javascript - $('#js-project-dropdown').glDropdown({ - data: function (term, callback) { - Api.projects(term, { order_by: 'last_activity_at' }, function (data) { - callback(data); - }); - }, - text: function (project) { - return project.name_with_namespace || project.name; - }, - selectable: true, - fieldName: "author_id", - filterable: true, - search: { - fields: ['name_with_namespace'] - }, - id: function (data) { - return data.id; - }, - isSelected: function (data) { - return data.id === 2; - } - }) .example %div diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml index 0e7f0b5ed4f..e9a04e6c122 100644 --- a/app/views/import/_githubish_status.html.haml +++ b/app/views/import/_githubish_status.html.haml @@ -25,7 +25,7 @@ %td = provider_project_link(provider, project.import_source) %td - = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + = link_to project.full_path, [project.namespace.becomes(Namespace), project] %td.job-status - if project.import_status == 'finished' %span diff --git a/app/views/import/base/create.js.haml b/app/views/import/base/create.js.haml index fde671e25a9..4dc3a4a0acf 100644 --- a/app/views/import/base/create.js.haml +++ b/app/views/import/base/create.js.haml @@ -4,7 +4,7 @@ job.attr("id", "project_#{@project.id}") target_field = job.find(".import-target") target_field.empty() - target_field.append('#{link_to @project.path_with_namespace, project_path(@project)}') + target_field.append('#{link_to @project.full_path, project_path(@project)}') $("table.import-jobs tbody").prepend(job) job.addClass("active").find(".import-actions").html("<i class='fa fa-spinner fa-spin'></i> started") - else diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml index e6058617ac9..9589e0956f4 100644 --- a/app/views/import/bitbucket/status.html.haml +++ b/app/views/import/bitbucket/status.html.haml @@ -35,7 +35,7 @@ %td = link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: '_blank', rel: 'noopener noreferrer' %td - = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + = link_to project.full_path, [project.namespace.becomes(Namespace), project] %td.job-status - if project.import_status == 'finished' %span diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml index 5de5da5e6a2..7b832c6a23a 100644 --- a/app/views/import/fogbugz/status.html.haml +++ b/app/views/import/fogbugz/status.html.haml @@ -33,7 +33,7 @@ %td = project.import_source %td - = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + = link_to project.full_path, [project.namespace.becomes(Namespace), project] %td.job-status - if project.import_status == 'finished' %span diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml index 7456799ca0e..37734414835 100644 --- a/app/views/import/gitlab/status.html.haml +++ b/app/views/import/gitlab/status.html.haml @@ -28,7 +28,7 @@ %td = link_to project.import_source, "https://gitlab.com/#{project.import_source}", target: "_blank" %td - = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + = link_to project.full_path, [project.namespace.becomes(Namespace), project] %td.job-status - if project.import_status == 'finished' %span diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml index 60de6bfe816..bc61aeece72 100644 --- a/app/views/import/google_code/status.html.haml +++ b/app/views/import/google_code/status.html.haml @@ -38,7 +38,7 @@ %td = link_to project.import_source, "https://code.google.com/p/#{project.import_source}", target: "_blank", rel: 'noopener noreferrer' %td - = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + = link_to project.full_path, [project.namespace.becomes(Namespace), project] %td.job-status - if project.import_status == 'finished' %span diff --git a/app/views/layouts/_google_analytics.html.haml b/app/views/layouts/_google_analytics.html.haml index 81e03c7eff2..98ea96b0b77 100644 --- a/app/views/layouts/_google_analytics.html.haml +++ b/app/views/layouts/_google_analytics.html.haml @@ -1,3 +1,4 @@ +-# haml-lint:disable InlineJavaScript :javascript var _gaq = _gaq || []; _gaq.push(['_setAccount', '#{extra_config.google_analytics_id}']); diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml index 4bb0dfc73fd..9704c9ec624 100644 --- a/app/views/layouts/_init_auto_complete.html.haml +++ b/app/views/layouts/_init_auto_complete.html.haml @@ -2,6 +2,7 @@ - noteable_type = @noteable.class if @noteable.present? - if project + -# haml-lint:disable InlineJavaScript :javascript gl.GfmAutoComplete = gl.GfmAutoComplete || {}; gl.GfmAutoComplete.dataSources = { diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 873220cc73d..c4f8cd71395 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -1,4 +1,4 @@ -.page-with-sidebar{ class: "#{('page-with-new-sidebar' if defined?(@new_sidebar) && @new_sidebar)} #{page_gutter_class}" } +.page-with-sidebar{ class: page_with_sidebar_class } - if show_new_nav? - if defined?(nav) && nav = render "layouts/nav/#{nav}" @@ -9,7 +9,7 @@ = render "layouts/nav/#{nav}" - if content_for?(:sub_nav) = yield :sub_nav - .content-wrapper{ class: "#{(layout_nav_class unless show_new_nav?)}" } + .content-wrapper{ class: layout_nav_class } - if show_new_nav? .mobile-overlay .alert-wrapper diff --git a/app/views/layouts/_piwik.html.haml b/app/views/layouts/_piwik.html.haml index 259b4f7cdfc..a888e8ae187 100644 --- a/app/views/layouts/_piwik.html.haml +++ b/app/views/layouts/_piwik.html.haml @@ -1,4 +1,5 @@ <!-- Piwik --> +-# haml-lint:disable InlineJavaScript :javascript var _paq = _paq || []; _paq.push(['trackPageView']); diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 38b95d11fd4..b53f382fa3d 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,8 +1,9 @@ !!! 5 -%html{ lang: I18n.locale, class: "#{page_class}" } +%html{ lang: I18n.locale, class: page_class } = render "layouts/head" %body{ class: @body_class, data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}", find_file: find_file_path } } = render "layouts/init_auto_complete" if @gfm_form + = render 'peek/bar' - if show_new_nav? = render "layouts/header/new" - else @@ -10,5 +11,3 @@ = render 'layouts/page', sidebar: sidebar, nav: nav = yield :scripts_body - - = render 'peek/bar' diff --git a/app/views/layouts/nav/_new_admin_sidebar.html.haml b/app/views/layouts/nav/_new_admin_sidebar.html.haml index 95443de40c2..8db3e69aed4 100644 --- a/app/views/layouts/nav/_new_admin_sidebar.html.haml +++ b/app/views/layouts/nav/_new_admin_sidebar.html.haml @@ -91,8 +91,8 @@ = nav_link(controller: :abuse_reports) do = link_to admin_abuse_reports_path, title: "Abuse Reports" do %span - Abuse Reports %span.badge.count= number_with_delimiter(AbuseReport.count(:all)) + Abuse Reports - if akismet_enabled? = nav_link(controller: :spam_logs) do diff --git a/app/views/layouts/nav/_new_group_sidebar.html.haml b/app/views/layouts/nav/_new_group_sidebar.html.haml index a7897c09e79..4fd9e213ead 100644 --- a/app/views/layouts/nav/_new_group_sidebar.html.haml +++ b/app/views/layouts/nav/_new_group_sidebar.html.haml @@ -28,9 +28,9 @@ = nav_link(path: ['groups#issues', 'labels#index', 'milestones#index']) do = link_to issues_group_path(@group), title: 'Issues' do %span - Issues - issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute %span.badge.count= number_with_delimiter(issues.count) + Issues %ul.sidebar-sub-level-items = nav_link(path: 'groups#issues', html_options: { class: 'home' }) do @@ -51,9 +51,9 @@ = nav_link(path: 'groups#merge_requests') do = link_to merge_requests_group_path(@group), title: 'Merge Requests' do %span - Merge Requests - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute %span.badge.count= number_with_delimiter(merge_requests.count) + Merge Requests = nav_link(path: 'group_members#index') do = link_to group_group_members_path(@group), title: 'Members' do %span diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml index 99adb83cd1f..54d56e9b873 100644 --- a/app/views/layouts/project.html.haml +++ b/app/views/layouts/project.html.haml @@ -10,6 +10,7 @@ - content_for :project_javascripts do - project = @target_project || @project - if current_user + -# haml-lint:disable InlineJavaScript :javascript window.uploads_path = "#{project_uploads_path(project)}"; diff --git a/app/views/layouts/snippets.html.haml b/app/views/layouts/snippets.html.haml index 57971205e0e..849075a0ba5 100644 --- a/app/views/layouts/snippets.html.haml +++ b/app/views/layouts/snippets.html.haml @@ -2,6 +2,7 @@ - content_for :page_specific_javascripts do - if @snippet && current_user + -# haml-lint:disable InlineJavaScript :javascript window.uploads_path = "#{upload_path('personal_snippet', id: @snippet.id)}"; diff --git a/app/views/peek/views/_host.html.haml b/app/views/peek/views/_host.html.haml new file mode 100644 index 00000000000..40769b5c6f6 --- /dev/null +++ b/app/views/peek/views/_host.html.haml @@ -0,0 +1,2 @@ +%span.current-host + = truncate(view.hostname) diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml index cf750378e25..2216708d354 100644 --- a/app/views/profiles/personal_access_tokens/index.html.haml +++ b/app/views/profiles/personal_access_tokens/index.html.haml @@ -1,5 +1,6 @@ - page_title "Personal Access Tokens" - @content_class = "limit-container-width" unless fluid_layout + = render 'profiles/head' .row.prepend-top-default @@ -19,7 +20,7 @@ %h5.prepend-top-0 Your New Personal Access Token .form-group - = text_field_tag 'created-personal-access-token', flash[:personal_access_token], readonly: true, class: "form-control", 'aria-describedby' => "created-personal-access-token-help-block" + = text_field_tag 'created-personal-access-token', flash[:personal_access_token], readonly: true, class: "form-control js-select-on-focus", 'aria-describedby' => "created-personal-access-token-help-block" = clipboard_button(text: flash[:personal_access_token], title: "Copy personal access token to clipboard", placement: "left") %span#created-personal-access-token-help-block.help-block.text-danger Make sure you save it - you won't be able to access it again. @@ -28,8 +29,3 @@ = render "shared/personal_access_tokens_form", path: profile_personal_access_tokens_path, impersonation: false, token: @personal_access_token, scopes: @scopes = render "shared/personal_access_tokens_table", impersonation: false, active_tokens: @active_personal_access_tokens, inactive_tokens: @inactive_personal_access_tokens - -:javascript - $("#created-personal-access-token").click(function() { - this.select(); - }); diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml index 037cb30efb9..33e062c1c9c 100644 --- a/app/views/profiles/two_factor_auths/show.html.haml +++ b/app/views/profiles/two_factor_auths/show.html.haml @@ -7,97 +7,92 @@ = render 'profiles/head' -- if inject_u2f_api? - - content_for :page_specific_javascripts do +- content_for :page_specific_javascripts do + - if inject_u2f_api? = page_specific_javascript_bundle_tag('u2f') + = page_specific_javascript_bundle_tag('two_factor_auth') -.row.prepend-top-default - .col-lg-4 - %h4.prepend-top-0 - Register Two-Factor Authentication App - %p - Use an app on your mobile device to enable two-factor authentication (2FA). - .col-lg-8 - - if current_user.two_factor_otp_enabled? - = icon "check inverse", base: "circle", class: "text-success", text: "You've already enabled two-factor authentication using mobile authenticator applications. You can disable it from your account settings page." - - else +.js-two-factor-auth{ 'data-two-factor-skippable' => "#{two_factor_skippable?}", 'data-two_factor_skip_url' => skip_profile_two_factor_auth_path } + .row.prepend-top-default + .col-lg-4 + %h4.prepend-top-0 + Register Two-Factor Authentication App %p - Download the Google Authenticator application from App Store or Google Play Store and scan this code. - More information is available in the #{link_to('documentation', help_page_path('profile/two_factor_authentication'))}. - .row.append-bottom-10 - .col-md-4 - = raw @qr_code - .col-md-8 - .account-well - %p.prepend-top-0.append-bottom-0 - Can't scan the code? - %p.prepend-top-0.append-bottom-0 - To add the entry manually, provide the following details to the application on your phone. - %p.prepend-top-0.append-bottom-0 - Account: - = @account_string - %p.prepend-top-0.append-bottom-0 - Key: - = current_user.otp_secret.scan(/.{4}/).join(' ') - %p.two-factor-new-manual-content - Time based: Yes - = form_tag profile_two_factor_auth_path, method: :post do |f| - - if @error - .alert.alert-danger - = @error - .form-group - = label_tag :pin_code, nil, class: "label-light" - = text_field_tag :pin_code, nil, class: "form-control", required: true - .prepend-top-default - = submit_tag 'Register with two-factor app', class: 'btn btn-success' + Use an app on your mobile device to enable two-factor authentication (2FA). + .col-lg-8 + - if current_user.two_factor_otp_enabled? + = icon "check inverse", base: "circle", class: "text-success", text: "You've already enabled two-factor authentication using mobile authenticator applications. You can disable it from your account settings page." + - else + %p + Download the Google Authenticator application from App Store or Google Play Store and scan this code. + More information is available in the #{link_to('documentation', help_page_path('profile/two_factor_authentication'))}. + .row.append-bottom-10 + .col-md-4 + = raw @qr_code + .col-md-8 + .account-well + %p.prepend-top-0.append-bottom-0 + Can't scan the code? + %p.prepend-top-0.append-bottom-0 + To add the entry manually, provide the following details to the application on your phone. + %p.prepend-top-0.append-bottom-0 + Account: + = @account_string + %p.prepend-top-0.append-bottom-0 + Key: + = current_user.otp_secret.scan(/.{4}/).join(' ') + %p.two-factor-new-manual-content + Time based: Yes + = form_tag profile_two_factor_auth_path, method: :post do |f| + - if @error + .alert.alert-danger + = @error + .form-group + = label_tag :pin_code, nil, class: "label-light" + = text_field_tag :pin_code, nil, class: "form-control", required: true + .prepend-top-default + = submit_tag 'Register with two-factor app', class: 'btn btn-success' -%hr + %hr -.row.prepend-top-default - - .col-lg-4 - %h4.prepend-top-0 - Register Universal Two-Factor (U2F) Device - %p - Use a hardware device to add the second factor of authentication. - %p - As U2F devices are only supported by a few browsers, we require that you set up a - two-factor authentication app before a U2F device. That way you'll always be able to - log in - even when you're using an unsupported browser. - .col-lg-8 - - if @u2f_registration.errors.present? - = form_errors(@u2f_registration) - = render "u2f/register" + .row.prepend-top-default + .col-lg-4 + %h4.prepend-top-0 + Register Universal Two-Factor (U2F) Device + %p + Use a hardware device to add the second factor of authentication. + %p + As U2F devices are only supported by a few browsers, we require that you set up a + two-factor authentication app before a U2F device. That way you'll always be able to + log in - even when you're using an unsupported browser. + .col-lg-8 + - if @u2f_registration.errors.present? + = form_errors(@u2f_registration) + = render "u2f/register" - %hr + %hr - %h5 U2F Devices (#{@u2f_registrations.length}) + %h5 U2F Devices (#{@u2f_registrations.length}) - - if @u2f_registrations.present? - .table-responsive - %table.table.table-bordered.u2f-registrations - %colgroup - %col{ width: "50%" } - %col{ width: "30%" } - %col{ width: "20%" } - %thead - %tr - %th Name - %th Registered On - %th - %tbody - - @u2f_registrations.each do |registration| + - if @u2f_registrations.present? + .table-responsive + %table.table.table-bordered.u2f-registrations + %colgroup + %col{ width: "50%" } + %col{ width: "30%" } + %col{ width: "20%" } + %thead %tr - %td= registration.name.presence || "<no name set>" - %td= registration.created_at.to_date.to_s(:medium) - %td= link_to "Delete", profile_u2f_registration_path(registration), method: :delete, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to delete this device? This action cannot be undone." } - - - else - .settings-message.text-center - You don't have any U2F devices registered yet. - + %th Name + %th Registered On + %th + %tbody + - @u2f_registrations.each do |registration| + %tr + %td= registration.name.presence || "<no name set>" + %td= registration.created_at.to_date.to_s(:medium) + %td= link_to "Delete", profile_u2f_registration_path(registration), method: :delete, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to delete this device? This action cannot be undone." } -- if two_factor_skippable? - :javascript - var button = "<a class='btn btn-xs btn-warning pull-right' data-method='patch' href='#{skip_profile_two_factor_auth_path}'>Configure it later</a>"; - $(".flash-alert").append(button); + - else + .settings-message.text-center + You don't have any U2F devices registered yet. diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml index ecc966ed453..ad63f5e73ae 100644 --- a/app/views/projects/_activity.html.haml +++ b/app/views/projects/_activity.html.haml @@ -8,9 +8,3 @@ .content_list.project-activity{ :"data-href" => activity_project_path(@project) } = spinner - -:javascript - var activity = new gl.Activities(); - $(document).on('page:restore', function (event) { - activity.reloadActivities() - }) diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml index b2959ef6d31..03ab1bb59e4 100644 --- a/app/views/projects/blob/_new_dir.html.haml +++ b/app/views/projects/blob/_new_dir.html.haml @@ -20,6 +20,3 @@ - unless can?(current_user, :push_code, @project) .inline.prepend-left-10 = commit_in_fork_help - -:javascript - new NewCommitForm($('.js-create-dir-form')) diff --git a/app/views/projects/blob/_remove.html.haml b/app/views/projects/blob/_remove.html.haml index 6a4a657fa8c..750bdef3308 100644 --- a/app/views/projects/blob/_remove.html.haml +++ b/app/views/projects/blob/_remove.html.haml @@ -13,6 +13,3 @@ .col-sm-offset-2.col-sm-10 = button_tag 'Delete file', class: 'btn btn-remove btn-remove-file' = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" - -:javascript - new NewCommitForm($('.js-delete-blob-form')) diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index 03eefcc2b4d..2baaaf6ac5b 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -28,8 +28,4 @@ .form-actions = button_tag 'Create branch', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', project_branches_path(@project), class: 'btn btn-cancel' - -:javascript - var availableRefs = #{@project.repository.ref_names.to_json}; - - new NewBranchForm($('.js-create-branch-form'), availableRefs) +%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 419fbe99af8..09bcd187e59 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -1,4 +1,4 @@ -.page-content-header +.page-content-header.js-commit-box{ 'data-commit-path' => branches_project_commit_path(@project, @commit.id) } .header-main-content = render partial: 'signature', object: @commit.signature %strong @@ -79,6 +79,3 @@ = render 'shared/mini_pipeline_graph', pipeline: last_pipeline, klass: 'js-commit-pipeline-graph' in = time_interval_in_words last_pipeline.duration - -:javascript - $(".commit-info.branches").load("#{branches_project_commit_path(@project, @commit.id)}"); diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 12b73ecdf13..e7da47032be 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -5,7 +5,7 @@ - notes = commit.notes - note_count = notes.user.count -- cache_key = [project.path_with_namespace, commit.id, current_application_settings, note_count, @path.presence, current_controller?(:commits)] +- cache_key = [project.full_path, commit.id, current_application_settings, note_count, @path.presence, current_controller?(:commits)] - cache_key.push(commit.status(ref)) if commit.status(ref) = cache(cache_key, expires_in: 1.day) do diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index bd2d900997e..7ae56086177 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -11,34 +11,32 @@ = content_for :sub_nav do = render "head" -%div{ class: container_class } - .tree-holder - .nav-block - .tree-ref-container - .tree-ref-holder - = render 'shared/ref_switcher', destination: 'commits' +.js-project-commits-show{ 'data-commits-limit' => @limit } + %div{ class: container_class } + .tree-holder + .nav-block + .tree-ref-container + .tree-ref-holder + = render 'shared/ref_switcher', destination: 'commits' + + %ul.breadcrumb.repo-breadcrumb + = commits_breadcrumbs + .tree-controls.hidden-xs.hidden-sm + - if @merge_request.present? + .control + = link_to _("View open merge request"), project_merge_request_path(@project, @merge_request), class: 'btn' + - elsif create_mr_button?(@repository.root_ref, @ref) + .control + = link_to _("Create merge request"), create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' - %ul.breadcrumb.repo-breadcrumb - = commits_breadcrumbs - .tree-controls.hidden-xs.hidden-sm - - if @merge_request.present? .control - = link_to _("View open merge request"), project_merge_request_path(@project, @merge_request), class: 'btn' - - elsif create_mr_button?(@repository.root_ref, @ref) + = form_tag(project_commits_path(@project, @id), method: :get, class: 'commits-search-form', data: { 'signatures-path' => namespace_project_signatures_path }) do + = search_field_tag :search, params[:search], { placeholder: _('Filter by commit message'), id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false } .control - = link_to _("Create merge request"), create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' + = link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn' do + = icon("rss") - .control - = form_tag(project_commits_path(@project, @id), method: :get, class: 'commits-search-form', data: { 'signatures-path' => namespace_project_signatures_path }) do - = search_field_tag :search, params[:search], { placeholder: _('Filter by commit message'), id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false } - .control - = link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn' do - = icon("rss") - - %div{ id: dom_id(@project) } - %ol#commits-list.list-unstyled.content_list - = render 'commits', project: @project, ref: @ref - = spinner - -:javascript - CommitsList.init(#{@limit}); + %div{ id: dom_id(@project) } + %ol#commits-list.list-unstyled.content_list + = render 'commits', project: @project, ref: @ref + = spinner diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml index e3bf48ee47f..021575160ea 100644 --- a/app/views/projects/find_file/show.html.haml +++ b/app/views/projects/find_file/show.html.haml @@ -1,7 +1,7 @@ - page_title "Find File", @ref = render "projects/commits/head" -.file-finder-holder.tree-holder.clearfix +.file-finder-holder.tree-holder.clearfix.js-file-finder{ 'data-file-find-url': "#{escape_javascript(project_files_path(@project, @ref, @options.merge(format: :json)))}", 'data-find-tree-url': escape_javascript(project_tree_path(@project, @ref)), 'data-blob-url-template': escape_javascript(project_blob_path(@project, @id || @commit.id)) } .nav-block .tree-ref-holder = render 'shared/ref_switcher', destination: 'find_file', path: @path @@ -17,11 +17,3 @@ %table.table.files-slider{ class: "table_#{@hex_path} tree-table table-striped" } %tbody = spinner nil, true - -:javascript - var projectFindFile = new ProjectFindFile($(".file-finder-holder"), { - url: "#{escape_javascript(project_files_path(@project, @ref, @options.merge(format: :json)))}", - treeUrl: "#{escape_javascript(project_tree_path(@project, @ref))}", - blobUrlTemplate: "#{escape_javascript(project_blob_path(@project, @id || @commit.id))}" - }); - new ShortcutsFindFile(projectFindFile); diff --git a/app/views/projects/graphs/charts.html.haml b/app/views/projects/graphs/charts.html.haml index 249b9d82ad9..228c8c84792 100644 --- a/app/views/projects/graphs/charts.html.haml +++ b/app/views/projects/graphs/charts.html.haml @@ -3,8 +3,9 @@ - if show_new_nav? - add_to_breadcrumbs("Repository", project_tree_path(@project)) - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_d3') - = page_specific_javascript_bundle_tag('graphs') + = webpack_bundle_tag('common_d3') + = webpack_bundle_tag('graphs') + = webpack_bundle_tag('graphs_charts') = render "projects/commits/head" .repo-charts{ class: container_class } @@ -75,55 +76,10 @@ Commits per day hour (UTC) %canvas#hour-chart -:javascript - var responsiveChart = function (selector, data) { - var options = { "scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2, maintainAspectRatio: false }; - // get selector by context - var ctx = selector.get(0).getContext("2d"); - // pointing parent container to make chart.js inherit its width - var container = $(selector).parent(); - var generateChart = function() { - selector.attr('width', $(container).width()); - if (window.innerWidth < 768) { - // Scale fonts if window width lower than 768px (iPad portrait) - options.scaleFontSize = 8 - } - return new Chart(ctx).Bar(data, options); - }; - // enabling auto-resizing - $(window).resize(generateChart); - return generateChart(); - }; - - var chartData = function (keys, values) { - var data = { - labels : keys, - datasets : [{ - fillColor : "rgba(220,220,220,0.5)", - strokeColor : "rgba(220,220,220,1)", - barStrokeWidth: 1, - barValueSpacing: 1, - barDatasetSpacing: 1, - data : values - }] - }; - return data; - }; - - var hourData = chartData(#{@commits_per_time.keys.to_json}, #{@commits_per_time.values.to_json}); - responsiveChart($('#hour-chart'), hourData); - - var dayData = chartData(#{@commits_per_week_days.keys.to_json}, #{@commits_per_week_days.values.to_json}); - responsiveChart($('#weekday-chart'), dayData); - - var monthData = chartData(#{@commits_per_month.keys.to_json}, #{@commits_per_month.values.to_json}); - responsiveChart($('#month-chart'), monthData); - - var data = #{@languages.to_json}; - var ctx = $("#languages-chart").get(0).getContext("2d"); - var options = { - scaleOverlay: true, - responsive: true, - maintainAspectRatio: false - } - var myPieChart = new Chart(ctx).Pie(data, options); +%script#projectChartData{ type: "application/json" } + - projectChartData = {}; + - projectChartData['hour'] = { 'keys' => @commits_per_time.keys, 'values' => @commits_per_time.values } + - projectChartData['weekDays'] = { 'keys' => @commits_per_week_days.keys, 'values' => @commits_per_week_days.values } + - projectChartData['month'] = { 'keys' => @commits_per_month.keys, 'values' => @commits_per_month.values } + - projectChartData['languages'] = @languages + = projectChartData.to_json.html_safe diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml index 4256a8c4d7e..f41a0d8293b 100644 --- a/app/views/projects/graphs/show.html.haml +++ b/app/views/projects/graphs/show.html.haml @@ -1,15 +1,16 @@ - @no_container = true - page_title "Contributors" - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_d3') - = page_specific_javascript_bundle_tag('graphs') + = webpack_bundle_tag('common_d3') + = webpack_bundle_tag('graphs') + = webpack_bundle_tag('graphs_show') - if show_new_nav? - add_to_breadcrumbs("Repository", project_tree_path(@project)) = render 'projects/commits/head' -%div{ class: container_class } +.js-graphs-show{ class: container_class, 'data-project-graph-path': project_graph_path(@project, current_ref, format: :json) } .sub-header-block .tree-ref-holder = render 'shared/ref_switcher', destination: 'graphs' @@ -33,24 +34,3 @@ #contributors-master #contributors.clearfix %ol.contributors-list.clearfix - - - -:javascript - $.ajax({ - type: "GET", - url: "#{project_graph_path(@project, current_ref, format: :json)}", - dataType: "json", - success: function (data) { - var graph = new ContributorsStatGraph(); - graph.init(data); - - $("#brush_change").change(function(){ - graph.change_date_header(); - graph.redraw_authors(); - }); - - $(".stat-graph").fadeIn(); - $(".loading-graph").hide(); - } - }); diff --git a/app/views/projects/imports/show.html.haml b/app/views/projects/imports/show.html.haml index c52b3860636..8c490773a56 100644 --- a/app/views/projects/imports/show.html.haml +++ b/app/views/projects/imports/show.html.haml @@ -10,5 +10,3 @@ - if @project.external_import? %p.monospace git clone --bare #{@project.safe_import_url} %p Please wait while we import the repository for you. Refresh at will. - :javascript - new ProjectImport(); diff --git a/app/views/projects/mattermosts/_team_selection.html.haml b/app/views/projects/mattermosts/_team_selection.html.haml index 3bdb5d0adc4..20acd476f73 100644 --- a/app/views/projects/mattermosts/_team_selection.html.haml +++ b/app/views/projects/mattermosts/_team_selection.html.haml @@ -33,7 +33,7 @@ Suggestions: %code= 'gitlab' %code= @project.path # Path contains no spaces, but dashes - %code= @project.path_with_namespace + %code= @project.full_path %p Reserved: = link_to 'https://docs.mattermost.com/help/messaging/executing-commands.html#built-in-commands', target: '__blank' do diff --git a/app/views/projects/merge_requests/creations/_new_compare.html.haml b/app/views/projects/merge_requests/creations/_new_compare.html.haml index 8958b2cf5e1..9d5cebdda53 100644 --- a/app/views/projects/merge_requests/creations/_new_compare.html.haml +++ b/app/views/projects/merge_requests/creations/_new_compare.html.haml @@ -41,7 +41,7 @@ - projects = target_projects(@project) .merge-request-select.dropdown = f.hidden_field :target_project_id - = dropdown_toggle f.object.target_project.path_with_namespace, { toggle: "dropdown", field_name: "#{f.object_name}[target_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-target-project" } + = dropdown_toggle f.object.target_project.full_path, { toggle: "dropdown", field_name: "#{f.object_name}[target_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-target-project" } .dropdown-menu.dropdown-menu-selectable.dropdown-target-project = dropdown_title("Select target project") = dropdown_filter("Search projects") diff --git a/app/views/projects/merge_requests/dropdowns/_project.html.haml b/app/views/projects/merge_requests/dropdowns/_project.html.haml index 25d5dc92f8a..aaf1ab00eeb 100644 --- a/app/views/projects/merge_requests/dropdowns/_project.html.haml +++ b/app/views/projects/merge_requests/dropdowns/_project.html.haml @@ -2,4 +2,4 @@ - projects.each do |project| %li %a{ href: "#", class: "#{('is-active' if selected == project.id)}", data: { id: project.id } } - = project.path_with_namespace + = project.full_path diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 87cc23fc649..25109f0f414 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -4,6 +4,8 @@ - page_title 'New Project' - header_title "Projects", dashboard_projects_path - visibility_level = params.dig(:project, :visibility_level) || default_project_visibility +- content_for :page_specific_javascripts do + = webpack_bundle_tag 'project_new' .project-edit-container .project-edit-errors @@ -111,46 +113,3 @@ %i.fa.fa-spinner.fa-spin Creating project & repository. %p Please wait a moment, this page will automatically refresh when ready. - -:javascript - var importBtnTooltip = "Please enter a valid project name."; - var $importBtnWrapper = $('.import_gitlab_project'); - - $('.how_to_import_link').bind('click', function (e) { - e.preventDefault(); - var import_modal = $(this).next(".modal").show(); - }); - - $('.modal-header .close').bind('click', function() { - $(".modal").hide(); - }); - - $('.btn_import_gitlab_project').bind('click', function() { - var _href = $("a.btn_import_gitlab_project").attr("href"); - $(".btn_import_gitlab_project").attr("href", _href + '?namespace_id=' + $("#project_namespace_id").val() + '&path=' + $("#project_path").val()); - }); - - $('.btn_import_gitlab_project').attr('disabled', $('#project_path').val().trim().length === 0); - $importBtnWrapper.attr('title', importBtnTooltip); - - $('#new_project').submit(function(){ - var $path = $('#project_path'); - $path.val($path.val().trim()); - }); - - $('#project_path').keyup(function(){ - if($(this).val().trim().length !== 0) { - $('.btn_import_gitlab_project').attr('disabled', false); - $importBtnWrapper.attr('title',''); - $importBtnWrapper.removeClass('has-tooltip'); - } else { - $('.btn_import_gitlab_project').attr('disabled',true); - $importBtnWrapper.addClass('has-tooltip'); - } - }); - - $('#project_import_url').disable(); - $('.import_git').click(function( event ) { - $projectImportUrl = $('#project_import_url'); - $projectImportUrl.attr('disabled', !$projectImportUrl.attr('disabled')); - }); diff --git a/app/views/projects/pipelines/charts/_pipeline_times.haml b/app/views/projects/pipelines/charts/_pipeline_times.haml index 1292f580a81..a5dbd1b1532 100644 --- a/app/views/projects/pipelines/charts/_pipeline_times.haml +++ b/app/views/projects/pipelines/charts/_pipeline_times.haml @@ -1,27 +1,10 @@ +- content_for :page_specific_javascripts do + = webpack_bundle_tag('pipelines_times') + %div %p.light = _("Commit duration in minutes for last 30 commits") %canvas#build_timesChart{ height: 200 } -:javascript - var data = { - labels : #{@charts[:pipeline_times].labels.to_json}, - datasets : [ - { - fillColor : "rgba(220,220,220,0.5)", - strokeColor : "rgba(220,220,220,1)", - barStrokeWidth: 1, - barValueSpacing: 1, - barDatasetSpacing: 1, - data : #{@charts[:pipeline_times].pipeline_times.to_json} - } - ] - } - var ctx = $("#build_timesChart").get(0).getContext("2d"); - var options = { scaleOverlay: true, responsive: true, maintainAspectRatio: false }; - if (window.innerWidth < 768) { - // Scale fonts if window width lower than 768px (iPad portrait) - options.scaleFontSize = 8 - } - new Chart(ctx).Bar(data, options); +%script#pipelinesTimesChartsData{ type: "application/json" }= { :labels => @charts[:pipeline_times].labels, :values => @charts[:pipeline_times].pipeline_times }.to_json.html_safe diff --git a/app/views/projects/pipelines/charts/_pipelines.haml b/app/views/projects/pipelines/charts/_pipelines.haml index be884448087..02f1ef4b6da 100644 --- a/app/views/projects/pipelines/charts/_pipelines.haml +++ b/app/views/projects/pipelines/charts/_pipelines.haml @@ -1,3 +1,6 @@ +- content_for :page_specific_javascripts do + = webpack_bundle_tag('pipelines_charts') + %h4= _("Pipelines charts") %p @@ -26,31 +29,8 @@ = _("Jobs for last year") %canvas#yearChart.padded{ height: 250 } -- [:week, :month, :year].each do |scope| - :javascript - var data = { - labels : #{@charts[scope].labels.to_json}, - datasets : [ - { - fillColor : "#7f8fa4", - strokeColor : "#7f8fa4", - pointColor : "#7f8fa4", - pointStrokeColor : "#EEE", - data : #{@charts[scope].total.to_json} - }, - { - fillColor : "#44aa22", - strokeColor : "#44aa22", - pointColor : "#44aa22", - pointStrokeColor : "#fff", - data : #{@charts[scope].success.to_json} - } - ] - } - var ctx = $("##{scope}Chart").get(0).getContext("2d"); - var options = { scaleOverlay: true, responsive: true, maintainAspectRatio: false }; - if (window.innerWidth < 768) { - // Scale fonts if window width lower than 768px (iPad portrait) - options.scaleFontSize = 8 - } - new Chart(ctx).Line(data, options); +%script#pipelinesChartsData{ type: "application/json" } + - chartData = [] + - [:week, :month, :year].each do |scope| + - chartData.push({ 'scope' => scope, 'labels' => @charts[scope].labels, 'totalValues' => @charts[scope].total, 'successValues' => @charts[scope].success }) + = chartData.to_json.html_safe diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml index c966df62856..4ad37d0e882 100644 --- a/app/views/projects/pipelines/new.html.haml +++ b/app/views/projects/pipelines/new.html.haml @@ -20,7 +20,4 @@ = f.submit 'Create pipeline', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', project_pipelines_path(@project), class: 'btn btn-cancel' -:javascript - var availableRefs = #{@project.repository.ref_names.to_json}; - - new NewBranchForm($('.js-new-pipeline-form'), availableRefs) +%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe diff --git a/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml b/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml index ef3599460f1..5dbcbf7eba6 100644 --- a/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml +++ b/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml @@ -39,7 +39,7 @@ Suggestions: %code= 'gitlab' %code= @project.path # Path contains no spaces, but dashes - %code= @project.path_with_namespace + %code= @project.full_path .form-group = label_tag :request_url, 'Request URL', class: 'col-sm-2 col-xs-12 control-label' diff --git a/app/views/projects/services/slack_slash_commands/_help.html.haml b/app/views/projects/services/slack_slash_commands/_help.html.haml index 73b99453a4b..c31c95608c6 100644 --- a/app/views/projects/services/slack_slash_commands/_help.html.haml +++ b/app/views/projects/services/slack_slash_commands/_help.html.haml @@ -33,7 +33,7 @@ Suggestions: %code= 'gitlab' %code= @project.path # Path contains no spaces, but dashes - %code= @project.path_with_namespace + %code= @project.full_path .form-group = label_tag :url, 'URL', class: 'col-sm-2 col-xs-12 control-label' diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index f1bbaf40387..521b4d927bc 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -40,7 +40,4 @@ .form-actions = button_tag 'Create tag', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', project_tags_path(@project), class: 'btn btn-cancel' - -:javascript - window.gl = window.gl || { }; - window.gl.availableRefs = #{@project.repository.ref_names.to_json}; +%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml index 6560bd5ab3f..820b947804e 100644 --- a/app/views/projects/tree/_tree_content.html.haml +++ b/app/views/projects/tree/_tree_content.html.haml @@ -1,4 +1,4 @@ -.tree-content-holder +.tree-content-holder.js-tree-content{ 'data-logs-path': @logs_path } .table-holder %table.table#tree-slider{ class: "table_#{@hex_path} tree-table" } %thead @@ -22,9 +22,3 @@ - if can_edit_tree? = render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: project_create_blob_path(@project, @id), method: :post = render 'projects/blob/new_dir' - -:javascript - // Load last commit log for each file in tree - $('#tree-slider').waitForImages(function() { - gl.utils.ajaxGet("#{escape_javascript(@logs_path)}"); - }); diff --git a/app/views/projects/wikis/_sidebar.html.haml b/app/views/projects/wikis/_sidebar.html.haml index 62873d3aa66..e71ce1f357f 100644 --- a/app/views/projects/wikis/_sidebar.html.haml +++ b/app/views/projects/wikis/_sidebar.html.haml @@ -19,6 +19,3 @@ More Pages = render 'projects/wikis/new' - -:javascript - new Sidebar(); diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml index e64dd6085fe..e740fb93ea4 100644 --- a/app/views/projects/wikis/git_access.html.haml +++ b/app/views/projects/wikis/git_access.html.haml @@ -7,7 +7,7 @@ .git-access-header Clone repository - %strong= @project_wiki.path_with_namespace + %strong= @project_wiki.full_path = render "shared/clone_panel", project: @project_wiki diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index b08267357e5..e7510c1d1ec 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -37,7 +37,7 @@ = link_to 'Edit', '#', class: 'edit-link pull-right' .value.hide-collapsed - if issuable.milestone - = link_to issuable.milestone.title, project_milestone_path(@project, issuable.milestone), class: "bold has-tooltip", title: milestone_remaining_days(issuable.milestone), data: { container: "body", html: 1 } + = link_to issuable.milestone.title, milestone_path(issuable.milestone), class: "bold has-tooltip", title: milestone_remaining_days(issuable.milestone), data: { container: "body", html: 1 } - else %span.no-value None diff --git a/app/views/u2f/_register.html.haml b/app/views/u2f/_register.html.haml index 00788e77b6b..093b2d82813 100644 --- a/app/views/u2f/_register.html.haml +++ b/app/views/u2f/_register.html.haml @@ -37,7 +37,3 @@ .col-md-3 = hidden_field_tag 'u2f_registration[device_response]', nil, class: 'form-control', required: true, id: "js-device-response" = submit_tag "Register U2F device", class: "btn btn-success" - -:javascript - var u2fRegister = new U2FRegister($("#js-register-u2f"), gon.u2f); - u2fRegister.start(); diff --git a/app/workers/git_garbage_collect_worker.rb b/app/workers/git_garbage_collect_worker.rb index d369b639ae9..c95497dfaba 100644 --- a/app/workers/git_garbage_collect_worker.rb +++ b/app/workers/git_garbage_collect_worker.rb @@ -5,6 +5,12 @@ class GitGarbageCollectWorker sidekiq_options retry: false + GITALY_MIGRATED_TASKS = { + gc: :garbage_collect, + full_repack: :repack_full, + incremental_repack: :repack_incremental + }.freeze + def perform(project_id, task = :gc, lease_key = nil, lease_uuid = nil) project = Project.find(project_id) task = task.to_sym @@ -15,8 +21,14 @@ class GitGarbageCollectWorker Gitlab::GitLogger.info(description) - output, status = Gitlab::Popen.popen(cmd, repo_path) - Gitlab::GitLogger.error("#{description} failed:\n#{output}") unless status.zero? + gitaly_migrate(GITALY_MIGRATED_TASKS[task]) do |is_enabled| + if is_enabled + gitaly_call(task, project.repository.raw_repository) + else + output, status = Gitlab::Popen.popen(cmd, repo_path) + Gitlab::GitLogger.error("#{description} failed:\n#{output}") unless status.zero? + end + end # Refresh the branch cache in case garbage collection caused a ref lookup to fail flush_ref_caches(project) if task == :gc @@ -26,6 +38,19 @@ class GitGarbageCollectWorker private + ## `repository` has to be a Gitlab::Git::Repository + def gitaly_call(task, repository) + client = Gitlab::GitalyClient::RepositoryService.new(repository) + case task + when :gc + client.garbage_collect(bitmaps_enabled?) + when :full_repack + client.repack_full(bitmaps_enabled?) + when :incremental_repack + client.repack_incremental + end + end + def command(task) case task when :gc @@ -55,4 +80,14 @@ class GitGarbageCollectWorker config_value = write_bitmaps ? 'true' : 'false' %W[git -c repack.writeBitmaps=#{config_value}] end + + def gitaly_migrate(method, &block) + Gitlab::GitalyClient.migrate(method, &block) + rescue GRPC::NotFound => e + Gitlab::GitLogger.error("#{method} failed:\nRepository not found") + raise Gitlab::Git::Repository::NoRepository.new(e) + rescue GRPC::BadStatus => e + Gitlab::GitLogger.error("#{method} failed:\n#{e}") + raise Gitlab::Git::CommandError.new(e) + end end diff --git a/app/workers/irker_worker.rb b/app/workers/irker_worker.rb index 22f67fa9e9f..3dd14466994 100644 --- a/app/workers/irker_worker.rb +++ b/app/workers/irker_worker.rb @@ -66,7 +66,7 @@ class IrkerWorker end def send_new_branch(project, repo_name, committer, branch) - repo_path = project.path_with_namespace + repo_path = project.full_path newbranch = "#{Gitlab.config.gitlab.url}/#{repo_path}/branches" newbranch = "\x0302\x1f#{newbranch}\x0f" if @colors @@ -109,7 +109,7 @@ class IrkerWorker end def send_commits_count(data, project, repo, committer, branch) - url = compare_url data, project.path_with_namespace + url = compare_url data, project.full_path commits = colorize_commits data['total_commits_count'] new_commits = 'new commit' diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index 625476b7e01..6be541abd3e 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -16,7 +16,7 @@ class RepositoryImportWorker Gitlab::Metrics.add_event(:import_repository, import_url: @project.import_url, - path: @project.path_with_namespace) + path: @project.full_path) project.update_columns(import_jid: self.jid, import_error: nil) diff --git a/changelogs/unreleased/29385-add_shrug_command.yml b/changelogs/unreleased/29385-add_shrug_command.yml new file mode 100644 index 00000000000..14b8f486d5c --- /dev/null +++ b/changelogs/unreleased/29385-add_shrug_command.yml @@ -0,0 +1,4 @@ +--- +title: Add /shrug and /tableflip commands +merge_request: 10068 +author: Alex Ives diff --git a/changelogs/unreleased/33620-remove-events-from-notification_settings.yml b/changelogs/unreleased/33620-remove-events-from-notification_settings.yml new file mode 100644 index 00000000000..f5f3ef3fb82 --- /dev/null +++ b/changelogs/unreleased/33620-remove-events-from-notification_settings.yml @@ -0,0 +1,4 @@ +--- +title: Remove events column from notification settings table +merge_request: +author: diff --git a/changelogs/unreleased/34492-firefox-job.yml b/changelogs/unreleased/34492-firefox-job.yml new file mode 100644 index 00000000000..881b8f649ea --- /dev/null +++ b/changelogs/unreleased/34492-firefox-job.yml @@ -0,0 +1,4 @@ +--- +title: Use jQuery to control scroll behavior in job log for cross browser consistency +merge_request: +author: diff --git a/changelogs/unreleased/34519-extend-api-group-secret-variable.yml b/changelogs/unreleased/34519-extend-api-group-secret-variable.yml new file mode 100644 index 00000000000..e0b625c392f --- /dev/null +++ b/changelogs/unreleased/34519-extend-api-group-secret-variable.yml @@ -0,0 +1,4 @@ +--- +title: Extend API for Group Secret Variable +merge_request: 12936 +author: diff --git a/changelogs/unreleased/34869-bump-rubocop-to-0-49-1-and-rubocop-rspec-to-1-15-1.yml b/changelogs/unreleased/34869-bump-rubocop-to-0-49-1-and-rubocop-rspec-to-1-15-1.yml new file mode 100644 index 00000000000..0eb2d069719 --- /dev/null +++ b/changelogs/unreleased/34869-bump-rubocop-to-0-49-1-and-rubocop-rspec-to-1-15-1.yml @@ -0,0 +1,4 @@ +--- +title: Bump rubocop to 0.49.1 and rubocop-rspec to 1.15.1 +merge_request: +author: Takuya Noguchi diff --git a/changelogs/unreleased/35567-fix-relative-urls-in-webpack-public-path.yml b/changelogs/unreleased/35567-fix-relative-urls-in-webpack-public-path.yml deleted file mode 100644 index 41b506681f9..00000000000 --- a/changelogs/unreleased/35567-fix-relative-urls-in-webpack-public-path.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix asynchronous javascript paths when GitLab is installed under a relative - URL -merge_request: 13165 -author: diff --git a/changelogs/unreleased/35697-allow-logged-in-user-to-read-user-list.yml b/changelogs/unreleased/35697-allow-logged-in-user-to-read-user-list.yml new file mode 100644 index 00000000000..54b2e71bef9 --- /dev/null +++ b/changelogs/unreleased/35697-allow-logged-in-user-to-read-user-list.yml @@ -0,0 +1,4 @@ +--- +title: Allow any logged in users to read_users_list even if it's restricted +merge_request: 13201 +author: diff --git a/changelogs/unreleased/breadcrumbs-collapsed-title-width-fix.yml b/changelogs/unreleased/breadcrumbs-collapsed-title-width-fix.yml new file mode 100644 index 00000000000..988fdacb5fd --- /dev/null +++ b/changelogs/unreleased/breadcrumbs-collapsed-title-width-fix.yml @@ -0,0 +1,4 @@ +--- +title: Fixed breadcrumbs title aggressively collapsing +merge_request: +author: diff --git a/changelogs/unreleased/fix-group-milestone-link-in-issuable-sidebar.yml b/changelogs/unreleased/fix-group-milestone-link-in-issuable-sidebar.yml new file mode 100644 index 00000000000..1558e575e6d --- /dev/null +++ b/changelogs/unreleased/fix-group-milestone-link-in-issuable-sidebar.yml @@ -0,0 +1,4 @@ +--- +title: Fix links to group milestones from issue and merge request sidebar +merge_request: +author: diff --git a/changelogs/unreleased/help-page-breadcrumb-title-fix.yml b/changelogs/unreleased/help-page-breadcrumb-title-fix.yml deleted file mode 100644 index 040fe4b9916..00000000000 --- a/changelogs/unreleased/help-page-breadcrumb-title-fix.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixed new navigation breadcrumb title on help pages -merge_request: -author: diff --git a/changelogs/unreleased/mk-fix-deploy-key-deletion.yml b/changelogs/unreleased/mk-fix-deploy-key-deletion.yml new file mode 100644 index 00000000000..9ff2e49b14c --- /dev/null +++ b/changelogs/unreleased/mk-fix-deploy-key-deletion.yml @@ -0,0 +1,4 @@ +--- +title: Fix deletion of deploy keys linked to other projects +merge_request: 13162 +author: diff --git a/changelogs/unreleased/mk-fix-wiki-backup.yml b/changelogs/unreleased/mk-fix-wiki-backup.yml new file mode 100644 index 00000000000..ba9c1e85955 --- /dev/null +++ b/changelogs/unreleased/mk-fix-wiki-backup.yml @@ -0,0 +1,4 @@ +--- +title: Fix improperly skipped backups of wikis. +merge_request: 13096 +author: diff --git a/changelogs/unreleased/pawel-ensure_temp_files_are_deleted_in_fs_metrics-35457.yml b/changelogs/unreleased/pawel-ensure_temp_files_are_deleted_in_fs_metrics-35457.yml deleted file mode 100644 index 1186dc59dc7..00000000000 --- a/changelogs/unreleased/pawel-ensure_temp_files_are_deleted_in_fs_metrics-35457.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Ensure filesystem metrics test files are deleted -merge_request: -author: diff --git a/changelogs/unreleased/pawel-prometheus_client_pid_reuse_error.yml b/changelogs/unreleased/pawel-prometheus_client_pid_reuse_error.yml deleted file mode 100644 index dfff4c23308..00000000000 --- a/changelogs/unreleased/pawel-prometheus_client_pid_reuse_error.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix Prometheus client PID reuse bug -merge_request: 13130 -author: diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index e9bf2df490f..73a68c6da1b 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -282,7 +282,7 @@ production: &base # # Example: '/etc/ca.pem' # - ca_cert: '' + ca_file: '' # Specifies the SSL version for OpenSSL to use, if the OpenSSL default # is not appropriate. diff --git a/config/webpack.config.js b/config/webpack.config.js index 1bcbad3a60d..e81ef2abddb 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -39,6 +39,8 @@ var config = { environments_folder: './environments/folder/environments_folder_bundle.js', filtered_search: './filtered_search/filtered_search_bundle.js', graphs: './graphs/graphs_bundle.js', + graphs_charts: './graphs/graphs_charts.js', + graphs_show: './graphs/graphs_show.js', group: './group.js', groups: './groups/index.js', groups_list: './groups_list.js', @@ -55,8 +57,11 @@ var config = { notes: './notes/index.js', pdf_viewer: './blob/pdf_viewer.js', pipelines: './pipelines/pipelines_bundle.js', - pipelines_details: './pipelines/pipeline_details_bundle.js', + pipelines_charts: './pipelines/pipelines_charts.js', + pipelines_details: './pipelines/pipeline_details_bundle.js', + pipelines_times: './pipelines/pipelines_times.js', profile: './profile/profile_bundle.js', + project_new: './projects/project_new.js', prometheus_metrics: './prometheus_metrics', protected_branches: './protected_branches', protected_tags: './protected_tags', @@ -68,9 +73,12 @@ var config = { stl_viewer: './blob/stl_viewer.js', terminal: './terminal/terminal_bundle.js', u2f: ['vendor/u2f'], + ui_development_kit: './ui_development_kit.js', + users: './users/index.js', raven: './raven/index.js', vue_merge_request_widget: './vue_merge_request_widget/index.js', test: './test.js', + two_factor_auth: './two_factor_auth.js', performance_bar: './performance_bar.js', webpack_runtime: './webpack.js', }, diff --git a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb index c0cb9d78748..bcdae272209 100644 --- a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb +++ b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb @@ -16,6 +16,7 @@ class MigrateProcessCommitWorkerJobs < ActiveRecord::Migration end def repository_path + # TODO: review if the change from Legacy storage needs to reflect here as well. File.join(repository_storage_path, read_attribute(:path_with_namespace) + '.git') end diff --git a/db/post_migrate/20170728101014_remove_events_from_notification_settings.rb b/db/post_migrate/20170728101014_remove_events_from_notification_settings.rb new file mode 100644 index 00000000000..cd533391d8d --- /dev/null +++ b/db/post_migrate/20170728101014_remove_events_from_notification_settings.rb @@ -0,0 +1,9 @@ +class RemoveEventsFromNotificationSettings < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + remove_column :notification_settings, :events, :text + end +end diff --git a/db/schema.rb b/db/schema.rb index 0abdb987b77..4ba218e1e0d 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: 20170725145659) do +ActiveRecord::Schema.define(version: 20170728101014) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -254,32 +254,32 @@ ActiveRecord::Schema.define(version: 20170725145659) do add_index "ci_builds", ["updated_at"], name: "index_ci_builds_on_updated_at", using: :btree add_index "ci_builds", ["user_id"], name: "index_ci_builds_on_user_id", using: :btree - create_table "ci_pipeline_schedule_variables", force: :cascade do |t| + create_table "ci_group_variables", force: :cascade do |t| t.string "key", null: false t.text "value" t.text "encrypted_value" t.string "encrypted_value_salt" t.string "encrypted_value_iv" - t.integer "pipeline_schedule_id", null: false + t.integer "group_id", null: false + t.boolean "protected", default: false, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false end - add_index "ci_pipeline_schedule_variables", ["pipeline_schedule_id", "key"], name: "index_ci_pipeline_schedule_variables_on_schedule_id_and_key", unique: true, using: :btree + add_index "ci_group_variables", ["group_id", "key"], name: "index_ci_group_variables_on_group_id_and_key", unique: true, using: :btree - create_table "ci_group_variables", force: :cascade do |t| + create_table "ci_pipeline_schedule_variables", force: :cascade do |t| t.string "key", null: false t.text "value" t.text "encrypted_value" t.string "encrypted_value_salt" t.string "encrypted_value_iv" - t.integer "group_id", null: false - t.boolean "protected", default: false, null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.integer "pipeline_schedule_id", null: false + t.datetime "created_at" + t.datetime "updated_at" end - add_index "ci_group_variables", ["group_id", "key"], name: "index_ci_group_variables_on_group_id_and_key", unique: true, using: :btree + add_index "ci_pipeline_schedule_variables", ["pipeline_schedule_id", "key"], name: "index_ci_pipeline_schedule_variables_on_schedule_id_and_key", unique: true, using: :btree create_table "ci_pipeline_schedules", force: :cascade do |t| t.string "description" @@ -981,7 +981,6 @@ ActiveRecord::Schema.define(version: 20170725145659) do t.integer "level", default: 0, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.text "events" t.boolean "new_note" t.boolean "new_issue" t.boolean "reopen_issue" @@ -1624,8 +1623,8 @@ ActiveRecord::Schema.define(version: 20170725145659) do add_foreign_key "ci_builds", "ci_pipelines", column: "auto_canceled_by_id", name: "fk_a2141b1522", on_delete: :nullify add_foreign_key "ci_builds", "ci_stages", column: "stage_id", name: "fk_3a9eaa254d", on_delete: :cascade add_foreign_key "ci_builds", "projects", name: "fk_befce0568a", on_delete: :cascade - add_foreign_key "ci_pipeline_schedule_variables", "ci_pipeline_schedules", column: "pipeline_schedule_id", name: "fk_41c35fda51", on_delete: :cascade add_foreign_key "ci_group_variables", "namespaces", column: "group_id", name: "fk_33ae4d58d8", on_delete: :cascade + add_foreign_key "ci_pipeline_schedule_variables", "ci_pipeline_schedules", column: "pipeline_schedule_id", name: "fk_41c35fda51", on_delete: :cascade add_foreign_key "ci_pipeline_schedules", "projects", name: "fk_8ead60fcc4", on_delete: :cascade add_foreign_key "ci_pipeline_schedules", "users", column: "owner_id", name: "fk_9ea99f58d2", on_delete: :nullify add_foreign_key "ci_pipeline_variables", "ci_pipelines", column: "pipeline_id", name: "fk_f29c5f4380", on_delete: :cascade diff --git a/doc/README.md b/doc/README.md index cc63ecb7eab..8bb8e147cd1 100644 --- a/doc/README.md +++ b/doc/README.md @@ -44,16 +44,17 @@ Shortcuts to GitLab's most visited docs: ### Projects and groups -- [Create a project](gitlab-basics/create-project.md) -- [Fork a project](gitlab-basics/fork-project.md) -- [Importing and exporting projects between instances](user/project/settings/import_export.md). -- [Project access](public_access/public_access.md): Setting up your project's visibility to public, internal, or private. +- [Projects](user/project/index.md): + - [Create a project](gitlab-basics/create-project.md) + - [Fork a project](gitlab-basics/fork-project.md) + - [Importing and exporting projects between instances](user/project/settings/import_export.md). + - [Project access](public_access/public_access.md): Setting up your project's visibility to public, internal, or private. + - [GitLab Pages](user/project/pages/index.md): Build, test, and deploy your static website with GitLab Pages. - [Groups](user/group/index.md): Organize your projects in groups. - - [GitLab Subgroups](user/group/subgroups/index.md) + - [Subgroups](user/group/subgroups/index.md) - [Search through GitLab](user/search/index.md): Search for issues, merge requests, projects, groups, todos, and issues in Issue Boards. - [Snippets](user/snippets.md): Snippets allow you to create little bits of code. - [Wikis](user/project/wiki/index.md): Enhance your repository documentation with built-in wikis. -- [GitLab Pages](user/project/pages/index.md): Build, test, and deploy your static website with GitLab Pages. ### Repository diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md index a7395e03d1c..425c924cdf2 100644 --- a/doc/administration/auth/ldap.md +++ b/doc/administration/auth/ldap.md @@ -96,7 +96,7 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server # # Example: '/etc/ca.pem' # - ca_cert: '' + ca_file: '' # Specifies the SSL version for OpenSSL to use, if the OpenSSL default # is not appropriate. @@ -259,9 +259,9 @@ group you can use the following syntax: (memberOf:1.2.840.113556.1.4.1941:=CN=My Group,DC=Example,DC=com) ``` -Find more information about this "LDAP_MATCHING_RULE_IN_CHAIN" filter at +Find more information about this "LDAP_MATCHING_RULE_IN_CHAIN" filter at https://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx. Support for -nested members in the user filter should not be confused with +nested members in the user filter should not be confused with [group sync nested groups support (EE only)](https://docs.gitlab.com/ee/administration/auth/ldap-ee.html#supported-ldap-group-types-attributes). Please note that GitLab does not support the custom filter syntax used by diff --git a/doc/administration/monitoring/performance/img/performance_bar.png b/doc/administration/monitoring/performance/img/performance_bar.png Binary files differindex d38293d2ed6..b3c6bc474e3 100644 --- a/doc/administration/monitoring/performance/img/performance_bar.png +++ b/doc/administration/monitoring/performance/img/performance_bar.png diff --git a/doc/api/README.md b/doc/api/README.md index fe29563eaca..1d2226e2ae8 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -11,7 +11,8 @@ following locations: - [Award Emoji](award_emoji.md) - [Branches](branches.md) - [Broadcast Messages](broadcast_messages.md) -- [Build Variables](build_variables.md) +- [Project-level Variables](project_level_variables.md) +- [Group-level Variables](group_level_variables.md) - [Commits](commits.md) - [Deployments](deployments.md) - [Deploy Keys](deploy_keys.md) diff --git a/doc/api/group_level_variables.md b/doc/api/group_level_variables.md new file mode 100644 index 00000000000..e19be7b35c4 --- /dev/null +++ b/doc/api/group_level_variables.md @@ -0,0 +1,125 @@ +# Group-level Variables API + +## List group variables + +Get list of a group's variables. + +``` +GET /groups/:id/variables +``` + +| Attribute | Type | required | Description | +|-----------|---------|----------|---------------------| +| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | + +``` +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/1/variables" +``` + +```json +[ + { + "key": "TEST_VARIABLE_1", + "value": "TEST_1" + }, + { + "key": "TEST_VARIABLE_2", + "value": "TEST_2" + } +] +``` + +## Show variable details + +Get the details of a group's specific variable. + +``` +GET /groups/:id/variables/:key +``` + +| Attribute | Type | required | Description | +|-----------|---------|----------|-----------------------| +| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | +| `key` | string | yes | The `key` of a variable | + +``` +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/1/variables/TEST_VARIABLE_1" +``` + +```json +{ + "key": "TEST_VARIABLE_1", + "value": "TEST_1" +} +``` + +## Create variable + +Create a new variable. + +``` +POST /groups/:id/variables +``` + +| Attribute | Type | required | Description | +|-------------|---------|----------|-----------------------| +| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | +| `key` | string | yes | The `key` of a variable; must have no more than 255 characters; only `A-Z`, `a-z`, `0-9`, and `_` are allowed | +| `value` | string | yes | The `value` of a variable | +| `protected` | boolean | no | Whether the variable is protected | + +``` +curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/1/variables" --form "key=NEW_VARIABLE" --form "value=new value" +``` + +```json +{ + "key": "NEW_VARIABLE", + "value": "new value", + "protected": false +} +``` + +## Update variable + +Update a group's variable. + +``` +PUT /groups/:id/variables/:key +``` + +| Attribute | Type | required | Description | +|-------------|---------|----------|-------------------------| +| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | +| `key` | string | yes | The `key` of a variable | +| `value` | string | yes | The `value` of a variable | +| `protected` | boolean | no | Whether the variable is protected | + +``` +curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/1/variables/NEW_VARIABLE" --form "value=updated value" +``` + +```json +{ + "key": "NEW_VARIABLE", + "value": "updated value", + "protected": true +} +``` + +## Remove variable + +Remove a group's variable. + +``` +DELETE /groups/:id/variables/:key +``` + +| Attribute | Type | required | Description | +|-----------|---------|----------|-------------------------| +| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | +| `key` | string | yes | The `key` of a variable | + +``` +curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/1/variables/VARIABLE_1" +``` diff --git a/doc/api/build_variables.md b/doc/api/project_level_variables.md index d4f00256ed3..82ac0b09027 100644 --- a/doc/api/build_variables.md +++ b/doc/api/project_level_variables.md @@ -1,8 +1,8 @@ -# Build Variables API +# Project-level Variables API ## List project variables -Get list of a project's build variables. +Get list of a project's variables. ``` GET /projects/:id/variables @@ -31,7 +31,7 @@ curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/ ## Show variable details -Get the details of a project's specific build variable. +Get the details of a project's specific variable. ``` GET /projects/:id/variables/:key @@ -55,7 +55,7 @@ curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/ ## Create variable -Create a new build variable. +Create a new variable. ``` POST /projects/:id/variables @@ -82,7 +82,7 @@ curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitl ## Update variable -Update a project's build variable. +Update a project's variable. ``` PUT /projects/:id/variables/:key @@ -109,7 +109,7 @@ curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitla ## Remove variable -Remove a project's build variable. +Remove a project's variable. ``` DELETE /projects/:id/variables/:key diff --git a/doc/articles/index.md b/doc/articles/index.md index a4e41517d83..d7e662d737f 100644 --- a/doc/articles/index.md +++ b/doc/articles/index.md @@ -7,40 +7,106 @@ to provide the community with guidance on specific processes to achieve certain They are written by members of the GitLab Team and by [Community Writers](https://about.gitlab.com/handbook/product/technical-writing/community-writers/). +Part of the articles listed below link to the [GitLab Blog](https://about.gitlab.com/blog/), +where they were originally published. + ## Authentication -- **LDAP** - - [How to configure LDAP with GitLab CE](how_to_configure_ldap_gitlab_ce/index.md) - - [How to configure LDAP with GitLab EE](https://docs.gitlab.com/ee/articles/how_to_configure_ldap_gitlab_ee/) +Learn how to explore GitLab's supported [authentications methods](../topics/authentication/index.md): + +| Article title | Category | Publishing date | +| :------------ | :------: | --------------: | +| **LDAP** | +| [How to configure LDAP with GitLab CE](how_to_configure_ldap_gitlab_ce/index.md)| Admin guide | 2017/05/03 | +| [How to configure LDAP with GitLab EE](https://docs.gitlab.com/ee/articles/how_to_configure_ldap_gitlab_ee/) | Admin guide | 2017/05/03 | + +## Build, test, and deploy with GitLab CI/CD + +Build, test, and deploy the software you develop with [GitLab CI/CD](../ci/README.md): + +| Article title | Category | Publishing date | +| :------------ | :------: | --------------: | +| [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/) | Concepts | 2017/07/13 | +| [Dockerizing GitLab Review Apps](https://about.gitlab.com/2017/07/11/dockerizing-review-apps/) | Concepts | 2017/07/11 | +| [Continuous Integration: From Jenkins to GitLab Using Docker](https://about.gitlab.com/2017/07/27/docker-my-precious/) | Concepts | 2017/07/27 | +| [Continuous Delivery of a Spring Boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/) | Tutorial | 2016/12/14 | +| [Setting up GitLab CI for Android projects](https://about.gitlab.com/2016/11/30/setting-up-gitlab-ci-for-android-projects/) | Tutorial | 2016/11/30 | +| [Automated Debian Package Build with GitLab CI](https://about.gitlab.com/2016/10/12/automated-debian-package-build-with-gitlab-ci/) | Tutorial | 2016/10/12 | +| [Building an Elixir Release into a Docker image using GitLab CI](https://about.gitlab.com/2016/08/11/building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1/) | Tutorial | 2016/08/11 | +| [Continuous Delivery with GitLab and Convox](https://about.gitlab.com/2016/06/09/continuous-delivery-with-gitlab-and-convox/) | Technical overview | 2016/06/09 | +| [How to use GitLab CI and MacStadium to build your macOS or iOS projects](https://about.gitlab.com/2017/05/15/how-to-use-macstadium-and-gitlab-ci-to-build-your-macos-or-ios-projects/) | Technical overview | 2017/05/15 | +| [Setting up GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/) | Tutorial | 2016/03/10 | ## Git -- [How to install Git](how_to_install_git/index.md) +Learn how to use [Git with GitLab](../topics/git/index.md): + +| Article title | Category | Publishing date | +| :------------ | :------: | --------------: | +| [Why Git is Worth the Learning Curve](https://about.gitlab.com/2017/05/17/learning-curve-is-the-biggest-challenge-developers-face-with-git/) | Concepts | 2017/05/17 | +| [How to install Git](how_to_install_git/index.md) | Tutorial | 2017/05/15 | +| [Getting Started with Git LFS](https://about.gitlab.com/2017/01/30/getting-started-with-git-lfs-tutorial/) | Tutorial | 2017/01/30 | +| [Git Tips & Tricks](https://about.gitlab.com/2016/12/08/git-tips-and-tricks/) | Technical overview | 2016/12/08 | ## GitLab Pages -- **GitLab Pages from A to Z** - - [Part 1: Static sites and GitLab Pages domains](../user/project/pages/getting_started_part_one.md) - - [Part 2: Quick start guide - Setting up GitLab Pages](../user/project/pages/getting_started_part_two.md) - - [Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](../user/project/pages/getting_started_part_three.md) - - [Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](../user/project/pages/getting_started_part_four.md) -- [Building a new GitLab Docs site with Nanoc, GitLab CI, and GitLab Pages](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) -- [GitLab CI: Deployment & Environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) +Learn how to deploy a static website with [GitLab Pages](../user/project/pages/index.md#getting-started): -## Sofware development +| Article title | Category | Publishing date | +| :------------ | :------: | --------------: | +| **Series: GitLab Pages from A to Z:** | +| [- Part 1: Static sites and GitLab Pages domains](../user/project/pages/getting_started_part_one.md)| User guide | 2017/02/22 | +| [- Part 2: Quick start guide - Setting up GitLab Pages](../user/project/pages/getting_started_part_two.md)| User guide | 2017/02/22 | +| [- Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](../user/project/pages/getting_started_part_three.md)| User guide | 2017/02/22 | +| [- Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](../user/project/pages/getting_started_part_four.md)| User guide | 2017/02/22 | +| [Setting up GitLab Pages with CloudFlare Certificates](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) | Tutorial | 2017/02/07 | +| [Building a new GitLab Docs site with Nanoc, GitLab CI, and GitLab Pages](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) | Tutorial | 2016/12/07 | +| [Publish Code Coverage Report with GitLab Pages](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/) | Tutorial | 2016/11/03 | +| [GitLab CI: Deployment & Environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) | Tutorial | 2016/08/26 | +| [Posting to your GitLab Pages blog from iOS](https://about.gitlab.com/2016/08/19/posting-to-your-gitlab-pages-blog-from-ios/) | Tutorial | 2016/08/19 | +| **Series: Static Site Generator:** | +| [- Part 1: Dynamic vs Static Websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/) | Tutorial | 2016/06/03 | +| [- Part 2: Modern Static Site Generators](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/) | Tutorial | 2016/06/10 | +| [- Part 3: Build any SSG site with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/) | Tutorial | 2016/06/17 | +| [Securing your GitLab Pages with TLS and Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/) | Tutorial | 2016/04/11 | +| [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) | Tutorial | 2016/04/07 | -- [In 13 minutes from Kubernetes to a complete application development tool](https://about.gitlab.com/2016/11/14/idea-to-production/) -- [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/) -- [Fast and Natural Continuous Integration with GitLab CI](https://about.gitlab.com/2017/05/22/fast-and-natural-continuous-integration-with-gitlab-ci/) -- [GitLab Workflow, an Overview](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/) -- [Continuous Integration, Delivery, and Deployment with GitLab](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) +## Install and maintain GitLab -## Build, test, and deploy with GitLab CI/CD +Install, upgrade, integrate, migrate to GitLab: + +| Article title | Category | Publishing date | +| :------------ | :------: | --------------: | +| [Video Tutorial: Idea to Production on Google Container Engine (GKE)](https://about.gitlab.com/2017/01/23/video-tutorial-idea-to-production-on-google-container-engine-gke/) | Tutorial | 2017/01/23 | +| [How to Setup a GitLab Instance on Microsoft Azure](https://about.gitlab.com/2016/07/13/how-to-setup-a-gitlab-instance-on-microsoft-azure/) | Tutorial | 2016/07/13 | +| [Get started with OpenShift Origin 3 and GitLab](https://about.gitlab.com/2016/06/28/get-started-with-openshift-origin-3-and-gitlab/) | Tutorial | 2016/06/28 | +| [Getting started with GitLab and DigitalOcean](https://about.gitlab.com/2016/04/27/getting-started-with-gitlab-and-digitalocean/) | Tutorial | 2016/04/27 | + +## Software development + +Learn how to explore the best of GitLab's software development's capabilities: + +| Article title | Category | Publishing date | +| :------------ | :------: | --------------: | +| [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/) | Concepts | 2017/07/13 | +| [From 2/3 of the Self-Hosted Git Market, to the Next-Generation CI System, to Auto DevOps](https://about.gitlab.com/2017/06/29/whats-next-for-gitlab-ci/)| Concepts | 2017/06/29 | +| [Fast and Natural Continuous Integration with GitLab CI](https://about.gitlab.com/2017/05/22/fast-and-natural-continuous-integration-with-gitlab-ci/) | Concepts | 2017/05/22 | +| [Demo: GitLab Service Desk](https://about.gitlab.com/2017/05/09/demo-service-desk/) | Feature highlight | 2017/05/09 | +| [Demo - Mapping Work Versus Time, With Burndown Charts](https://about.gitlab.com/2017/04/25/mapping-work-to-do-versus-time-with-burndown-charts/) | Feature highlight | 2017/04/25 | +| [Demo – Cloud Native Development with GitLab](https://about.gitlab.com/2017/04/18/cloud-native-demo/) | Feature highlight | 2017/04/18 | +| [Demo - Mastering Code Review With GitLab](https://about.gitlab.com/2017/03/17/demo-mastering-code-review-with-gitlab/) | Feature highlight | 2017/03/17 | +| [In 13 minutes from Kubernetes to a complete application development tool](https://about.gitlab.com/2016/11/14/idea-to-production/) | Technical overview | 2016/11/14 | +| [GitLab Workflow, an Overview](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/) | Technical overview | 2016/10/25 | +| [Trends in Version Control Land: Microservices](https://about.gitlab.com/2016/08/16/trends-in-version-control-land-microservices/) | Concepts | 2016/08/16 | +| [Continuous Integration, Delivery, and Deployment with GitLab](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) | Concepts | 2016/08/05 | +| [Trends in Version Control Land: Innersourcing](https://about.gitlab.com/2016/07/07/trends-version-control-innersourcing/) | Concepts | 2016/07/07 | +| [Tutorial: It's all connected in GitLab](https://about.gitlab.com/2016/03/08/gitlab-tutorial-its-all-connected/) | Technical overview | 2016/03/08 | + +## Technologies -**Build, test, and deploy** the software you develop with **[GitLab CI/CD](../ci/README.md)** +| Article title | Category | Publishing date | +| :------------ | :------: | --------------: | +| [Why we are not leaving the cloud](https://about.gitlab.com/2017/03/02/why-we-are-not-leaving-the-cloud/) | Concepts | 2017/03/02 | +| [Why We Chose Vue.js](https://about.gitlab.com/2016/10/20/why-we-chose-vue/) | Concepts | 2016/10/20 | +| [Markdown Kramdown Tips & Tricks](https://about.gitlab.com/2016/07/19/markdown-kramdown-tips-and-tricks/) | Technical overview | 2016/07/19 | -- [Continuous Delivery of a Spring Boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/) -- [Automated Debian Package Build with GitLab CI](https://about.gitlab.com/2016/10/12/automated-debian-package-build-with-gitlab-ci/) -- [Building an Elixir Release into a Docker image using GitLab CI](https://about.gitlab.com/2016/08/11/building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1/) -- [Setting up GitLab CI for Android projects](https://about.gitlab.com/2016/11/30/setting-up-gitlab-ci-for-android-projects/) -- [How to use GitLab CI and MacStadium to build your macOS or iOS projects](https://about.gitlab.com/2017/05/15/how-to-use-macstadium-and-gitlab-ci-to-build-your-macos-or-ios-projects/) diff --git a/doc/development/code_review.md b/doc/development/code_review.md index e3f37616757..64a89976300 100644 --- a/doc/development/code_review.md +++ b/doc/development/code_review.md @@ -11,6 +11,8 @@ There are a few rules to get your merge request accepted: **approved by a [frontend maintainer][projects]**. 1. If your merge request includes frontend and backend changes [^1], it must be **approved by a [frontend and a backend maintainer][projects]**. + 1. If your merge request includes a new dependency or a filesystem change, it must + be **approved by a [Build team member][team]**. See [how to work with the Build team][build handbook] for more details. 1. To lower the amount of merge requests maintainers need to review, you can ask or assign any [reviewers][projects] for a first review. 1. If you need some guidance (e.g. it's your first merge request), feel free @@ -194,3 +196,4 @@ Largely based on the [thoughtbot code review guide]. [projects]: https://about.gitlab.com/handbook/engineering/projects/ [team]: https://about.gitlab.com/team/ +[build handbook]: https://about.gitlab.com/handbook/build/handbook/build#how-to-work-with-build diff --git a/doc/integration/slash_commands.md b/doc/integration/slash_commands.md index 5d880ba785c..aa52b5415cf 100644 --- a/doc/integration/slash_commands.md +++ b/doc/integration/slash_commands.md @@ -2,7 +2,11 @@ Slash commands in Mattermost and Slack allow you to control GitLab and view GitLab content right inside your chat client, without having to leave it. For Slack, this requires a [project service configuration](../user/project/integrations/slack_slash_commands.md). Simply type the command as a message in your chat client to activate it. -Commands are scoped to a project, with a trigger term that is specified during configuration. (We suggest you use the project name as the trigger term for simplicty and clarity.) Taking the trigger term as `project-name`, the commands are: +Commands are scoped to a project, with a trigger term that is specified during configuration. + +We suggest you use the project name as the trigger term for simplicity and clarity. + +Taking the trigger term as `project-name`, the commands are: | Command | Effect | @@ -12,3 +16,18 @@ Commands are scoped to a project, with a trigger term that is specified during c | `/project-name issue show <id>` | Shows the issue with id `<id>` | | `/project-name issue search <query>` | Shows up to 5 issues matching `<query>` | | `/project-name deploy <from> to <to>` | Deploy from the `<from>` environment to the `<to>` environment | + +## Issue commands + +It is possible to create new issue, display issue details and search up to 5 issues. + +## Deploy command + +In order to deploy to an environment, GitLab will try to find a deployment +manual action in the pipeline. + +If there is only one action for a given environment, it is going to be triggered. +If there is more than one action defined, GitLab will try to find an action +which name equals the environment name we want to deploy to. + +Command will return an error when no matching action has been found. diff --git a/doc/user/group/index.md b/doc/user/group/index.md index 2691cf7d671..08da721c71d 100644 --- a/doc/user/group/index.md +++ b/doc/user/group/index.md @@ -42,7 +42,7 @@ In GitLab, a namespace is a unique name to be used as a user name, a group name, For example, consider a user called John: -1. John creates his account on GitLab.com with the username `jonh`; +1. John creates his account on GitLab.com with the username `john`; his profile will be accessed under `https://gitlab.example.com/john` 1. John creates a group for his team with the groupname `john-team`; his group and its projects will be accessed under `https://gitlab.example.com/john-team` diff --git a/doc/user/index.md b/doc/user/index.md index 522cf932349..1281cc6e4f0 100644 --- a/doc/user/index.md +++ b/doc/user/index.md @@ -66,7 +66,7 @@ For more use cases please check our [Technical Articles](../articles/index.md). ## Projects -In GitLab, you can create projects for numerous reasons, such as, host +In GitLab, you can create [projects](project/index.md) for numerous reasons, such as, host your code, use it as an issue tracker, collaborate on code, and continuously build, test, and deploy your app with built-in GitLab CI/CD. Or, you can do it all at once, from one single project. diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md index 0e3747817d2..7d25970fcb1 100644 --- a/doc/user/profile/index.md +++ b/doc/user/profile/index.md @@ -23,7 +23,7 @@ On your profile page, you will see the following information: - Personal information - Activity stream: see your activity streamline and the history of your contributions - Groups: [groups](../group/index.md) you're a member of -- Contributed projects: projects you contributed to +- Contributed projects: [projects](../project/index.md) you contributed to - Personal projects: your personal projects (respecting the project's visibility level) - Snippets: your personal code [snippets](../snippets.md#personal-snippets) diff --git a/doc/user/project/index.md b/doc/user/project/index.md new file mode 100644 index 00000000000..91a19600951 --- /dev/null +++ b/doc/user/project/index.md @@ -0,0 +1,107 @@ +# Projects + +In GitLab, you can create projects for hosting +your codebase, use it as an issue tracker, collaborate on code, and continuously +build, test, and deploy your app with built-in GitLab CI/CD. + +Your projects can be [available](../../public_access/public_access.md) +publicly, internally, or privately, at your choice. GitLab does not limit +the number of private projects you create. + +## Project's features + +When you create a project in GitLab, you'll have access to a large number of +[features](https://about.gitlab.com/features/): + +**Issues and merge requests:** + +- [Issue tracker](issues/index.md): Discuss implementations with your team within issues + - [Issue Boards](issue_board.md): Organize and prioritize your workflow + - [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards) (**EES/EEP**): Allow your teams to create their own workflows (Issue Boards) for the same project +- [Repositories](repository/index.md): Host your code in a fully +integrated platform + - [Protected branches](protected_branches.md): Prevent collaborators + from messing with history or pushing code without review + - [Protected tags](protected_tags.md): Control over who has + permission to create tags, and prevent accidental update or deletion +- [Merge Requests](merge_requests/index.md): Apply your branching +strategy and get reviewed by your team + - [Merge Request Approvals](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) (**EES/EEP**): Ask for approval before + implementing a change + - [Fix merge conflicts from the UI](merge_requests/resolve_conflicts.md): + Your Git diff tool right from GitLab's UI + - [Review Apps](../../ci/review_apps/index.md): Live preview the results + of the changes proposed in a merge request in a per-branch basis +- [Labels](labels.md): Organize issues and merge requests by labels +- [Time Tracking](../../workflow/time_tracking.md): Track estimate time +and time spent on + the conclusion of an issue or merge request +- [Milestones](milestones/index.md): Work towards a target date +- [Description templates](description_templates.md): Define context-specific +templates for issue and merge request description fields for your project +- [Slash commands (quick actions)](quick_actions.md): Textual shortcuts for +common actions on issues or merge requests + +**GitLab CI/CD:** + +- [GitLab CI/CD](../../ci/README.md): GitLab's built-in [Continuous Integration, Delivery, and Deployment](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) tool + - [Container Registry](container_registry.md): Build and push Docker + images out-of-the-box + - [Auto Deploy](../../ci/autodeploy/index.md): Configure GitLab CI/CD + to automatically set up your app's deployment + - [Enable and disable GitLab CI](../../ci/enable_or_disable_ci.md) + - [Pipelines](../../ci/pipelines.md#pipelines): Configure and visualize + your GitLab CI/CD pipelines from the UI + - [Scheduled Pipelines](pipelines/schedules.md): Schedule a pipeline + to start at a chosen time + - [Pipeline Graphs](../../ci/pipelines.md#pipeline-graphs): View your + entire pipeline from the UI + - [Job artifacts](pipelines/job_artifacts.md): Define, + browse, and download job artifacts + - [Pipeline settings](pipelines/settings.md): Set up Git strategy (choose the default way your repository is fetched from GitLab in a job), + timeout (defines the maximum amount of time in minutes that a job is able run), custom path for `.gitlab-ci.yml`, test coverage parsing, pipeline's visibility, and much more +- [GitLab Pages](pages/index.md): Build, test, and deploy your static +website with GitLab Pages + +**Other features:** + +- [Cycle Analytics](cycle_analytics.md): Review your development lifecycle +- [Koding integration](koding.md) (not available on GitLab.com): Integrate +with Koding to have access to a web terminal right from the GitLab UI +- [Syntax highlighting](highlighting.md): An alternative to customize +your code blocks, overriding GitLab's default choice of language + +### Project's integrations + +[Integrate your project](integrations/index.md) with Jira, Mattermost, +Kubernetes, Slack, and a lot more. + +## New project + +Learn how to [create a new project](../../gitlab-basics/create-project.md) in GitLab. + +### Fork a project + +You can [fork a project](../../gitlab-basics/fork-project.md) in order to: + +- Collaborate on code by forking a project and creating a merge request +from your fork to the upstream project +- Fork a sample project to work on the top of that + +## Import or export a project + +- Import a project from: + - [GitHub to GitLab](../../workflow/importing/import_projects_from_github.md) + - [BitBucket to GitLab](../../workflow/importing/import_projects_from_bitbucket.md) + - [Gitea to GitLab](../../workflow/importing/import_projects_from_gitea.md) + - [FogBugz to GitLab](../../workflow/importing/import_projects_from_fogbugz.md) +- [Export a project from GitLab](settings/import_export.md#exporting-a-project-and-its-data) +- [Importing and exporting projects between GitLab instances](settings/import_export.md) + +## Leave a project + +**Leave project** will only display on the project's dashboard +when a project is part of a group (under a +[group namespace](../group/index.md#namespaces)). +If you choose to leave a project you will no longer be a project +member, therefore, unable to contribute. diff --git a/doc/user/project/koding.md b/doc/user/project/koding.md index c56a1efe3c2..455e2ee47b4 100644 --- a/doc/user/project/koding.md +++ b/doc/user/project/koding.md @@ -1,4 +1,4 @@ -# Koding & GitLab +# Koding integration > [Introduced][ce-5909] in GitLab 8.11. diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md index bb41eb41795..4b2c435a120 100644 --- a/doc/user/project/repository/index.md +++ b/doc/user/project/repository/index.md @@ -2,7 +2,7 @@ A [repository](https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository) is what you use to store your codebase in GitLab and change it with version control. -A repository is part of a project, which has a lot of other features. +A repository is part of a [project](../index.md), which has a lot of other features. ## Create a repository diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index c6cabace25b..420ac8a695a 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -30,8 +30,8 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps expect(@merge_request.source_project).to eq @forked_project expect(@merge_request.source_branch).to eq "fix" expect(@merge_request.target_branch).to eq "master" - expect(page).to have_content @forked_project.path_with_namespace - expect(page).to have_content @project.path_with_namespace + expect(page).to have_content @forked_project.full_path + expect(page).to have_content @project.full_path expect(page).to have_content @merge_request.source_branch expect(page).to have_content @merge_request.target_branch @@ -43,10 +43,10 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps expect(page).to have_content('Target branch') first('.js-source-project').click - first('.dropdown-source-project a', text: @forked_project.path_with_namespace) + first('.dropdown-source-project a', text: @forked_project.full_path) first('.js-target-project').click - first('.dropdown-target-project a', text: @project.path_with_namespace) + first('.dropdown-target-project a', text: @project.full_path) first('.js-source-branch').click wait_for_requests @@ -81,8 +81,8 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps expect(@merge_request.source_project).to eq @forked_project expect(@merge_request.source_branch).to eq "fix" expect(@merge_request.target_branch).to eq "master" - expect(page).to have_content @forked_project.path_with_namespace - expect(page).to have_content @project.path_with_namespace + expect(page).to have_content @forked_project.full_path + expect(page).to have_content @project.full_path expect(page).to have_content @merge_request.source_branch expect(page).to have_content @merge_request.target_branch end diff --git a/features/steps/project/redirects.rb b/features/steps/project/redirects.rb index b2ceb8dd9a8..cbe6f93f87e 100644 --- a/features/steps/project/redirects.rb +++ b/features/steps/project/redirects.rb @@ -47,7 +47,7 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps step 'I should be redirected to "Community" page' do project = Project.find_by(name: 'Community') - expect(current_path).to eq "/#{project.path_with_namespace}" + expect(current_path).to eq "/#{project.full_path}" expect(status_code).to eq 200 end @@ -61,7 +61,7 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps step 'I should be redirected to "Enterprise" page' do project = Project.find_by(name: 'Enterprise') - expect(current_path).to eq "/#{project.path_with_namespace}" + expect(current_path).to eq "/#{project.full_path}" expect(status_code).to eq 200 end end diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb index 6a478c50e5e..2b8da2a6f19 100644 --- a/features/steps/project/wiki.rb +++ b/features/steps/project/wiki.rb @@ -142,7 +142,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps end step 'I should see non-escaped link in the pages list' do - expect(page).to have_xpath("//a[@href='/#{project.path_with_namespace}/wikis/one/two/three-test']") + expect(page).to have_xpath("//a[@href='/#{project.full_path}/wikis/one/two/three-test']") end step 'I edit the Wiki page with a path' do diff --git a/lib/api/api.rb b/lib/api/api.rb index 045a0db1842..ae10da2d85f 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -139,6 +139,7 @@ module API mount ::API::Triggers mount ::API::Users mount ::API::Variables + mount ::API::GroupVariables mount ::API::Version route :any, '*path' do diff --git a/lib/api/group_variables.rb b/lib/api/group_variables.rb new file mode 100644 index 00000000000..f64da4ab77b --- /dev/null +++ b/lib/api/group_variables.rb @@ -0,0 +1,96 @@ +module API + class GroupVariables < Grape::API + include PaginationParams + + before { authenticate! } + before { authorize! :admin_build, user_group } + + params do + requires :id, type: String, desc: 'The ID of a group' + end + + resource :groups, requirements: { id: %r{[^/]+} } do + desc 'Get group-level variables' do + success Entities::Variable + end + params do + use :pagination + end + get ':id/variables' do + variables = user_group.variables + present paginate(variables), with: Entities::Variable + end + + desc 'Get a specific variable from a group' do + success Entities::Variable + end + params do + requires :key, type: String, desc: 'The key of the variable' + end + get ':id/variables/:key' do + key = params[:key] + variable = user_group.variables.find_by(key: key) + + return not_found!('GroupVariable') unless variable + + present variable, with: Entities::Variable + end + + desc 'Create a new variable in a group' do + success Entities::Variable + end + params do + requires :key, type: String, desc: 'The key of the variable' + requires :value, type: String, desc: 'The value of the variable' + optional :protected, type: String, desc: 'Whether the variable is protected' + end + post ':id/variables' do + variable_params = declared_params(include_missing: false) + + variable = user_group.variables.create(variable_params) + + if variable.valid? + present variable, with: Entities::Variable + else + render_validation_error!(variable) + end + end + + desc 'Update an existing variable from a group' do + success Entities::Variable + end + params do + optional :key, type: String, desc: 'The key of the variable' + optional :value, type: String, desc: 'The value of the variable' + optional :protected, type: String, desc: 'Whether the variable is protected' + end + put ':id/variables/:key' do + variable = user_group.variables.find_by(key: params[:key]) + + return not_found!('GroupVariable') unless variable + + variable_params = declared_params(include_missing: false).except(:key) + + if variable.update(variable_params) + present variable, with: Entities::Variable + else + render_validation_error!(variable) + end + end + + desc 'Delete an existing variable from a group' do + success Entities::Variable + end + params do + requires :key, type: String, desc: 'The key of the variable' + end + delete ':id/variables/:key' do + variable = user_group.variables.find_by(key: params[:key]) + not_found!('GroupVariable') unless variable + + status 204 + variable.destroy + end + end + end +end diff --git a/lib/api/runner.rb b/lib/api/runner.rb index 405d25ca3c1..88fc62d33df 100644 --- a/lib/api/runner.rb +++ b/lib/api/runner.rb @@ -90,7 +90,7 @@ module API if result.valid? if result.build Gitlab::Metrics.add_event(:build_found, - project: result.build.project.path_with_namespace) + project: result.build.project.full_path) present result.build, with: Entities::JobRequest::Response else Gitlab::Metrics.add_event(:build_not_found) @@ -119,7 +119,7 @@ module API job.trace.set(params[:trace]) if params[:trace] Gitlab::Metrics.add_event(:update_build, - project: job.project.path_with_namespace) + project: job.project.full_path) case params[:state].to_s when 'success' diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index a1685c77916..88821ae56e0 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -7,7 +7,7 @@ module Backup prepare Project.find_each(batch_size: 1000) do |project| - progress.print " * #{project.path_with_namespace} ... " + progress.print " * #{project.full_path} ... " path_to_project_repo = path_to_repo(project) path_to_project_bundle = path_to_bundle(project) @@ -42,7 +42,7 @@ module Backup path_to_wiki_bundle = path_to_bundle(wiki) if File.exist?(path_to_wiki_repo) - progress.print " * #{wiki.path_with_namespace} ... " + progress.print " * #{wiki.full_path} ... " if empty_repo?(wiki) progress.puts " [SKIPPED]".color(:cyan) else @@ -71,11 +71,11 @@ module Backup end Project.find_each(batch_size: 1000) do |project| - progress.print " * #{project.path_with_namespace} ... " + progress.print " * #{project.full_path} ... " path_to_project_repo = path_to_repo(project) path_to_project_bundle = path_to_bundle(project) - project.ensure_dir_exist + project.ensure_storage_path_exist cmd = if File.exist?(path_to_project_bundle) %W(#{Gitlab.config.git.bin_path} clone --bare #{path_to_project_bundle} #{path_to_project_repo}) @@ -104,7 +104,7 @@ module Backup path_to_wiki_bundle = path_to_bundle(wiki) if File.exist?(path_to_wiki_bundle) - progress.print " * #{wiki.path_with_namespace} ... " + progress.print " * #{wiki.full_path} ... " # If a wiki bundle exists, first remove the empty repo # that was initialized with ProjectWiki.new() and then @@ -142,11 +142,11 @@ module Backup end def path_to_bundle(project) - File.join(backup_repos_path, project.path_with_namespace + '.bundle') + File.join(backup_repos_path, project.disk_path + '.bundle') end def path_to_tars(project, dir = nil) - path = File.join(backup_repos_path, project.path_with_namespace) + path = File.join(backup_repos_path, project.disk_path) if dir File.join(path, "#{dir}.tar") @@ -185,13 +185,14 @@ module Backup def progress_warn(project, cmd, output) progress.puts "[WARNING] Executing #{cmd}".color(:orange) - progress.puts "Ignoring error on #{project.path_with_namespace} - #{output}".color(:orange) + progress.puts "Ignoring error on #{project.full_path} - #{output}".color(:orange) end def empty_repo?(project_or_wiki) + project_or_wiki.repository.expire_exists_cache # protect backups from stale cache project_or_wiki.repository.empty_repo? rescue => e - progress.puts "Ignoring repository error and continuing backing up project: #{project_or_wiki.path_with_namespace} - #{e.message}".color(:orange) + progress.puts "Ignoring repository error and continuing backing up project: #{project_or_wiki.full_path} - #{e.message}".color(:orange) false end diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb index 7a262dd025c..685b43605ae 100644 --- a/lib/banzai/filter/abstract_reference_filter.rb +++ b/lib/banzai/filter/abstract_reference_filter.rb @@ -259,7 +259,7 @@ module Banzai found = [] projects.each do |project| - ref = project.path_with_namespace + ref = project.full_path get_or_set_cache(cache, ref) { project } found << ref end @@ -277,7 +277,7 @@ module Banzai end def current_project_path - @current_project_path ||= project.path_with_namespace + @current_project_path ||= project.full_path end def current_project_namespace_path diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb index c2fed57a0d8..758f15c8a67 100644 --- a/lib/banzai/filter/relative_link_filter.rb +++ b/lib/banzai/filter/relative_link_filter.rb @@ -51,7 +51,7 @@ module Banzai uri.path = [ relative_url_root, - context[:project].path_with_namespace, + context[:project].full_path, uri_type(file_path), Addressable::URI.escape(ref), Addressable::URI.escape(file_path) diff --git a/lib/banzai/filter/upload_link_filter.rb b/lib/banzai/filter/upload_link_filter.rb index 45bb66dc99f..09844931be5 100644 --- a/lib/banzai/filter/upload_link_filter.rb +++ b/lib/banzai/filter/upload_link_filter.rb @@ -28,7 +28,7 @@ module Banzai end def build_url(uri) - File.join(Gitlab.config.gitlab.url, project.path_with_namespace, uri) + File.join(Gitlab.config.gitlab.url, project.full_path, uri) end def project diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb index e2e91ce99cd..79058c02ce5 100644 --- a/lib/ci/api/builds.rb +++ b/lib/ci/api/builds.rb @@ -29,7 +29,7 @@ module Ci if result.valid? if result.build Gitlab::Metrics.add_event(:build_found, - project: result.build.project.path_with_namespace) + project: result.build.project.full_path) present result.build, with: Entities::BuildDetails else @@ -64,7 +64,7 @@ module Ci build.trace.set(params[:trace]) if params[:trace] Gitlab::Metrics.add_event(:update_build, - project: build.project.path_with_namespace) + project: build.project.full_path) case params[:state].to_s when 'success' diff --git a/lib/github/import.rb b/lib/github/import.rb index ff5d7db2705..cea4be5460b 100644 --- a/lib/github/import.rb +++ b/lib/github/import.rb @@ -93,7 +93,7 @@ module Github def fetch_wiki_repository wiki_url = "https://#{options.fetch(:token)}@github.com/#{repo}.wiki.git" - wiki_path = "#{project.path_with_namespace}.wiki" + wiki_path = "#{project.full_path}.wiki" unless project.wiki.repository_exists? gitlab_shell.import_repository(project.repository_storage_path, wiki_path, wiki_url) diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 5a6d9ae99a0..28bbf3b384e 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -61,7 +61,7 @@ module Gitlab def import_wiki return if project.wiki.repository_exists? - path_with_namespace = "#{project.path_with_namespace}.wiki" + path_with_namespace = "#{project.full_path}.wiki" import_url = project.import_url.sub(/\.git\z/, ".git/wiki") gitlab_shell.import_repository(project.repository_storage_path, path_with_namespace, import_url) rescue StandardError => e diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb index d2863a4da71..6d7de52cb80 100644 --- a/lib/gitlab/diff/file.rb +++ b/lib/gitlab/diff/file.rb @@ -79,13 +79,6 @@ module Gitlab @new_content_sha = refs&.head_sha end - def new_content_commit - return @new_content_commit if defined?(@new_content_commit) - - sha = new_content_commit - @new_content_commit = repository.commit(sha) if sha - end - def old_content_sha return if new_file? return @old_content_sha if defined?(@old_content_sha) @@ -94,13 +87,6 @@ module Gitlab @old_content_sha = refs&.base_sha end - def old_content_commit - return @old_content_commit if defined?(@old_content_commit) - - sha = old_content_sha - @old_content_commit = repository.commit(sha) if sha - end - def new_blob return @new_blob if defined?(@new_blob) @@ -123,10 +109,6 @@ module Gitlab new_content_sha || old_content_sha end - def content_commit - new_content_commit || old_content_commit - end - def blob new_blob || old_blob end diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb index 85e6db0a689..72d7d4f84d1 100644 --- a/lib/gitlab/ee_compat_check.rb +++ b/lib/gitlab/ee_compat_check.rb @@ -181,8 +181,6 @@ module Gitlab end def find_merge_base_with_master(branch:) - return if merge_base_found? - # Start with (Math.exp(3).to_i = 20) until (Math.exp(6).to_i = 403) # In total we go (20 + 54 + 148 + 403 = 625) commits deeper depth = 20 diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb index dd1d9dcd555..cd9d3a6483f 100644 --- a/lib/gitlab/email/message/repository_push.rb +++ b/lib/gitlab/email/message/repository_push.rb @@ -117,7 +117,7 @@ module Gitlab def subject subject_text = '[Git]' - subject_text << "[#{project.path_with_namespace}]" + subject_text << "[#{project.full_path}]" subject_text << "[#{ref_name}]" if @action == :push subject_text << ' ' diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index a3bc79109f8..88529ba2c47 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -636,6 +636,33 @@ module Gitlab @attributes.attributes(path) end + def languages(ref = nil) + Gitlab::GitalyClient.migrate(:commit_languages) do |is_enabled| + if is_enabled + gitaly_commit_client.languages(ref) + else + ref ||= rugged.head.target_id + languages = Linguist::Repository.new(rugged, ref).languages + total = languages.map(&:last).sum + + languages = languages.map do |language| + name, share = language + color = Linguist::Language[name].color || "##{Digest::SHA256.hexdigest(name)[0...6]}" + { + value: (share.to_f * 100 / total).round(2), + label: name, + color: color, + highlight: color + } + end + + languages.sort do |x, y| + y[:value] <=> x[:value] + end + end + end + end + def gitaly_repository Gitlab::GitalyClient::Util.repository(@storage, @relative_path) end diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb index c6e52b530b3..a834781b1f1 100644 --- a/lib/gitlab/gitaly_client/commit_service.rb +++ b/lib/gitlab/gitaly_client/commit_service.rb @@ -118,6 +118,13 @@ module Gitlab consume_commits_response(response) end + def languages(ref = nil) + request = Gitaly::CommitLanguagesRequest.new(repository: @gitaly_repo, revision: ref || '') + response = GitalyClient.call(@repository.storage, :commit_service, :commit_languages, request) + + response.languages.map { |l| { value: l.share.round(2), label: l.name, color: l.color, highlight: l.color } } + end + private def commit_diff_request_params(commit, options = {}) diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb index f5d84ea8762..13e75b256a7 100644 --- a/lib/gitlab/gitaly_client/repository_service.rb +++ b/lib/gitlab/gitaly_client/repository_service.rb @@ -4,12 +4,28 @@ module Gitlab def initialize(repository) @repository = repository @gitaly_repo = repository.gitaly_repository + @storage = repository.storage end def exists? request = Gitaly::RepositoryExistsRequest.new(repository: @gitaly_repo) - GitalyClient.call(@repository.storage, :repository_service, :exists, request).exists + GitalyClient.call(@storage, :repository_service, :exists, request).exists + end + + def garbage_collect(create_bitmap) + request = Gitaly::GarbageCollectRequest.new(repository: @gitaly_repo, create_bitmap: create_bitmap) + GitalyClient.call(@storage, :repository_service, :garbage_collect, request) + end + + def repack_full(create_bitmap) + request = Gitaly::RepackFullRequest.new(repository: @gitaly_repo, create_bitmap: create_bitmap) + GitalyClient.call(@storage, :repository_service, :repack_full, request) + end + + def repack_incremental + request = Gitaly::RepackIncrementalRequest.new(repository: @gitaly_repo) + GitalyClient.call(@storage, :repository_service, :repack_incremental, request) end end end diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index a8c0b47e786..266b1a6fece 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -254,7 +254,7 @@ module Gitlab def import_wiki unless project.wiki.repository_exists? wiki = WikiFormatter.new(project) - gitlab_shell.import_repository(project.repository_storage_path, wiki.path_with_namespace, wiki.import_url) + gitlab_shell.import_repository(project.repository_storage_path, wiki.disk_path, wiki.import_url) end rescue Gitlab::Shell::Error => e # GitHub error message when the wiki repo has not been created, diff --git a/lib/gitlab/github_import/wiki_formatter.rb b/lib/gitlab/github_import/wiki_formatter.rb index 6c592ff469c..0396122eeb9 100644 --- a/lib/gitlab/github_import/wiki_formatter.rb +++ b/lib/gitlab/github_import/wiki_formatter.rb @@ -7,8 +7,8 @@ module Gitlab @project = project end - def path_with_namespace - "#{project.path_with_namespace}.wiki" + def disk_path + "#{project.disk_path}.wiki" end def import_url diff --git a/lib/gitlab/import_export/repo_restorer.rb b/lib/gitlab/import_export/repo_restorer.rb index c824d3ea9fc..32ca2809b2f 100644 --- a/lib/gitlab/import_export/repo_restorer.rb +++ b/lib/gitlab/import_export/repo_restorer.rb @@ -13,7 +13,7 @@ module Gitlab def restore return true unless File.exist?(@path_to_bundle) - gitlab_shell.import_repository(@project.repository_storage_path, @project.path_with_namespace, @path_to_bundle) + gitlab_shell.import_repository(@project.repository_storage_path, @project.disk_path, @path_to_bundle) rescue => e @shared.error(e) false diff --git a/lib/gitlab/import_export/uploads_saver.rb b/lib/gitlab/import_export/uploads_saver.rb index 62a2553675c..f9ae5079d7c 100644 --- a/lib/gitlab/import_export/uploads_saver.rb +++ b/lib/gitlab/import_export/uploads_saver.rb @@ -24,6 +24,7 @@ module Gitlab end def uploads_path + # TODO: decide what to do with uploads. We will use UUIDs here too? File.join(Rails.root.join('public/uploads'), @project.path_with_namespace) end end diff --git a/lib/gitlab/ldap/authentication.rb b/lib/gitlab/ldap/authentication.rb index 4745311402c..ed1de73f8c6 100644 --- a/lib/gitlab/ldap/authentication.rb +++ b/lib/gitlab/ldap/authentication.rb @@ -42,7 +42,7 @@ module Gitlab end def adapter - OmniAuth::LDAP::Adaptor.new(config.options.symbolize_keys) + OmniAuth::LDAP::Adaptor.new(config.omniauth_options) end def config diff --git a/lib/gitlab/quick_actions/dsl.rb b/lib/gitlab/quick_actions/dsl.rb index a4a97236ffc..536765305e1 100644 --- a/lib/gitlab/quick_actions/dsl.rb +++ b/lib/gitlab/quick_actions/dsl.rb @@ -105,9 +105,32 @@ module Gitlab # # Awesome code block # end def command(*command_names, &block) + define_command(CommandDefinition, *command_names, &block) + end + + # Registers a new substitution which is recognizable from body of email or + # comment. + # It accepts aliases and takes a block with the formatted content. + # + # Example: + # + # command :my_substitution, :alias_for_my_substitution do |text| + # "#{text} MY AWESOME SUBSTITUTION" + # end + def substitution(*substitution_names, &block) + define_command(SubstitutionDefinition, *substitution_names, &block) + end + + def definition_by_name(name) + command_definitions_by_name[name.to_sym] + end + + private + + def define_command(klass, *command_names, &block) name, *aliases = command_names - definition = CommandDefinition.new( + definition = klass.new( name, aliases: aliases, description: @description, @@ -130,10 +153,6 @@ module Gitlab @condition_block = nil @parse_params_block = nil end - - def definition_by_name(name) - command_definitions_by_name[name.to_sym] - end end end end diff --git a/lib/gitlab/quick_actions/extractor.rb b/lib/gitlab/quick_actions/extractor.rb index 09576be7156..3ebfa3bd4b8 100644 --- a/lib/gitlab/quick_actions/extractor.rb +++ b/lib/gitlab/quick_actions/extractor.rb @@ -46,6 +46,8 @@ module Gitlab end end + content, commands = perform_substitutions(content, commands) + [content.strip, commands] end @@ -110,6 +112,26 @@ module Gitlab }mx end + def perform_substitutions(content, commands) + return unless content + + substitution_definitions = self.command_definitions.select do |definition| + definition.is_a?(Gitlab::QuickActions::SubstitutionDefinition) + end + + substitution_definitions.each do |substitution| + match_data = substitution.match(content) + if match_data + command = [substitution.name.to_s] + command << match_data[1] unless match_data[1].empty? + commands << command + end + content = substitution.perform_substitution(self, content) + end + + [content, commands] + end + def command_names(opts) command_definitions.flat_map do |command| next if command.noop? diff --git a/lib/gitlab/quick_actions/substitution_definition.rb b/lib/gitlab/quick_actions/substitution_definition.rb new file mode 100644 index 00000000000..032c49ed159 --- /dev/null +++ b/lib/gitlab/quick_actions/substitution_definition.rb @@ -0,0 +1,24 @@ +module Gitlab + module QuickActions + class SubstitutionDefinition < CommandDefinition + # noop?=>true means these won't get extracted or removed by Gitlab::QuickActions::Extractor#extract_commands + # QuickActions::InterpretService#perform_substitutions handles them separately + def noop? + true + end + + def match(content) + content.match %r{^/#{all_names.join('|')} ?(.*)$} + end + + def perform_substitution(context, content) + return unless content + + all_names.each do |a_name| + content.gsub!(%r{/#{a_name} ?(.*)$}, execute_block(action_block, context, '\1')) + end + content + end + end + end +end diff --git a/lib/gitlab/slash_commands/deploy.rb b/lib/gitlab/slash_commands/deploy.rb index e71eb15d604..93e00ab75a1 100644 --- a/lib/gitlab/slash_commands/deploy.rb +++ b/lib/gitlab/slash_commands/deploy.rb @@ -21,29 +21,34 @@ module Gitlab from = match[:from] to = match[:to] - actions = find_actions(from, to) + action = find_action(from, to) - if actions.none? - Gitlab::SlashCommands::Presenters::Deploy.new(nil).no_actions - elsif actions.one? - action = play!(from, to, actions.first) - Gitlab::SlashCommands::Presenters::Deploy.new(action).present(from, to) + if action.nil? + Gitlab::SlashCommands::Presenters::Deploy + .new(action).action_not_found else - Gitlab::SlashCommands::Presenters::Deploy.new(actions).too_many_actions + deployment = action.play(current_user) + + Gitlab::SlashCommands::Presenters::Deploy + .new(deployment).present(from, to) end end private - def play!(from, to, action) - action.play(current_user) - end - - def find_actions(from, to) + def find_action(from, to) environment = project.environments.find_by(name: from) - return [] unless environment + return unless environment - environment.actions_for(to).select(&:starts_environment?) + actions = environment.actions_for(to).select do |action| + action.starts_environment? + end + + if actions.many? + actions.find { |action| action.name == to.to_s } + else + actions.first + end end end end diff --git a/lib/gitlab/slash_commands/presenters/deploy.rb b/lib/gitlab/slash_commands/presenters/deploy.rb index b8dc77bd37b..ebae0f57f9b 100644 --- a/lib/gitlab/slash_commands/presenters/deploy.rb +++ b/lib/gitlab/slash_commands/presenters/deploy.rb @@ -3,17 +3,14 @@ module Gitlab module Presenters class Deploy < Presenters::Base def present(from, to) - message = "Deployment started from #{from} to #{to}. [Follow its progress](#{resource_url})." + message = "Deployment started from #{from} to #{to}. " \ + "[Follow its progress](#{resource_url})." in_channel_response(text: message) end - def no_actions - ephemeral_response(text: "No action found to be executed") - end - - def too_many_actions - ephemeral_response(text: "Too many actions defined") + def action_not_found + ephemeral_response(text: "Couldn't find a deployment manual action.") end end end diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab index c5f93336346..2f2de083dc0 100755 --- a/lib/support/init.d/gitlab +++ b/lib/support/init.d/gitlab @@ -291,7 +291,7 @@ start_gitlab() { fi if [ "$gitlab_workhorse_status" = "0" ]; then - echo "The GitLab Workhorse is already running with pid $spid, not restarting" + echo "The GitLab Workhorse is already running with pid $hpid, not restarting" else # No need to remove a socket, gitlab-workhorse does this itself. # Because gitlab-workhorse has multiple executables we need to fix @@ -313,7 +313,7 @@ start_gitlab() { if [ "$gitlab_pages_enabled" = true ]; then if [ "$gitlab_pages_status" = "0" ]; then - echo "The GitLab Pages is already running with pid $spid, not restarting" + echo "The GitLab Pages is already running with pid $gppid, not restarting" else $app_root/bin/daemon_with_pidfile $gitlab_pages_pid_path \ $gitlab_pages_dir/gitlab-pages $gitlab_pages_options \ @@ -421,7 +421,7 @@ print_status() { fi if [ "$gitlab_pages_enabled" = true ]; then if [ "$gitlab_pages_status" = "0" ]; then - echo "The GitLab Pages with pid $mpid is running." + echo "The GitLab Pages with pid $gppid is running." else printf "The GitLab Pages is \033[31mnot running\033[0m.\n" fi diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index 858f1cd7b34..dbb3b827b9a 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -527,7 +527,7 @@ namespace :gitlab do repo_dirs = user.authorized_projects.map do |p| File.join( p.repository_storage_path, - "#{p.path_with_namespace}.git" + "#{p.disk_path}.git" ) end diff --git a/lib/tasks/gitlab/gitaly.rake b/lib/tasks/gitlab/gitaly.rake index 9df07ea8d83..680e76af471 100644 --- a/lib/tasks/gitlab/gitaly.rake +++ b/lib/tasks/gitlab/gitaly.rake @@ -19,7 +19,10 @@ namespace :gitlab do Dir.chdir(args.dir) do create_gitaly_configuration - Bundler.with_original_env { run_command!([command]) } + # In CI we run scripts/gitaly-test-build instead of this command + unless ENV['CI'].present? + Bundler.with_original_env { run_command!(%w[/usr/bin/env -u BUNDLE_GEMFILE] + [command]) } + end end end @@ -30,7 +33,9 @@ namespace :gitlab do puts "# Gitaly storage configuration generated from #{Gitlab.config.source} on #{Time.current.to_s(:long)}" puts "# This is in TOML format suitable for use in Gitaly's config.toml file." - puts gitaly_configuration_toml + # Exclude gitaly-ruby configuration because that depends on the gitaly + # installation directory. + puts gitaly_configuration_toml(gitaly_ruby: false) end private @@ -41,7 +46,7 @@ namespace :gitlab do # only generate a configuration for the most common and simplest case: when # we have exactly one Gitaly process and we are sure it is running locally # because it uses a Unix socket. - def gitaly_configuration_toml + def gitaly_configuration_toml(gitaly_ruby: true) storages = [] address = nil @@ -60,6 +65,7 @@ namespace :gitlab do end config = { socket_path: address.sub(%r{\Aunix:}, ''), storage: storages } config[:auth] = { token: 'secret' } if Rails.env.test? + config[:'gitaly-ruby'] = { dir: File.join(Dir.pwd, 'ruby') } if gitaly_ruby TOML.dump(config) end diff --git a/lib/tasks/gitlab/list_repos.rake b/lib/tasks/gitlab/list_repos.rake index ffcc76e5498..b732db9db6e 100644 --- a/lib/tasks/gitlab/list_repos.rake +++ b/lib/tasks/gitlab/list_repos.rake @@ -9,7 +9,7 @@ namespace :gitlab do scope = scope.where('id IN (?) OR namespace_id in (?)', project_ids, namespace_ids) end scope.find_each do |project| - base = File.join(project.repository_storage_path, project.path_with_namespace) + base = File.join(project.repository_storage_path, project.disk_path) puts base + '.git' puts base + '.wiki.git' end diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake index ee2cdcdea1b..42825f29e32 100644 --- a/lib/tasks/gitlab/shell.rake +++ b/lib/tasks/gitlab/shell.rake @@ -80,7 +80,7 @@ namespace :gitlab do print '-' else if Gitlab::Shell.new.add_repository(project.repository_storage_path, - project.path_with_namespace) + project.disk_path) print '.' else print 'F' diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po index 78cf6d2dacc..ee00b816b84 100644 --- a/locale/pt_BR/gitlab.po +++ b/locale/pt_BR/gitlab.po @@ -413,14 +413,14 @@ msgstr[0] "Fork" msgstr[1] "Forks" msgid "ForkedFromProjectPath|Forked from" -msgstr "Forked de" +msgstr "Fork criado a partir de" msgid "From issue creation until deploy to production" msgstr "Da abertura de tarefas até a implantação para a produção" msgid "From merge request merge until deploy to production" msgstr "" -"Da aceitação da solicitação de incorporação até a implantação em produção" +"Do merge request até a implantação em produção" msgid "Go to your fork" msgstr "Ir para seu fork" @@ -576,7 +576,7 @@ msgid "NotificationEvent|Successful pipeline" msgstr "Pipeline bem sucedido" msgid "NotificationLevel|Custom" -msgstr "Personalizar" +msgstr "Personalizado" msgid "NotificationLevel|Disabled" msgstr "Desabilitado" @@ -881,9 +881,9 @@ msgid "" "the issue to a milestone, or add the issue to a list on your Issue Board. " "Begin creating issues to see data for this stage." msgstr "" -"A etapa de relatos mostra o tempo que leva desde a criação de uma issue até " -"sua atribuição a um marco, ou sua adição a uma lista no seu Issue Board. " -"Comece a criar issues para ver dados para esta etapa." +"A etapa de planejamento mostra o tempo que se leva desde a criação de uma " +"issue até sua atribuição à um milestone, ou sua adição a uma lista no seu " +"Issue Board. Comece a criar issues para ver dados para esta etapa." msgid "The phase of the development lifecycle." msgstr "A fase do ciclo de vida do desenvolvimento." @@ -974,7 +974,7 @@ msgid "Time before an issue gets scheduled" msgstr "Tempo até que uma issue seja agendada" msgid "Time before an issue starts implementation" -msgstr "Tempo até que uma issue comece a ser implementado" +msgstr "Tempo até que uma issue comece a ser implementada" msgid "Time between merge request creation and merge/close" msgstr "" diff --git a/rubocop/cop/migration/add_column_with_default_to_large_table.rb b/rubocop/cop/migration/add_column_with_default_to_large_table.rb index 87788b0d9c2..fb363f95b56 100644 --- a/rubocop/cop/migration/add_column_with_default_to_large_table.rb +++ b/rubocop/cop/migration/add_column_with_default_to_large_table.rb @@ -20,6 +20,7 @@ module RuboCop 'necessary'.freeze LARGE_TABLES = %i[ + ci_pipelines ci_builds events issues diff --git a/scripts/gitaly-test-build b/scripts/gitaly-test-build new file mode 100755 index 00000000000..44d314009e2 --- /dev/null +++ b/scripts/gitaly-test-build @@ -0,0 +1,10 @@ +#!/usr/bin/env ruby + +# This script assumes tmp/tests/gitaly already contains the correct +# Gitaly version. We just have to compile it and run its 'bundle +# install'. We have this separate script for that because weird things +# were happening in CI when we have a 'bundle exec' process that later +# called 'bundle install' using a different Gemfile, as happens with +# gitlab-ce and gitaly. + +abort 'gitaly build failed' unless system('make', chdir: 'tmp/tests/gitaly') diff --git a/scripts/gitaly-test-spawn b/scripts/gitaly-test-spawn new file mode 100755 index 00000000000..dd603eec7f6 --- /dev/null +++ b/scripts/gitaly-test-spawn @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby + +gitaly_dir = 'tmp/tests/gitaly' +args = %W[#{gitaly_dir}/gitaly #{gitaly_dir}/config.toml] + +# Print the PID of the spawned process +puts spawn(*args, [:out, :err] => 'log/gitaly-test.log') diff --git a/scripts/lint-conflicts.sh b/scripts/lint-conflicts.sh new file mode 100755 index 00000000000..f3877600c8c --- /dev/null +++ b/scripts/lint-conflicts.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +output=`git ls-files -z | grep -zvE '\.(rb|js|haml)$' | xargs -0n1 grep -HEn '^<<<<<<< '` +echo $output +test -z "$output" diff --git a/scripts/static-analysis b/scripts/static-analysis index 6d35684b97f..e4f80e8fc6f 100755 --- a/scripts/static-analysis +++ b/scripts/static-analysis @@ -11,7 +11,8 @@ tasks = [ %w[bundle exec rake brakeman], %w[bundle exec license_finder], %w[yarn run eslint], - %w[bundle exec rubocop --require rubocop-rspec] + %w[bundle exec rubocop --require rubocop-rspec], + %w[scripts/lint-conflicts.sh] ] failed_tasks = tasks.reduce({}) do |failures, task| diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb index 02bbc48dc59..19ee5e3bb7e 100644 --- a/spec/controllers/projects/blob_controller_spec.rb +++ b/spec/controllers/projects/blob_controller_spec.rb @@ -48,7 +48,7 @@ describe Projects::BlobController do let(:id) { 'markdown/doc' } it 'redirects' do expect(subject) - .to redirect_to("/#{project.path_with_namespace}/tree/markdown/doc") + .to redirect_to("/#{project.full_path}/tree/markdown/doc") end end end diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb index 9cd4e9dbf84..24defb4d657 100644 --- a/spec/controllers/projects/branches_controller_spec.rb +++ b/spec/controllers/projects/branches_controller_spec.rb @@ -33,7 +33,7 @@ describe Projects::BranchesController do let(:ref) { "master" } it 'redirects' do expect(subject) - .to redirect_to("/#{project.path_with_namespace}/tree/merge_branch") + .to redirect_to("/#{project.full_path}/tree/merge_branch") end end @@ -42,7 +42,7 @@ describe Projects::BranchesController do let(:ref) { "master" } it 'redirects' do expect(subject) - .to redirect_to("/#{project.path_with_namespace}/tree/alert('merge');") + .to redirect_to("/#{project.full_path}/tree/alert('merge');") end end @@ -82,7 +82,7 @@ describe Projects::BranchesController do issue_iid: issue.iid expect(subject) - .to redirect_to("/#{project.path_with_namespace}/tree/1-feature-branch") + .to redirect_to("/#{project.full_path}/tree/1-feature-branch") end it 'posts a system note' do diff --git a/spec/controllers/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb index e0de62e4454..5af03ae118c 100644 --- a/spec/controllers/projects/graphs_controller_spec.rb +++ b/spec/controllers/projects/graphs_controller_spec.rb @@ -24,37 +24,4 @@ describe Projects::GraphsController do expect(response).to redirect_to action: :charts end end - - describe 'GET charts' do - let(:linguist_repository) do - double(languages: { - 'Ruby' => 1000, - 'CoffeeScript' => 350, - 'NSIS' => 15 - }) - end - - let(:expected_values) do - nsis_color = "##{Digest::SHA256.hexdigest('NSIS')[0...6]}" - [ - # colors from Linguist: - { label: "Ruby", color: "#701516", highlight: "#701516" }, - { label: "CoffeeScript", color: "#244776", highlight: "#244776" }, - # colors from SHA256 fallback: - { label: "NSIS", color: nsis_color, highlight: nsis_color } - ] - end - - before do - allow(Linguist::Repository).to receive(:new).and_return(linguist_repository) - end - - it 'sets the correct colour according to language' do - get(:charts, namespace_id: project.namespace, project_id: project, id: 'master') - - expected_values.each do |val| - expect(assigns(:languages)).to include(a_hash_including(val)) - end - end - end end diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 2fce4b7a85f..0bfe83f1d98 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -609,7 +609,7 @@ describe Projects::MergeRequestsController do end it 'links to the environment on that project' do - expect(json_response.first['url']).to match /#{forked.path_with_namespace}/ + expect(json_response.first['url']).to match /#{forked.full_path}/ end end end diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb index 16cd2e076e5..775f3998f5d 100644 --- a/spec/controllers/projects/tree_controller_spec.rb +++ b/spec/controllers/projects/tree_controller_spec.rb @@ -81,7 +81,7 @@ describe Projects::TreeController do context 'redirect to blob' do let(:id) { 'master/README.md' } it 'redirects' do - redirect_url = "/#{project.path_with_namespace}/blob/master/README.md" + redirect_url = "/#{project.full_path}/blob/master/README.md" expect(subject) .to redirect_to(redirect_url) end @@ -107,7 +107,7 @@ describe Projects::TreeController do it 'redirects to the new directory' do expect(subject) - .to redirect_to("/#{project.path_with_namespace}/tree/#{branch_name}/#{path}") + .to redirect_to("/#{project.full_path}/tree/#{branch_name}/#{path}") expect(flash[:notice]).to eq('The directory has been successfully created.') end end @@ -118,7 +118,7 @@ describe Projects::TreeController do it 'does not allow overwriting of existing files' do expect(subject) - .to redirect_to("/#{project.path_with_namespace}/tree/master") + .to redirect_to("/#{project.full_path}/tree/master") expect(flash[:alert]).to eq('A file with this name already exists') end end diff --git a/spec/factories/milestones.rb b/spec/factories/milestones.rb index 113665ff11b..b5e2ec60072 100644 --- a/spec/factories/milestones.rb +++ b/spec/factories/milestones.rb @@ -17,7 +17,7 @@ FactoryGirl.define do state "closed" end - after(:build) do |milestone, evaluator| + after(:build, :stub) do |milestone, evaluator| if evaluator.group milestone.group = evaluator.group elsif evaluator.group_id diff --git a/spec/factories/notification_settings.rb b/spec/factories/notification_settings.rb index b5e96d18b8f..ee41997e41a 100644 --- a/spec/factories/notification_settings.rb +++ b/spec/factories/notification_settings.rb @@ -3,6 +3,5 @@ FactoryGirl.define do source factory: :empty_project user level 3 - events [] end end diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 485ed48d2de..a04befe809a 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -64,7 +64,7 @@ FactoryGirl.define do # We delete hooks so that gitlab-shell will not try to authenticate with # an API that isn't running - FileUtils.rm_r(File.join(project.repository_storage_path, "#{project.path_with_namespace}.git", 'hooks')) + FileUtils.rm_r(File.join(project.repository_storage_path, "#{project.disk_path}.git", 'hooks')) end end @@ -72,7 +72,7 @@ FactoryGirl.define do after(:create) do |project| raise "Failed to create repository!" unless project.create_repository - FileUtils.rm_r(File.join(project.repository_storage_path, "#{project.path_with_namespace}.git", 'refs')) + FileUtils.rm_r(File.join(project.repository_storage_path, "#{project.disk_path}.git", 'refs')) end end diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb index 97ffc54415c..034682dae27 100644 --- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb +++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb @@ -32,11 +32,13 @@ describe 'Admin > Users > Impersonation Tokens', js: true do check "api" check "read_user" - expect { click_on "Create impersonation token" }.to change { PersonalAccessTokensFinder.new(impersonation: true).execute.count } + click_on "Create impersonation token" + expect(active_impersonation_tokens).to have_text(name) expect(active_impersonation_tokens).to have_text('In') expect(active_impersonation_tokens).to have_text('api') expect(active_impersonation_tokens).to have_text('read_user') + expect(PersonalAccessTokensFinder.new(impersonation: true).execute.count).to equal(1) end end diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 15ec6f20763..0c9fcc60d30 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -282,7 +282,7 @@ describe 'Commits' do end # verified and the gpg user has a gitlab profile - click_on 'Verified' + click_on 'Verified', match: :first within '.popover' do expect(page).to have_content 'This commit was signed with a verified signature.' expect(page).to have_content 'Nannie Bernhard' @@ -295,7 +295,7 @@ describe 'Commits' do visit project_commits_path(project, :'signed-commits') - click_on 'Verified' + click_on 'Verified', match: :first within '.popover' do expect(page).to have_content 'This commit was signed with a verified signature.' expect(page).to have_content 'Nannie Bernhard' diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb index 11a74276898..d7f3d91e625 100644 --- a/spec/features/merge_requests/create_new_mr_spec.rb +++ b/spec/features/merge_requests/create_new_mr_spec.rb @@ -67,8 +67,8 @@ feature 'Create New Merge Request', js: true do visit project_new_merge_request_path(project, merge_request: { target_project_id: private_project.id }) - expect(page).not_to have_content private_project.path_with_namespace - expect(page).to have_content project.path_with_namespace + expect(page).not_to have_content private_project.full_path + expect(page).to have_content project.full_path end end @@ -78,8 +78,8 @@ feature 'Create New Merge Request', js: true do visit project_new_merge_request_path(project, merge_request: { source_project_id: private_project.id }) - expect(page).not_to have_content private_project.path_with_namespace - expect(page).to have_content project.path_with_namespace + expect(page).not_to have_content private_project.full_path + expect(page).to have_content project.full_path end end diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb index dbe98a38197..1dc89665020 100644 --- a/spec/features/projects/wiki/markdown_preview_spec.rb +++ b/spec/features/projects/wiki/markdown_preview_spec.rb @@ -38,10 +38,10 @@ feature 'Projects > Wiki > User previews markdown changes', js: true do expect(page).to have_content("regular link") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/regular\">regular link</a>") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a/b/relative\">relative link 1</a>") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a/b/c/relative\">relative link 2</a>") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a/b/c/e/f/relative\">relative link 3</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/relative\">relative link 1</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/relative\">relative link 2</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/e/f/relative\">relative link 3</a>") end end @@ -60,10 +60,10 @@ feature 'Projects > Wiki > User previews markdown changes', js: true do expect(page).to have_content("regular link") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/regular\">regular link</a>") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/relative\">relative link 1</a>") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>") end end @@ -82,10 +82,10 @@ feature 'Projects > Wiki > User previews markdown changes', js: true do expect(page).to have_content("regular link") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/regular\">regular link</a>") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/relative\">relative link 1</a>") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>") end end end @@ -115,10 +115,10 @@ feature 'Projects > Wiki > User previews markdown changes', js: true do expect(page).to have_content("regular link") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/regular\">regular link</a>") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a/b/relative\">relative link 1</a>") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a/b/c/relative\">relative link 2</a>") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a/b/c/e/f/relative\">relative link 3</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/relative\">relative link 1</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/relative\">relative link 2</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/e/f/relative\">relative link 3</a>") end end @@ -132,10 +132,10 @@ feature 'Projects > Wiki > User previews markdown changes', js: true do expect(page).to have_content("regular link") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/regular\">regular link</a>") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/relative\">relative link 1</a>") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>") end end @@ -149,10 +149,10 @@ feature 'Projects > Wiki > User previews markdown changes', js: true do expect(page).to have_content("regular link") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/regular\">regular link</a>") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/relative\">relative link 1</a>") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>") - expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>") + expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>") end end end diff --git a/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb b/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb index 9c58e336605..f1e7f5f2be8 100644 --- a/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb @@ -20,7 +20,7 @@ describe 'Projects > Wiki > User views Git access wiki page' do visit project_wiki_path(project, wiki_page) click_link 'Clone repository' - expect(page).to have_text("Clone repository #{project.wiki.path_with_namespace}") + expect(page).to have_text("Clone repository #{project.wiki.full_path}") expect(page).to have_text(project.wiki.http_url_to_repo) end end diff --git a/spec/helpers/gitlab_routing_helper_spec.rb b/spec/helpers/gitlab_routing_helper_spec.rb index 717ac1962d1..9aaed0edf87 100644 --- a/spec/helpers/gitlab_routing_helper_spec.rb +++ b/spec/helpers/gitlab_routing_helper_spec.rb @@ -1,6 +1,9 @@ require 'spec_helper' describe GitlabRoutingHelper do + let(:project) { build_stubbed(:empty_project) } + let(:group) { build_stubbed(:group) } + describe 'Project URL helpers' do describe '#project_member_path' do let(:project_member) { create(:project_member) } @@ -9,14 +12,10 @@ describe GitlabRoutingHelper do end describe '#request_access_project_members_path' do - let(:project) { build_stubbed(:empty_project) } - it { expect(request_access_project_members_path(project)).to eq request_access_project_project_members_path(project) } end describe '#leave_project_members_path' do - let(:project) { build_stubbed(:empty_project) } - it { expect(leave_project_members_path(project)).to eq leave_project_project_members_path(project) } end @@ -35,8 +34,6 @@ describe GitlabRoutingHelper do describe 'Group URL helpers' do describe '#group_members_url' do - let(:group) { build_stubbed(:group) } - it { expect(group_members_url(group)).to eq group_group_members_url(group) } end @@ -47,14 +44,10 @@ describe GitlabRoutingHelper do end describe '#request_access_group_members_path' do - let(:group) { build_stubbed(:group) } - it { expect(request_access_group_members_path(group)).to eq request_access_group_group_members_path(group) } end describe '#leave_group_members_path' do - let(:group) { build_stubbed(:group) } - it { expect(leave_group_members_path(group)).to eq leave_group_group_members_path(group) } end @@ -70,4 +63,44 @@ describe GitlabRoutingHelper do it { expect(resend_invite_group_member_path(group_member)).to eq resend_invite_group_group_member_path(group_member.source, group_member) } end end + + describe '#milestone_path' do + context 'for a group milestone' do + let(:milestone) { build_stubbed(:milestone, group: group, iid: 1) } + + it 'links to the group milestone page' do + expect(milestone_path(milestone)) + .to eq(group_milestone_path(group, milestone)) + end + end + + context 'for a project milestone' do + let(:milestone) { build_stubbed(:milestone, project: project, iid: 1) } + + it 'links to the project milestone page' do + expect(milestone_path(milestone)) + .to eq(project_milestone_path(project, milestone)) + end + end + end + + describe '#milestone_url' do + context 'for a group milestone' do + let(:milestone) { build_stubbed(:milestone, group: group, iid: 1) } + + it 'links to the group milestone page' do + expect(milestone_url(milestone)) + .to eq(group_milestone_url(group, milestone)) + end + end + + context 'for a project milestone' do + let(:milestone) { build_stubbed(:milestone, project: project, iid: 1) } + + it 'links to the project milestone page' do + expect(milestone_url(milestone)) + .to eq(project_milestone_url(project, milestone)) + end + end + end end diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb index a8d6044fda7..8fc94ce09db 100644 --- a/spec/helpers/labels_helper_spec.rb +++ b/spec/helpers/labels_helper_spec.rb @@ -7,7 +7,7 @@ describe LabelsHelper do context 'without subject' do it "uses the label's project" do - expect(link_to_label(label)).to match %r{<a href="/#{label.project.path_with_namespace}/issues\?label_name%5B%5D=#{label.name}">.*</a>} + expect(link_to_label(label)).to match %r{<a href="/#{label.project.full_path}/issues\?label_name%5B%5D=#{label.name}">.*</a>} end end @@ -32,7 +32,7 @@ describe LabelsHelper do ['issue', :issue, 'merge_request', :merge_request].each do |type| context "set to #{type}" do it 'links to correct page' do - expect(link_to_label(label, type: type)).to match %r{<a href="/#{label.project.path_with_namespace}/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}">.*</a>} + expect(link_to_label(label, type: type)).to match %r{<a href="/#{label.project.full_path}/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}">.*</a>} end end end diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb index 4b6a351cf70..70eb01c9c44 100644 --- a/spec/helpers/markup_helper_spec.rb +++ b/spec/helpers/markup_helper_spec.rb @@ -210,11 +210,11 @@ describe MarkupHelper do describe '#cross_project_reference' do it 'shows the full MR reference' do - expect(helper.cross_project_reference(project, merge_request)).to include(project.path_with_namespace) + expect(helper.cross_project_reference(project, merge_request)).to include(project.full_path) end it 'shows the full issue reference' do - expect(helper.cross_project_reference(project, issue)).to include(project.path_with_namespace) + expect(helper.cross_project_reference(project, issue)).to include(project.full_path) end end end diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb index 493a4ff9a93..b2a9e277a1c 100644 --- a/spec/helpers/merge_requests_helper_spec.rb +++ b/spec/helpers/merge_requests_helper_spec.rb @@ -34,8 +34,8 @@ describe MergeRequestsHelper do let(:fork_project) { create(:empty_project, forked_from_project: project) } let(:merge_request) { create(:merge_request, source_project: fork_project, target_project: project) } subject { format_mr_branch_names(merge_request) } - let(:source_title) { "#{fork_project.path_with_namespace}:#{merge_request.source_branch}" } - let(:target_title) { "#{project.path_with_namespace}:#{merge_request.target_branch}" } + let(:source_title) { "#{fork_project.full_path}:#{merge_request.source_branch}" } + let(:target_title) { "#{project.full_path}:#{merge_request.target_branch}" } it { is_expected.to eq([source_title, target_title]) } end diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 45066a60f50..faf26931efc 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -53,13 +53,13 @@ describe ProjectsHelper do end it "returns a valid cach key" do - expect(helper.send(:readme_cache_key)).to eq("#{project.path_with_namespace}-#{project.commit.id}-readme") + expect(helper.send(:readme_cache_key)).to eq("#{project.full_path}-#{project.commit.id}-readme") end it "returns a valid cache key if HEAD does not exist" do allow(project).to receive(:commit) { nil } - expect(helper.send(:readme_cache_key)).to eq("#{project.path_with_namespace}-nil-readme") + expect(helper.send(:readme_cache_key)).to eq("#{project.full_path}-nil-readme") end end diff --git a/spec/lib/banzai/filter/abstract_reference_filter_spec.rb b/spec/lib/banzai/filter/abstract_reference_filter_spec.rb index 27532f96f56..32d027b026b 100644 --- a/spec/lib/banzai/filter/abstract_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/abstract_reference_filter_spec.rb @@ -5,7 +5,7 @@ describe Banzai::Filter::AbstractReferenceFilter do describe '#references_per_project' do it 'returns a Hash containing references grouped per project paths' do - doc = Nokogiri::HTML.fragment("#1 #{project.path_with_namespace}#2") + doc = Nokogiri::HTML.fragment("#1 #{project.full_path}#2") filter = described_class.new(doc, project: project) expect(filter).to receive(:object_class).exactly(4).times.and_return(Issue) @@ -14,7 +14,7 @@ describe Banzai::Filter::AbstractReferenceFilter do refs = filter.references_per_project expect(refs).to be_an_instance_of(Hash) - expect(refs[project.path_with_namespace]).to eq(Set.new(%w[1 2])) + expect(refs[project.full_path]).to eq(Set.new(%w[1 2])) end end @@ -24,10 +24,10 @@ describe Banzai::Filter::AbstractReferenceFilter do filter = described_class.new(doc, project: project) expect(filter).to receive(:references_per_project) - .and_return({ project.path_with_namespace => Set.new(%w[1]) }) + .and_return({ project.full_path => Set.new(%w[1]) }) expect(filter.projects_per_reference) - .to eq({ project.path_with_namespace => project }) + .to eq({ project.full_path => project }) end end @@ -37,7 +37,7 @@ describe Banzai::Filter::AbstractReferenceFilter do context 'with RequestStore disabled' do it 'returns a list of Projects for a list of paths' do - expect(filter.find_projects_for_paths([project.path_with_namespace])) + expect(filter.find_projects_for_paths([project.full_path])) .to eq([project]) end @@ -49,7 +49,7 @@ describe Banzai::Filter::AbstractReferenceFilter do context 'with RequestStore enabled', :request_store do it 'returns a list of Projects for a list of paths' do - expect(filter.find_projects_for_paths([project.path_with_namespace])) + expect(filter.find_projects_for_paths([project.full_path])) .to eq([project]) end @@ -88,7 +88,7 @@ describe Banzai::Filter::AbstractReferenceFilter do doc = Nokogiri::HTML.fragment('') filter = described_class.new(doc, project: project) - expect(filter.current_project_path).to eq(project.path_with_namespace) + expect(filter.current_project_path).to eq(project.full_path) end end end diff --git a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb index 11d48387544..935146c17fc 100644 --- a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb @@ -100,7 +100,7 @@ describe Banzai::Filter::CommitRangeReferenceFilter do context 'cross-project / cross-namespace complete reference' do let(:project2) { create(:project, :public, :repository) } - let(:reference) { "#{project2.path_with_namespace}@#{commit1.id}...#{commit2.id}" } + let(:reference) { "#{project2.full_path}@#{commit1.id}...#{commit2.id}" } it 'links to a valid reference' do doc = reference_filter("See #{reference}") @@ -113,20 +113,20 @@ describe Banzai::Filter::CommitRangeReferenceFilter do doc = reference_filter("Fixed (#{reference}.)") expect(doc.css('a').first.text) - .to eql("#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}") + .to eql("#{project2.full_path}@#{commit1.short_id}...#{commit2.short_id}") end it 'has valid text' do doc = reference_filter("Fixed (#{reference}.)") - expect(doc.text).to eql("Fixed (#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}.)") + expect(doc.text).to eql("Fixed (#{project2.full_path}@#{commit1.short_id}...#{commit2.short_id}.)") end it 'ignores invalid commit IDs on the referenced project' do - exp = act = "Fixed #{project2.path_with_namespace}@#{commit1.id.reverse}...#{commit2.id}" + exp = act = "Fixed #{project2.full_path}@#{commit1.id.reverse}...#{commit2.id}" expect(reference_filter(act).to_html).to eq exp - exp = act = "Fixed #{project2.path_with_namespace}@#{commit1.id}...#{commit2.id.reverse}" + exp = act = "Fixed #{project2.full_path}@#{commit1.id}...#{commit2.id.reverse}" expect(reference_filter(act).to_html).to eq exp end end diff --git a/spec/lib/banzai/filter/commit_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_reference_filter_spec.rb index fb2a36d1ba1..c7cf1c1d582 100644 --- a/spec/lib/banzai/filter/commit_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/commit_reference_filter_spec.rb @@ -98,18 +98,18 @@ describe Banzai::Filter::CommitReferenceFilter do let(:namespace) { create(:namespace) } let(:project2) { create(:project, :public, :repository, namespace: namespace) } let(:commit) { project2.commit } - let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" } + let(:reference) { "#{project2.full_path}@#{commit.short_id}" } it 'link has valid text' do doc = reference_filter("See (#{reference}.)") - expect(doc.css('a').first.text).to eql("#{project2.path_with_namespace}@#{commit.short_id}") + expect(doc.css('a').first.text).to eql("#{project2.full_path}@#{commit.short_id}") end it 'has valid text' do doc = reference_filter("See (#{reference}.)") - expect(doc.text).to eql("See (#{project2.path_with_namespace}@#{commit.short_id}.)") + expect(doc.text).to eql("See (#{project2.full_path}@#{commit.short_id}.)") end it 'ignores invalid commit IDs on the referenced project' do @@ -124,7 +124,7 @@ describe Banzai::Filter::CommitReferenceFilter do let(:project) { create(:empty_project, namespace: namespace) } let(:project2) { create(:project, :public, :repository, namespace: namespace) } let(:commit) { project2.commit } - let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" } + let(:reference) { "#{project2.full_path}@#{commit.short_id}" } it 'link has valid text' do doc = reference_filter("See (#{reference}.)") @@ -150,7 +150,7 @@ describe Banzai::Filter::CommitReferenceFilter do let(:project) { create(:empty_project, namespace: namespace) } let(:project2) { create(:project, :public, :repository, namespace: namespace) } let(:commit) { project2.commit } - let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" } + let(:reference) { "#{project2.full_path}@#{commit.short_id}" } it 'link has valid text' do doc = reference_filter("See (#{reference}.)") diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb index 045bf3e0cc9..024a5cafb41 100644 --- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb @@ -127,7 +127,7 @@ describe Banzai::Filter::IssueReferenceFilter do let(:project2) { create(:empty_project, :public) } let(:issue) { create(:issue, project: project2) } - let(:reference) { "#{project2.path_with_namespace}##{issue.iid}" } + let(:reference) { "#{project2.full_path}##{issue.iid}" } it 'ignores valid references when cross-reference project uses external tracker' do expect_any_instance_of(described_class).to receive(:find_object) @@ -148,13 +148,13 @@ describe Banzai::Filter::IssueReferenceFilter do it 'link has valid text' do doc = reference_filter("Fixed (#{reference}.)") - expect(doc.css('a').first.text).to eql("#{project2.path_with_namespace}##{issue.iid}") + expect(doc.css('a').first.text).to eql("#{project2.full_path}##{issue.iid}") end it 'has valid text' do doc = reference_filter("Fixed (#{reference}.)") - expect(doc.text).to eq("Fixed (#{project2.path_with_namespace}##{issue.iid}.)") + expect(doc.text).to eq("Fixed (#{project2.full_path}##{issue.iid}.)") end it 'ignores invalid issue IDs on the referenced project' do @@ -171,7 +171,7 @@ describe Banzai::Filter::IssueReferenceFilter do let(:project) { create(:empty_project, :public, namespace: namespace) } let(:project2) { create(:empty_project, :public, namespace: namespace) } let(:issue) { create(:issue, project: project2) } - let(:reference) { "#{project2.path_with_namespace}##{issue.iid}" } + let(:reference) { "#{project2.full_path}##{issue.iid}" } it 'ignores valid references when cross-reference project uses external tracker' do expect_any_instance_of(described_class).to receive(:find_object) @@ -324,10 +324,10 @@ describe Banzai::Filter::IssueReferenceFilter do filter = described_class.new(doc, project: project) expect(filter).to receive(:projects_per_reference) - .and_return({ project.path_with_namespace => project }) + .and_return({ project.full_path => project }) expect(filter).to receive(:references_per_project) - .and_return({ project.path_with_namespace => Set.new([issue.iid]) }) + .and_return({ project.full_path => Set.new([issue.iid]) }) expect(filter.issues_per_project) .to eq({ project => { issue.iid => issue } }) diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb index 1daa6ac7f9e..dfd4c7a7279 100644 --- a/spec/lib/banzai/filter/label_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb @@ -368,7 +368,7 @@ describe Banzai::Filter::LabelReferenceFilter do describe 'cross-project / cross-namespace complete reference' do let(:project2) { create(:empty_project) } let(:label) { create(:label, project: project2, color: '#00ff00') } - let(:reference) { "#{project2.path_with_namespace}~#{label.name}" } + let(:reference) { "#{project2.full_path}~#{label.name}" } let!(:result) { reference_filter("See #{reference}") } it 'links to a valid reference' do @@ -400,7 +400,7 @@ describe Banzai::Filter::LabelReferenceFilter do let(:project) { create(:empty_project, namespace: namespace) } let(:project2) { create(:empty_project, namespace: namespace) } let(:label) { create(:label, project: project2, color: '#00ff00') } - let(:reference) { "#{project2.path_with_namespace}~#{label.name}" } + let(:reference) { "#{project2.full_path}~#{label.name}" } let!(:result) { reference_filter("See #{reference}") } it 'links to a valid reference' do @@ -466,7 +466,7 @@ describe Banzai::Filter::LabelReferenceFilter do let(:another_group) { create(:group) } let(:another_project) { create(:empty_project, :public, namespace: another_group) } let(:group_label) { create(:group_label, group: another_group, color: '#00ff00') } - let(:reference) { "#{another_project.path_with_namespace}~#{group_label.name}" } + let(:reference) { "#{another_project.full_path}~#{group_label.name}" } let!(:result) { reference_filter("See #{reference}", project: project) } it 'points to referenced project issues page' do @@ -501,7 +501,7 @@ describe Banzai::Filter::LabelReferenceFilter do let(:project) { create(:empty_project, :public, namespace: group) } let(:another_project) { create(:empty_project, :public, namespace: group) } let(:group_label) { create(:group_label, group: group, color: '#00ff00') } - let(:reference) { "#{another_project.path_with_namespace}~#{group_label.name}" } + let(:reference) { "#{another_project.full_path}~#{group_label.name}" } let!(:result) { reference_filter("See #{reference}", project: project) } it 'points to referenced project issues page' do @@ -535,7 +535,7 @@ describe Banzai::Filter::LabelReferenceFilter do let(:group) { create(:group) } let(:project) { create(:empty_project, :public, namespace: group) } let(:group_label) { create(:group_label, group: group, color: '#00ff00') } - let(:reference) { "#{project.path_with_namespace}~#{group_label.name}" } + let(:reference) { "#{project.full_path}~#{group_label.name}" } let!(:result) { reference_filter("See #{reference}", project: project) } it 'points to referenced project issues page' do diff --git a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb index 683972a3112..b693ae3eca2 100644 --- a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb @@ -102,7 +102,7 @@ describe Banzai::Filter::MergeRequestReferenceFilter do context 'cross-project / cross-namespace complete reference' do let(:project2) { create(:empty_project, :public) } let(:merge) { create(:merge_request, source_project: project2) } - let(:reference) { "#{project2.path_with_namespace}!#{merge.iid}" } + let(:reference) { "#{project2.full_path}!#{merge.iid}" } it 'links to a valid reference' do doc = reference_filter("See #{reference}") @@ -135,7 +135,7 @@ describe Banzai::Filter::MergeRequestReferenceFilter do let(:project) { create(:empty_project, :public, namespace: namespace) } let(:project2) { create(:empty_project, :public, namespace: namespace) } let!(:merge) { create(:merge_request, source_project: project2) } - let(:reference) { "#{project2.path_with_namespace}!#{merge.iid}" } + let(:reference) { "#{project2.full_path}!#{merge.iid}" } it 'links to a valid reference' do doc = reference_filter("See #{reference}") diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb index 8fe05dc2e53..79ff9419e4b 100644 --- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb @@ -152,7 +152,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do let(:namespace) { create(:namespace) } let(:another_project) { create(:empty_project, :public, namespace: namespace) } let(:milestone) { create(:milestone, project: another_project) } - let(:reference) { "#{another_project.path_with_namespace}%#{milestone.iid}" } + let(:reference) { "#{another_project.full_path}%#{milestone.iid}" } let!(:result) { reference_filter("See #{reference}") } it 'points to referenced project milestone page' do @@ -164,14 +164,14 @@ describe Banzai::Filter::MilestoneReferenceFilter do doc = reference_filter("See (#{reference}.)") expect(doc.css('a').first.text) - .to eq("#{milestone.name} in #{another_project.path_with_namespace}") + .to eq("#{milestone.name} in #{another_project.full_path}") end it 'has valid text' do doc = reference_filter("See (#{reference}.)") expect(doc.text) - .to eq("See (#{milestone.name} in #{another_project.path_with_namespace}.)") + .to eq("See (#{milestone.name} in #{another_project.full_path}.)") end it 'escapes the name attribute' do @@ -180,7 +180,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do doc = reference_filter("See #{reference}") expect(doc.css('a').first.text) - .to eq "#{milestone.name} in #{another_project.path_with_namespace}" + .to eq "#{milestone.name} in #{another_project.full_path}" end end @@ -189,7 +189,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do let(:project) { create(:empty_project, :public, namespace: namespace) } let(:another_project) { create(:empty_project, :public, namespace: namespace) } let(:milestone) { create(:milestone, project: another_project) } - let(:reference) { "#{another_project.path_with_namespace}%#{milestone.iid}" } + let(:reference) { "#{another_project.full_path}%#{milestone.iid}" } let!(:result) { reference_filter("See #{reference}") } it 'points to referenced project milestone page' do @@ -257,4 +257,28 @@ describe Banzai::Filter::MilestoneReferenceFilter do .to eq "#{milestone.name} in #{another_project.path}" end end + + describe 'cross project milestone references' do + let(:another_project) { create(:empty_project, :public) } + let(:project_path) { another_project.full_path } + let(:milestone) { create(:milestone, project: another_project) } + let(:reference) { milestone.to_reference(project) } + + let!(:result) { reference_filter("See #{reference}") } + + it 'points to referenced project milestone page' do + expect(result.css('a').first.attr('href')).to eq urls + .project_milestone_url(another_project, milestone) + end + + it 'contains cross project content' do + expect(result.css('a').first.text).to eq "#{milestone.name} in #{project_path}" + end + + it 'escapes the name attribute' do + allow_any_instance_of(Milestone).to receive(:title).and_return(%{"></a>whatever<a title="}) + doc = reference_filter("See #{reference}") + expect(doc.css('a').first.text).to eq "#{milestone.name} in #{project_path}" + end + end end diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb index ddebf2264d9..e97199672f3 100644 --- a/spec/lib/banzai/filter/relative_link_filter_spec.rb +++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb @@ -26,7 +26,7 @@ describe Banzai::Filter::RelativeLinkFilter do end let(:project) { create(:project, :repository) } - let(:project_path) { project.path_with_namespace } + let(:project_path) { project.full_path } let(:ref) { 'markdown' } let(:commit) { project.commit(ref) } let(:project_wiki) { nil } diff --git a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb index 5f548888223..a7eee7a4515 100644 --- a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb @@ -83,7 +83,7 @@ describe Banzai::Filter::SnippetReferenceFilter do let(:namespace) { create(:namespace) } let(:project2) { create(:empty_project, :public, namespace: namespace) } let!(:snippet) { create(:project_snippet, project: project2) } - let(:reference) { "#{project2.path_with_namespace}$#{snippet.id}" } + let(:reference) { "#{project2.full_path}$#{snippet.id}" } it 'links to a valid reference' do doc = reference_filter("See #{reference}") @@ -116,7 +116,7 @@ describe Banzai::Filter::SnippetReferenceFilter do let(:project) { create(:empty_project, :public, namespace: namespace) } let(:project2) { create(:empty_project, :public, namespace: namespace) } let!(:snippet) { create(:project_snippet, project: project2) } - let(:reference) { "#{project2.path_with_namespace}$#{snippet.id}" } + let(:reference) { "#{project2.full_path}$#{snippet.id}" } it 'links to a valid reference' do doc = reference_filter("See #{reference}") diff --git a/spec/lib/banzai/filter/upload_link_filter_spec.rb b/spec/lib/banzai/filter/upload_link_filter_spec.rb index 3bc9635b50e..74a23a9ab5e 100644 --- a/spec/lib/banzai/filter/upload_link_filter_spec.rb +++ b/spec/lib/banzai/filter/upload_link_filter_spec.rb @@ -52,21 +52,21 @@ describe Banzai::Filter::UploadLinkFilter do it 'rebuilds relative URL for a link' do doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) expect(doc.at_css('a')['href']) - .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" doc = filter(nested_link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) expect(doc.at_css('a')['href']) - .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" end it 'rebuilds relative URL for an image' do doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) expect(doc.at_css('img')['src']) - .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" doc = filter(nested_image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) expect(doc.at_css('img')['src']) - .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" end it 'does not modify absolute URL' do @@ -85,7 +85,7 @@ describe Banzai::Filter::UploadLinkFilter do .to receive(:image?).with(path).and_return(true) doc = filter(image(escaped)) - expect(doc.at_css('img')['src']).to match "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/%ED%95%9C%EA%B8%80.png" + expect(doc.at_css('img')['src']).to match "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/%ED%95%9C%EA%B8%80.png" end end diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb index 9b89f47ae7e..867f7d55af7 100644 --- a/spec/lib/extracts_path_spec.rb +++ b/spec/lib/extracts_path_spec.rb @@ -14,7 +14,7 @@ describe ExtractsPath do repo = double(ref_names: ['master', 'foo/bar/baz', 'v1.0.0', 'v2.0.0', 'release/app', 'release/app/v1.0.0']) allow(project).to receive(:repository).and_return(repo) - allow(project).to receive(:path_with_namespace) + allow(project).to receive(:full_path) .and_return('gitlab/gitlab-ci') allow(request).to receive(:format=) end @@ -29,7 +29,7 @@ describe ExtractsPath do it "log tree path has no escape sequences" do assign_ref_vars - expect(@logs_path).to eq("/#{@project.path_with_namespace}/refs/#{ref}/logs_tree/files/ruby/popen.rb") + expect(@logs_path).to eq("/#{@project.full_path}/refs/#{ref}/logs_tree/files/ruby/popen.rb") end context 'ref contains %20' do diff --git a/spec/lib/gitlab/backup/repository_spec.rb b/spec/lib/gitlab/backup/repository_spec.rb index db860b01ba4..3af69daa585 100644 --- a/spec/lib/gitlab/backup/repository_spec.rb +++ b/spec/lib/gitlab/backup/repository_spec.rb @@ -60,4 +60,58 @@ describe Backup::Repository do end end end + + describe '#empty_repo?' do + context 'for a wiki' do + let(:wiki) { create(:project_wiki) } + + context 'wiki repo has content' do + let!(:wiki_page) { create(:wiki_page, wiki: wiki) } + + before do + wiki.repository.exists? # initial cache + end + + context '`repository.exists?` is incorrectly cached as false' do + before do + repo = wiki.repository + repo.send(:cache).expire(:exists?) + repo.send(:cache).fetch(:exists?) { false } + repo.send(:instance_variable_set, :@exists, false) + end + + it 'returns false, regardless of bad cache value' do + expect(described_class.new.send(:empty_repo?, wiki)).to be_falsey + end + end + + context '`repository.exists?` is correctly cached as true' do + it 'returns false' do + expect(described_class.new.send(:empty_repo?, wiki)).to be_falsey + end + end + end + + context 'wiki repo does not have content' do + context '`repository.exists?` is incorrectly cached as true' do + before do + repo = wiki.repository + repo.send(:cache).expire(:exists?) + repo.send(:cache).fetch(:exists?) { true } + repo.send(:instance_variable_set, :@exists, true) + end + + it 'returns true, regardless of bad cache value' do + expect(described_class.new.send(:empty_repo?, wiki)).to be_truthy + end + end + + context '`repository.exists?` is correctly cached as false' do + it 'returns true' do + expect(described_class.new.send(:empty_repo?, wiki)).to be_truthy + end + end + end + end + end end diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb index cd2fa98b14c..d3d841b0668 100644 --- a/spec/lib/gitlab/diff/file_spec.rb +++ b/spec/lib/gitlab/diff/file_spec.rb @@ -47,14 +47,6 @@ describe Gitlab::Diff::File do end end - describe '#old_content_commit' do - it 'returns base commit' do - old_content_commit = diff_file.old_content_commit - - expect(old_content_commit.id).to eq('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') - end - end - describe '#old_blob' do it 'returns blob of commit of base commit' do old_data = diff_file.old_blob.data diff --git a/spec/lib/gitlab/email/message/repository_push_spec.rb b/spec/lib/gitlab/email/message/repository_push_spec.rb index 7b3291b8315..83c4d177cae 100644 --- a/spec/lib/gitlab/email/message/repository_push_spec.rb +++ b/spec/lib/gitlab/email/message/repository_push_spec.rb @@ -117,7 +117,7 @@ describe Gitlab::Email::Message::RepositoryPush do describe '#subject' do subject { message.subject } - it { is_expected.to include "[Git][#{project.path_with_namespace}]" } + it { is_expected.to include "[Git][#{project.full_path}]" } it { is_expected.to include "#{compare.commits.length} commits" } it { is_expected.to include compare.commits.first.message.split("\n").first } end diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb index c3016f63ebf..fef456eb416 100644 --- a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb +++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb @@ -39,8 +39,8 @@ describe Gitlab::Gfm::UploadsRewriter do it 'copies files' do expect(new_files).to all(exist) expect(old_paths).not_to match_array new_paths - expect(old_paths).to all(include(old_project.path_with_namespace)) - expect(new_paths).to all(include(new_project.path_with_namespace)) + expect(old_paths).to all(include(old_project.full_path)) + expect(new_paths).to all(include(new_project.full_path)) end it 'does not remove old files' do diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb index 3c784eda4f8..18320bb23b9 100644 --- a/spec/lib/gitlab/git/blob_spec.rb +++ b/spec/lib/gitlab/git/blob_spec.rb @@ -78,12 +78,18 @@ describe Gitlab::Git::Blob, seed_helper: true do context 'large file' do let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, 'files/images/6049019_460s.jpg') } let(:blob_size) { 111803 } + let(:stub_limit) { 1000 } + + before do + stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', stub_limit) + end it { expect(blob.size).to eq(blob_size) } - it { expect(blob.data.length).to eq(blob_size) } + it { expect(blob.data.length).to eq(stub_limit) } it 'check that this test is sane' do - expect(blob.size).to be <= Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE + # It only makes sense to test limiting if the blob is larger than the limit. + expect(blob.size).to be > Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE end it 'can load all data' do diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 50736d353ad..8e4a1f31ced 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1127,6 +1127,45 @@ describe Gitlab::Git::Repository, seed_helper: true do end end + describe '#languages' do + shared_examples 'languages' do + it 'returns exactly the expected results' do + languages = repository.languages('4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6') + expected_languages = [ + { value: 66.63, label: "Ruby", color: "#701516", highlight: "#701516" }, + { value: 22.96, label: "JavaScript", color: "#f1e05a", highlight: "#f1e05a" }, + { value: 7.9, label: "HTML", color: "#e44b23", highlight: "#e44b23" }, + { value: 2.51, label: "CoffeeScript", color: "#244776", highlight: "#244776" } + ] + + expect(languages.size).to eq(expected_languages.size) + + expected_languages.size.times do |i| + a = expected_languages[i] + b = languages[i] + + expect(a.keys.sort).to eq(b.keys.sort) + expect(a[:value]).to be_within(0.1).of(b[:value]) + + non_float_keys = a.keys - [:value] + expect(a.values_at(*non_float_keys)).to eq(b.values_at(*non_float_keys)) + end + end + + it "uses the repository's HEAD when no ref is passed" do + lang = repository.languages.first + + expect(lang[:label]).to eq('Ruby') + end + end + + it_behaves_like 'languages' + + context 'with rugged', skip_gitaly_mock: true do + it_behaves_like 'languages' + end + end + def create_remote_branch(repository, remote_name, branch_name, source_branch_name) source_branch = repository.branches.find { |branch| branch.name == source_branch_name } rugged = repository.rugged diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb index 0868c793a33..d71e0f84c65 100644 --- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::GitalyClient::CommitService do let(:project) { create(:project, :repository) } let(:storage_name) { project.repository_storage } - let(:relative_path) { project.path_with_namespace + '.git' } + let(:relative_path) { project.disk_path + '.git' } let(:repository) { project.repository } let(:repository_message) { repository.gitaly_repository } let(:revision) { '913c66a37b4a45b9769037c55c2d238bd0942d2e' } diff --git a/spec/lib/gitlab/gitaly_client/notification_service_spec.rb b/spec/lib/gitlab/gitaly_client/notification_service_spec.rb index d9597c4aa78..1bcdd5e5497 100644 --- a/spec/lib/gitlab/gitaly_client/notification_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/notification_service_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::GitalyClient::NotificationService do describe '#post_receive' do let(:project) { create(:empty_project) } let(:storage_name) { project.repository_storage } - let(:relative_path) { project.path_with_namespace + '.git' } + let(:relative_path) { project.disk_path + '.git' } subject { described_class.new(project.repository) } it 'sends a post_receive message' do diff --git a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb index 0b1c890f956..2ad24119476 100644 --- a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::GitalyClient::RefService do let(:project) { create(:empty_project) } let(:storage_name) { project.repository_storage } - let(:relative_path) { project.path_with_namespace + '.git' } + let(:relative_path) { project.disk_path + '.git' } let(:client) { described_class.new(project.repository) } describe '#branches' do diff --git a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb index de50265bc14..fcd90fab547 100644 --- a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb @@ -9,9 +9,9 @@ describe Gitlab::GithubImport::WikiFormatter do subject(:wiki) { described_class.new(project) } - describe '#path_with_namespace' do + describe '#disk_path' do it 'appends .wiki to project path' do - expect(wiki.path_with_namespace).to eq 'gitlabhq/gitlabhq.wiki' + expect(wiki.disk_path).to eq project.disk_path + '.wiki' end end diff --git a/spec/lib/gitlab/import_export/fork_spec.rb b/spec/lib/gitlab/import_export/fork_spec.rb index 08588a76fe6..0ff64cbe880 100644 --- a/spec/lib/gitlab/import_export/fork_spec.rb +++ b/spec/lib/gitlab/import_export/fork_spec.rb @@ -5,7 +5,7 @@ describe 'forked project import' do let!(:project_with_repo) { create(:project, name: 'test-repo-restorer', path: 'test-repo-restorer') } let!(:project) { create(:empty_project, name: 'test-repo-restorer-no-repo', path: 'test-repo-restorer-no-repo') } let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } - let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } + let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.full_path) } let(:forked_from_project) { create(:project) } let(:fork_link) { create(:forked_project_link, forked_from_project: project_with_repo) } let(:repo_saver) { Gitlab::ImportExport::RepoSaver.new(project: project_with_repo, shared: shared) } 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 0c7e733b01f..a278f89c1a1 100644 --- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::ImportExport::ProjectTreeSaver do describe 'saves the project tree into a json object' do - let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } + let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.full_path) } let(:project_tree_saver) { described_class.new(project: project, current_user: user, shared: shared) } let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } let(:user) { create(:user) } diff --git a/spec/lib/gitlab/import_export/repo_restorer_spec.rb b/spec/lib/gitlab/import_export/repo_restorer_spec.rb index d3be2965bf4..82935af2670 100644 --- a/spec/lib/gitlab/import_export/repo_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/repo_restorer_spec.rb @@ -6,7 +6,7 @@ describe Gitlab::ImportExport::RepoRestorer do let!(:project_with_repo) { create(:project, name: 'test-repo-restorer', path: 'test-repo-restorer') } let!(:project) { create(:empty_project) } let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } - let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } + let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.full_path) } let(:bundler) { Gitlab::ImportExport::RepoSaver.new(project: project_with_repo, shared: shared) } let(:bundle_path) { File.join(shared.export_path, Gitlab::ImportExport.project_bundle_filename) } let(:restorer) do diff --git a/spec/lib/gitlab/import_export/repo_saver_spec.rb b/spec/lib/gitlab/import_export/repo_saver_spec.rb index 87af13e0beb..0ba199bbb05 100644 --- a/spec/lib/gitlab/import_export/repo_saver_spec.rb +++ b/spec/lib/gitlab/import_export/repo_saver_spec.rb @@ -5,7 +5,7 @@ describe Gitlab::ImportExport::RepoSaver do let(:user) { create(:user) } let!(:project) { create(:empty_project, :public, name: 'searchable_project') } let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } - let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } + let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.full_path) } let(:bundler) { described_class.new(project: project, shared: shared) } before do diff --git a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb index 78137aeff5e..caf08e674d3 100644 --- a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb +++ b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb @@ -5,7 +5,7 @@ describe Gitlab::ImportExport::WikiRepoSaver do let(:user) { create(:user) } let!(:project) { create(:empty_project, :public, name: 'searchable_project') } let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } - let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } + let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.full_path) } let(:wiki_bundler) { described_class.new(project: project, shared: shared) } let!(:project_wiki) { ProjectWiki.new(project, user) } diff --git a/spec/lib/gitlab/quick_actions/dsl_spec.rb b/spec/lib/gitlab/quick_actions/dsl_spec.rb index a4bb3f911d7..ff59dc48bcb 100644 --- a/spec/lib/gitlab/quick_actions/dsl_spec.rb +++ b/spec/lib/gitlab/quick_actions/dsl_spec.rb @@ -42,13 +42,18 @@ describe Gitlab::QuickActions::Dsl do command :with_params_parsing do |parsed| parsed end + + params '<Comment>' + substitution :something do |text| + "#{text} Some complicated thing you want in here" + end end end describe '.command_definitions' do it 'returns an array with commands definitions' do no_args_def, explanation_with_aliases_def, dynamic_description_def, - cc_def, cond_action_def, with_params_parsing_def = + cc_def, cond_action_def, with_params_parsing_def, substitution_def = DummyClass.command_definitions expect(no_args_def.name).to eq(:no_args) @@ -104,6 +109,15 @@ describe Gitlab::QuickActions::Dsl do expect(with_params_parsing_def.condition_block).to be_nil expect(with_params_parsing_def.action_block).to be_a_kind_of(Proc) expect(with_params_parsing_def.parse_params_block).to be_a_kind_of(Proc) + + expect(substitution_def.name).to eq(:something) + expect(substitution_def.aliases).to eq([]) + expect(substitution_def.description).to eq('') + expect(substitution_def.explanation).to eq('') + expect(substitution_def.params).to eq(['<Comment>']) + expect(substitution_def.condition_block).to be_nil + expect(substitution_def.action_block.call('text')).to eq('text Some complicated thing you want in here') + expect(substitution_def.parse_params_block).to be_nil end end end diff --git a/spec/lib/gitlab/quick_actions/extractor_spec.rb b/spec/lib/gitlab/quick_actions/extractor_spec.rb index 9d32938e155..f7c288f2393 100644 --- a/spec/lib/gitlab/quick_actions/extractor_spec.rb +++ b/spec/lib/gitlab/quick_actions/extractor_spec.rb @@ -9,6 +9,11 @@ describe Gitlab::QuickActions::Extractor do command(:assign) { } command(:labels) { } command(:power) { } + command(:noop_command) + substitution(:substitution) { 'foo' } + substitution :shrug do |comment| + "#{comment} SHRUG" + end end.command_definitions end @@ -177,6 +182,38 @@ describe Gitlab::QuickActions::Extractor do expect(msg).to eq "hello\nworld" end + it 'does not extract noop commands' do + msg = %(hello\nworld\n/reopen\n/noop_command) + msg, commands = extractor.extract_commands(msg) + + expect(commands).to eq [['reopen']] + expect(msg).to eq "hello\nworld\n/noop_command" + end + + it 'extracts and performs substitution commands' do + msg = %(hello\nworld\n/reopen\n/substitution) + msg, commands = extractor.extract_commands(msg) + + expect(commands).to eq [['reopen'], ['substitution']] + expect(msg).to eq "hello\nworld\nfoo" + end + + it 'extracts and performs substitution commands' do + msg = %(hello\nworld\n/reopen\n/shrug this is great?) + msg, commands = extractor.extract_commands(msg) + + expect(commands).to eq [['reopen'], ['shrug', 'this is great?']] + expect(msg).to eq "hello\nworld\nthis is great? SHRUG" + end + + it 'extracts and performs substitution commands with comments' do + msg = %(hello\nworld\n/reopen\n/substitution wow this is a thing.) + msg, commands = extractor.extract_commands(msg) + + expect(commands).to eq [['reopen'], ['substitution', 'wow this is a thing.']] + expect(msg).to eq "hello\nworld\nfoo" + end + it 'extracts multiple commands' do msg = %(hello\n/power @user.name %9.10 ~"bar baz.2" label\nworld\n/reopen) msg, commands = extractor.extract_commands(msg) diff --git a/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb b/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb new file mode 100644 index 00000000000..1bb8bc51c96 --- /dev/null +++ b/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe Gitlab::QuickActions::SubstitutionDefinition do + let(:content) do + <<EOF +Hello! Let's do this! +/sub_name I like this stuff +EOF + end + subject do + described_class.new(:sub_name, action_block: proc { |text| "#{text} foo" }) + end + + describe '#perform_substitution!' do + it 'returns nil if content is nil' do + expect(subject.perform_substitution(self, nil)).to be_nil + end + + it 'performs the substitution by default' do + expect(subject.perform_substitution(self, content)).to eq <<EOF +Hello! Let's do this! +I like this stuff foo +EOF + end + end + + describe '#match' do + it 'checks the content for the command' do + expect(subject.match(content)).to be_truthy + end + + it 'returns the match data' do + data = subject.match(content) + expect(data).to be_a(MatchData) + expect(data[1]).to eq('I like this stuff') + end + + it 'is nil if content does not have the command' do + expect(subject.match('blah')).to be_falsey + end + end +end diff --git a/spec/lib/gitlab/slash_commands/command_spec.rb b/spec/lib/gitlab/slash_commands/command_spec.rb index f0ecf59406a..88f73bf90cd 100644 --- a/spec/lib/gitlab/slash_commands/command_spec.rb +++ b/spec/lib/gitlab/slash_commands/command_spec.rb @@ -80,7 +80,7 @@ describe Gitlab::SlashCommands::Command do it 'returns error' do expect(subject[:response_type]).to be(:ephemeral) - expect(subject[:text]).to include('Too many actions defined') + expect(subject[:text]).to include("Couldn't find a deployment manual action.") end end end diff --git a/spec/lib/gitlab/slash_commands/deploy_spec.rb b/spec/lib/gitlab/slash_commands/deploy_spec.rb index e52aaed7328..c3fb7d5adea 100644 --- a/spec/lib/gitlab/slash_commands/deploy_spec.rb +++ b/spec/lib/gitlab/slash_commands/deploy_spec.rb @@ -22,7 +22,7 @@ describe Gitlab::SlashCommands::Deploy do context 'if no environment is defined' do it 'does not execute an action' do expect(subject[:response_type]).to be(:ephemeral) - expect(subject[:text]).to eq("No action found to be executed") + expect(subject[:text]).to eq "Couldn't find a deployment manual action." end end @@ -35,12 +35,12 @@ describe Gitlab::SlashCommands::Deploy do context 'without actions' do it 'does not execute an action' do expect(subject[:response_type]).to be(:ephemeral) - expect(subject[:text]).to eq("No action found to be executed") + expect(subject[:text]).to eq "Couldn't find a deployment manual action." end end - context 'with action' do - let!(:manual1) do + context 'when single action has been matched' do + before do create(:ci_build, :manual, pipeline: pipeline, name: 'first', environment: 'production') @@ -48,31 +48,61 @@ describe Gitlab::SlashCommands::Deploy do it 'returns success result' do expect(subject[:response_type]).to be(:in_channel) - expect(subject[:text]).to start_with('Deployment started from staging to production') + expect(subject[:text]) + .to start_with('Deployment started from staging to production') end + end + + context 'when more than one action has been matched' do + context 'when there is no specific actions with a environment name' do + before do + create(:ci_build, :manual, pipeline: pipeline, + name: 'first', + environment: 'production') - context 'when duplicate action exists' do - let!(:manual2) do create(:ci_build, :manual, pipeline: pipeline, name: 'second', environment: 'production') end - it 'returns error' do + it 'returns error about too many actions defined' do + expect(subject[:text]).to eq("Couldn't find a deployment manual action.") expect(subject[:response_type]).to be(:ephemeral) - expect(subject[:text]).to eq('Too many actions defined') end end - context 'when teardown action exists' do - let!(:teardown) do + context 'when one of the actions is environement specific action' do + before do + create(:ci_build, :manual, pipeline: pipeline, + name: 'first', + environment: 'production') + + create(:ci_build, :manual, pipeline: pipeline, + name: 'production', + environment: 'production') + end + + it 'deploys to production' do + expect(subject[:text]) + .to start_with('Deployment started from staging to production') + expect(subject[:response_type]).to be(:in_channel) + end + end + + context 'when one of the actions is a teardown action' do + before do + create(:ci_build, :manual, pipeline: pipeline, + name: 'first', + environment: 'production') + create(:ci_build, :manual, :teardown_environment, pipeline: pipeline, name: 'teardown', environment: 'production') end - it 'returns the success message' do + it 'deploys to production' do + expect(subject[:text]) + .to start_with('Deployment started from staging to production') expect(subject[:response_type]).to be(:in_channel) - expect(subject[:text]).to start_with('Deployment started from staging to production') end end end diff --git a/spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb b/spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb index dee3c77db27..d16d122c64e 100644 --- a/spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb +++ b/spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb @@ -17,8 +17,8 @@ describe Gitlab::SlashCommands::Presenters::Deploy do end end - describe '#no_actions' do - subject { described_class.new(nil).no_actions } + describe '#action_not_found' do + subject { described_class.new(nil).action_not_found } it { is_expected.to have_key(:text) } it { is_expected.to have_key(:response_type) } @@ -27,21 +27,7 @@ describe Gitlab::SlashCommands::Presenters::Deploy do it 'tells the user there is no action' do expect(subject[:response_type]).to be(:ephemeral) - expect(subject[:text]).to eq("No action found to be executed") - end - end - - describe '#too_many_actions' do - subject { described_class.new([]).too_many_actions } - - it { is_expected.to have_key(:text) } - it { is_expected.to have_key(:response_type) } - it { is_expected.to have_key(:status) } - it { is_expected.not_to have_key(:attachments) } - - it 'tells the user there is no action' do - expect(subject[:response_type]).to be(:ephemeral) - expect(subject[:text]).to eq("Too many actions defined") + expect(subject[:text]).to eq "Couldn't find a deployment manual action." end end end diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb index 50422020823..7412f22640c 100644 --- a/spec/lib/gitlab/url_builder_spec.rb +++ b/spec/lib/gitlab/url_builder_spec.rb @@ -8,7 +8,7 @@ describe Gitlab::UrlBuilder do url = described_class.build(commit) - expect(url).to eq "#{Settings.gitlab['url']}/#{commit.project.path_with_namespace}/commit/#{commit.id}" + expect(url).to eq "#{Settings.gitlab['url']}/#{commit.project.full_path}/commit/#{commit.id}" end end @@ -18,7 +18,7 @@ describe Gitlab::UrlBuilder do url = described_class.build(issue) - expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}" + expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.full_path}/issues/#{issue.iid}" end end @@ -28,7 +28,7 @@ describe Gitlab::UrlBuilder do url = described_class.build(merge_request) - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}" + expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.full_path}/merge_requests/#{merge_request.iid}" end end @@ -39,7 +39,7 @@ describe Gitlab::UrlBuilder do url = described_class.build(note) - expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" + expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.full_path}/commit/#{note.commit_id}#note_#{note.id}" end end @@ -49,7 +49,7 @@ describe Gitlab::UrlBuilder do url = described_class.build(note) - expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" + expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.full_path}/commit/#{note.commit_id}#note_#{note.id}" end end @@ -60,7 +60,7 @@ describe Gitlab::UrlBuilder do url = described_class.build(note) - expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}#note_#{note.id}" + expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.full_path}/issues/#{issue.iid}#note_#{note.id}" end end @@ -71,7 +71,7 @@ describe Gitlab::UrlBuilder do url = described_class.build(note) - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" + expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.full_path}/merge_requests/#{merge_request.iid}#note_#{note.id}" end end @@ -82,7 +82,7 @@ describe Gitlab::UrlBuilder do url = described_class.build(note) - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" + expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.full_path}/merge_requests/#{merge_request.iid}#note_#{note.id}" end end @@ -93,7 +93,7 @@ describe Gitlab::UrlBuilder do url = described_class.build(note) - expect(url).to eq "#{Settings.gitlab['url']}/#{project_snippet.project.path_with_namespace}/snippets/#{note.noteable_id}#note_#{note.id}" + expect(url).to eq "#{Settings.gitlab['url']}/#{project_snippet.project.full_path}/snippets/#{note.noteable_id}#note_#{note.id}" end end diff --git a/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb b/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb index 49e750a3f4d..dd634f2c024 100644 --- a/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb +++ b/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb @@ -4,7 +4,7 @@ require Rails.root.join('db', 'post_migrate', '20170502101023_cleanup_namespacel describe CleanupNamespacelessPendingDeleteProjects do before do # Stub after_save callbacks that will fail when Project has no namespace - allow_any_instance_of(Project).to receive(:ensure_dir_exist).and_return(nil) + allow_any_instance_of(Project).to receive(:ensure_storage_path_exist).and_return(nil) allow_any_instance_of(Project).to receive(:update_project_statistics).and_return(nil) end diff --git a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb index 5b633dd349b..cf2d5827306 100644 --- a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb +++ b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb @@ -20,7 +20,7 @@ describe MigrateProcessCommitWorkerJobs do .find_including_path(project.id) expect(migration_project[:path_with_namespace]) - .to eq(project.path_with_namespace) + .to eq(project.full_path) end end diff --git a/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb b/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb index 42109fd0743..6f7a730edff 100644 --- a/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb +++ b/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb @@ -44,7 +44,7 @@ describe TurnNestedGroupsIntoRegularGroupsForMysql do end it 'renames projects of the nested group' do - expect(updated_project.path_with_namespace) + expect(updated_project.full_path) .to eq("#{parent_group.name}-#{child_group.name}/#{updated_project.path}") end diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 2285c338599..595c54890ab 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -161,7 +161,7 @@ eos end it 'detects issues that this commit is marked as closing' do - ext_ref = "#{other_project.path_with_namespace}##{other_issue.iid}" + ext_ref = "#{other_project.full_path}##{other_issue.iid}" allow(commit).to receive_messages( safe_message: "Fixes ##{issue.iid} and #{ext_ref}", diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index b2dd02553c1..d9903b9b951 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -250,7 +250,7 @@ describe MergeRequest do end it 'returns a String reference with the full path' do - expect(merge_request.to_reference(full: true)).to eq(project.path_with_namespace + '!1') + expect(merge_request.to_reference(full: true)).to eq(project.full_path + '!1') end end diff --git a/spec/models/notification_setting_spec.rb b/spec/models/notification_setting_spec.rb index 07e296424ca..2a0d102d3fe 100644 --- a/spec/models/notification_setting_spec.rb +++ b/spec/models/notification_setting_spec.rb @@ -63,24 +63,20 @@ RSpec.describe NotificationSetting do end end - describe 'event_enabled?' do + describe '#event_enabled?' do before do subject.update!(user: create(:user)) end context 'for an event with a matching column name' do - before do - subject.update!(events: { new_note: true }.to_json) - end - it 'returns the value of the column' do - subject.update!(new_note: false) + subject.update!(new_note: true) - expect(subject.event_enabled?(:new_note)).to be(false) + expect(subject.event_enabled?(:new_note)).to be(true) end context 'when the column has a nil value' do - it 'returns the value from the events hash' do + it 'returns false' do expect(subject.event_enabled?(:new_note)).to be(false) end end diff --git a/spec/models/project_label_spec.rb b/spec/models/project_label_spec.rb index add7e85f388..c47f32d8d77 100644 --- a/spec/models/project_label_spec.rb +++ b/spec/models/project_label_spec.rb @@ -105,14 +105,14 @@ describe ProjectLabel do context 'using name' do it 'returns cross reference with label name' do expect(label.to_reference(project, format: :name)) - .to eq %Q(#{label.project.path_with_namespace}~"#{label.name}") + .to eq %Q(#{label.project.full_path}~"#{label.name}") end end context 'using id' do it 'returns cross reference with label id' do expect(label.to_reference(project, format: :id)) - .to eq %Q(#{label.project.path_with_namespace}~#{label.id}) + .to eq %Q(#{label.project.full_path}~#{label.id}) end end end diff --git a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb index d19dab8fd39..45cc78c8ff4 100644 --- a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb +++ b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb @@ -31,9 +31,9 @@ describe GitlabIssueTrackerService do end it 'gives the correct path' do - expect(service.project_url).to eq("http://#{Gitlab.config.gitlab.host}/gitlab/root/#{project.path_with_namespace}/issues") - expect(service.new_issue_url).to eq("http://#{Gitlab.config.gitlab.host}/gitlab/root/#{project.path_with_namespace}/issues/new") - expect(service.issue_url(432)).to eq("http://#{Gitlab.config.gitlab.host}/gitlab/root/#{project.path_with_namespace}/issues/432") + expect(service.project_url).to eq("http://#{Gitlab.config.gitlab.host}/gitlab/root/#{project.full_path}/issues") + expect(service.new_issue_url).to eq("http://#{Gitlab.config.gitlab.host}/gitlab/root/#{project.full_path}/issues/new") + expect(service.issue_url(432)).to eq("http://#{Gitlab.config.gitlab.host}/gitlab/root/#{project.full_path}/issues/432") end end @@ -43,9 +43,9 @@ describe GitlabIssueTrackerService do end it 'gives the correct path' do - expect(service.issue_tracker_path).to eq("/gitlab/root/#{project.path_with_namespace}/issues") - expect(service.new_issue_path).to eq("/gitlab/root/#{project.path_with_namespace}/issues/new") - expect(service.issue_path(432)).to eq("/gitlab/root/#{project.path_with_namespace}/issues/432") + expect(service.issue_tracker_path).to eq("/gitlab/root/#{project.full_path}/issues") + expect(service.new_issue_path).to eq("/gitlab/root/#{project.full_path}/issues/new") + expect(service.issue_path(432)).to eq("/gitlab/root/#{project.full_path}/issues/432") end end end diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 8f34b44930e..bc9374d6dbb 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -135,7 +135,7 @@ describe JiraService do body: hash_including( GlobalID: "GitLab", object: { - url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/#{merge_request.diff_head_sha}", + url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/#{merge_request.diff_head_sha}", title: "GitLab: Solved by commit #{merge_request.diff_head_sha}.", icon: { title: "GitLab", url16x16: "https://gitlab.com/favicon.ico" }, status: { resolved: true } @@ -159,7 +159,7 @@ describe JiraService do @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project)) expect(WebMock).to have_requested(:post, @comment_url).with( - body: /#{custom_base_url}\/#{project.path_with_namespace}\/commit\/#{merge_request.diff_head_sha}/ + body: /#{custom_base_url}\/#{project.full_path}\/commit\/#{merge_request.diff_head_sha}/ ).once end @@ -174,7 +174,7 @@ describe JiraService do @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project)) expect(WebMock).to have_requested(:post, @comment_url).with( - body: /#{Gitlab.config.gitlab.url}\/#{project.path_with_namespace}\/commit\/#{merge_request.diff_head_sha}/ + body: /#{Gitlab.config.gitlab.url}\/#{project.full_path}\/commit\/#{merge_request.diff_head_sha}/ ).once end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 473b7a88d61..29cc9d35fc9 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -306,6 +306,7 @@ describe Project do it { is_expected.to respond_to(:execute_hooks) } it { is_expected.to respond_to(:owner) } it { is_expected.to respond_to(:path_with_namespace) } + it { is_expected.to respond_to(:full_path) } end describe 'delegation' do @@ -460,7 +461,7 @@ describe Project do end it 'returns the address to create a new issue' do - address = "p+#{project.path_with_namespace}+#{user.incoming_email_token}@gl.ab" + address = "p+#{project.full_path}+#{user.incoming_email_token}@gl.ab" expect(project.new_issue_address(user)).to eq(address) end @@ -1155,6 +1156,33 @@ describe Project do end end + describe '#pages_url' do + let(:group) { create :group, name: group_name } + let(:project) { create :empty_project, namespace: group, name: project_name } + let(:domain) { 'Example.com' } + + subject { project.pages_url } + + before do + allow(Settings.pages).to receive(:host).and_return(domain) + allow(Gitlab.config.pages).to receive(:url).and_return('http://example.com') + end + + context 'group page' do + let(:group_name) { 'Group' } + let(:project_name) { 'group.example.com' } + + it { is_expected.to eq("http://group.example.com") } + end + + context 'project page' do + let(:group_name) { 'Group' } + let(:project_name) { 'Project' } + + it { is_expected.to eq("http://group.example.com/project") } + end + end + describe '.search' do let(:project) { create(:empty_project, description: 'kitten mittens') } @@ -1341,7 +1369,7 @@ describe Project do context 'using a regular repository' do it 'creates the repository' do expect(shell).to receive(:add_repository) - .with(project.repository_storage_path, project.path_with_namespace) + .with(project.repository_storage_path, project.disk_path) .and_return(true) expect(project.repository).to receive(:after_create) @@ -1351,7 +1379,7 @@ describe Project do it 'adds an error if the repository could not be created' do expect(shell).to receive(:add_repository) - .with(project.repository_storage_path, project.path_with_namespace) + .with(project.repository_storage_path, project.disk_path) .and_return(false) expect(project.repository).not_to receive(:after_create) @@ -1384,7 +1412,7 @@ describe Project do .and_return(false) allow(shell).to receive(:add_repository) - .with(project.repository_storage_path, project.path_with_namespace) + .with(project.repository_storage_path, project.disk_path) .and_return(true) expect(project).to receive(:create_repository).with(force: true) @@ -1408,7 +1436,7 @@ describe Project do .and_return(false) expect(shell).to receive(:add_repository) - .with(project.repository_storage_path, project.path_with_namespace) + .with(project.repository_storage_path, project.disk_path) .and_return(true) project.ensure_repository @@ -1572,7 +1600,7 @@ describe Project do before do allow_any_instance_of(Gitlab::Shell).to receive(:import_repository) - .with(project.repository_storage_path, project.path_with_namespace, project.import_url) + .with(project.repository_storage_path, project.disk_path, project.import_url) .and_return(true) expect_any_instance_of(Repository).to receive(:after_import) @@ -1710,7 +1738,7 @@ describe Project 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.full_path) + forked_from_project.disk_path, project.namespace.full_path) project.add_import_job end @@ -2238,19 +2266,43 @@ describe Project do end describe '#remove_private_deploy_keys' do - it 'removes the private deploy keys of a project' do - project = create(:empty_project) + let!(:project) { create(:empty_project) } + + context 'for a private deploy key' do + let!(:key) { create(:deploy_key, public: false) } + let!(:deploy_keys_project) { create(:deploy_keys_project, deploy_key: key, project: project) } + + context 'when the key is not linked to another project' do + it 'removes the key' do + project.remove_private_deploy_keys - private_key = create(:deploy_key, public: false) - public_key = create(:deploy_key, public: true) + expect(project.deploy_keys).not_to include(key) + end + end - create(:deploy_keys_project, deploy_key: private_key, project: project) - create(:deploy_keys_project, deploy_key: public_key, project: project) + context 'when the key is linked to another project' do + before do + another_project = create(:empty_project) + create(:deploy_keys_project, deploy_key: key, project: another_project) + end - project.remove_private_deploy_keys + it 'does not remove the key' do + project.remove_private_deploy_keys - expect(project.deploy_keys.where(public: false).any?).to eq(false) - expect(project.deploy_keys.where(public: true).any?).to eq(true) + expect(project.deploy_keys).to include(key) + end + end + end + + context 'for a public deploy key' do + let!(:key) { create(:deploy_key, public: true) } + let!(:deploy_keys_project) { create(:deploy_keys_project, deploy_key: key, project: project) } + + it 'does not remove the key' do + project.remove_private_deploy_keys + + expect(project.deploy_keys).to include(key) + end end end end diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb index c6ceb092810..484fcfc88a3 100644 --- a/spec/models/project_wiki_spec.rb +++ b/spec/models/project_wiki_spec.rb @@ -15,19 +15,23 @@ describe ProjectWiki do describe "#path_with_namespace" do it "returns the project path with namespace with the .wiki extension" do - expect(subject.path_with_namespace).to eq(project.path_with_namespace + ".wiki") + expect(subject.path_with_namespace).to eq(project.full_path + '.wiki') + end + + it 'returns the same value as #full_path' do + expect(subject.path_with_namespace).to eq(subject.full_path) end end describe '#web_url' do it 'returns the full web URL to the wiki' do - expect(subject.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/wikis/home") + expect(subject.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.full_path}/wikis/home") end end describe "#url_to_repo" do it "returns the correct ssh url to the repo" do - expect(subject.url_to_repo).to eq(gitlab_shell.url_to_repo(subject.path_with_namespace)) + expect(subject.url_to_repo).to eq(gitlab_shell.url_to_repo(subject.full_path)) end end @@ -41,7 +45,7 @@ describe ProjectWiki do let(:project) { create :empty_project } it 'returns the full http url to the repo' do - expected_url = "#{Gitlab.config.gitlab.url}/#{subject.path_with_namespace}.git" + expected_url = "#{Gitlab.config.gitlab.url}/#{subject.full_path}.git" expect(project_wiki.http_url_to_repo).to eq(expected_url) expect(project_wiki.http_url_to_repo).not_to include('@') @@ -50,7 +54,7 @@ describe ProjectWiki do describe "#wiki_base_path" do it "returns the wiki base path" do - wiki_base_path = "#{Gitlab.config.gitlab.relative_url_root}/#{project.path_with_namespace}/wikis" + wiki_base_path = "#{Gitlab.config.gitlab.relative_url_root}/#{project.full_path}/wikis" expect(subject.wiki_base_path).to eq(wiki_base_path) end @@ -77,7 +81,7 @@ describe ProjectWiki do allow_any_instance_of(Gitlab::Shell).to receive(:add_repository) do create_temp_repo("#{Rails.root}/tmp/test-git-base-path/non-existant.wiki.git") end - allow(project).to receive(:path_with_namespace).and_return("non-existant") + allow(project).to receive(:full_path).and_return("non-existant") end describe '#empty?' do @@ -269,7 +273,7 @@ describe ProjectWiki do describe '#create_repo!' do it 'creates a repository' do expect(subject).to receive(:init_repo) - .with(subject.path_with_namespace) + .with(subject.full_path) .and_return(true) expect(subject.repository).to receive(:after_create) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 07ed66e127a..0fd3a4d622a 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -962,7 +962,7 @@ describe Repository do end it 'returns false if no full path can be constructed' do - allow(repository).to receive(:path_with_namespace).and_return(nil) + allow(repository).to receive(:full_path).and_return(nil) expect(repository.exists?).to eq(false) end diff --git a/spec/requests/api/group_variables_spec.rb b/spec/requests/api/group_variables_spec.rb new file mode 100644 index 00000000000..402ea057cc5 --- /dev/null +++ b/spec/requests/api/group_variables_spec.rb @@ -0,0 +1,221 @@ +require 'spec_helper' + +describe API::GroupVariables do + let(:group) { create(:group) } + let(:user) { create(:user) } + + describe 'GET /groups/:id/variables' do + let!(:variable) { create(:ci_group_variable, group: group) } + + context 'authorized user with proper permissions' do + before do + group.add_master(user) + end + + it 'returns group variables' do + get api("/groups/#{group.id}/variables", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_a(Array) + end + end + + context 'authorized user with invalid permissions' do + it 'does not return group variables' do + get api("/groups/#{group.id}/variables", user) + + expect(response).to have_http_status(403) + end + end + + context 'unauthorized user' do + it 'does not return group variables' do + get api("/groups/#{group.id}/variables") + + expect(response).to have_http_status(401) + end + end + end + + describe 'GET /groups/:id/variables/:key' do + let!(:variable) { create(:ci_group_variable, group: group) } + + context 'authorized user with proper permissions' do + before do + group.add_master(user) + end + + it 'returns group variable details' do + get api("/groups/#{group.id}/variables/#{variable.key}", user) + + expect(response).to have_http_status(200) + expect(json_response['value']).to eq(variable.value) + expect(json_response['protected']).to eq(variable.protected?) + end + + it 'responds with 404 Not Found if requesting non-existing variable' do + get api("/groups/#{group.id}/variables/non_existing_variable", user) + + expect(response).to have_http_status(404) + end + end + + context 'authorized user with invalid permissions' do + it 'does not return group variable details' do + get api("/groups/#{group.id}/variables/#{variable.key}", user) + + expect(response).to have_http_status(403) + end + end + + context 'unauthorized user' do + it 'does not return group variable details' do + get api("/groups/#{group.id}/variables/#{variable.key}") + + expect(response).to have_http_status(401) + end + end + end + + describe 'POST /groups/:id/variables' do + context 'authorized user with proper permissions' do + let!(:variable) { create(:ci_group_variable, group: group) } + + before do + group.add_master(user) + end + + it 'creates variable' do + expect do + post api("/groups/#{group.id}/variables", user), key: 'TEST_VARIABLE_2', value: 'VALUE_2', protected: true + end.to change{group.variables.count}.by(1) + + expect(response).to have_http_status(201) + expect(json_response['key']).to eq('TEST_VARIABLE_2') + expect(json_response['value']).to eq('VALUE_2') + expect(json_response['protected']).to be_truthy + end + + it 'creates variable with optional attributes' do + expect do + post api("/groups/#{group.id}/variables", user), key: 'TEST_VARIABLE_2', value: 'VALUE_2' + end.to change{group.variables.count}.by(1) + + expect(response).to have_http_status(201) + expect(json_response['key']).to eq('TEST_VARIABLE_2') + expect(json_response['value']).to eq('VALUE_2') + expect(json_response['protected']).to be_falsey + end + + it 'does not allow to duplicate variable key' do + expect do + post api("/groups/#{group.id}/variables", user), key: variable.key, value: 'VALUE_2' + end.to change{group.variables.count}.by(0) + + expect(response).to have_http_status(400) + end + end + + context 'authorized user with invalid permissions' do + it 'does not create variable' do + post api("/groups/#{group.id}/variables", user) + + expect(response).to have_http_status(403) + end + end + + context 'unauthorized user' do + it 'does not create variable' do + post api("/groups/#{group.id}/variables") + + expect(response).to have_http_status(401) + end + end + end + + describe 'PUT /groups/:id/variables/:key' do + let!(:variable) { create(:ci_group_variable, group: group) } + + context 'authorized user with proper permissions' do + before do + group.add_master(user) + end + + it 'updates variable data' do + initial_variable = group.variables.first + value_before = initial_variable.value + + put api("/groups/#{group.id}/variables/#{variable.key}", user), value: 'VALUE_1_UP', protected: true + + updated_variable = group.variables.first + + expect(response).to have_http_status(200) + expect(value_before).to eq(variable.value) + expect(updated_variable.value).to eq('VALUE_1_UP') + expect(updated_variable).to be_protected + end + + it 'responds with 404 Not Found if requesting non-existing variable' do + put api("/groups/#{group.id}/variables/non_existing_variable", user) + + expect(response).to have_http_status(404) + end + end + + context 'authorized user with invalid permissions' do + it 'does not update variable' do + put api("/groups/#{group.id}/variables/#{variable.key}", user) + + expect(response).to have_http_status(403) + end + end + + context 'unauthorized user' do + it 'does not update variable' do + put api("/groups/#{group.id}/variables/#{variable.key}") + + expect(response).to have_http_status(401) + end + end + end + + describe 'DELETE /groups/:id/variables/:key' do + let!(:variable) { create(:ci_group_variable, group: group) } + + context 'authorized user with proper permissions' do + before do + group.add_master(user) + end + + it 'deletes variable' do + expect do + delete api("/groups/#{group.id}/variables/#{variable.key}", user) + + expect(response).to have_http_status(204) + end.to change{group.variables.count}.by(-1) + end + + it 'responds with 404 Not Found if requesting non-existing variable' do + delete api("/groups/#{group.id}/variables/non_existing_variable", user) + + expect(response).to have_http_status(404) + end + end + + context 'authorized user with invalid permissions' do + it 'does not delete variable' do + delete api("/groups/#{group.id}/variables/#{variable.key}", user) + + expect(response).to have_http_status(403) + end + end + + context 'unauthorized user' do + it 'does not delete variable' do + delete api("/groups/#{group.id}/variables/#{variable.key}") + + expect(response).to have_http_status(401) + end + end + end +end diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb index ce9b9ac1eb3..fb312d3cb7d 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal_spec.rb @@ -301,7 +301,7 @@ describe API::Internal do context 'project as /namespace/project' do it do - pull(key, project_with_repo_path('/' + project.path_with_namespace)) + pull(key, project_with_repo_path('/' + project.full_path)) expect(response).to have_http_status(200) expect(json_response["status"]).to be_truthy @@ -312,7 +312,7 @@ describe API::Internal do context 'project as namespace/project' do it do - pull(key, project_with_repo_path(project.path_with_namespace)) + pull(key, project_with_repo_path(project.full_path)) expect(response).to have_http_status(200) expect(json_response["status"]).to be_truthy diff --git a/spec/requests/api/notification_settings_spec.rb b/spec/requests/api/notification_settings_spec.rb index f619b7e6eaf..d0e7a82e607 100644 --- a/spec/requests/api/notification_settings_spec.rb +++ b/spec/requests/api/notification_settings_spec.rb @@ -72,8 +72,8 @@ describe API::NotificationSettings do expect(response).to have_http_status(200) expect(json_response['level']).to eq(user.reload.notification_settings_for(project).level) - expect(json_response['events']['new_note']).to eq(true) - expect(json_response['events']['new_issue']).to eq(false) + expect(json_response['events']['new_note']).to be_truthy + expect(json_response['events']['new_issue']).to be_falsey end end diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 66b165b438b..2dc7be22f8f 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -16,38 +16,44 @@ describe API::Users do it "returns authorization error when the `username` parameter is not passed" do get api("/users") - expect(response).to have_http_status(403) + expect(response).to have_gitlab_http_status(403) end it "returns the user when a valid `username` parameter is passed" do - user = create(:user) - get api("/users"), username: user.username - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(1) expect(json_response[0]['id']).to eq(user.id) expect(json_response[0]['username']).to eq(user.username) end - it "returns authorization error when the `username` parameter refers to an inaccessible user" do - user = create(:user) - - stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) - - get api("/users"), username: user.username - - expect(response).to have_http_status(403) - end - it "returns an empty response when an invalid `username` parameter is passed" do get api("/users"), username: 'invalid' - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(0) end + + context "when public level is restricted" do + before do + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) + end + + it "returns authorization error when the `username` parameter refers to an inaccessible user" do + get api("/users"), username: user.username + + expect(response).to have_gitlab_http_status(403) + end + + it "returns authorization error when the `username` parameter is not passed" do + get api("/users") + + expect(response).to have_gitlab_http_status(403) + end + end end context "when authenticated" do @@ -58,10 +64,10 @@ describe API::Users do end context 'when authenticate as a regular user' do - it "renders 403" do + it "renders 200" do get api("/users", user) - expect(response).to have_gitlab_http_status(403) + expect(response).to have_gitlab_http_status(200) end end diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index d4a3e8b13e1..d5c16d8f601 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -123,7 +123,7 @@ describe 'Git HTTP requests' do context "when requesting the Wiki" do let(:wiki) { ProjectWiki.new(project) } - let(:path) { "/#{wiki.repository.path_with_namespace}.git" } + let(:path) { "/#{wiki.repository.full_path}.git" } context "when the project is public" do let(:project) { create(:project, :repository, :public, :wiki_enabled) } @@ -139,7 +139,7 @@ describe 'Git HTTP requests' do download(path) do |response| json_body = ActiveSupport::JSON.decode(response.body) - expect(json_body['RepoPath']).to include(wiki.repository.path_with_namespace) + expect(json_body['RepoPath']).to include(wiki.repository.full_path) end end end @@ -222,7 +222,7 @@ describe 'Git HTTP requests' do end context "when the project exists" do - let(:path) { "#{project.path_with_namespace}.git" } + let(:path) { "#{project.full_path}.git" } context "when the project is public" do let(:project) { create(:project, :repository, :public) } @@ -286,7 +286,7 @@ describe 'Git HTTP requests' do context 'when the request is not from gitlab-workhorse' do it 'raises an exception' do expect do - get("/#{project.path_with_namespace}.git/info/refs?service=git-upload-pack") + get("/#{project.full_path}.git/info/refs?service=git-upload-pack") end.to raise_error(JWT::DecodeError) end end @@ -294,7 +294,7 @@ describe 'Git HTTP requests' do context 'when the repo is public' do context 'but the repo is disabled' do let(:project) { create(:project, :public, :repository, :repository_disabled) } - let(:path) { "#{project.path_with_namespace}.git" } + let(:path) { "#{project.full_path}.git" } let(:env) { {} } it_behaves_like 'pulls require Basic HTTP Authentication' @@ -303,7 +303,7 @@ describe 'Git HTTP requests' do context 'but the repo is enabled' do let(:project) { create(:project, :public, :repository, :repository_enabled) } - let(:path) { "#{project.path_with_namespace}.git" } + let(:path) { "#{project.full_path}.git" } let(:env) { {} } it_behaves_like 'pulls are allowed' @@ -421,7 +421,7 @@ describe 'Git HTTP requests' do @token = Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: "api") end - let(:path) { "#{project.path_with_namespace}.git" } + let(:path) { "#{project.full_path}.git" } let(:env) { { user: 'oauth2', password: @token.token } } it_behaves_like 'pulls are allowed' @@ -431,7 +431,7 @@ describe 'Git HTTP requests' do context 'when user has 2FA enabled' do let(:user) { create(:user, :two_factor) } let(:access_token) { create(:personal_access_token, user: user) } - let(:path) { "#{project.path_with_namespace}.git" } + let(:path) { "#{project.full_path}.git" } before do project.team << [user, :master] @@ -580,7 +580,7 @@ describe 'Git HTTP requests' do end context 'when build created by system is authenticated' do - let(:path) { "#{project.path_with_namespace}.git" } + let(:path) { "#{project.full_path}.git" } let(:env) { { user: 'gitlab-ci-token', password: build.token } } it_behaves_like 'pulls are allowed' @@ -602,7 +602,7 @@ describe 'Git HTTP requests' do # We are "authenticated" as CI using a valid token here. But we are # not authorized to see any other project, so return "not found". it "rejects pulls for other project with 404 Not Found" do - clone_get("#{other_project.path_with_namespace}.git", env) + clone_get("#{other_project.full_path}.git", env) expect(response).to have_http_status(:not_found) expect(response.body).to eq(git_access_error(:project_not_found)) @@ -616,7 +616,7 @@ describe 'Git HTTP requests' do end shared_examples 'can download code only' do - let(:path) { "#{project.path_with_namespace}.git" } + let(:path) { "#{project.full_path}.git" } let(:env) { { user: 'gitlab-ci-token', password: build.token } } it_behaves_like 'pulls are allowed' @@ -646,7 +646,7 @@ describe 'Git HTTP requests' do it_behaves_like 'can download code only' it 'downloads from other project get status 403' do - clone_get "#{other_project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token + clone_get "#{other_project.full_path}.git", user: 'gitlab-ci-token', password: build.token expect(response).to have_http_status(:forbidden) end @@ -658,7 +658,7 @@ describe 'Git HTTP requests' do it_behaves_like 'can download code only' it 'downloads from other project get status 404' do - clone_get "#{other_project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token + clone_get "#{other_project.full_path}.git", user: 'gitlab-ci-token', password: build.token expect(response).to have_http_status(:not_found) end @@ -671,7 +671,7 @@ describe 'Git HTTP requests' do let(:project) { create(:project, :repository, :public, path: 'project.git-project') } context "GET info/refs" do - let(:path) { "/#{project.path_with_namespace}/info/refs" } + let(:path) { "/#{project.full_path}/info/refs" } context "when no params are added" do before do @@ -679,7 +679,7 @@ describe 'Git HTTP requests' do end it "redirects to the .git suffix version" do - expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs") + expect(response).to redirect_to("/#{project.full_path}.git/info/refs") end end @@ -691,7 +691,7 @@ describe 'Git HTTP requests' do end it "redirects to the .git suffix version" do - expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs?service=#{params[:service]}") + expect(response).to redirect_to("/#{project.full_path}.git/info/refs?service=#{params[:service]}") end end @@ -703,7 +703,7 @@ describe 'Git HTTP requests' do end it "redirects to the .git suffix version" do - expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs?service=#{params[:service]}") + expect(response).to redirect_to("/#{project.full_path}.git/info/refs?service=#{params[:service]}") end end @@ -722,13 +722,13 @@ describe 'Git HTTP requests' do context "POST git-upload-pack" do it "fails to find a route" do - expect { clone_post(project.path_with_namespace) }.to raise_error(ActionController::RoutingError) + expect { clone_post(project.full_path) }.to raise_error(ActionController::RoutingError) end end context "POST git-receive-pack" do it "fails to find a route" do - expect { push_post(project.path_with_namespace) }.to raise_error(ActionController::RoutingError) + expect { push_post(project.full_path) }.to raise_error(ActionController::RoutingError) end end end @@ -744,7 +744,7 @@ describe 'Git HTTP requests' do Blob.decorate(Gitlab::Git::Blob.find(project.repository, 'master', 'bar/branch-test.txt'), project) end - get "/#{project.path_with_namespace}/blob/master/info/refs" + get "/#{project.full_path}/blob/master/info/refs" end it "returns the file" do @@ -754,7 +754,7 @@ describe 'Git HTTP requests' do context "when the file does not exist" do before do - get "/#{project.path_with_namespace}/blob/master/info/refs" + get "/#{project.full_path}/blob/master/info/refs" end it "returns not found" do diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb index 697b150ab34..4f1a90750b3 100644 --- a/spec/requests/lfs_http_spec.rb +++ b/spec/requests/lfs_http_spec.rb @@ -701,7 +701,7 @@ describe 'Git LFS API and storage' do expect(json_response['objects']).to be_kind_of(Array) expect(json_response['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") expect(json_response['objects'].first['size']).to eq(1575078) - expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") + expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.full_path}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") expect(json_response['objects'].first['actions']['upload']['header']).to eq('Authorization' => authorization) end end diff --git a/spec/requests/request_profiler_spec.rb b/spec/requests/request_profiler_spec.rb index 51fbfecec4b..9afeb2983b0 100644 --- a/spec/requests/request_profiler_spec.rb +++ b/spec/requests/request_profiler_spec.rb @@ -15,7 +15,7 @@ describe 'Request Profiler' do it 'creates a profile of the request' do project = create(:project, namespace: user.namespace) time = Time.now - path = "/#{project.path_with_namespace}" + path = "/#{project.full_path}" Timecop.freeze(time) do get path, nil, 'X-Profile-Token' => Gitlab::RequestProfiler.profile_token diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb index 66a8a93b168..0ae839ce0b3 100644 --- a/spec/services/auth/container_registry_authentication_service_spec.rb +++ b/spec/services/auth/container_registry_authentication_service_spec.rb @@ -46,7 +46,7 @@ describe Auth::ContainerRegistryAuthenticationService do shared_examples 'an accessible' do let(:access) do [{ 'type' => 'repository', - 'name' => project.path_with_namespace, + 'name' => project.full_path, 'actions' => actions }] end @@ -97,7 +97,7 @@ describe Auth::ContainerRegistryAuthenticationService do describe '#full_access_token' do let(:project) { create(:empty_project) } - let(:token) { described_class.full_access_token(project.path_with_namespace) } + let(:token) { described_class.full_access_token(project.full_path) } subject { { token: token } } @@ -124,7 +124,7 @@ describe Auth::ContainerRegistryAuthenticationService do end let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:push" } + { scope: "repository:#{project.full_path}:push" } end it_behaves_like 'a pushable' @@ -138,7 +138,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'when pulling from root level repository' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull" } + { scope: "repository:#{project.full_path}:pull" } end it_behaves_like 'a pullable' @@ -152,7 +152,7 @@ describe Auth::ContainerRegistryAuthenticationService do end let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:push,pull" } + { scope: "repository:#{project.full_path}:push,pull" } end it_behaves_like 'a pullable' @@ -165,7 +165,7 @@ describe Auth::ContainerRegistryAuthenticationService do end let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull,push" } + { scope: "repository:#{project.full_path}:pull,push" } end it_behaves_like 'an inaccessible' @@ -178,7 +178,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'allow anyone to pull images' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull" } + { scope: "repository:#{project.full_path}:pull" } end it_behaves_like 'a pullable' @@ -187,7 +187,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'disallow anyone to push images' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:push" } + { scope: "repository:#{project.full_path}:push" } end it_behaves_like 'an inaccessible' @@ -210,7 +210,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'for internal user' do context 'allow anyone to pull images' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull" } + { scope: "repository:#{project.full_path}:pull" } end it_behaves_like 'a pullable' @@ -219,7 +219,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'disallow anyone to push images' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:push" } + { scope: "repository:#{project.full_path}:push" } end it_behaves_like 'an inaccessible' @@ -230,7 +230,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'for external user' do let(:current_user) { create(:user, external: true) } let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull,push" } + { scope: "repository:#{project.full_path}:pull,push" } end it_behaves_like 'an inaccessible' @@ -255,7 +255,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'allow to pull and push images' do let(:current_params) do - { scope: "repository:#{current_project.path_with_namespace}:pull,push" } + { scope: "repository:#{current_project.full_path}:pull,push" } end it_behaves_like 'a pullable and pushable' do @@ -270,7 +270,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'for other projects' do context 'when pulling' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull" } + { scope: "repository:#{project.full_path}:pull" } end context 'allow for public' do @@ -337,7 +337,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'when pushing' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:push" } + { scope: "repository:#{project.full_path}:push" } end context 'disallow for all' do @@ -371,7 +371,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'disallow when pulling' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull" } + { scope: "repository:#{project.full_path}:pull" } end it_behaves_like 'an inaccessible' @@ -399,7 +399,7 @@ describe Auth::ContainerRegistryAuthenticationService do let(:project) { create(:empty_project, :private) } let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull" } + { scope: "repository:#{project.full_path}:pull" } end it_behaves_like 'a forbidden' @@ -410,7 +410,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'when pulling and pushing' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull,push" } + { scope: "repository:#{project.full_path}:pull,push" } end it_behaves_like 'a pullable' @@ -419,7 +419,7 @@ describe Auth::ContainerRegistryAuthenticationService do context 'when pushing' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:push" } + { scope: "repository:#{project.full_path}:push" } end it_behaves_like 'a forbidden' diff --git a/spec/services/ci/pipeline_trigger_service_spec.rb b/spec/services/ci/pipeline_trigger_service_spec.rb index 945a2fe1a6b..e9188a83b89 100644 --- a/spec/services/ci/pipeline_trigger_service_spec.rb +++ b/spec/services/ci/pipeline_trigger_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::PipelineTriggerService, services: true do +describe Ci::PipelineTriggerService do let(:project) { create(:project, :repository) } before do diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index 3fb677b65be..82724ccd281 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -453,7 +453,7 @@ describe GitPushService, services: true do let(:message) { "this is some work.\n\ncloses JIRA-1" } let(:comment_body) do { - body: "Issue solved with [#{closing_commit.id}|http://#{Gitlab.config.gitlab.host}/#{project.path_with_namespace}/commit/#{closing_commit.id}]." + body: "Issue solved with [#{closing_commit.id}|http://#{Gitlab.config.gitlab.host}/#{project.full_path}/commit/#{closing_commit.id}]." }.to_json end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 49d6fc7853f..5b69426cbaa 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -694,17 +694,6 @@ describe NotificationService do let!(:subscriber_to_label_1) { create(:user) { |u| label_1.toggle_subscription(u, project) } } let!(:subscriber_to_label_2) { create(:user) { |u| label_2.toggle_subscription(u, project) } } - it "emails subscribers of the issue's added labels only" do - notification.relabeled_issue(issue, [group_label_2, label_2], @u_disabled) - - should_not_email(subscriber_to_label_1) - should_not_email(subscriber_to_group_label_1) - should_not_email(subscriber_to_group_label_2_on_another_project) - should_email(subscriber_1_to_group_label_2) - should_email(subscriber_2_to_group_label_2) - should_email(subscriber_to_label_2) - end - it "emails the current user if they've opted into notifications about their activity" do subscriber_to_label_2.notified_of_own_activity = true notification.relabeled_issue(issue, [group_label_2, label_2], subscriber_to_label_2) @@ -721,6 +710,12 @@ describe NotificationService do it "doesn't send email to anyone but subscribers of the given labels" do notification.relabeled_issue(issue, [group_label_2, label_2], @u_disabled) + should_not_email(subscriber_to_label_1) + should_not_email(subscriber_to_group_label_1) + should_not_email(subscriber_to_group_label_2_on_another_project) + should_email(subscriber_1_to_group_label_2) + should_email(subscriber_2_to_group_label_2) + should_email(subscriber_to_label_2) should_not_email(issue.assignees.first) should_not_email(issue.author) should_not_email(@u_watcher) @@ -730,12 +725,6 @@ describe NotificationService do should_not_email(@watcher_and_subscriber) should_not_email(@unsubscriber) should_not_email(@u_participating) - should_not_email(subscriber_to_label_1) - should_not_email(subscriber_to_group_label_1) - should_not_email(subscriber_to_group_label_2_on_another_project) - should_email(subscriber_1_to_group_label_2) - should_email(subscriber_2_to_group_label_2) - should_email(subscriber_to_label_2) end context 'confidential issues' do @@ -878,11 +867,6 @@ describe NotificationService do end describe '#new_merge_request' do - before do - update_custom_notification(:new_merge_request, @u_guest_custom, resource: project) - update_custom_notification(:new_merge_request, @u_custom_global) - end - it do notification.new_merge_request(merge_request, @u_disabled) @@ -1008,7 +992,7 @@ describe NotificationService do let!(:subscriber_to_label_1) { create(:user) { |u| label_1.toggle_subscription(u, project) } } let!(:subscriber_to_label_2) { create(:user) { |u| label_2.toggle_subscription(u, project) } } - it "emails subscribers of the merge request's added labels only" do + it "doesn't send email to anyone but subscribers of the given labels" do notification.relabeled_merge_request(merge_request, [group_label_2, label_2], @u_disabled) should_not_email(subscriber_to_label_1) @@ -1017,11 +1001,6 @@ describe NotificationService do should_email(subscriber_1_to_group_label_2) should_email(subscriber_2_to_group_label_2) should_email(subscriber_to_label_2) - end - - it "doesn't send email to anyone but subscribers of the given labels" do - notification.relabeled_merge_request(merge_request, [group_label_2, label_2], @u_disabled) - should_not_email(merge_request.assignee) should_not_email(merge_request.author) should_not_email(@u_watcher) @@ -1031,12 +1010,6 @@ describe NotificationService do should_not_email(@unsubscriber) should_not_email(@u_participating) should_not_email(@u_lazy_participant) - should_not_email(subscriber_to_label_1) - should_not_email(subscriber_to_group_label_1) - should_not_email(subscriber_to_group_label_2_on_another_project) - should_email(subscriber_1_to_group_label_2) - should_email(subscriber_2_to_group_label_2) - should_email(subscriber_to_label_2) end end @@ -1081,12 +1054,12 @@ describe NotificationService do should_email(merge_request.assignee) should_email(@u_watcher) + should_email(@u_guest_watcher) + should_email(@u_guest_custom) + should_email(@u_custom_global) should_email(@u_participant_mentioned) should_email(@subscriber) should_email(@watcher_and_subscriber) - should_email(@u_guest_watcher) - should_email(@u_custom_global) - should_email(@u_guest_custom) should_not_email(@unsubscriber) should_not_email(@u_participating) should_not_email(@u_disabled) diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb index 67fe610f92a..c2a6cafa4c8 100644 --- a/spec/services/projects/import_service_spec.rb +++ b/spec/services/projects/import_service_spec.rb @@ -26,7 +26,7 @@ describe Projects::ImportService do result = subject.execute expect(result[:status]).to eq :error - expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.path_with_namespace} - The repository could not be created." + expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - The repository could not be created." end end @@ -52,7 +52,7 @@ describe Projects::ImportService do result = subject.execute expect(result[:status]).to eq :error - expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.path_with_namespace} - Failed to import the repository" + expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - Failed to import the repository" end it 'does not remove the GitHub remote' do @@ -86,7 +86,7 @@ describe Projects::ImportService do result = subject.execute expect(result[:status]).to eq :error - expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.path_with_namespace} - Failed to import the repository" + expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - Failed to import the repository" end end end @@ -129,7 +129,7 @@ describe Projects::ImportService do result = subject.execute expect(result[:status]).to eq :error - expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.path_with_namespace} - The remote data could not be imported." + expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - The remote data could not be imported." end it 'fails if importer raise an error' do @@ -139,7 +139,7 @@ describe Projects::ImportService do result = subject.execute expect(result[:status]).to eq :error - expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.path_with_namespace} - Github: failed to connect API" + expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - Github: failed to connect API" end it 'expires content cache after error' do diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index ae32e85b2a7..2cb60cbcfc4 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -37,18 +37,18 @@ describe Projects::TransferService do end it 'executes system hooks' do - expect_any_instance_of(described_class).to receive(:execute_system_hooks) - - transfer_project(project, user, group) + transfer_project(project, user, group) do |service| + expect(service).to receive(:execute_system_hooks) + end end end context 'when transfer fails' do let!(:original_path) { project_path(project) } - def attempt_project_transfer + def attempt_project_transfer(&block) expect do - transfer_project(project, user, group) + transfer_project(project, user, group, &block) end.to raise_error(ActiveRecord::ActiveRecordError) end @@ -59,7 +59,7 @@ describe Projects::TransferService do end def project_path(project) - File.join(project.repository_storage_path, "#{project.path_with_namespace}.git") + File.join(project.repository_storage_path, "#{project.disk_path}.git") end def current_path @@ -80,9 +80,9 @@ describe Projects::TransferService do end it "doesn't run system hooks" do - expect_any_instance_of(described_class).not_to receive(:execute_system_hooks) - - attempt_project_transfer + attempt_project_transfer do |service| + expect(service).not_to receive(:execute_system_hooks) + end end end @@ -120,7 +120,11 @@ describe Projects::TransferService do end def transfer_project(project, user, new_namespace) - Projects::TransferService.new(project, user).execute(new_namespace) + service = Projects::TransferService.new(project, user) + + yield(service) if block_given? + + service.execute(new_namespace) end context 'visibility level' do diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb index 42a4a2591ea..92bde2c92bf 100644 --- a/spec/services/quick_actions/interpret_service_spec.rb +++ b/spec/services/quick_actions/interpret_service_spec.rb @@ -9,13 +9,13 @@ describe QuickActions::InterpretService do let(:inprogress) { create(:label, project: project, title: 'In Progress') } let(:bug) { create(:label, project: project, title: 'Bug') } let(:note) { build(:note, commit_id: merge_request.diff_head_sha) } + let(:service) { described_class.new(project, developer) } before do project.team << [developer, :developer] end describe '#execute' do - let(:service) { described_class.new(project, developer) } let(:merge_request) { create(:merge_request, source_project: project) } shared_examples 'reopen command' do @@ -270,6 +270,22 @@ describe QuickActions::InterpretService do end end + shared_examples 'shrug command' do + it 'appends ¯\_(ツ)_/¯ to the comment' do + new_content, _ = service.execute(content, issuable) + + expect(new_content).to end_with(described_class::SHRUG) + end + end + + shared_examples 'tableflip command' do + it 'appends (╯°□°)╯︵ ┻━┻ to the comment' do + new_content, _ = service.execute(content, issuable) + + expect(new_content).to end_with(described_class::TABLEFLIP) + end + end + it_behaves_like 'reopen command' do let(:content) { '/reopen' } let(:issuable) { issue } @@ -775,6 +791,30 @@ describe QuickActions::InterpretService do end end + context '/shrug command' do + it_behaves_like 'shrug command' do + let(:content) { '/shrug people are people' } + let(:issuable) { issue } + end + + it_behaves_like 'shrug command' do + let(:content) { '/shrug' } + let(:issuable) { issue } + end + end + + context '/tableflip command' do + it_behaves_like 'tableflip command' do + let(:content) { '/tableflip curse your sudden but enviable betrayal' } + let(:issuable) { issue } + end + + it_behaves_like 'tableflip command' do + let(:content) { '/tableflip' } + let(:issuable) { issue } + end + end + context '/target_branch command' do let(:non_empty_project) { create(:project, :repository) } let(:another_merge_request) { create(:merge_request, author: developer, source_project: non_empty_project) } diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index cb59493c343..5c20263a532 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -667,7 +667,7 @@ describe SystemNoteService do end it 'mentions referenced project' do - expect(subject.note).to include new_project.path_with_namespace + expect(subject.note).to include new_project.full_path end end @@ -873,7 +873,7 @@ describe SystemNoteService do describe "existing reference" do before do allow(JIRA::Resource::Remotelink).to receive(:all).and_return([]) - message = "[#{author.name}|http://localhost/#{author.username}] mentioned this issue in [a commit of #{project.path_with_namespace}|http://localhost/#{project.path_with_namespace}/commit/#{commit.id}]:\n'#{commit.title.chomp}'" + message = "[#{author.name}|http://localhost/#{author.username}] mentioned this issue in [a commit of #{project.full_path}|http://localhost/#{project.full_path}/commit/#{commit.id}]:\n'#{commit.title.chomp}'" allow_any_instance_of(JIRA::Resource::Issue).to receive(:comments).and_return([OpenStruct.new(body: message)]) end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 85335643921..609998d6e9c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -129,10 +129,14 @@ RSpec.configure do |config| config.before(:example, :migration) do ActiveRecord::Migrator .migrate(migrations_paths, previous_migration.version) + + ActiveRecord::Base.descendants.each(&:reset_column_information) end config.after(:example, :migration) do ActiveRecord::Migrator.migrate(migrations_paths) + + ActiveRecord::Base.descendants.each(&:reset_column_information) end config.around(:each, :nested_groups) do |example| diff --git a/spec/support/notify_shared_examples.rb b/spec/support/notify_shared_examples.rb index 76411065265..70799bce721 100644 --- a/spec/support/notify_shared_examples.rb +++ b/spec/support/notify_shared_examples.rb @@ -50,7 +50,7 @@ shared_examples 'an email with X-GitLab headers containing project details' do aggregate_failures do is_expected.to have_header('X-GitLab-Project', /#{project.name}/) is_expected.to have_header('X-GitLab-Project-Id', /#{project.id}/) - is_expected.to have_header('X-GitLab-Project-Path', /#{project.path_with_namespace}/) + is_expected.to have_header('X-GitLab-Project-Path', /#{project.full_path}/) end end end diff --git a/spec/support/project_hook_data_shared_example.rb b/spec/support/project_hook_data_shared_example.rb index 7dbaa6a6459..1eb405d4be8 100644 --- a/spec/support/project_hook_data_shared_example.rb +++ b/spec/support/project_hook_data_shared_example.rb @@ -8,7 +8,7 @@ RSpec.shared_examples 'project hook data with deprecateds' do |project_key: :pro expect(data[project_key][:git_ssh_url]).to eq(project.ssh_url_to_repo) expect(data[project_key][:namespace]).to eq(project.namespace.name) expect(data[project_key][:visibility_level]).to eq(project.visibility_level) - expect(data[project_key][:path_with_namespace]).to eq(project.path_with_namespace) + expect(data[project_key][:path_with_namespace]).to eq(project.full_path) expect(data[project_key][:default_branch]).to eq(project.default_branch) expect(data[project_key][:homepage]).to eq(project.web_url) expect(data[project_key][:url]).to eq(project.url_to_repo) @@ -27,7 +27,7 @@ RSpec.shared_examples 'project hook data' do |project_key: :project| expect(data[project_key][:git_ssh_url]).to eq(project.ssh_url_to_repo) expect(data[project_key][:namespace]).to eq(project.namespace.name) expect(data[project_key][:visibility_level]).to eq(project.visibility_level) - expect(data[project_key][:path_with_namespace]).to eq(project.path_with_namespace) + expect(data[project_key][:path_with_namespace]).to eq(project.full_path) expect(data[project_key][:default_branch]).to eq(project.default_branch) end end diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 86f9568c12e..f0603dfadde 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -144,10 +144,13 @@ module TestEnv end def start_gitaly(gitaly_dir) - gitaly_exec = File.join(gitaly_dir, 'gitaly') - gitaly_config = File.join(gitaly_dir, 'config.toml') - log_file = Rails.root.join('log/gitaly-test.log').to_s - @gitaly_pid = Bundler.with_original_env { spawn(gitaly_exec, gitaly_config, [:out, :err] => log_file) } + if ENV['CI'].present? + # Gitaly has been spawned outside this process already + return + end + + spawn_script = Rails.root.join('scripts/gitaly-test-spawn').to_s + @gitaly_pid = Bundler.with_original_env { IO.popen([spawn_script], &:read).to_i } end def stop_gitaly diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 71580a788d0..fae92451b46 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -117,7 +117,7 @@ describe 'gitlab:app namespace rake task' do describe 'backup creation and deletion using custom_hooks' do let(:project) { create(:project, :repository) } - let(:user_backup_path) { "repositories/#{project.path_with_namespace}" } + let(:user_backup_path) { "repositories/#{project.disk_path}" } before(:each) do @origin_cd = Dir.pwd @@ -261,8 +261,8 @@ describe 'gitlab:app namespace rake task' do %W{tar -tvf #{@backup_tar} repositories} ) expect(exit_status).to eq(0) - expect(tar_contents).to match("repositories/#{project_a.path_with_namespace}.bundle") - expect(tar_contents).to match("repositories/#{project_b.path_with_namespace}.bundle") + expect(tar_contents).to match("repositories/#{project_a.disk_path}.bundle") + expect(tar_contents).to match("repositories/#{project_b.disk_path}.bundle") end end end # backup_create task diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb index d42d2423f15..695231c7d15 100644 --- a/spec/tasks/gitlab/gitaly_rake_spec.rb +++ b/spec/tasks/gitlab/gitaly_rake_spec.rb @@ -41,6 +41,16 @@ describe 'gitlab:gitaly namespace rake task' do end describe 'gmake/make' do + let(:command_preamble) { %w[/usr/bin/env -u BUNDLE_GEMFILE] } + + before(:all) do + @old_env_ci = ENV.delete('CI') + end + + after(:all) do + ENV['CI'] = @old_env_ci if @old_env_ci + end + before do FileUtils.mkdir_p(clone_path) expect(Dir).to receive(:chdir).with(clone_path).and_call_original @@ -49,12 +59,12 @@ describe 'gitlab:gitaly namespace rake task' do context 'gmake is available' do before do expect_any_instance_of(Object).to receive(:checkout_or_clone_version) - allow_any_instance_of(Object).to receive(:run_command!).with(['gmake']).and_return(true) + allow_any_instance_of(Object).to receive(:run_command!).with(command_preamble + ['gmake']).and_return(true) end it 'calls gmake in the gitaly directory' do expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['/usr/bin/gmake', 0]) - expect_any_instance_of(Object).to receive(:run_command!).with(['gmake']).and_return(true) + expect_any_instance_of(Object).to receive(:run_command!).with(command_preamble + ['gmake']).and_return(true) run_rake_task('gitlab:gitaly:install', clone_path) end @@ -63,12 +73,12 @@ describe 'gitlab:gitaly namespace rake task' do context 'gmake is not available' do before do expect_any_instance_of(Object).to receive(:checkout_or_clone_version) - allow_any_instance_of(Object).to receive(:run_command!).with(['make']).and_return(true) + allow_any_instance_of(Object).to receive(:run_command!).with(command_preamble + ['make']).and_return(true) end it 'calls make in the gitaly directory' do expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['', 42]) - expect_any_instance_of(Object).to receive(:run_command!).with(['make']).and_return(true) + expect_any_instance_of(Object).to receive(:run_command!).with(command_preamble + ['make']).and_return(true) run_rake_task('gitlab:gitaly:install', clone_path) end diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb index 309b3172da1..05f971dfd13 100644 --- a/spec/workers/git_garbage_collect_worker_spec.rb +++ b/spec/workers/git_garbage_collect_worker_spec.rb @@ -9,17 +9,51 @@ describe GitGarbageCollectWorker do subject { described_class.new } describe "#perform" do - it "flushes ref caches when the task is 'gc'" do - expect(subject).to receive(:command).with(:gc).and_return([:the, :command]) - expect(Gitlab::Popen).to receive(:popen) - .with([:the, :command], project.repository.path_to_repo).and_return(["", 0]) + shared_examples 'flushing ref caches' do |gitaly| + it "flushes ref caches when the task if 'gc'" do + expect(subject).to receive(:command).with(:gc).and_return([:the, :command]) + + if gitaly + expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect) + .and_return(nil) + else + expect(Gitlab::Popen).to receive(:popen) + .with([:the, :command], project.repository.path_to_repo).and_return(["", 0]) + end + + expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original + expect_any_instance_of(Repository).to receive(:branch_names).and_call_original + expect_any_instance_of(Repository).to receive(:branch_count).and_call_original + expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original + + subject.perform(project.id) + end + end + + context "with Gitaly turned on" do + it_should_behave_like 'flushing ref caches', true + end + + context "with Gitaly turned off", skip_gitaly_mock: true do + it_should_behave_like 'flushing ref caches', false + end - expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original - expect_any_instance_of(Repository).to receive(:branch_names).and_call_original - expect_any_instance_of(Repository).to receive(:branch_count).and_call_original - expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original + context "repack_full" do + it "calls Gitaly" do + expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:repack_full) + .and_return(nil) - subject.perform(project.id) + subject.perform(project.id, :full_repack) + end + end + + context "repack_incremental" do + it "calls Gitaly" do + expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:repack_incremental) + .and_return(nil) + + subject.perform(project.id, :incremental_repack) + end end shared_examples 'gc tasks' do diff --git a/spec/workers/namespaceless_project_destroy_worker_spec.rb b/spec/workers/namespaceless_project_destroy_worker_spec.rb index 8533b7b85e9..f9e23d648ec 100644 --- a/spec/workers/namespaceless_project_destroy_worker_spec.rb +++ b/spec/workers/namespaceless_project_destroy_worker_spec.rb @@ -5,7 +5,7 @@ describe NamespacelessProjectDestroyWorker do before do # Stub after_save callbacks that will fail when Project has no namespace - allow_any_instance_of(Project).to receive(:ensure_dir_exist).and_return(nil) + allow_any_instance_of(Project).to receive(:ensure_storage_path_exist).and_return(nil) allow_any_instance_of(Project).to receive(:update_project_statistics).and_return(nil) end |