Commit 2f252d8f authored by Kamil Trzcinski's avatar Kamil Trzcinski

Merge branch 'master' into improve-pipeline-design

parents 8d033236 16ca3ee6
require: rubocop-rspec
AllCops: AllCops:
TargetRubyVersion: 2.1 TargetRubyVersion: 2.1
# Cop names are not displayed in offense messages by default. Change behavior # Cop names are not displayed in offense messages by default. Change behavior
...@@ -152,7 +154,7 @@ Style/ConstantName: ...@@ -152,7 +154,7 @@ Style/ConstantName:
# Use def with parentheses when there are arguments. # Use def with parentheses when there are arguments.
Style/DefWithParentheses: Style/DefWithParentheses:
Enabled: false Enabled: true
# Checks for use of deprecated Hash methods. # Checks for use of deprecated Hash methods.
Style/DeprecatedHashMethods: Style/DeprecatedHashMethods:
...@@ -299,7 +301,7 @@ Style/IndentHash: ...@@ -299,7 +301,7 @@ Style/IndentHash:
# Use Kernel#loop for infinite loops. # Use Kernel#loop for infinite loops.
Style/InfiniteLoop: Style/InfiniteLoop:
Enabled: false Enabled: true
# Use the new lambda literal syntax for single-line blocks. # Use the new lambda literal syntax for single-line blocks.
Style/Lambda: Style/Lambda:
...@@ -333,6 +335,12 @@ Style/MethodName: ...@@ -333,6 +335,12 @@ Style/MethodName:
Style/ModuleFunction: Style/ModuleFunction:
Enabled: false 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: false
EnforcedStyle: symmetrical
# Avoid multi-line chains of blocks. # Avoid multi-line chains of blocks.
Style/MultilineBlockChain: Style/MultilineBlockChain:
Enabled: false Enabled: false
...@@ -341,10 +349,22 @@ Style/MultilineBlockChain: ...@@ -341,10 +349,22 @@ Style/MultilineBlockChain:
Style/MultilineBlockLayout: Style/MultilineBlockLayout:
Enabled: true 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: false
EnforcedStyle: symmetrical
# Do not use then for multi-line if/unless. # Do not use then for multi-line if/unless.
Style/MultilineIfThen: Style/MultilineIfThen:
Enabled: false Enabled: false
# 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 # Checks indentation of method calls with the dot operator that span more than
# one line. # one line.
Style/MultilineMethodCallIndentation: Style/MultilineMethodCallIndentation:
...@@ -525,10 +545,9 @@ Style/SpaceAfterSemicolon: ...@@ -525,10 +545,9 @@ Style/SpaceAfterSemicolon:
Style/SpaceAroundEqualsInParameterDefault: Style/SpaceAroundEqualsInParameterDefault:
Enabled: false Enabled: false
# TODO: Enable SpaceAroundKeyword Cop.
# Use a space around keywords if appropriate. # Use a space around keywords if appropriate.
Style/SpaceAroundKeyword: Style/SpaceAroundKeyword:
Enabled: false Enabled: true
# Use a single space around operators. # Use a single space around operators.
Style/SpaceAroundOperators: Style/SpaceAroundOperators:
...@@ -755,19 +774,19 @@ Lint/BlockAlignment: ...@@ -755,19 +774,19 @@ Lint/BlockAlignment:
# Default values in optional keyword arguments and optional ordinal arguments # Default values in optional keyword arguments and optional ordinal arguments
# should not refer back to the name of the argument. # should not refer back to the name of the argument.
Lint/CircularArgumentReference: Lint/CircularArgumentReference:
Enabled: false Enabled: true
# Checks for condition placed in a confusing position relative to the keyword. # Checks for condition placed in a confusing position relative to the keyword.
Lint/ConditionPosition: Lint/ConditionPosition:
Enabled: false Enabled: true
# Check for debugger calls. # Check for debugger calls.
Lint/Debugger: Lint/Debugger:
Enabled: false Enabled: true
# Align ends corresponding to defs correctly. # Align ends corresponding to defs correctly.
Lint/DefEndAlignment: Lint/DefEndAlignment:
Enabled: false Enabled: true
# Check for deprecated class method calls. # Check for deprecated class method calls.
Lint/DeprecatedClassMethods: Lint/DeprecatedClassMethods:
...@@ -783,15 +802,15 @@ Lint/DuplicatedKey: ...@@ -783,15 +802,15 @@ Lint/DuplicatedKey:
# Check for immutable argument given to each_with_object. # Check for immutable argument given to each_with_object.
Lint/EachWithObjectArgument: Lint/EachWithObjectArgument:
Enabled: false Enabled: true
# Check for odd code arrangement in an else block. # Check for odd code arrangement in an else block.
Lint/ElseLayout: Lint/ElseLayout:
Enabled: false Enabled: true
# Checks for empty ensure block. # Checks for empty ensure block.
Lint/EmptyEnsure: Lint/EmptyEnsure:
Enabled: false Enabled: true
# Checks for empty string interpolation. # Checks for empty string interpolation.
Lint/EmptyInterpolation: Lint/EmptyInterpolation:
...@@ -799,37 +818,36 @@ Lint/EmptyInterpolation: ...@@ -799,37 +818,36 @@ Lint/EmptyInterpolation:
# Align ends correctly. # Align ends correctly.
Lint/EndAlignment: Lint/EndAlignment:
Enabled: false Enabled: true
# END blocks should not be placed inside method definitions. # END blocks should not be placed inside method definitions.
Lint/EndInMethod: Lint/EndInMethod:
Enabled: false Enabled: true
# Do not use return in an ensure block. # Do not use return in an ensure block.
Lint/EnsureReturn: Lint/EnsureReturn:
Enabled: false Enabled: true
# The use of eval represents a serious security risk. # The use of eval represents a serious security risk.
Lint/Eval: Lint/Eval:
Enabled: false Enabled: true
# Catches floating-point literals too large or small for Ruby to represent. # Catches floating-point literals too large or small for Ruby to represent.
Lint/FloatOutOfRange: Lint/FloatOutOfRange:
Enabled: false Enabled: true
# The number of parameters to format/sprint must match the fields. # The number of parameters to format/sprint must match the fields.
Lint/FormatParameterMismatch: Lint/FormatParameterMismatch:
Enabled: false Enabled: true
# Don't suppress exception. # Don't suppress exception.
Lint/HandleExceptions: Lint/HandleExceptions:
Enabled: false Enabled: false
# TODO: Enable ImplicitStringConcatenation Cop.
# Checks for adjacent string literals on the same line, which could better be # Checks for adjacent string literals on the same line, which could better be
# represented as a single string literal. # represented as a single string literal.
Lint/ImplicitStringConcatenation: Lint/ImplicitStringConcatenation:
Enabled: false Enabled: true
# TODO: Enable IneffectiveAccessModifier Cop. # TODO: Enable IneffectiveAccessModifier Cop.
# Checks for attempts to use `private` or `protected` to set the visibility # Checks for attempts to use `private` or `protected` to set the visibility
...@@ -840,7 +858,7 @@ Lint/IneffectiveAccessModifier: ...@@ -840,7 +858,7 @@ Lint/IneffectiveAccessModifier:
# Checks for invalid character literals with a non-escaped whitespace # Checks for invalid character literals with a non-escaped whitespace
# character. # character.
Lint/InvalidCharacterLiteral: Lint/InvalidCharacterLiteral:
Enabled: false Enabled: true
# Checks of literals used in conditions. # Checks of literals used in conditions.
Lint/LiteralInCondition: Lint/LiteralInCondition:
...@@ -848,7 +866,7 @@ Lint/LiteralInCondition: ...@@ -848,7 +866,7 @@ Lint/LiteralInCondition:
# Checks for literals used in interpolation. # Checks for literals used in interpolation.
Lint/LiteralInInterpolation: Lint/LiteralInInterpolation:
Enabled: false Enabled: true
# Use Kernel#loop with break rather than begin/end/until or begin/end/while # Use Kernel#loop with break rather than begin/end/until or begin/end/while
# for post-loop tests. # for post-loop tests.
...@@ -857,11 +875,11 @@ Lint/Loop: ...@@ -857,11 +875,11 @@ Lint/Loop:
# Do not use nested method definitions. # Do not use nested method definitions.
Lint/NestedMethodDefinition: Lint/NestedMethodDefinition:
Enabled: false Enabled: true
# Do not omit the accumulator when calling `next` in a `reduce`/`inject` block. # Do not omit the accumulator when calling `next` in a `reduce`/`inject` block.
Lint/NextWithoutAccumulator: Lint/NextWithoutAccumulator:
Enabled: false Enabled: true
# Checks for method calls with a space before the opening parenthesis. # Checks for method calls with a space before the opening parenthesis.
Lint/ParenthesesAsGroupedExpression: Lint/ParenthesesAsGroupedExpression:
...@@ -870,11 +888,11 @@ Lint/ParenthesesAsGroupedExpression: ...@@ -870,11 +888,11 @@ Lint/ParenthesesAsGroupedExpression:
# Checks for `rand(1)` calls. Such calls always return `0` and most likely # Checks for `rand(1)` calls. Such calls always return `0` and most likely
# a mistake. # a mistake.
Lint/RandOne: Lint/RandOne:
Enabled: false Enabled: true
# Use parentheses in the method call to avoid confusion about precedence. # Use parentheses in the method call to avoid confusion about precedence.
Lint/RequireParentheses: Lint/RequireParentheses:
Enabled: false Enabled: true
# Avoid rescuing the Exception class. # Avoid rescuing the Exception class.
Lint/RescueException: Lint/RescueException:
...@@ -909,7 +927,7 @@ Lint/UnusedMethodArgument: ...@@ -909,7 +927,7 @@ Lint/UnusedMethodArgument:
# Unreachable code. # Unreachable code.
Lint/UnreachableCode: Lint/UnreachableCode:
Enabled: false Enabled: true
# Checks for useless access modifiers. # Checks for useless access modifiers.
Lint/UselessAccessModifier: Lint/UselessAccessModifier:
...@@ -921,19 +939,19 @@ Lint/UselessAssignment: ...@@ -921,19 +939,19 @@ Lint/UselessAssignment:
# Checks for comparison of something with itself. # Checks for comparison of something with itself.
Lint/UselessComparison: Lint/UselessComparison:
Enabled: false Enabled: true
# Checks for useless `else` in `begin..end` without `rescue`. # Checks for useless `else` in `begin..end` without `rescue`.
Lint/UselessElseWithoutRescue: Lint/UselessElseWithoutRescue:
Enabled: false Enabled: true
# Checks for useless setter call to a local variable. # Checks for useless setter call to a local variable.
Lint/UselessSetterCall: Lint/UselessSetterCall:
Enabled: false Enabled: true
# Possible use of operator/literal/variable in void context. # Possible use of operator/literal/variable in void context.
Lint/Void: Lint/Void:
Enabled: false Enabled: true
##################### Performance ############################ ##################### Performance ############################
...@@ -942,11 +960,10 @@ Lint/Void: ...@@ -942,11 +960,10 @@ Lint/Void:
Performance/Casecmp: Performance/Casecmp:
Enabled: true Enabled: true
# TODO: Enable DoubleStartEndWith Cop.
# Use `str.{start,end}_with?(x, ..., y, ...)` instead of # Use `str.{start,end}_with?(x, ..., y, ...)` instead of
# `str.{start,end}_with?(x, ...) || str.{start,end}_with?(y, ...)`. # `str.{start,end}_with?(x, ...) || str.{start,end}_with?(y, ...)`.
Performance/DoubleStartEndWith: Performance/DoubleStartEndWith:
Enabled: false Enabled: true
# TODO: Enable EndWith Cop. # TODO: Enable EndWith Cop.
# Use `end_with?` instead of a regex match anchored to the end of a string. # Use `end_with?` instead of a regex match anchored to the end of a string.
...@@ -957,10 +974,9 @@ Performance/EndWith: ...@@ -957,10 +974,9 @@ Performance/EndWith:
Performance/LstripRstrip: Performance/LstripRstrip:
Enabled: true Enabled: true
# TODO: Enable RangeInclude Cop.
# Use `Range#cover?` instead of `Range#include?`. # Use `Range#cover?` instead of `Range#include?`.
Performance/RangeInclude: Performance/RangeInclude:
Enabled: false Enabled: true
# TODO: Enable RedundantBlockCall Cop. # TODO: Enable RedundantBlockCall Cop.
# Use `yield` instead of `block.call`. # Use `yield` instead of `block.call`.
...@@ -980,16 +996,14 @@ Performance/RedundantMerge: ...@@ -980,16 +996,14 @@ Performance/RedundantMerge:
MaxKeyValuePairs: 2 MaxKeyValuePairs: 2
Enabled: false Enabled: false
# TODO: Enable RedundantSortBy Cop.
# Use `sort` instead of `sort_by { |x| x }`. # Use `sort` instead of `sort_by { |x| x }`.
Performance/RedundantSortBy: Performance/RedundantSortBy:
Enabled: false Enabled: true
# TODO: Enable StartWith Cop.
# Use `start_with?` instead of a regex match anchored to the beginning of a # Use `start_with?` instead of a regex match anchored to the beginning of a
# string. # string.
Performance/StartWith: Performance/StartWith:
Enabled: false Enabled: true
# Use `tr` instead of `gsub` when you are replacing the same number of # Use `tr` instead of `gsub` when you are replacing the same number of
# characters. Use `delete` instead of `gsub` when you are deleting # characters. Use `delete` instead of `gsub` when you are deleting
...@@ -1025,11 +1039,11 @@ Rails/Delegate: ...@@ -1025,11 +1039,11 @@ Rails/Delegate:
# Prefer `find_by` over `where.first`. # Prefer `find_by` over `where.first`.
Rails/FindBy: Rails/FindBy:
Enabled: false Enabled: true
# Prefer `all.find_each` over `all.find`. # Prefer `all.find_each` over `all.find`.
Rails/FindEach: Rails/FindEach:
Enabled: false Enabled: true
# Prefer has_many :through to has_and_belongs_to_many. # Prefer has_many :through to has_and_belongs_to_many.
Rails/HasAndBelongsToMany: Rails/HasAndBelongsToMany:
...@@ -1041,7 +1055,7 @@ Rails/Output: ...@@ -1041,7 +1055,7 @@ Rails/Output:
# Checks for incorrect grammar when using methods like `3.day.ago`. # Checks for incorrect grammar when using methods like `3.day.ago`.
Rails/PluralizationGrammar: Rails/PluralizationGrammar:
Enabled: false Enabled: true
# Checks for `read_attribute(:attr)` and `write_attribute(:attr, val)`. # Checks for `read_attribute(:attr)` and `write_attribute(:attr, val)`.
Rails/ReadWriteAttribute: Rails/ReadWriteAttribute:
...@@ -1049,7 +1063,7 @@ Rails/ReadWriteAttribute: ...@@ -1049,7 +1063,7 @@ Rails/ReadWriteAttribute:
# Checks the arguments of ActiveRecord scopes. # Checks the arguments of ActiveRecord scopes.
Rails/ScopeArgs: Rails/ScopeArgs:
Enabled: false Enabled: true
# Checks the correct usage of time zone aware methods. # Checks the correct usage of time zone aware methods.
# http://danilenko.org/2012/7/6/rails_timezones # http://danilenko.org/2012/7/6/rails_timezones
...@@ -1059,3 +1073,65 @@ Rails/TimeZone: ...@@ -1059,3 +1073,65 @@ Rails/TimeZone:
# Use validates :attribute, hash of validations. # Use validates :attribute, hash of validations.
Rails/Validation: Rails/Validation:
Enabled: false Enabled: false
##################### RSpec ##################################
# Check that instances are not being stubbed globally.
RSpec/AnyInstance:
Enabled: false
# Check that the first argument to the top level describe is the tested class or
# module.
RSpec/DescribeClass:
Enabled: false
# Use `described_class` for tested class / module.
RSpec/DescribeMethod:
Enabled: false
# Checks that the second argument to top level describe is the tested method
# name.
RSpec/DescribedClass:
Enabled: false
# Checks for long example.
RSpec/ExampleLength:
Enabled: false
Max: 5
# Do not use should when describing your tests.
RSpec/ExampleWording:
Enabled: false
CustomTransform:
be: is
have: has
not: does not
IgnoredWords: []
# Checks the file and folder naming of the spec file.
RSpec/FilePath:
Enabled: false
CustomTransform:
RuboCop: rubocop
RSpec: rspec
# Checks if there are focused specs.
RSpec/Focus:
Enabled: true
# Checks for the usage of instance variables.
RSpec/InstanceVariable:
Enabled: false
# Checks for multiple top-level describes.
RSpec/MultipleDescribes:
Enabled: false
# Enforces the usage of the same method on all negative message expectations.
RSpec/NotToNot:
EnforcedStyle: not_to
Enabled: false
# Prefer using verifying doubles over normal doubles.
RSpec/VerifiedDoubles:
Enabled: false
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 8.8.0 (unreleased) v 8.9.0 (unreleased)
- Redesign navigation for project pages
v 8.8.2 (unreleased)
- Fix Error 500 when accessing application settings due to nil disabled OAuth sign-in sources
- Fix Error 500 in CI charts by gracefully handling commits with no durations
v 8.8.1
- Add documentation for the "Health Check" feature
- Allow anonymous users to access a public project's pipelines
- Fix MySQL compatibility in zero downtime migrations helpers
- Fix the CI login to Container Registry (the gitlab-ci-token user)
v 8.8.0
- Implement GFM references for milestones (Alejandro Rodríguez) - Implement GFM references for milestones (Alejandro Rodríguez)
- Snippets tab under user profile. !4001 (Long Nguyen) - Snippets tab under user profile. !4001 (Long Nguyen)
- Fix error when using link to uploads in global snippets - Fix error when using link to uploads in global snippets
...@@ -14,6 +27,7 @@ v 8.8.0 (unreleased) ...@@ -14,6 +27,7 @@ v 8.8.0 (unreleased)
- Project#open_branches has been cleaned up and no longer loads entire records into memory. - Project#open_branches has been cleaned up and no longer loads entire records into memory.
- Escape HTML in commit titles in system note messages - Escape HTML in commit titles in system note messages
- Improve design of Pipeline View - Improve design of Pipeline View
- Fix scope used when accessing container registry
- Fix creation of Ci::Commit object which can lead to pending, failed in some scenarios - Fix creation of Ci::Commit object which can lead to pending, failed in some scenarios
- Improve multiple branch push performance by memoizing permission checking - Improve multiple branch push performance by memoizing permission checking
- Log to application.log when an admin starts and stops impersonating a user - Log to application.log when an admin starts and stops impersonating a user
......
...@@ -293,9 +293,10 @@ group :development, :test do ...@@ -293,9 +293,10 @@ group :development, :test do
gem 'spring-commands-spinach', '~> 1.1.0' gem 'spring-commands-spinach', '~> 1.1.0'
gem 'spring-commands-teaspoon', '~> 0.0.2' gem 'spring-commands-teaspoon', '~> 0.0.2'
gem 'rubocop', '~> 0.38.0', require: false gem 'rubocop', '~> 0.40.0', require: false
gem 'rubocop-rspec', '~> 1.5.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false gem 'scss_lint', '~> 0.47.0', require: false
gem 'coveralls', '~> 0.8.2', require: false gem 'coveralls', '~> 0.8.2', require: false
gem 'simplecov', '~> 0.11.0', require: false gem 'simplecov', '~> 0.11.0', require: false
gem 'flog', require: false gem 'flog', require: false
gem 'flay', require: false gem 'flay', require: false
...@@ -325,7 +326,7 @@ gem "mail_room", "~> 0.7" ...@@ -325,7 +326,7 @@ gem "mail_room", "~> 0.7"
gem 'email_reply_parser', '~> 0.5.8' gem 'email_reply_parser', '~> 0.5.8'
## CI ## CI
gem 'activerecord-session_store', '~> 0.1.0' gem 'activerecord-session_store', '~> 1.0.0'
gem "nested_form", '~> 0.3.2' gem "nested_form", '~> 0.3.2'
# OAuth # OAuth
......
...@@ -33,10 +33,12 @@ GEM ...@@ -33,10 +33,12 @@ GEM
activemodel (= 4.2.6) activemodel (= 4.2.6)
activesupport (= 4.2.6) activesupport (= 4.2.6)
arel (~> 6.0) arel (~> 6.0)
activerecord-session_store (0.1.2) activerecord-session_store (1.0.0)
actionpack (>= 4.0.0, < 5) actionpack (>= 4.0, < 5.1)
activerecord (>= 4.0.0, < 5) activerecord (>= 4.0, < 5.1)
railties (>= 4.0.0, < 5) multi_json (~> 1.11, >= 1.11.2)
rack (>= 1.5.2, < 3)
railties (>= 4.0, < 5.1)
activesupport (4.2.6) activesupport (4.2.6)
i18n (~> 0.7) i18n (~> 0.7)
json (~> 1.7, >= 1.7.7) json (~> 1.7, >= 1.7.7)
...@@ -549,7 +551,7 @@ GEM ...@@ -549,7 +551,7 @@ GEM
orm_adapter (0.5.0) orm_adapter (0.5.0)
paranoia (2.1.4) paranoia (2.1.4)
activerecord (~> 4.0) activerecord (~> 4.0)
parser (2.3.0.6) parser (2.3.1.0)
ast (~> 2.2) ast (~> 2.2)
pg (0.18.4) pg (0.18.4)
poltergeist (1.9.0) poltergeist (1.9.0)
...@@ -684,15 +686,17 @@ GEM ...@@ -684,15 +686,17 @@ GEM
rspec-retry (0.4.5) rspec-retry (0.4.5)
rspec-core rspec-core
rspec-support (3.4.1) rspec-support (3.4.1)
rubocop (0.38.0) rubocop (0.40.0)
parser (>= 2.3.0.6, < 3.0) parser (>= 2.3.1.0, < 3.0)
powerpack (~> 0.1) powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0) rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1) unicode-display_width (~> 1.0, >= 1.0.1)
rubocop-rspec (1.5.0)
rubocop (>= 0.40.0)
ruby-fogbugz (0.2.1) ruby-fogbugz (0.2.1)
crack (~> 0.4) crack (~> 0.4)
ruby-progressbar (1.7.5) ruby-progressbar (1.8.1)
ruby-saml (1.1.2) ruby-saml (1.1.2)
nokogiri (>= 1.5.10) nokogiri (>= 1.5.10)
uuid (~> 2.3) uuid (~> 2.3)
...@@ -839,7 +843,7 @@ GEM ...@@ -839,7 +843,7 @@ GEM
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.2) unf_ext (0.0.7.2)
unicode-display_width (1.0.2) unicode-display_width (1.0.5)
unicorn (4.9.0) unicorn (4.9.0)
kgio (~> 2.6) kgio (~> 2.6)
rack rack
...@@ -883,7 +887,7 @@ PLATFORMS ...@@ -883,7 +887,7 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
RedCloth (~> 4.2.9) RedCloth (~> 4.2.9)
ace-rails-ap (~> 4.0.2) ace-rails-ap (~> 4.0.2)
activerecord-session_store (~> 0.1.0) activerecord-session_store (~> 1.0.0)
acts-as-taggable-on (~> 3.4) acts-as-taggable-on (~> 3.4)
addressable (~> 2.3.8) addressable (~> 2.3.8)
after_commit_queue after_commit_queue
...@@ -1013,7 +1017,8 @@ DEPENDENCIES ...@@ -1013,7 +1017,8 @@ DEPENDENCIES
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.4.0) rspec-rails (~> 3.4.0)
rspec-retry rspec-retry
rubocop (~> 0.38.0) rubocop (~> 0.40.0)
rubocop-rspec (~> 1.5.0)
ruby-fogbugz (~> 0.2.1) ruby-fogbugz (~> 0.2.1)
sanitize (~> 2.0) sanitize (~> 2.0)
sass-rails (~> 5.0.0) sass-rails (~> 5.0.0)
...@@ -1058,4 +1063,4 @@ DEPENDENCIES ...@@ -1058,4 +1063,4 @@ DEPENDENCIES
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
BUNDLED WITH BUNDLED WITH
1.12.3 1.12.4
8.8.0-pre 8.9.0-pre
...@@ -119,7 +119,7 @@ class Dispatcher ...@@ -119,7 +119,7 @@ class Dispatcher
new UsersSelect() new UsersSelect()
when 'projects' when 'projects'
new NamespaceSelect() new NamespaceSelect()
when 'dashboard' when 'dashboard', 'root'
shortcut_handler = new ShortcutsDashboardNavigation() shortcut_handler = new ShortcutsDashboardNavigation()
when 'profiles' when 'profiles'
new Profile() new Profile()
......
...@@ -11,6 +11,7 @@ class @DueDateSelect ...@@ -11,6 +11,7 @@ class @DueDateSelect
$block = $dropdown.closest('.block') $block = $dropdown.closest('.block')
$selectbox = $dropdown.closest('.selectbox') $selectbox = $dropdown.closest('.selectbox')
$value = $block.find('.value') $value = $block.find('.value')
$valueContent = $block.find('.value-content')
$sidebarValue = $('.js-due-date-sidebar-value', $block) $sidebarValue = $('.js-due-date-sidebar-value', $block)
fieldName = $dropdown.data('field-name') fieldName = $dropdown.data('field-name')
...@@ -23,11 +24,15 @@ class @DueDateSelect ...@@ -23,11 +24,15 @@ class @DueDateSelect
$value.removeAttr('style') $value.removeAttr('style')
) )
addDueDate = -> addDueDate = (isDropdown) ->
# Create the post date # Create the post date
value = $("input[name='#{fieldName}']").val() value = $("input[name='#{fieldName}']").val()
date = new Date value.replace(new RegExp('-', 'g'), ',')
mediumDate = $.datepicker.formatDate 'M d, yy', date if value isnt ''
date = new Date value.replace(new RegExp('-', 'g'), ',')
mediumDate = $.datepicker.formatDate 'M d, yy', date
else
mediumDate = 'None'
data = {} data = {}
data[abilityName] = {} data[abilityName] = {}
...@@ -39,23 +44,35 @@ class @DueDateSelect ...@@ -39,23 +44,35 @@ class @DueDateSelect
data: data data: data
beforeSend: -> beforeSend: ->
$loading.fadeIn() $loading.fadeIn()
$dropdown.trigger('loading.gl.dropdown') if isDropdown
$selectbox.hide() $dropdown.trigger('loading.gl.dropdown')
$selectbox.hide()
$value.removeAttr('style') $value.removeAttr('style')
$value.html(mediumDate) $valueContent.html(mediumDate)
$sidebarValue.html(mediumDate) $sidebarValue.html(mediumDate)
if value isnt ''
$('.js-remove-due-date-holder').removeClass 'hidden'
else
$('.js-remove-due-date-holder').addClass 'hidden'
).done (data) -> ).done (data) ->
$dropdown.trigger('loaded.gl.dropdown') if isDropdown
$dropdown.dropdown('toggle') $dropdown.trigger('loaded.gl.dropdown')
$dropdown.dropdown('toggle')
$loading.fadeOut() $loading.fadeOut()
$block.on 'click', '.js-remove-due-date', (e) ->
e.preventDefault()
$("input[name='#{fieldName}']").val ''
addDueDate(false)
$datePicker.datepicker( $datePicker.datepicker(
dateFormat: 'yy-mm-dd', dateFormat: 'yy-mm-dd',
defaultDate: $("input[name='#{fieldName}']").val() defaultDate: $("input[name='#{fieldName}']").val()
altField: "input[name='#{fieldName}']" altField: "input[name='#{fieldName}']"
onSelect: -> onSelect: ->
addDueDate() addDueDate(true)
) )
$(document) $(document)
......
...@@ -512,7 +512,7 @@ class GitLabDropdown ...@@ -512,7 +512,7 @@ class GitLabDropdown
return false return false
if currentKeyCode is 13 if currentKeyCode is 13
@selectRowAtIndex currentIndex @selectRowAtIndex if currentIndex < 0 then 0 else currentIndex
removeArrayKeyEvent: -> removeArrayKeyEvent: ->
$('body').off 'keydown' $('body').off 'keydown'
......
...@@ -20,6 +20,15 @@ class @IssuableForm ...@@ -20,6 +20,15 @@ class @IssuableForm
@initWip() @initWip()
$issuableDueDate = $('#issuable-due-date')
if $issuableDueDate.length
$('.datepicker').datepicker(
dateFormat: 'yy-mm-dd',
onSelect: (dateText, inst) ->
$issuableDueDate.val dateText
).datepicker 'setDate', $.datepicker.parseDate('yy-mm-dd', $issuableDueDate.val())
initAutosave: -> initAutosave: ->
new Autosave @titleField, [ new Autosave @titleField, [
document.location.pathname, document.location.pathname,
......
...@@ -113,7 +113,7 @@ class @MergeRequestWidget ...@@ -113,7 +113,7 @@ class @MergeRequestWidget
switch state switch state
when "failed", "canceled", "not_found" when "failed", "canceled", "not_found"
@setMergeButtonClass('btn-danger') @setMergeButtonClass('btn-danger')
when "running", "pending" when "running"
@setMergeButtonClass('btn-warning') @setMergeButtonClass('btn-warning')
when "success" when "success"
@setMergeButtonClass('btn-create') @setMergeButtonClass('btn-create')
...@@ -126,6 +126,6 @@ class @MergeRequestWidget ...@@ -126,6 +126,6 @@ class @MergeRequestWidget
$('.ci_widget:visible .ci-coverage').text(text) $('.ci_widget:visible .ci-coverage').text(text)
setMergeButtonClass: (css_class) -> setMergeButtonClass: (css_class) ->
$('.accept_merge_request') $('.js-merge-button')
.removeClass('btn-danger btn-warning btn-create') .removeClass('btn-danger btn-warning btn-create')
.addClass(css_class) .addClass(css_class)
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
class @ShortcutsDashboardNavigation extends Shortcuts class @ShortcutsDashboardNavigation extends Shortcuts
constructor: -> constructor: ->
super() super()
Mousetrap.bind('g a', -> ShortcutsDashboardNavigation.findAndFollowLink('.shortcuts-activity')) Mousetrap.bind('g a', -> ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-activity'))
Mousetrap.bind('g i', -> ShortcutsDashboardNavigation.findAndFollowLink('.shortcuts-issues')) Mousetrap.bind('g i', -> ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-issues'))
Mousetrap.bind('g m', -> ShortcutsDashboardNavigation.findAndFollowLink('.shortcuts-merge_requests')) Mousetrap.bind('g m', -> ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-merge_requests'))
Mousetrap.bind('g p', -> ShortcutsDashboardNavigation.findAndFollowLink('.shortcuts-projects')) Mousetrap.bind('g p', -> ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-projects'))
@findAndFollowLink: (selector) -> @findAndFollowLink: (selector) ->
link = $(selector).attr('href') link = $(selector).attr('href')
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
&.s32 { font-size: 20px; line-height: 32px; } &.s32 { font-size: 20px; line-height: 32px; }
&.s40 { font-size: 16px; line-height: 40px; } &.s40 { font-size: 16px; line-height: 40px; }
&.s60 { font-size: 32px; line-height: 60px; } &.s60 { font-size: 32px; line-height: 60px; }
&.s70 { font-size: 34px; line-height: 70px; }
&.s90 { font-size: 36px; line-height: 90px; } &.s90 { font-size: 36px; line-height: 90px; }
&.s110 { font-size: 40px; line-height: 112px; font-weight: 300; } &.s110 { font-size: 40px; line-height: 112px; font-weight: 300; }
&.s140 { font-size: 72px; line-height: 140px; } &.s140 { font-size: 72px; line-height: 140px; }
......
...@@ -24,8 +24,8 @@ ...@@ -24,8 +24,8 @@
background-color: $background-color; background-color: $background-color;
padding: $gl-padding; padding: $gl-padding;
margin-bottom: 0; margin-bottom: 0;
border-top: 1px solid $border-color; border-top: 1px solid $white-dark;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $white-dark;
color: $gl-gray; color: $gl-gray;
&.oneline-block { &.oneline-block {
...@@ -110,9 +110,9 @@ ...@@ -110,9 +110,9 @@
.cover-title { .cover-title {
color: $gl-header-color; color: $gl-header-color;
margin: 0; margin: 0;
font-size: 23px; font-size: 24px;
font-weight: normal; font-weight: normal;
margin: 16px 0 5px; margin-bottom: 5px;
color: #4c4e54; color: #4c4e54;
font-size: 23px; font-size: 23px;
line-height: 1.1; line-height: 1.1;
...@@ -137,7 +137,6 @@ ...@@ -137,7 +137,6 @@
} }
.cover-desc { .cover-desc {
padding: 0 $gl-padding 3px;
color: $gl-text-color; color: $gl-text-color;
&.username:last-child { &.username:last-child {
...@@ -205,7 +204,7 @@ ...@@ -205,7 +204,7 @@
.content-block { .content-block {
padding: $gl-padding 0; padding: $gl-padding 0;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $white-dark;
&.oneline-block { &.oneline-block {
line-height: 36px; line-height: 36px;
......
...@@ -28,10 +28,6 @@ input[type='text'].danger { ...@@ -28,10 +28,6 @@ input[type='text'].danger {
} }
label { label {
&.control-label {
@extend .col-sm-2;
}
&.inline-label { &.inline-label {
margin: 0; margin: 0;
} }
...@@ -41,6 +37,10 @@ label { ...@@ -41,6 +37,10 @@ label {
} }
} }
.control-label {
@extend .col-sm-2;
}
.inline-input-group { .inline-input-group {
width: 250px; width: 250px;
} }
......
...@@ -48,10 +48,6 @@ ...@@ -48,10 +48,6 @@
display: block; display: block;
} }
.project-home-desc {
font-size: 21px;
}
.project-repo-buttons, .project-repo-buttons,
.git-clone-holder { .git-clone-holder {
display: none; display: none;
......
...@@ -196,7 +196,7 @@ ...@@ -196,7 +196,7 @@
position: fixed; position: fixed;
top: $header-height; top: $header-height;
width: 100%; width: 100%;
z-index: 1; z-index: 3;
background: $background-color; background: $background-color;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
transition-duration: .3s; transition-duration: .3s;
...@@ -238,6 +238,10 @@ ...@@ -238,6 +238,10 @@
@media (max-width: $screen-xs-min) { @media (max-width: $screen-xs-min) {
margin-left: 0; margin-left: 0;
} }
li.active {
font-weight: bold;
}
} }
} }
...@@ -246,6 +250,11 @@ ...@@ -246,6 +250,11 @@
height: 51px; height: 51px;
white-space: nowrap; white-space: nowrap;
overflow-x: auto; overflow-x: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
&::-webkit-scrollbar {
display: none;
}
li { li {
...@@ -279,4 +288,8 @@ ...@@ -279,4 +288,8 @@
margin-top: 96px; margin-top: 96px;
} }
} }
.right-sidebar {
top: ($header-height * 2) + 2;
}
} }
...@@ -119,8 +119,8 @@ $border-white-light: #f1f2f4; ...@@ -119,8 +119,8 @@ $border-white-light: #f1f2f4;
$border-white-normal: #d6dae2; $border-white-normal: #d6dae2;
$border-white-dark: #c6cacf; $border-white-dark: #c6cacf;
$border-gray-light: rgba(0, 0, 0, 0.06); $border-gray-light: #dcdcdc;
$border-gray-normal: rgba(0, 0, 0, 0.10);; $border-gray-normal: rgba(0, 0, 0, 0.10);
$border-gray-dark: #c6cacf; $border-gray-dark: #c6cacf;
$border-green-light: #2faa60; $border-green-light: #2faa60;
......
...@@ -150,6 +150,10 @@ ...@@ -150,6 +150,10 @@
font-weight: 600; font-weight: 600;
} }
.light {
font-weight: normal;
}
.sidebar-collapsed-icon { .sidebar-collapsed-icon {
display: none; display: none;
} }
......
...@@ -26,8 +26,13 @@ ...@@ -26,8 +26,13 @@
} }
.project-home-panel { .project-home-panel {
padding-bottom: 40px; background: $white-light;
border-bottom: 1px solid $border-color; text-align: left;
padding: 24px 0;
.container-fluid {
position: relative;
}
.cover-controls { .cover-controls {
.project-settings-dropdown { .project-settings-dropdown {
...@@ -43,21 +48,55 @@ ...@@ -43,21 +48,55 @@
} }
} }
.project-identicon-holder { .cover-title {
margin-bottom: 16px; margin-bottom: 0;
}
.project-image-container {
@include make-sm-column(1);
max-width: 86px;
min-width: 86px;
padding-right: 0;
margin: 11px 0;
.avatar, .identicon { @media (max-width: $screen-md-max) {
margin: 0 auto; padding-left: 0;
float: none; margin: 0 0 10px;
max-width: none;
min-width: none;
.avatar.s70 {
margin: auto;
}
} }
}
.identicon { .project-info {
@include border-radius(50%); @include make-sm-column(10);
h1 {
font-size: 24px;
font-weight: normal;
margin: 0;
}
.project-home-desc {
p {
margin: 0;
}
} }
} }
.identicon {
float: left;
@include border-radius(50%);
}
.avatar {
float: none;
}
.notifications-btn { .notifications-btn {
margin-top: -28px;
.fa-bell { .fa-bell {
margin-right: 6px; margin-right: 6px;
...@@ -69,28 +108,45 @@ ...@@ -69,28 +108,45 @@
} }
.project-repo-buttons { .project-repo-buttons {
margin-top: 20px; font-size: 0;
margin-bottom: 0;
.count-buttons { .btn {
display: block; @include btn-gray;
margin-bottom: 20px; padding: 3px 10px;
} text-transform: none;
background-color: $background-color;
.clone-row { .fa {
.split-repo-buttons, color: $layout-link-gray;
.project-clone-holder {
display: inline-block;
} }
.split-repo-buttons { .fa-caret-down {
margin: 0 12px; margin-left: 3px;
} }
} }
.btn { .btn-group:not(:first-child):not(:last-child) > .btn {
@include btn-gray; border-top-right-radius: 3px;
text-transform: none; border-bottom-right-radius: 3px;
}
form {
margin-left: 10px;
}
.count-buttons {
display: inline-block;
vertical-align: top;
margin-top: 16px;
}
.project-clone-holder {
display: inline-block;
margin-top: 16px;
input {
height: 29px;
}
} }
.count-with-arrow { .count-with-arrow {
...@@ -140,14 +196,18 @@ ...@@ -140,14 +196,18 @@
line-height: 13px; line-height: 13px;
padding: $gl-vert-padding $gl-padding; padding: $gl-vert-padding $gl-padding;
letter-spacing: .4px; letter-spacing: .4px;
padding: 10px 14px; padding: 7px 14px;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
touch-action: manipulation; touch-action: manipulation;
cursor: pointer; cursor: pointer;
background-image: none; background-image: none;
white-space: nowrap; white-space: nowrap;
margin: 0 11px 0 4px; margin: 0 10px 0 4px;
a {
color: inherit;
}
&:hover { &:hover {
background: #fff; background: #fff;
...@@ -155,13 +215,37 @@ ...@@ -155,13 +215,37 @@
} }
} }
} }
.project-right-buttons {
position: absolute;
right: 16px;
bottom: 0;
.btn {
padding: 3px 10px;
background-color: $background-color;
}
@media (max-width: 1304px) {
top: 0;
}
}
@media (max-width: $screen-md-max) {
text-align: center;
.project-info,
.project-image-container {
width: 100%;
}
}
} }
.split-one { .split-one {
display: inline-table; display: inline-table;
margin-right: 12px; margin-right: 12px;
a { > a {
margin: -1px; margin: -1px;
} }
} }
...@@ -285,11 +369,11 @@ a.deploy-project-label { ...@@ -285,11 +369,11 @@ a.deploy-project-label {
} }
.project-stats { .project-stats {
text-align: center;
margin-top: $gl-padding; margin-top: $gl-padding;
margin-bottom: 0; margin-bottom: 0;
padding-top: 10px; padding: 16px 0;
padding-bottom: 4px; background-color: $white-light;
font-size: 0;
ul.nav { ul.nav {
display: inline-block; display: inline-block;
...@@ -300,12 +384,11 @@ a.deploy-project-label { ...@@ -300,12 +384,11 @@ a.deploy-project-label {
} }
.nav > li > a { .nav > li > a {
@include btn-default;
@include btn-gray;
background-color: transparent; background-color: transparent;
border: 1px solid #f7f8fa; margin-right: 12px;
margin-left: 12px; padding: 0 10px;
font-size: 15px;
color: $notes-light-color;
} }
li { li {
...@@ -325,6 +408,10 @@ a.deploy-project-label { ...@@ -325,6 +408,10 @@ a.deploy-project-label {
background-color: #f0f2f5; background-color: #f0f2f5;
} }
} }
&.row-content-block.second-block {
margin-top: 0;
}
} }
pre.light-well { pre.light-well {
...@@ -442,9 +529,14 @@ pre.light-well { ...@@ -442,9 +529,14 @@ pre.light-well {
border-top: 0; border-top: 0;
.edit-project-readme { .edit-project-readme {
z-index: 100; z-index: 2;
position: relative; position: relative;
} }
.wiki h1 {
border-bottom: none;
padding: 0;
}
} }
.git-clone-holder { .git-clone-holder {
......
...@@ -36,7 +36,7 @@ class JwtController < ApplicationController ...@@ -36,7 +36,7 @@ class JwtController < ApplicationController
end end
def authenticate_project(login, password) def authenticate_project(login, password)
if login == 'gitlab_ci_token' if login == 'gitlab-ci-token'
Project.find_by(builds_enabled: true, runners_token: password) Project.find_by(builds_enabled: true, runners_token: password)
end end
end end
......
...@@ -110,8 +110,7 @@ module ApplicationHelper ...@@ -110,8 +110,7 @@ module ApplicationHelper
] ]
# If reference is commit id - we should add it to branch/tag selectbox # If reference is commit id - we should add it to branch/tag selectbox
if(@ref && !options.flatten.include?(@ref) && if @ref && !options.flatten.include?(@ref) && @ref =~ /\A[0-9a-zA-Z]{6,52}\z/
@ref =~ /\A[0-9a-zA-Z]{6,52}\z/)
options << ['Commit', [@ref]] options << ['Commit', [@ref]]
end end
......
...@@ -13,7 +13,7 @@ module GitlabMarkdownHelper ...@@ -13,7 +13,7 @@ module GitlabMarkdownHelper
def link_to_gfm(body, url, html_options = {}) def link_to_gfm(body, url, html_options = {})
return "" if body.blank? return "" if body.blank?
escaped_body = if body =~ /\A\<img/ escaped_body = if body.start_with?('<img')
body body
else else
escape_once(body) escape_once(body)
......
...@@ -144,6 +144,10 @@ module ProjectsHelper ...@@ -144,6 +144,10 @@ module ProjectsHelper
nav_tabs << :merge_requests nav_tabs << :merge_requests
end end
if can?(current_user, :read_pipeline, project)
nav_tabs << :pipelines
end
if can?(current_user, :read_build, project) if can?(current_user, :read_build, project)
nav_tabs << :builds nav_tabs << :builds
end end
......
...@@ -95,7 +95,9 @@ module TabHelper ...@@ -95,7 +95,9 @@ module TabHelper
end end
def project_tab_class def project_tab_class
return "active" if current_page?(controller: "/projects", action: :edit, id: @project) if controller.controller_path.start_with?('projects')
return 'active'
end
if ['services', 'hooks', 'deploy_keys', 'protected_branches'].include? controller.controller_name if ['services', 'hooks', 'deploy_keys', 'protected_branches'].include? controller.controller_name
"active" "active"
...@@ -112,7 +114,7 @@ module TabHelper ...@@ -112,7 +114,7 @@ module TabHelper
end end
def profile_tab_class def profile_tab_class
if controller.controller_path =~ /\Aprofiles/ if controller.controller_path.start_with?('profiles')
return 'active' return 'active'
end end
......
...@@ -60,6 +60,7 @@ class Ability ...@@ -60,6 +60,7 @@ class Ability
:read_project_member, :read_project_member,
:read_merge_request, :read_merge_request,
:read_note, :read_note,
:read_pipeline,
:read_commit_status, :read_commit_status,
:read_container_image, :read_container_image,
:download_code :download_code
......
...@@ -7,7 +7,7 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -7,7 +7,7 @@ class ApplicationSetting < ActiveRecord::Base
serialize :restricted_visibility_levels serialize :restricted_visibility_levels
serialize :import_sources serialize :import_sources
serialize :disabled_oauth_sign_in_sources serialize :disabled_oauth_sign_in_sources, Array
serialize :restricted_signup_domains, Array serialize :restricted_signup_domains, Array
attr_accessor :restricted_signup_domains_raw attr_accessor :restricted_signup_domains_raw
......
...@@ -253,7 +253,7 @@ module Network ...@@ -253,7 +253,7 @@ module Network
leaves = [] leaves = []
leaves.push(commit) if commit.space.zero? leaves.push(commit) if commit.space.zero?
while true loop do
return leaves if commit.parents(@map).count.zero? return leaves if commit.parents(@map).count.zero?
commit = commit.parents(@map).first commit = commit.parents(@map).first
......
...@@ -6,7 +6,7 @@ module Auth ...@@ -6,7 +6,7 @@ module Auth
return error('not found', 404) unless registry.enabled return error('not found', 404) unless registry.enabled
if params[:offline_token] if params[:offline_token]
return error('unauthorized', 401) unless current_user return error('unauthorized', 401) unless current_user || project
else else
return error('forbidden', 403) unless scope return error('forbidden', 403) unless scope
end end
...@@ -20,7 +20,7 @@ module Auth ...@@ -20,7 +20,7 @@ module Auth
token.issuer = registry.issuer token.issuer = registry.issuer
token.audience = AUDIENCE token.audience = AUDIENCE
token[:access] = names.map do |name| token[:access] = names.map do |name|
{ type: 'repository', name: name, actions: %w(pull push) } { type: 'repository', name: name, actions: %w(*) }
end end
token.encoded token.encoded
end end
......
%ul.nav.nav-sidebar %ul.nav.nav-sidebar
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: 'home'}) do = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do
= link_to dashboard_projects_path, title: 'Projects' do = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
= icon('bookmark fw') = icon('bookmark fw')
%span %span
Projects Projects
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
Todos Todos
%span.count.todos-pending-count= number_with_delimiter(todos_pending_count) %span.count.todos-pending-count= number_with_delimiter(todos_pending_count)
= nav_link(path: 'dashboard#activity') do = nav_link(path: 'dashboard#activity') do
= link_to activity_dashboard_path, class: 'shortcuts-activity', title: 'Activity' do = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
= icon('dashboard fw') = icon('dashboard fw')
%span %span
Activity Activity
...@@ -26,13 +26,13 @@ ...@@ -26,13 +26,13 @@
%span %span
Milestones Milestones
= nav_link(path: 'dashboard#issues') do = nav_link(path: 'dashboard#issues') do
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues' do = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
= icon('exclamation-circle fw') = icon('exclamation-circle fw')
%span %span
Issues Issues
%span.count= number_with_delimiter(current_user.assigned_issues.opened.count) %span.count= number_with_delimiter(current_user.assigned_issues.opened.count)
= nav_link(path: 'dashboard#merge_requests') do = nav_link(path: 'dashboard#merge_requests') do
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests' do = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
= icon('tasks fw') = icon('tasks fw')
%span %span
Merge Requests Merge Requests
......
%ul.nav.nav-sidebar - if current_user
- if @project.group .controls
= nav_link do - access = user_max_access_in_project(current_user.id, @project)
= link_to group_path(@project.group), title: 'Go to group', class: 'back-link' do - can_edit = can?(current_user, :admin_project, @project)
= icon('caret-square-o-left fw') .dropdown.project-settings-dropdown
%span %a.dropdown-new.btn.btn-default#project-settings-button{href: '#', 'data-toggle' => 'dropdown'}
Go to group = icon('cog')
- else = icon('caret-down')
= nav_link do %ul.dropdown-menu.dropdown-menu-align-right
= link_to root_path, title: 'Go to dashboard', class: 'back-link' do = render 'layouts/nav/project_settings'
= icon('caret-square-o-left fw') %li.divider
%span - if can_edit
Go to dashboard %li
= link_to edit_project_path(@project) do
%li.separate-item Edit Project
- if access
%li
= link_to leave_namespace_project_project_members_path(@project.namespace, @project),
data: { confirm: leave_project_message(@project) }, method: :delete, title: 'Leave project' do
Leave Project
%ul.nav-links
= nav_link(path: 'projects#show', html_options: {class: 'home'}) do = nav_link(path: 'projects#show', html_options: {class: 'home'}) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
= icon('bookmark fw') = icon('bookmark fw')
...@@ -38,20 +44,21 @@ ...@@ -38,20 +44,21 @@
%span %span
Commits Commits
- if project_nav_tab? :builds - if project_nav_tab? :pipelines
= nav_link(controller: :pipelines) do = nav_link(controller: :pipelines) do
= link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do = link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
= icon('ship fw') = icon('ship fw')
%span %span
Pipelines Pipelines
%span.count.ci_counter= number_with_delimiter(@project.ci_commits.running_or_pending.count) %span.badge.count.ci_counter= number_with_delimiter(@project.ci_commits.running_or_pending.count)
- if project_nav_tab? :builds
= nav_link(controller: %w(builds)) do = nav_link(controller: %w(builds)) do
= link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do = link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do
= icon('cubes fw') = icon('cubes fw')
%span %span
Builds Builds
%span.count.builds_counter= number_with_delimiter(@project.builds.running_or_pending.count(:all)) %span.badge.count.builds_counter= number_with_delimiter(@project.builds.running_or_pending.count(:all))
- if project_nav_tab? :container_registry - if project_nav_tab? :container_registry
= nav_link(controller: %w(container_registry)) do = nav_link(controller: %w(container_registry)) do
...@@ -81,7 +88,7 @@ ...@@ -81,7 +88,7 @@
%span %span
Issues Issues
- if @project.default_issues_tracker? - if @project.default_issues_tracker?
%span.count.issue_counter= number_with_delimiter(@project.issues.visible_to_user(current_user).opened.count) %span.badge.count.issue_counter= number_with_delimiter(@project.issues.visible_to_user(current_user).opened.count)
- if project_nav_tab? :merge_requests - if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do = nav_link(controller: :merge_requests) do
...@@ -89,14 +96,7 @@ ...@@ -89,14 +96,7 @@
= icon('tasks fw') = icon('tasks fw')
%span %span
Merge Requests Merge Requests
%span.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count) %span.badge.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count)
- if project_nav_tab? :team
= nav_link(controller: [:project_members, :teams]) do
= link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do
= icon('users fw')
%span
Members
- if project_nav_tab? :labels - if project_nav_tab? :labels
= nav_link(controller: :labels) do = nav_link(controller: :labels) do
...@@ -112,13 +112,6 @@ ...@@ -112,13 +112,6 @@
%span %span
Wiki Wiki
- if project_nav_tab? :forks
= nav_link(controller: :forks, action: :index) do
= link_to namespace_project_forks_path(@project.namespace, @project), title: 'Forks' do
= icon('code-fork fw')
%span
Forks
- if project_nav_tab? :snippets - if project_nav_tab? :snippets
= nav_link(controller: :snippets) do = nav_link(controller: :snippets) do
= link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do
...@@ -126,13 +119,6 @@ ...@@ -126,13 +119,6 @@
%span %span
Snippets Snippets
- if project_nav_tab? :settings
= nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
= link_to edit_project_path(@project), title: 'Settings' do
= icon('cogs fw')
%span
Settings
-# Global shortcut to network page for compatibility -# Global shortcut to network page for compatibility
- if project_nav_tab? :network - if project_nav_tab? :network
%li.hidden %li.hidden
......
%ul.nav.nav-sidebar - if project_nav_tab? :team
= nav_link do = nav_link(controller: [:project_members, :teams]) do
= link_to project_path(@project), title: 'Go to project', class: 'back-link' do = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do
= icon('caret-square-o-left fw')
%span %span
Go to project Members
%li.separate-item - if @project.allowed_to_share_with_group?
= nav_link(controller: :group_links) do
%ul.sidebar-subnav = link_to namespace_project_group_links_path(@project.namespace, @project), title: "Groups" do
= nav_link(path: 'projects#edit') do %span
= link_to edit_project_path(@project), title: 'Project Settings' do Groups
= icon('pencil-square-o fw') = nav_link(controller: :deploy_keys) do
%span = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys' do
Project Settings %span
- if @project.allowed_to_share_with_group? Deploy Keys
= nav_link(controller: :group_links) do = nav_link(controller: :hooks) do
= link_to namespace_project_group_links_path(@project.namespace, @project), title: "Groups" do = link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Webhooks' do
= icon('share-square-o fw') %span
%span Webhooks
Groups = nav_link(controller: :services) do
= nav_link(controller: :deploy_keys) do = link_to namespace_project_services_path(@project.namespace, @project), title: 'Services' do
= link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys' do %span
= icon('key fw') Services
%span = nav_link(controller: :protected_branches) do
Deploy Keys = link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches' do
= nav_link(controller: :hooks) do %span
= link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Webhooks' do Protected Branches
= icon('link fw')
%span
Webhooks
= nav_link(controller: :services) do
= link_to namespace_project_services_path(@project.namespace, @project), title: 'Services' do
= icon('cogs fw')
%span
Services
= nav_link(controller: :protected_branches) do
= link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches' do
= icon('lock fw')
%span
Protected Branches
- if @project.builds_enabled? - if @project.builds_enabled?
= nav_link(controller: :runners) do = nav_link(controller: :runners) do
= link_to namespace_project_runners_path(@project.namespace, @project), title: 'Runners' do = link_to namespace_project_runners_path(@project.namespace, @project), title: 'Runners' do
= icon('cog fw') %span
%span Runners
Runners = nav_link(controller: :variables) do
= nav_link(controller: :variables) do = link_to namespace_project_variables_path(@project.namespace, @project), title: 'Variables' do
= link_to namespace_project_variables_path(@project.namespace, @project), title: 'Variables' do %span
= icon('code fw') Variables
%span = nav_link(controller: :triggers) do
Variables = link_to namespace_project_triggers_path(@project.namespace, @project), title: 'Triggers' do
= nav_link(controller: :triggers) do %span
= link_to namespace_project_triggers_path(@project.namespace, @project), title: 'Triggers' do Triggers
= icon('retweet fw') = nav_link(controller: :badges) do
%span = link_to namespace_project_badges_path(@project.namespace, @project), title: 'Badges' do
Triggers %span
= nav_link(controller: :badges) do Badges
= link_to namespace_project_badges_path(@project.namespace, @project), title: 'Badges' do
= icon('star-half-empty fw')
%span
Badges
- page_title @project.name_with_namespace - page_title @project.name_with_namespace
- page_description @project.description unless page_description - page_description @project.description unless page_description
- header_title project_title(@project) unless header_title - header_title project_title(@project) unless header_title
- sidebar "project" unless sidebar - nav "project"
- content_for :scripts_body_top do - content_for :scripts_body_top do
- project = @target_project || @project - project = @target_project || @project
......
- page_title "Settings" - page_title "Settings"
- header_title project_title(@project, "Settings", edit_project_path(@project)) - nav "project"
- sidebar "project_settings"
= render template: "layouts/project" = render template: "layouts/project"
- empty_repo = @project.empty_repo? - empty_repo = @project.empty_repo?
.project-home-panel.cover-block.clearfix{:class => ("empty-project" if empty_repo)} .project-home-panel.cover-block.clearfix{:class => ("empty-project" if empty_repo)}
.project-identicon-holder .container-fluid.container-limited
= project_icon(@project, alt: '', class: 'project-avatar avatar s90') .row
.cover-title.project-home-desc .project-image-container
%h1 = project_icon(@project, alt: '', class: 'project-avatar avatar s70')
= @project.name .project-info
%span.visibility-icon.has-tooltip{data: { container: 'body' }, title: visibility_icon_description(@project)} .cover-title.project-home-desc
= visibility_level_icon(@project.visibility_level, fw: false) %h1
= @project.name
- if @project.description.present? %span.visibility-icon.has-tooltip{data: { container: 'body' }, title: visibility_icon_description(@project)}
.cover-desc.project-home-desc = visibility_level_icon(@project.visibility_level, fw: false)
= markdown(@project.description, pipeline: :description)
- if @project.description.present?
- if forked_from_project = @project.forked_from_project .cover-desc.project-home-desc
.cover-desc = markdown(@project.description, pipeline: :description)
Forked from
= link_to project_path(forked_from_project) do - if forked_from_project = @project.forked_from_project
= forked_from_project.namespace.try(:name) .cover-desc
Forked from
.cover-controls = link_to project_path(forked_from_project) do
- if current_user = forked_from_project.namespace.try(:name)
= link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), class: 'btn btn-gray' do
= icon('rss') .project-repo-buttons
- access = user_max_access_in_project(current_user.id, @project) .count-buttons
- can_edit = can?(current_user, :admin_project, @project) = render 'projects/buttons/star'
- if access || can_edit = render 'projects/buttons/fork'
%span.dropdown.project-settings-dropdown
%a.dropdown-new.btn.btn-gray#project-settings-button{href: '#', 'data-toggle' => 'dropdown'} .project-clone-holder
= icon('cog') = render "shared/clone_panel"
= icon('angle-down')
%ul.dropdown-menu.dropdown-menu-right .project-repo-buttons.btn-group.project-right-buttons
- if can_edit = render "projects/buttons/download"
%li = render 'projects/buttons/dropdown'
= link_to edit_project_path(@project) do
Edit Project
- if access
%li
= link_to leave_namespace_project_project_members_path(@project.namespace, @project),
data: { confirm: leave_project_message(@project) }, method: :delete, title: 'Leave project' do
Leave Project
.project-repo-buttons
.split-one.count-buttons
= render 'projects/buttons/star'
= render 'projects/buttons/fork'
.clone-row
.project-clone-holder
= render "shared/clone_panel"
.split-repo-buttons
.btn-group.pull-left
= render "projects/buttons/download"
= render 'projects/buttons/dropdown'
= render 'projects/buttons/notifications' = render 'projects/buttons/notifications'
:javascript :javascript
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
.btn-group .btn-group
%a.btn.dropdown-toggle{href: '#', "data-toggle" => "dropdown"} %a.btn.dropdown-toggle{href: '#', "data-toggle" => "dropdown"}
= icon('plus') = icon('plus')
%ul.dropdown-menu.dropdown-menu-right.project-home-dropdown %ul.dropdown-menu.dropdown-menu-align-right.project-home-dropdown
- can_create_issue = can?(current_user, :create_issue, @project) - can_create_issue = can?(current_user, :create_issue, @project)
- merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project)) - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
- can_create_snippet = can?(current_user, :create_snippet, @project) - can_create_snippet = can?(current_user, :create_snippet, @project)
......
...@@ -12,7 +12,8 @@ ...@@ -12,7 +12,8 @@
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has-tooltip' do = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has-tooltip' do
= icon('code-fork fw') = icon('code-fork fw')
Fork Fork
= link_to namespace_project_forks_path(@project.namespace, @project), class: 'count-with-arrow' do %div.count-with-arrow
%span.arrow %span.arrow
%span.count %span.count
= @project.forks_count = link_to namespace_project_forks_path(@project.namespace, @project) do
= @project.forks_count
- if @notification_setting - if @notification_setting
= form_for @notification_setting, url: namespace_project_notification_setting_path(@project.namespace.becomes(Namespace), @project), method: :patch, remote: true, html: { class: 'inline', id: 'notification-form' } do |f| = form_for @notification_setting, url: namespace_project_notification_setting_path(@project.namespace.becomes(Namespace), @project), method: :patch, remote: true, html: { class: 'inline', id: 'notification-form' } do |f|
= f.hidden_field :level = f.hidden_field :level
%span.dropdown .dropdown
%a.dropdown-new.btn.notifications-btn#notifications-button{href: '#', "data-toggle" => "dropdown"} %a.dropdown-new.btn.notifications-btn#notifications-button{href: '#', "data-toggle" => "dropdown"}
= icon('bell') = icon('bell')
= notification_title(@notification_setting.level) = notification_title(@notification_setting.level)
= icon('angle-down') = icon('caret-down')
%ul.dropdown-menu.dropdown-menu-right.project-home-dropdown %ul.dropdown-menu.dropdown-menu-align-right.project-home-dropdown
- NotificationSetting.levels.each do |level| - NotificationSetting.levels.each do |level|
= notification_list_item(level.first, @notification_setting) = notification_list_item(level.first, @notification_setting)
...@@ -42,18 +42,18 @@ ...@@ -42,18 +42,18 @@
%td %td
= build.name = build.name
.pull-right %td
.label-container .label-container
- if build.tags.any? - if build.tags.any?
- build.tags.each do |tag| - build.tags.each do |tag|
%span.label.label-primary %span.label.label-primary
= tag = tag
- if build.try(:trigger_request) - if build.try(:trigger_request)
%span.label.label-info triggered %span.label.label-info triggered
- if build.try(:allow_failure) - if build.try(:allow_failure)
%span.label.label-danger allowed to fail %span.label.label-danger allowed to fail
- if defined?(retried) && retried - if defined?(retried) && retried
%span.label.label-warning retried %span.label.label-warning retried
%td.duration %td.duration
- if build.duration - if build.duration
......
...@@ -37,5 +37,16 @@ ...@@ -37,5 +37,16 @@
.table-holder .table-holder
%table.table.builds %table.table.builds
%thead
%tr
%th Status
%th Build ID
%th Name
%th Tags
%th Duration
%th Finished at
- if @project.build_coverage_enabled?
%th Coverage
%th
- ci_commit.statuses.stages.each do |stage| - ci_commit.statuses.stages.each do |stage|
= render 'projects/commit/ci_stage', stage: stage, statuses: ci_commit.statuses.where(stage: stage) = render 'projects/commit/ci_stage', stage: stage, statuses: ci_commit.statuses.where(stage: stage)
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%hr %hr
%ul.content-list %ul.content-list
.light.prepend-top-default %li.light.prepend-top-default
%p %p
A 'container image' is a snapshot of a container. A 'container image' is a snapshot of a container.
You can host your container images with GitLab. You can host your container images with GitLab.
......
...@@ -13,50 +13,50 @@ ...@@ -13,50 +13,50 @@
= render "home_panel" = render "home_panel"
.project-stats.row-content-block.second-block .project-stats.row-content-block.second-block
%ul.nav .container-fluid.container-limited
%li %ul.nav
= link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
= pluralize(number_with_delimiter(@project.commit_count), 'commit')
%li
= link_to namespace_project_branches_path(@project.namespace, @project) do
= pluralize(number_with_delimiter(@repository.branch_names.count), 'branch')
%li
= link_to namespace_project_tags_path(@project.namespace, @project) do
= pluralize(number_with_delimiter(@repository.tag_names.count), 'tag')
%li
= link_to project_files_path(@project) do
= repository_size
- if default_project_view != 'readme' && @repository.readme
%li %li
= link_to 'Readme', readme_path(@project) = link_to project_files_path(@project) do
Files (#{repository_size})
- if @repository.changelog
%li %li
= link_to 'Changelog', changelog_path(@project) = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
#{'Commit'.pluralize(@project.commit_count)} (#{number_with_delimiter(@project.commit_count)})
- if @repository.license_blob
%li %li
= link_to license_short_name(@project), license_path(@project) = link_to namespace_project_branches_path(@project.namespace, @project) do
#{'Branch'.pluralize(@repository.branch_names.count)} (#{number_with_delimiter(@repository.branch_names.count)})
- if @repository.contribution_guide
%li %li
= link_to 'Contribution guide', contribution_guide_path(@project) = link_to namespace_project_tags_path(@project.namespace, @project) do
#{'Tag'.pluralize(@repository.tag_names.count)} (#{number_with_delimiter(@repository.tag_names.count)})
- if default_project_view != 'readme' && @repository.readme
%li
= link_to 'Readme', readme_path(@project)
- if @repository.changelog
%li
= link_to 'Changelog', changelog_path(@project)
- if @repository.license_blob
%li
= link_to license_short_name(@project), license_path(@project)
- if @repository.contribution_guide
%li
= link_to 'Contribution guide', contribution_guide_path(@project)
- if current_user && can_push_branch?(@project, @project.default_branch) - if current_user && can_push_branch?(@project, @project.default_branch)
- unless @repository.changelog - unless @repository.changelog
%li.missing %li.missing
= link_to add_special_file_path(@project, file_name: 'CHANGELOG') do = link_to add_special_file_path(@project, file_name: 'CHANGELOG') do
Add Changelog Add Changelog
- unless @repository.license_blob - unless @repository.license_blob
%li.missing %li.missing
= link_to add_special_file_path(@project, file_name: 'LICENSE') do = link_to add_special_file_path(@project, file_name: 'LICENSE') do
Add License Add License
- unless @repository.contribution_guide - unless @repository.contribution_guide
%li.missing %li.missing
= link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do = link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do
Add Contribution guide Add Contribution guide
- if @repository.commit - if @repository.commit
.content-block.second-block.white .content-block.second-block.white
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
%a#clone-dropdown.clone-dropdown-btn.btn{href: '#', 'data-toggle' => 'dropdown'} %a#clone-dropdown.clone-dropdown-btn.btn{href: '#', 'data-toggle' => 'dropdown'}
%span %span
= default_clone_protocol.upcase = default_clone_protocol.upcase
= icon('angle-down') = icon('caret-down')
%ul.dropdown-menu.dropdown-menu-right.clone-options-dropdown %ul.dropdown-menu.dropdown-menu-right.clone-options-dropdown
%li %li
= ssh_clone_button(project) = ssh_clone_button(project)
......
...@@ -3,4 +3,4 @@ ...@@ -3,4 +3,4 @@
- groups.each_with_index do |group, i| - groups.each_with_index do |group, i|
= render "shared/groups/group", group: group = render "shared/groups/group", group: group
- else - else
%h3 No groups found .nothing-here-block No groups found
...@@ -44,45 +44,53 @@ ...@@ -44,45 +44,53 @@
This issue is confidential and should only be visible to team members This issue is confidential and should only be visible to team members
- if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project) - if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project)
- has_due_date = issuable.has_attribute?(:due_date)
%hr %hr
.form-group .row
.issue-assignee %div{ class: (has_due_date ? "col-lg-6" : "col-sm-12") }
= f.label :assignee_id, "Assignee", class: 'control-label' .form-group.issue-assignee
.col-sm-10 = f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}"
.issuable-form-select-holder .col-sm-10{ class: ("col-lg-8" if has_due_date) }
= users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
placeholder: 'Select assignee', class: 'custom-form-control', null_user: true,
selected: issuable.assignee_id, project: @target_project || @project,
first_user: true, current_user: true, include_blank: true)
&nbsp;
= link_to 'Assign to me', '#', class: 'btn assign-to-me-link'
.form-group
.issue-milestone
= f.label :milestone_id, "Milestone", class: 'control-label'
.col-sm-10
- if milestone_options(issuable).present?
.issuable-form-select-holder .issuable-form-select-holder
= f.select(:milestone_id, milestone_options(issuable), = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
{ include_blank: true }, { class: 'select2', data: { placeholder: 'Select milestone' } }) placeholder: 'Select assignee', class: 'custom-form-control', null_user: true,
- else selected: issuable.assignee_id, project: @target_project || @project,
.prepend-top-10 first_user: true, current_user: true, include_blank: true)
%span.light No open milestones available. %div
&nbsp; = link_to 'Assign to me', '#', class: 'assign-to-me-link prepend-top-5 inline'
- if can? current_user, :admin_milestone, issuable.project .form-group.issue-milestone
= link_to 'Create new milestone', new_namespace_project_milestone_path(issuable.project.namespace, issuable.project), target: :blank = f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
.form-group .col-sm-10{ class: ("col-lg-8" if has_due_date) }
- has_labels = issuable.project.labels.any? - if milestone_options(issuable).present?
= f.label :label_ids, "Labels", class: 'control-label' .issuable-form-select-holder
.col-sm-10{ class: ('issuable-form-padding-top' if !has_labels) } = f.select(:milestone_id, milestone_options(issuable),
- if has_labels { include_blank: true }, { class: 'select2', data: { placeholder: 'Select milestone' } })
.issuable-form-select-holder - else
= f.collection_select :label_ids, issuable.project.labels.all, :id, :name, .prepend-top-10
{ selected: issuable.label_ids }, multiple: true, class: 'select2', data: { placeholder: "Select labels" } %span.light No open milestones available.
- else - if can? current_user, :admin_milestone, issuable.project
%span.light No labels yet. %div
&nbsp; = link_to 'Create new milestone', new_namespace_project_milestone_path(issuable.project.namespace, issuable.project), target: :blank, class: "prepend-top-5 inline"
- if can? current_user, :admin_label, issuable.project .form-group
= link_to 'Create new label', new_namespace_project_label_path(issuable.project.namespace, issuable.project), target: :blank - has_labels = issuable.project.labels.any?
= f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
- if has_labels
.issuable-form-select-holder
= f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
{ selected: issuable.label_ids }, multiple: true, class: 'select2', data: { placeholder: "Select labels" }
- else
%span.light No labels yet.
- if can? current_user, :admin_label, issuable.project
%div
= link_to 'Create new label', new_namespace_project_label_path(issuable.project.namespace, issuable.project), target: :blank, class: "prepend-top-5 inline"
- if has_due_date
.col-lg-6
.form-group
= f.label :due_date, "Due date", class: "control-label"
= f.hidden_field :due_date, id: "issuable-due-date"
.col-sm-10
.datepicker
- if issuable.can_move?(current_user) - if issuable.can_move?(current_user)
%hr %hr
......
...@@ -87,10 +87,16 @@ ...@@ -87,10 +87,16 @@
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
= link_to 'Edit', '#', class: 'edit-link pull-right' = link_to 'Edit', '#', class: 'edit-link pull-right'
.value.bold.hide-collapsed .value.bold.hide-collapsed
- if issuable.due_date %span.value-content
= issuable.due_date.to_s(:medium) - if issuable.due_date
- else = issuable.due_date.to_s(:medium)
.light None - else
None
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
%span.light.js-remove-due-date-holder{ class: ("hidden" if issuable.due_date.nil?) }
\-
%a.js-remove-due-date{ href: "#", role: "button" }
remove due date
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
.selectbox.hide-collapsed .selectbox.hide-collapsed
= f.hidden_field :due_date, value: issuable.due_date = f.hidden_field :due_date, value: issuable.due_date
......
...@@ -3,6 +3,6 @@ ...@@ -3,6 +3,6 @@
%li %li
= link_to user, title: user.name, class: "darken" do = link_to user, title: user.name, class: "darken" do
= image_tag avatar_icon(user, 32), class: "avatar s32" = image_tag avatar_icon(user, 32), class: "avatar s32"
%strong= truncate(user.name, lenght: 40) %strong= truncate(user.name, length: 40)
%br %br
%small.cgray= user.username %small.cgray= user.username
...@@ -179,11 +179,11 @@ production: &base ...@@ -179,11 +179,11 @@ production: &base
registry: registry:
# enabled: true # enabled: true
# host: registry.example.com # host: registry.example.com
# port: 5000 # port: 5005
# api_url: http://localhost:5000/ # api_url: http://localhost:5000/ # internal address to the registry, will be used by GitLab to directly communicate with API
# key: config/registry.key # key_path: config/registry.key
# issuer: omnibus-certificate
# path: shared/registry # path: shared/registry
# issuer: gitlab-issuer
# #
# 2. GitLab CI settings # 2. GitLab CI settings
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
- [Profile Settings](profile/README.md) - [Profile Settings](profile/README.md)
- [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat. - [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat.
- [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects. - [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects.
- [Container Registry](container_registry/README.md) Learn how to use GitLab Container Registry.
- [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects. - [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
- [Webhooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project. - [Webhooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
- [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN. - [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
...@@ -41,8 +42,10 @@ ...@@ -41,8 +42,10 @@
- [Git LFS configuration](workflow/lfs/lfs_administration.md) - [Git LFS configuration](workflow/lfs/lfs_administration.md)
- [Housekeeping](administration/housekeeping.md) Keep your Git repository tidy and fast. - [Housekeeping](administration/housekeeping.md) Keep your Git repository tidy and fast.
- [GitLab Performance Monitoring](monitoring/performance/introduction.md) Configure GitLab and InfluxDB for measuring performance metrics - [GitLab Performance Monitoring](monitoring/performance/introduction.md) Configure GitLab and InfluxDB for measuring performance metrics
- [Monitoring uptime](monitoring/health_check.md) Check the server status using the health check endpoint
- [Sidekiq Troubleshooting](administration/troubleshooting/sidekiq.md) Debug when Sidekiq appears hung and is not processing jobs - [Sidekiq Troubleshooting](administration/troubleshooting/sidekiq.md) Debug when Sidekiq appears hung and is not processing jobs
- [High Availability](administration/high_availability/README.md) Configure multiple servers for scaling or high availability - [High Availability](administration/high_availability/README.md) Configure multiple servers for scaling or high availability
- [Container Registry](administration/container_registry.md) Configure Docker Registry with GitLab
## Contributor documentation ## Contributor documentation
......
# GitLab Container Registry Administration
> **Note:**
This feature was [introduced][ce-4040] in GitLab 8.8.
With the Docker Container Registry integrated into GitLab, every project can
have its own space to store its Docker images.
You can read more about Docker Registry at https://docs.docker.com/registry/introduction/.
---
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Enable the Container Registry](#enable-the-container-registry)
- [Container Registry domain configuration](#container-registry-domain-configuration)
- [Configure Container Registry under an existing GitLab domain](#configure-container-registry-under-an-existing-gitlab-domain)
- [Configure Container Registry under its own domain](#configure-container-registry-under-its-own-domain)
- [Disable Container Registry site-wide](#disable-container-registry-site-wide)
- [Disable Container Registry per project](#disable-container-registry-per-project)
- [Disable Container Registry for new projects site-wide](#disable-container-registry-for-new-projects-site-wide)
- [Container Registry storage path](#container-registry-storage-path)
- [Storage limitations](#storage-limitations)
- [Changelog](#changelog)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Enable the Container Registry
**Omnibus GitLab installations**
All you have to do is configure the domain name under which the Container
Registry will listen to. Read [#container-registry-domain-configuration](#container-registry-domain-configuration)
and pick one of the two options that fits your case.
>**Note:**
The container Registry works under HTTPS by default. Using HTTP is possible
but not recommended and out of the scope of this document.
Read the [insecure Registry documentation][docker-insecure] if you want to
implement this.
---
**Installations from source**
If you have installed GitLab from source:
1. You will have to [install Docker Registry][registry-deploy] by yourself.
1. After the installation is complete, you will have to configure the Registry's
settings in `gitlab.yml` in order to enable it.
1. Use the sample NGINX configuration file that is found under
[`lib/support/nginx/registry-ssl`][registry-ssl] and edit it to match the
`host`, `port` and TLS certs paths.
The contents of `gitlab.yml` are:
```
registry:
enabled: true
host: registry.gitlab.example.com
port: 5005
api_url: http://localhost:5000/
key_path: config/registry.key
path: shared/registry
issuer: gitlab-issuer
```
where:
| Parameter | Description |
| --------- | ----------- |
| `enabled` | `true` or `false`. Enables the Registry in GitLab. By default this is `false`. |
| `host` | The host URL under which the Registry will run and the users will be able to use. |
| `port` | The port under which the external Registry domain will listen on. |
| `api_url` | The internal API URL under which the Registry is exposed to. It defaults to `http://localhost:5000`. |
| `key_path`| The private key location that is a pair of Registry's `rootcertbundle`. Read the [token auth configuration documentation][token-config]. |
| `path` | This should be the same directory like specified in Registry's `rootdirectory`. Read the [storage configuration documentation][storage-config]. This path needs to be readable by the GitLab user, the web-server user and the Registry user. Read more in [#container-registry-storage-path](#container-registry-storage-path). |
| `issuer` | This should be the same value as configured in Registry's `issuer`. Read the [token auth configuration documentation][token-config]. |
>**Note:**
GitLab does not ship with a Registry init file. Hence, [restarting GitLab][restart gitlab]
will not restart the Registry should you modify its settings. Read the upstream
documentation on how to achieve that.
## Container Registry domain configuration
There are two ways you can configure the Registry's external domain.
- Either [use the existing GitLab domain][existing-domain] where in that case
the Registry will have to listen on a port and reuse GitLab's TLS certificate,
- or [use a completely separate domain][new-domain] with a new TLS certificate
for that domain.
Since the container Registry requires a TLS certificate, in the end it all boils
down to how easy or pricey is to get a new one.
Please take this into consideration before configuring the Container Registry
for the first time.
### Configure Container Registry under an existing GitLab domain
If the Registry is configured to use the existing GitLab domain, you can
expose the Registry on a port so that you can reuse the existing GitLab TLS
certificate.
Assuming that the GitLab domain is `https://gitlab.example.com` and the port the
Registry is exposed to the outside world is `4567`, here is what you need to set
in `gitlab.rb` or `gitlab.yml` if you are using Omnibus GitLab or installed
GitLab from source respectively.
---
**Omnibus GitLab installations**
1. Your `/etc/gitlab/gitlab.rb` should contain the Registry URL as well as the
path to the existing TLS certificate and key used by GitLab:
```ruby
registry_external_url 'https://gitlab.example.com:4567'
```
Note how the `registry_external_url` is listening on HTTPS under the
existing GitLab URL, but on a different port.
If your TLS certificate is not in `/etc/gitlab/ssl/gitlab.example.com.crt`
and key not in `/etc/gitlab/ssl/gitlab.example.com.key` uncomment the lines
below:
```ruby
registry_nginx['ssl_certificate'] = "/path/to/certificate.pem"
registry_nginx['ssl_certificate_key'] = "/path/to/certificate.key"
```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
---
**Installations from source**
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
configure it with the following settings:
```
registry:
enabled: true
host: gitlab.example.com
port: 4567
```
1. Save the file and [restart GitLab][] for the changes to take effect.
1. Make the relevant changes in NGINX as well (domain, port, TLS certificates path).
---
Users should now be able to login to the Container Registry with their GitLab
credentials using:
```bash
docker login gitlab.example.com:4567
```
### Configure Container Registry under its own domain
If the Registry is configured to use its own domain, you will need a TLS
certificate for that specific domain (e.g., `registry.example.com`) or maybe
a wildcard certificate if hosted under a subdomain of your existing GitLab
domain (e.g., `registry.gitlab.example.com`).
Let's assume that you want the container Registry to be accessible at
`https://registry.gitlab.example.com`.
---
**Omnibus GitLab installations**
1. Place your TLS certificate and key in
`/etc/gitlab/ssl/registry.gitlab.example.com.crt` and
`/etc/gitlab/ssl/registry.gitlab.example.com.key` and make sure they have
correct permissions:
```bash
chmod 600 /etc/gitlab/ssl/registry.gitlab.example.com.*
```
1. Once the TLS certificate is in place, edit `/etc/gitlab/gitlab.rb` with:
```ruby
registry_external_url 'https://registry.gitlab.example.com'
```
Note how the `registry_external_url` is listening on HTTPS.
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
> **Note:**
If you have a [wildcard certificate][], you need to specify the path to the
certificate in addition to the URL, in this case `/etc/gitlab/gitlab.rb` will
look like:
>
```ruby
registry_nginx['ssl_certificate'] = "/etc/gitlab/ssl/certificate.pem"
registry_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/certificate.key"
```
---
**Installations from source**
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
configure it with the following settings:
```
registry:
enabled: true
host: registry.gitlab.example.com
```
1. Save the file and [restart GitLab][] for the changes to take effect.
1. Make the relevant changes in NGINX as well (domain, port, TLS certificates path).
---
Users should now be able to login to the Container Registry using their GitLab
credentials:
```bash
docker login registry.gitlab.example.com
```
## Disable Container Registry site-wide
>**Note:**
Disabling the Registry in the Rails GitLab application as set by the following
steps, will not remove any existing Docker images. This is handled by the
Registry application itself.
**Omnibus GitLab**
1. Open `/etc/gitlab/gitlab.rb` and set `registry['enable']` to `false`:
```ruby
registry['enable'] = false
```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
---
**Installations from source**
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
set `enabled` to `false`:
```
registry:
enabled: false
```
1. Save the file and [restart GitLab][] for the changes to take effect.
## Disable Container Registry per project
If Registry is enabled in your GitLab instance, but you don't need it for your
project, you can disable it from your project's settings. Read the user guide
on how to achieve that.
## Disable Container Registry for new projects site-wide
If the Container Registry is enabled, then it will be available on all new
projects. To disable this function and let the owners of a project to enable
the Container Registry by themselves, follow the steps below.
---
**Omnibus GitLab installations**
1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
```ruby
gitlab_rails['gitlab_default_projects_features_container_registry'] = false
```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
---
**Installations from source**
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `default_projects_features`
entry and configure it so that `container_registry` is set to `false`:
```
## Default project features settings
default_projects_features:
issues: true
merge_requests: true
wiki: true
snippets: false
builds: true
container_registry: false
```
1. Save the file and [restart GitLab][] for the changes to take effect.
## Container Registry storage path
To change the storage path where Docker images will be stored, follow the
steps below.
This path is accessible to:
- the user running the Container Registry daemon,
- the user running GitLab
> **Warning** You should confirm that all GitLab, Registry and web server users
have access to this directory.
---
**Omnibus GitLab installations**
The default location where images are stored in Omnibus, is
`/var/opt/gitlab/gitlab-rails/shared/registry`. To change it:
1. Edit `/etc/gitlab/gitlab.rb`:
```ruby
gitlab_rails['registry_path'] = "/path/to/registry/storage"
```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
---
**Installations from source**
The default location where images are stored in source installations, is
`/home/git/gitlab/shared/registry`. To change it:
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
change the `path` setting:
```
registry:
path: shared/registry
```
1. Save the file and [restart GitLab][] for the changes to take effect.
## Storage limitations
Currently, there is no storage limitation, which means a user can upload an
infinite amount of Docker images with arbitrary sizes. This setting will be
configurable in future releases.
## Changelog
**GitLab 8.8 ([source docs][8-8-docs])**
- GitLab Container Registry feature was introduced.
[reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure
[restart gitlab]: restart_gitlab.md#installations-from-source
[wildcard certificate]: https://en.wikipedia.org/wiki/Wildcard_certificate
[ce-4040]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4040
[docker-insecure]: https://docs.docker.com/registry/insecure/
[registry-deploy]: https://docs.docker.com/registry/deploying/
[storage-config]: https://docs.docker.com/registry/configuration/#storage
[token-config]: https://docs.docker.com/registry/configuration/#token
[8-8-docs]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-8-stable/doc/administration/container_registry.md
[registry-ssl]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/support/nginx/registry-ssl
[existing-domain]: #configure-container-registry-under-an-existing-gitlab-domain
[new-domain]: #configure-container-registry-under-its-own-domain
...@@ -265,7 +265,6 @@ GET /groups/:id/members ...@@ -265,7 +265,6 @@ GET /groups/:id/members
{ {
"id": 1, "id": 1,
"username": "raymond_smith", "username": "raymond_smith",
"email": "ray@smith.org",
"name": "Raymond Smith", "name": "Raymond Smith",
"state": "active", "state": "active",
"created_at": "2012-10-22T14:13:35Z", "created_at": "2012-10-22T14:13:35Z",
...@@ -274,7 +273,6 @@ GET /groups/:id/members ...@@ -274,7 +273,6 @@ GET /groups/:id/members
{ {
"id": 2, "id": 2,
"username": "john_doe", "username": "john_doe",
"email": "joh@doe.org",
"name": "John Doe", "name": "John Doe",
"state": "active", "state": "active",
"created_at": "2012-10-22T14:13:35Z", "created_at": "2012-10-22T14:13:35Z",
......
# GitLab Container Registry
> **Note:**
This feature was [introduced][ce-4040] in GitLab 8.8.
> **Note:**
This document is about the user guide. To learn how to enable GitLab Container
Registry across your GitLab instance, visit the
[administrator documentation](../administration/container_registry.md).
With the Docker Container Registry integrated into GitLab, every project can
have its own space to store its Docker images.
You can read more about Docker Registry at https://docs.docker.com/registry/introduction/.
---
## Enable the Container Registry for your project
1. First, ask your system administrator to enable GitLab Container Registry
following the [administration documentation](../administration/container_registry.md).
If you are using GitLab.com, this is enabled by default so you can start using
the Registry immediately.
1. Go to your project's settings and enable the **Container Registry** feature
on your project. For new projects this might be enabled by default. For
existing projects you will have to explicitly enable it.
![Enable Container Registry](img/project_feature.png)
## Build and push images
After you save your project's settings, you should see a new link in the
sidebar called **Container Registry**. Following this link will get you to
your project's Registry panel where you can see how to login to the Container
Registry using your GitLab credentials.
For example if the Registry's URL is `registry.example.com`, the you should be
able to login with:
```
docker login registry.example.com
```
Building and publishing images should be a straightforward process. Just make
sure that you are using the Registry URL with the namespace and project name
that is hosted on GitLab:
```
docker build -t registry.example.com/group/project .
docker push registry.example.com/group/project
```
## Use images from GitLab Container Registry
To download and run a container from images hosted in GitLab Container Registry,
use `docker run`:
```
docker run [options] registry.example.com/group/project [arguments]
```
For more information on running Docker containers, visit the
[Docker documentation][docker-docs].
## Control Container Registry from within GitLab
GitLab offers a simple Container Registry management panel. Go to your project
and click **Container Registry** in the left sidebar.
This view will show you all tags in your project and will easily allow you to
delete them.
![Container Registry panel](img/container_registry.png)
## Build and push images using GitLab CI
> **Note:**
This feature requires GitLab 8.8 and GitLab Runner 1.2.
Make sure that your GitLab Runner is configured to allow building docker images.
You have to check the [Using Docker Build documentation](../../ci/docker/using_docker_build.md).
You can use [docker:dind](https://hub.docker.com/_/docker/) to build your images,
and this is how `.gitlab-ci.yml` should look like:
```
build_image:
image: docker:git
services:
- docker:dind
stage: build
script:
- docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.example.com
- docker build -t registry.example.com/group/project:latest .
- docker push registry.example.com/group/project:latest
```
You have to use the credentials of the special `gitlab-ci-token` user with its
password stored in `$CI_BUILD_TOKEN` in order to push to the Registry connected
to your project. This allows you to automated building and deployment of your
Docker images.
## Limitations
In order to use a container image from your private project as an `image:` in
your `.gitlab-ci.yml`, you have to follow the
[Using a private Docker Registry][private-docker]
documentation. This workflow will be simplified in the future.
[ce-4040]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4040
[docker-docs]: https://docs.docker.com/engine/userguide/intro/
[private-docker]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/configuration/advanced-configuration.md#using-a-private-docker-registry
...@@ -394,7 +394,7 @@ GitLab Shell is an SSH access and repository management software developed speci ...@@ -394,7 +394,7 @@ GitLab Shell is an SSH access and repository management software developed speci
cd /home/git cd /home/git
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
cd gitlab-workhorse cd gitlab-workhorse
sudo -u git -H git checkout v0.7.1 sudo -u git -H git checkout v0.7.3
sudo -u git -H make sudo -u git -H make
### Initialize Database and Activate Advanced Features ### Initialize Database and Activate Advanced Features
......
# Health Check
>**Note:** This feature was [introduced][ce-3888] in GitLab 8.8.
GitLab provides a health check endpoint for uptime monitoring on the `health_check` web
endpoint. The health check reports on the overall system status based on the status of
the database connection, the state of the database migrations, and the ability to write
and access the cache. This endpoint can be provided to uptime monitoring services like
[Pingdom][pingdom], [Nagios][nagios-health], and [NewRelic][newrelic-health].
## Access Token
An access token needs to be provided while accessing the health check endpoint. The current
accepted token can be found on the `admin/heath_check` page of your GitLab instance.
![access token](img/health_check_token.png)
The access token can be passed as a URL parameter:
```
https://gitlab.example.com/health_check.json?token=ACCESS_TOKEN
```
or as an HTTP header:
```bash
curl -H "TOKEN: ACCESS_TOKEN" https://gitlab.example.com/health_check.json
```
## Using the Endpoint
Once you have the access token, health information can be retrieved as plain text, JSON,
or XML using the `health_check` endpoint:
- `https://gitlab.example.com/health_check?token=ACCESS_TOKEN`
- `https://gitlab.example.com/health_check.json?token=ACCESS_TOKEN`
- `https://gitlab.example.com/health_check.xml?token=ACCESS_TOKEN`
You can also ask for the status of specific services:
- `https://gitlab.example.com/health_check/cache.json?token=ACCESS_TOKEN`
- `https://gitlab.example.com/health_check/database.json?token=ACCESS_TOKEN`
- `https://gitlab.example.com/health_check/migrations.json?token=ACCESS_TOKEN`
For example, the JSON output of the following health check:
```bash
curl -H "TOKEN: ACCESS_TOKEN" https://gitlab.example.com/health_check.json
```
would be like:
```
{"healthy":true,"message":"success"}
```
## Status
On failure, the endpoint will return a `500` HTTP status code. On success, the endpoint
will return a valid successful HTTP status code, and a `success` message. Ideally your
uptime monitoring should look for the success message.
[ce-3888]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3888
[pingdom]: https://www.pingdom.com
[nagios-health]: https://nagios-plugins.org/doc/man/check_http.html
[newrelic-health]: https://docs.newrelic.com/docs/alerts/alert-policies/downtime-alerts/availability-monitoring
...@@ -30,11 +30,6 @@ Feature: Project Active Tab ...@@ -30,11 +30,6 @@ Feature: Project Active Tab
Then the active main tab should be Merge Requests Then the active main tab should be Merge Requests
And no other main tabs should be active And no other main tabs should be active
Scenario: On Project Members
Given I visit my project's members page
Then the active main tab should be Members
And no other main tabs should be active
Scenario: On Project Wiki Scenario: On Project Wiki
Given I visit my project's wiki page Given I visit my project's wiki page
Then the active main tab should be Wiki Then the active main tab should be Wiki
...@@ -49,13 +44,6 @@ Feature: Project Active Tab ...@@ -49,13 +44,6 @@ Feature: Project Active Tab
# Sub Tabs: Settings # Sub Tabs: Settings
Scenario: On Project Settings/Edit
Given I visit my project's settings page
And I click the "Edit" tab
Then the active sub nav should be Edit
And no other sub navs should be active
And the active main tab should be Settings
Scenario: On Project Settings/Hooks Scenario: On Project Settings/Hooks
Given I visit my project's settings page Given I visit my project's settings page
And I click the "Hooks" tab And I click the "Hooks" tab
...@@ -70,6 +58,12 @@ Feature: Project Active Tab ...@@ -70,6 +58,12 @@ Feature: Project Active Tab
And no other sub navs should be active And no other sub navs should be active
And the active main tab should be Settings And the active main tab should be Settings
Scenario: On Project Members
Given I visit my project's members page
Then the active sub nav should be Members
And no other sub navs should be active
And the active main tab should be Settings
# Sub Tabs: Commits # Sub Tabs: Commits
Scenario: On Project Commits/Commits Scenario: On Project Commits/Commits
......
...@@ -18,15 +18,6 @@ Feature: Project ...@@ -18,15 +18,6 @@ Feature: Project
Then I should see the default project avatar Then I should see the default project avatar
And I should not see the "Remove avatar" button And I should not see the "Remove avatar" button
Scenario: I should have back to group button
And project "Shop" belongs to group
And I visit project "Shop" page
Then I should see back to group button
Scenario: I should have back to group button
And I visit project "Shop" page
Then I should see back to dashboard button
Scenario: I should have readme on page Scenario: I should have readme on page
And I visit project "Shop" page And I visit project "Shop" page
Then I should see project "Shop" README Then I should see project "Shop" README
......
class Spinach::Features::AdminActiveTab < Spinach::FeatureSteps class Spinach::Features::AdminActiveTab < Spinach::FeatureSteps
include SharedAuthentication include SharedAuthentication
include SharedPaths include SharedPaths
include SharedActiveTab include SharedSidebarActiveTab
step 'the active main tab should be Home' do step 'the active main tab should be Home' do
ensure_active_main_tab('Overview') ensure_active_main_tab('Overview')
...@@ -34,4 +34,12 @@ class Spinach::Features::AdminActiveTab < Spinach::FeatureSteps ...@@ -34,4 +34,12 @@ class Spinach::Features::AdminActiveTab < Spinach::FeatureSteps
step 'the active main tab should be Messages' do step 'the active main tab should be Messages' do
ensure_active_main_tab('Messages') ensure_active_main_tab('Messages')
end end
step 'no other main tabs should be active' do
expect(page).to have_selector('.nav-sidebar > li.active', count: 1)
end
def ensure_active_main_tab(content)
expect(find('.nav-sidebar > li.active')).to have_content(content)
end
end end
class Spinach::Features::DashboardActiveTab < Spinach::FeatureSteps class Spinach::Features::DashboardActiveTab < Spinach::FeatureSteps
include SharedAuthentication include SharedAuthentication
include SharedPaths include SharedPaths
include SharedActiveTab include SharedSidebarActiveTab
step 'the active main tab should be Help' do
ensure_active_main_tab('Help')
end
end end
...@@ -2,5 +2,6 @@ class Spinach::Features::DashboardShortcuts < Spinach::FeatureSteps ...@@ -2,5 +2,6 @@ class Spinach::Features::DashboardShortcuts < Spinach::FeatureSteps
include SharedAuthentication include SharedAuthentication
include SharedPaths include SharedPaths
include SharedProject include SharedProject
include SharedActiveTab include SharedSidebarActiveTab
include SharedShortcuts
end end
...@@ -22,8 +22,4 @@ class Spinach::Features::ProfileActiveTab < Spinach::FeatureSteps ...@@ -22,8 +22,4 @@ class Spinach::Features::ProfileActiveTab < Spinach::FeatureSteps
step 'the active main tab should be Audit Log' do step 'the active main tab should be Audit Log' do
ensure_active_main_tab('Audit Log') ensure_active_main_tab('Audit Log')
end end
def ensure_active_main_tab(content)
expect(find('.layout-nav li.active')).to have_content(content)
end
end end
...@@ -16,12 +16,14 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps ...@@ -16,12 +16,14 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
end end
step 'I click the "Snippets" tab' do step 'I click the "Snippets" tab' do
click_link('Snippets') page.within('.layout-nav') do
click_link('Snippets')
end
end end
step 'I click the "Edit" tab' do step 'I click the "Edit Project"' do
page.within '.sidebar-subnav' do page.within '.layout-nav .controls' do
click_link('Project Settings') click_link('Edit Project')
end end
end end
...@@ -33,14 +35,10 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps ...@@ -33,14 +35,10 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
click_link('Deploy Keys') click_link('Deploy Keys')
end end
step 'the active sub nav should be Team' do step 'the active sub nav should be Members' do
ensure_active_sub_nav('Members') ensure_active_sub_nav('Members')
end end
step 'the active sub nav should be Edit' do
ensure_active_sub_nav('Project')
end
step 'the active sub nav should be Hooks' do step 'the active sub nav should be Hooks' do
ensure_active_sub_nav('Webhooks') ensure_active_sub_nav('Webhooks')
end end
...@@ -56,7 +54,9 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps ...@@ -56,7 +54,9 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
end end
step 'I click the "Branches" tab' do step 'I click the "Branches" tab' do
click_link('Branches') page.within '.content' do
click_link('Branches')
end
end end
step 'I click the "Tags" tab' do step 'I click the "Tags" tab' do
...@@ -82,11 +82,15 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps ...@@ -82,11 +82,15 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
# Sub Tabs: Issues # Sub Tabs: Issues
step 'I click the "Milestones" tab' do step 'I click the "Milestones" tab' do
click_link('Milestones') page.within('.layout-nav') do
click_link('Milestones')
end
end end
step 'I click the "Labels" tab' do step 'I click the "Labels" tab' do
click_link('Labels') page.within('.layout-nav') do
click_link('Labels')
end
end end
step 'the active sub tab should be Issues' do step 'the active sub tab should be Issues' do
......
...@@ -36,7 +36,7 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps ...@@ -36,7 +36,7 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps
end end
step 'I goto the Merge Requests page' do step 'I goto the Merge Requests page' do
page.within '.page-sidebar-expanded' do page.within '.layout-nav' do
click_link "Merge Requests" click_link "Merge Requests"
end end
end end
......
...@@ -114,7 +114,9 @@ class Spinach::Features::Project < Spinach::FeatureSteps ...@@ -114,7 +114,9 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end end
step 'I should not see "Snippets" button' do step 'I should not see "Snippets" button' do
expect(page).not_to have_link 'Snippets' page.within '.content' do
expect(page).not_to have_link 'Snippets'
end
end end
step 'project "Shop" belongs to group' do step 'project "Shop" belongs to group' do
...@@ -123,14 +125,6 @@ class Spinach::Features::Project < Spinach::FeatureSteps ...@@ -123,14 +125,6 @@ class Spinach::Features::Project < Spinach::FeatureSteps
@project.save! @project.save!
end end
step 'I should see back to dashboard button' do
expect(page).to have_content 'Go to dashboard'
end
step 'I should see back to group button' do
expect(page).to have_content 'Go to group'
end
step 'I click notifications drop down button' do step 'I click notifications drop down button' do
click_link 'notifications-button' click_link 'notifications-button'
end end
......
...@@ -52,7 +52,7 @@ class Spinach::Features::ProjectMilestone < Spinach::FeatureSteps ...@@ -52,7 +52,7 @@ class Spinach::Features::ProjectMilestone < Spinach::FeatureSteps
end end
step 'I click link "Labels"' do step 'I click link "Labels"' do
page.within('.nav-links') do page.within('.layout-nav .nav-links') do
page.find(:xpath, "//a[@href='#tab-labels']").click page.find(:xpath, "//a[@href='#tab-labels']").click
end end
end end
......
...@@ -3,6 +3,7 @@ class Spinach::Features::ProjectShortcuts < Spinach::FeatureSteps ...@@ -3,6 +3,7 @@ class Spinach::Features::ProjectShortcuts < Spinach::FeatureSteps
include SharedPaths include SharedPaths
include SharedProject include SharedProject
include SharedProjectTab include SharedProjectTab
include SharedShortcuts
step 'I press "g" and "f"' do step 'I press "g" and "f"' do
find('body').native.send_key('g') find('body').native.send_key('g')
......
...@@ -337,13 +337,15 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -337,13 +337,15 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end end
step 'I should see buttons for allowed commands' do step 'I should see buttons for allowed commands' do
expect(page).to have_content 'Raw' page.within '.content' do
expect(page).to have_content 'History' expect(page).to have_content 'Raw'
expect(page).to have_content 'Permalink' expect(page).to have_content 'History'
expect(page).not_to have_content 'Edit' expect(page).to have_content 'Permalink'
expect(page).not_to have_content 'Blame' expect(page).not_to have_content 'Edit'
expect(page).to have_content 'Delete' expect(page).not_to have_content 'Blame'
expect(page).to have_content 'Replace' expect(page).to have_content 'Delete'
expect(page).to have_content 'Replace'
end
end end
step 'I should see a notice about a new fork having been created' do step 'I should see a notice about a new fork having been created' do
......
...@@ -2,7 +2,7 @@ module SharedActiveTab ...@@ -2,7 +2,7 @@ module SharedActiveTab
include Spinach::DSL include Spinach::DSL
def ensure_active_main_tab(content) def ensure_active_main_tab(content)
expect(find('.nav-sidebar > li.active')).to have_content(content) expect(find('.layout-nav li.active')).to have_content(content)
end end
def ensure_active_sub_tab(content) def ensure_active_sub_tab(content)
...@@ -10,11 +10,11 @@ module SharedActiveTab ...@@ -10,11 +10,11 @@ module SharedActiveTab
end end
def ensure_active_sub_nav(content) def ensure_active_sub_nav(content)
expect(find('.sidebar-subnav > li.active')).to have_content(content) expect(find('.layout-nav .controls li.active')).to have_content(content)
end end
step 'no other main tabs should be active' do step 'no other main tabs should be active' do
expect(page).to have_selector('.nav-sidebar > li.active', count: 1) expect(page).to have_selector('.layout-nav .nav-links > li.active', count: 1)
end end
step 'no other sub tabs should be active' do step 'no other sub tabs should be active' do
...@@ -22,26 +22,6 @@ module SharedActiveTab ...@@ -22,26 +22,6 @@ module SharedActiveTab
end end
step 'no other sub navs should be active' do step 'no other sub navs should be active' do
expect(page).to have_selector('.sidebar-subnav > li.active', count: 1) expect(page).to have_selector('.layout-nav .controls li.active', count: 1)
end
step 'the active main tab should be Home' do
ensure_active_main_tab('Projects')
end
step 'the active main tab should be Projects' do
ensure_active_main_tab('Projects')
end
step 'the active main tab should be Issues' do
ensure_active_main_tab('Issues')
end
step 'the active main tab should be Merge Requests' do
ensure_active_main_tab('Merge Requests')
end
step 'the active main tab should be Help' do
ensure_active_main_tab('Help')
end end
end end
...@@ -111,7 +111,7 @@ module SharedIssuable ...@@ -111,7 +111,7 @@ module SharedIssuable
step 'I sort the list by "Oldest updated"' do step 'I sort the list by "Oldest updated"' do
find('button.dropdown-toggle.btn').click find('button.dropdown-toggle.btn').click
page.within('ul.dropdown-menu.dropdown-menu-align-right li') do page.within('.content ul.dropdown-menu.dropdown-menu-align-right li') do
click_link "Oldest updated" click_link "Oldest updated"
end end
end end
...@@ -119,7 +119,7 @@ module SharedIssuable ...@@ -119,7 +119,7 @@ module SharedIssuable
step 'I sort the list by "Least popular"' do step 'I sort the list by "Least popular"' do
find('button.dropdown-toggle.btn').click find('button.dropdown-toggle.btn').click
page.within('ul.dropdown-menu.dropdown-menu-align-right li') do page.within('.content ul.dropdown-menu.dropdown-menu-align-right li') do
click_link 'Least popular' click_link 'Least popular'
end end
end end
...@@ -127,13 +127,13 @@ module SharedIssuable ...@@ -127,13 +127,13 @@ module SharedIssuable
step 'I sort the list by "Most popular"' do step 'I sort the list by "Most popular"' do
find('button.dropdown-toggle.btn').click find('button.dropdown-toggle.btn').click
page.within('ul.dropdown-menu.dropdown-menu-align-right li') do page.within('.content ul.dropdown-menu.dropdown-menu-align-right li') do
click_link 'Most popular' click_link 'Most popular'
end end
end end
step 'The list should be sorted by "Oldest updated"' do step 'The list should be sorted by "Oldest updated"' do
page.within('div.dropdown.inline.prepend-left-10') do page.within('.content div.dropdown.inline.prepend-left-10') do
expect(page.find('button.dropdown-toggle.btn')).to have_content('Oldest updated') expect(page.find('button.dropdown-toggle.btn')).to have_content('Oldest updated')
end end
end end
......
...@@ -41,9 +41,7 @@ module SharedProjectTab ...@@ -41,9 +41,7 @@ module SharedProjectTab
end end
step 'the active main tab should be Settings' do step 'the active main tab should be Settings' do
page.within '.nav-sidebar' do expect(page).to have_selector('.layout-nav .nav-links > li.active', count: 0)
expect(page).to have_content('Go to project')
end
end end
step 'the active main tab should be Activity' do step 'the active main tab should be Activity' do
......
module SharedActiveTab module SharedShortcuts
include Spinach::DSL include Spinach::DSL
step 'I press "g" and "p"' do step 'I press "g" and "p"' do
......
module SharedSidebarActiveTab
include Spinach::DSL
step 'the active main tab should be Help' do
ensure_active_main_tab('Help')
end
step 'no other main tabs should be active' do
expect(page).to have_selector('.nav-sidebar > li.active', count: 1)
end
def ensure_active_main_tab(content)
expect(find('.nav-sidebar li.active')).to have_content(content)
end
step 'the active main tab should be Home' do
ensure_active_main_tab('Projects')
end
step 'the active main tab should be Projects' do
ensure_active_main_tab('Projects')
end
step 'the active main tab should be Issues' do
ensure_active_main_tab('Issues')
end
step 'the active main tab should be Merge Requests' do
ensure_active_main_tab('Merge Requests')
end
step 'the active main tab should be Help' do
ensure_active_main_tab('Help')
end
end
...@@ -29,7 +29,7 @@ module API ...@@ -29,7 +29,7 @@ module API
@current_user @current_user
end end
def sudo_identifier() def sudo_identifier
identifier ||= params[SUDO_PARAM] || env[SUDO_HEADER] identifier ||= params[SUDO_PARAM] || env[SUDO_HEADER]
# Regex for integers # Regex for integers
......
...@@ -98,7 +98,7 @@ module Ci ...@@ -98,7 +98,7 @@ module Ci
open_new_tag open_new_tag
s = StringScanner.new(ansi) s = StringScanner.new(ansi)
while(!s.eos?) until s.eos?
if s.scan(/\e([@-_])(.*?)([@-~])/) if s.scan(/\e([@-_])(.*?)([@-~])/)
handle_sequence(s) handle_sequence(s)
elsif s.scan(/\e(([@-_])(.*?)?)?$/) elsif s.scan(/\e(([@-_])(.*?)?)?$/)
......
...@@ -64,7 +64,8 @@ module Ci ...@@ -64,7 +64,8 @@ module Ci
commits.each do |commit| commits.each do |commit|
@labels << commit.short_sha @labels << commit.short_sha
@build_times << (commit.duration / 60) duration = commit.duration || 0
@build_times << (duration / 60)
end end
end end
end end
......
...@@ -121,7 +121,7 @@ module Gitlab ...@@ -121,7 +121,7 @@ module Gitlab
def get(url) def get(url)
response = api.get(url) response = api.get(url)
raise Unauthorized if (400..499).include?(response.code.to_i) raise Unauthorized if (400..499).cover?(response.code.to_i)
response response
end end
......
...@@ -39,7 +39,15 @@ module Gitlab ...@@ -39,7 +39,15 @@ module Gitlab
def update_column_in_batches(table, column, value) def update_column_in_batches(table, column, value)
quoted_table = quote_table_name(table) quoted_table = quote_table_name(table)
quoted_column = quote_column_name(column) quoted_column = quote_column_name(column)
quoted_value = quote(value)
##
# Workaround for #17711
#
# It looks like for MySQL `ActiveRecord::Base.conntection.quote(true)`
# returns correct value (1), but `ActiveRecord::Migration.new.quote`
# returns incorrect value ('true'), which causes migrations to fail.
#
quoted_value = connection.quote(value)
processed = 0 processed = 0
total = exec_query("SELECT COUNT(*) AS count FROM #{quoted_table}"). total = exec_query("SELECT COUNT(*) AS count FROM #{quoted_table}").
......
## Lines starting with two hashes (##) are comments with information.
## Lines starting with one hash (#) are configuration parameters that can be uncommented.
##
###################################
## configuration ##
###################################
## Redirects all HTTP traffic to the HTTPS host
server {
listen *:80;
server_name registry.gitlab.example.com;
server_tokens off; ## Don't show the nginx version number, a security best practice
return 301 https://$http_host:$request_uri;
access_log /var/log/nginx/gitlab_registry_access.log gitlab_access;
error_log /var/log/nginx/gitlab_registry_error.log;
}
server {
# If a different port is specified in https://gitlab.com/gitlab-org/gitlab-ce/blob/8-8-stable/config/gitlab.yml.example#L182,
# it should be declared here as well
listen *:443 ssl http2;
server_name registry.gitlab.example.com;
server_tokens off; ## Don't show the nginx version number, a security best practice
client_max_body_size 0;
chunked_transfer_encoding on;
## Strong SSL Security
## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/
ssl on;
ssl_certificate /etc/gitlab/ssl/registry.gitlab.example.com.crt
ssl_certificate_key /etc/gitlab/ssl/registry.gitlab.example.com.key
ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4';
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_session_timeout 5m;
access_log /var/log/gitlab/nginx/gitlab_registry_access.log gitlab_access;
error_log /var/log/gitlab/nginx/gitlab_registry_error.log;
location / {
proxy_set_header Host $http_host; # required for docker client's sake
proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
proxy_pass http://localhost:5000;
}
}
unless Rails.env.production? unless Rails.env.production?
require 'rubocop/rake_task' require 'rubocop/rake_task'
RuboCop::RakeTask.new RuboCop::RakeTask.new
end end
...@@ -28,7 +28,7 @@ describe Projects::GroupLinksController do ...@@ -28,7 +28,7 @@ describe Projects::GroupLinksController do
expect(group.shared_projects).to include project expect(group.shared_projects).to include project
end end
it 'redirects to project group links page'do it 'redirects to project group links page' do
expect(response).to redirect_to( expect(response).to redirect_to(
namespace_project_group_links_path(project.namespace, project) namespace_project_group_links_path(project.namespace, project)
) )
......
...@@ -5,7 +5,7 @@ describe 'factories' do ...@@ -5,7 +5,7 @@ describe 'factories' do
describe "#{factory.name} factory" do describe "#{factory.name} factory" do
let(:entity) { build(factory.name) } let(:entity) { build(factory.name) }
it 'does not raise error when created 'do it 'does not raise error when created' do
expect { entity }.to_not raise_error expect { entity }.to_not raise_error
end end
......
...@@ -64,6 +64,64 @@ describe 'Issues', feature: true do ...@@ -64,6 +64,64 @@ describe 'Issues', feature: true do
end end
end end
describe 'due date', js: true do
context 'on new form' do
before do
visit new_namespace_project_issue_path(project.namespace, project)
end
it 'should save with due date' do
date = Date.today.at_beginning_of_month
fill_in 'issue_title', with: 'bug 345'
fill_in 'issue_description', with: 'bug description'
page.within '.datepicker' do
click_link date.day
end
expect(find('#issuable-due-date', visible: false).value).to eq date.to_s
click_button 'Submit issue'
page.within '.issuable-sidebar' do
expect(page).to have_content date.to_s(:medium)
end
end
end
context 'on edit form' do
let(:issue) { create(:issue, author: @user,project: project, due_date: Date.today.at_beginning_of_month.to_s) }
before do
visit edit_namespace_project_issue_path(project.namespace, project, issue)
end
it 'should save with due date' do
date = Date.today.at_beginning_of_month
expect(find('#issuable-due-date', visible: false).value).to eq date.to_s
date = date.tomorrow
fill_in 'issue_title', with: 'bug 345'
fill_in 'issue_description', with: 'bug description'
page.within '.datepicker' do
click_link date.day
end
expect(find('#issuable-due-date', visible: false).value).to eq date.to_s
click_button 'Save changes'
page.within '.issuable-sidebar' do
expect(page).to have_content date.to_s(:medium)
end
end
end
end
describe 'Issue info' do describe 'Issue info' do
it 'excludes award_emoji from comment count' do it 'excludes award_emoji from comment count' do
issue = create(:issue, author: @user, assignee: @user, project: project, title: 'foobar') issue = create(:issue, author: @user, assignee: @user, project: project, title: 'foobar')
...@@ -331,7 +389,7 @@ describe 'Issues', feature: true do ...@@ -331,7 +389,7 @@ describe 'Issues', feature: true do
page.within '.assignee' do page.within '.assignee' do
click_link 'Edit' click_link 'Edit'
end end
page.within '.dropdown-menu-user' do page.within '.dropdown-menu-user' do
click_link @user.name click_link @user.name
end end
...@@ -431,6 +489,43 @@ describe 'Issues', feature: true do ...@@ -431,6 +489,43 @@ describe 'Issues', feature: true do
end end
end end
describe 'due date' do
context 'update due on issue#show', js: true do
let(:issue) { create(:issue, project: project, author: @user, assignee: @user) }
before do
visit namespace_project_issue_path(project.namespace, project, issue)
end
it 'should add due date to issue' do
page.within '.due_date' do
click_link 'Edit'
page.within '.ui-datepicker-calendar' do
first('.ui-state-default').click
end
expect(page).to have_no_content 'None'
end
end
it 'should remove due date from issue' do
page.within '.due_date' do
click_link 'Edit'
page.within '.ui-datepicker-calendar' do
first('.ui-state-default').click
end
expect(page).to have_no_content 'None'
click_link 'remove due date'
expect(page).to have_content 'None'
end
end
end
end
def first_issue def first_issue
page.all('ul.issues-list > li').first.text page.all('ul.issues-list > li').first.text
end end
......
...@@ -24,6 +24,12 @@ describe "Pipelines" do ...@@ -24,6 +24,12 @@ describe "Pipelines" do
end end
end end
context 'anonymous access' do
before { visit namespace_project_pipelines_path(project.namespace, project) }
it { expect(page).to have_http_status(:success) }
end
context 'cancelable pipeline' do context 'cancelable pipeline' do
let!(:running) { create(:ci_build, :running, commit: pipeline, stage: 'test', commands: 'test') } let!(:running) { create(:ci_build, :running, commit: pipeline, stage: 'test', commands: 'test') }
......
...@@ -8,12 +8,10 @@ feature 'list of badges' do ...@@ -8,12 +8,10 @@ feature 'list of badges' do
project = create(:project) project = create(:project)
project.team << [user, :master] project.team << [user, :master]
login_as(user) login_as(user)
visit edit_namespace_project_path(project.namespace, project) visit namespace_project_badges_path(project.namespace, project)
end end
scenario 'user displays list of badges' do scenario 'user displays list of badges' do
click_link 'Badges'
expect(page).to have_content 'build status' expect(page).to have_content 'build status'
expect(page).to have_content 'Markdown' expect(page).to have_content 'Markdown'
expect(page).to have_content 'HTML' expect(page).to have_content 'HTML'
...@@ -26,7 +24,6 @@ feature 'list of badges' do ...@@ -26,7 +24,6 @@ feature 'list of badges' do
end end
scenario 'user changes current ref on badges list page', js: true do scenario 'user changes current ref on badges list page', js: true do
click_link 'Badges'
select2('improve/awesome', from: '#ref') select2('improve/awesome', from: '#ref')
expect(page).to have_content 'badges/improve/awesome/build.svg' expect(page).to have_content 'badges/improve/awesome/build.svg'
......
...@@ -25,7 +25,7 @@ feature 'project owner creates a license file', feature: true, js: true do ...@@ -25,7 +25,7 @@ feature 'project owner creates a license file', feature: true, js: true do
file_content = find('.file-content') file_content = find('.file-content')
expect(file_content).to have_content('The MIT License (MIT)') expect(file_content).to have_content('The MIT License (MIT)')
expect(file_content).to have_content("Copyright (c) 2016 #{project.namespace.human_name}") expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
fill_in :commit_message, with: 'Add a LICENSE file', visible: true fill_in :commit_message, with: 'Add a LICENSE file', visible: true
click_button 'Commit Changes' click_button 'Commit Changes'
...@@ -33,7 +33,7 @@ feature 'project owner creates a license file', feature: true, js: true do ...@@ -33,7 +33,7 @@ feature 'project owner creates a license file', feature: true, js: true do
expect(current_path).to eq( expect(current_path).to eq(
namespace_project_blob_path(project.namespace, project, 'master/LICENSE')) namespace_project_blob_path(project.namespace, project, 'master/LICENSE'))
expect(page).to have_content('The MIT License (MIT)') expect(page).to have_content('The MIT License (MIT)')
expect(page).to have_content("Copyright (c) 2016 #{project.namespace.human_name}") expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
end end
scenario 'project master creates a license file from the "Add license" link' do scenario 'project master creates a license file from the "Add license" link' do
...@@ -48,7 +48,7 @@ feature 'project owner creates a license file', feature: true, js: true do ...@@ -48,7 +48,7 @@ feature 'project owner creates a license file', feature: true, js: true do
file_content = find('.file-content') file_content = find('.file-content')
expect(file_content).to have_content('The MIT License (MIT)') expect(file_content).to have_content('The MIT License (MIT)')
expect(file_content).to have_content("Copyright (c) 2016 #{project.namespace.human_name}") expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
fill_in :commit_message, with: 'Add a LICENSE file', visible: true fill_in :commit_message, with: 'Add a LICENSE file', visible: true
click_button 'Commit Changes' click_button 'Commit Changes'
...@@ -56,6 +56,6 @@ feature 'project owner creates a license file', feature: true, js: true do ...@@ -56,6 +56,6 @@ feature 'project owner creates a license file', feature: true, js: true do
expect(current_path).to eq( expect(current_path).to eq(
namespace_project_blob_path(project.namespace, project, 'master/LICENSE')) namespace_project_blob_path(project.namespace, project, 'master/LICENSE'))
expect(page).to have_content('The MIT License (MIT)') expect(page).to have_content('The MIT License (MIT)')
expect(page).to have_content("Copyright (c) 2016 #{project.namespace.human_name}") expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
end end
end end
...@@ -24,7 +24,7 @@ feature 'project owner sees a link to create a license file in empty project', f ...@@ -24,7 +24,7 @@ feature 'project owner sees a link to create a license file in empty project', f
file_content = find('.file-content') file_content = find('.file-content')
expect(file_content).to have_content('The MIT License (MIT)') expect(file_content).to have_content('The MIT License (MIT)')
expect(file_content).to have_content("Copyright (c) 2016 #{project.namespace.human_name}") expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
fill_in :commit_message, with: 'Add a LICENSE file', visible: true fill_in :commit_message, with: 'Add a LICENSE file', visible: true
# Remove pre-receive hook so we can push without auth # Remove pre-receive hook so we can push without auth
...@@ -34,6 +34,6 @@ feature 'project owner sees a link to create a license file in empty project', f ...@@ -34,6 +34,6 @@ feature 'project owner sees a link to create a license file in empty project', f
expect(current_path).to eq( expect(current_path).to eq(
namespace_project_blob_path(project.namespace, project, 'master/LICENSE')) namespace_project_blob_path(project.namespace, project, 'master/LICENSE'))
expect(page).to have_content('The MIT License (MIT)') expect(page).to have_content('The MIT License (MIT)')
expect(page).to have_content("Copyright (c) 2016 #{project.namespace.human_name}") expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
end end
end end
...@@ -12,7 +12,7 @@ feature 'Master updates tag', feature: true do ...@@ -12,7 +12,7 @@ feature 'Master updates tag', feature: true do
context 'from the tags list page' do context 'from the tags list page' do
scenario 'updates the release notes' do scenario 'updates the release notes' do
page.within(first('.controls')) do page.within(first('.content-list .controls')) do
click_link 'Edit release notes' click_link 'Edit release notes'
end end
......
...@@ -12,5 +12,12 @@ describe Ci::Charts, lib: true do ...@@ -12,5 +12,12 @@ describe Ci::Charts, lib: true do
chart = Ci::Charts::BuildTime.new(@commit.project) chart = Ci::Charts::BuildTime.new(@commit.project)
expect(chart.build_times).to eq([2]) expect(chart.build_times).to eq([2])
end end
it 'should handle nil build times' do
create(:ci_commit, duration: nil, project: @commit.project)
chart = Ci::Charts::BuildTime.new(@commit.project)
expect(chart.build_times).to eq([2, 0])
end
end end
end end
...@@ -2,15 +2,13 @@ require 'spec_helper' ...@@ -2,15 +2,13 @@ require 'spec_helper'
describe Gitlab::Database::MigrationHelpers, lib: true do describe Gitlab::Database::MigrationHelpers, lib: true do
let(:model) do let(:model) do
Class.new do ActiveRecord::Migration.new.extend(
include Gitlab::Database::MigrationHelpers Gitlab::Database::MigrationHelpers
)
def method_missing(name, *args, &block)
ActiveRecord::Base.connection.send(name, *args, &block)
end
end.new
end end
before { allow(model).to receive(:puts) }
describe '#add_concurrent_index' do describe '#add_concurrent_index' do
context 'outside a transaction' do context 'outside a transaction' do
before do before do
...@@ -60,6 +58,12 @@ describe Gitlab::Database::MigrationHelpers, lib: true do ...@@ -60,6 +58,12 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
expect(Project.where(import_error: 'foo').count).to eq(5) expect(Project.where(import_error: 'foo').count).to eq(5)
end end
it 'updates boolean values correctly' do
model.update_column_in_batches(:projects, :archived, true)
expect(Project.where(archived: true).count).to eq(5)
end
end end
describe '#add_column_with_default' do describe '#add_column_with_default' do
......
...@@ -49,7 +49,7 @@ describe ApplicationSetting, 'TokenAuthenticatable' do ...@@ -49,7 +49,7 @@ describe ApplicationSetting, 'TokenAuthenticatable' do
context 'token is generated' do context 'token is generated' do
before { subject.send("reset_#{token_field}!") } before { subject.send("reset_#{token_field}!") }
it 'persists a new token 'do it 'persists a new token' do
expect(subject.send(:read_attribute, token_field)).to be_a String expect(subject.send(:read_attribute, token_field)).to be_a String
end end
end end
......
...@@ -57,7 +57,7 @@ describe API::Licenses, api: true do ...@@ -57,7 +57,7 @@ describe API::Licenses, api: true do
end end
it 'replaces placeholder values' do it 'replaces placeholder values' do
expect(json_response['content']).to include('Copyright (c) 2016 Anton') expect(json_response['content']).to include("Copyright (c) #{Time.now.year} Anton")
end end
end end
...@@ -70,7 +70,7 @@ describe API::Licenses, api: true do ...@@ -70,7 +70,7 @@ describe API::Licenses, api: true do
it 'replaces placeholder values' do it 'replaces placeholder values' do
expect(json_response['content']).to include('My Awesome Project') expect(json_response['content']).to include('My Awesome Project')
expect(json_response['content']).to include('Copyright (C) 2016 Anton') expect(json_response['content']).to include("Copyright (C) #{Time.now.year} Anton")
end end
end end
...@@ -83,7 +83,7 @@ describe API::Licenses, api: true do ...@@ -83,7 +83,7 @@ describe API::Licenses, api: true do
it 'replaces placeholder values' do it 'replaces placeholder values' do
expect(json_response['content']).to include('My Awesome Project') expect(json_response['content']).to include('My Awesome Project')
expect(json_response['content']).to include('Copyright (C) 2016 Anton') expect(json_response['content']).to include("Copyright (C) #{Time.now.year} Anton")
end end
end end
...@@ -96,7 +96,7 @@ describe API::Licenses, api: true do ...@@ -96,7 +96,7 @@ describe API::Licenses, api: true do
it 'replaces placeholder values' do it 'replaces placeholder values' do
expect(json_response['content']).to include('My Awesome Project') expect(json_response['content']).to include('My Awesome Project')
expect(json_response['content']).to include('Copyright (C) 2016 Anton') expect(json_response['content']).to include("Copyright (C) #{Time.now.year} Anton")
end end
end end
...@@ -108,7 +108,7 @@ describe API::Licenses, api: true do ...@@ -108,7 +108,7 @@ describe API::Licenses, api: true do
end end
it 'replaces placeholder values' do it 'replaces placeholder values' do
expect(json_response['content']).to include('Copyright 2016 Anton') expect(json_response['content']).to include("Copyright #{Time.now.year} Anton")
end end
end end
...@@ -128,7 +128,7 @@ describe API::Licenses, api: true do ...@@ -128,7 +128,7 @@ describe API::Licenses, api: true do
it 'replaces the copyright owner placeholder with the name of the current user' do it 'replaces the copyright owner placeholder with the name of the current user' do
get api('/licenses/mit', user) get api('/licenses/mit', user)
expect(json_response['content']).to include("Copyright (c) 2016 #{user.name}") expect(json_response['content']).to include("Copyright (c) #{Time.now.year} #{user.name}")
end end
end end
end end
......
...@@ -23,7 +23,7 @@ describe JwtController do ...@@ -23,7 +23,7 @@ describe JwtController do
context 'when using authorized request' do context 'when using authorized request' do
context 'using CI token' do context 'using CI token' do
let(:project) { create(:empty_project, runners_token: 'token', builds_enabled: builds_enabled) } let(:project) { create(:empty_project, runners_token: 'token', builds_enabled: builds_enabled) }
let(:headers) { { authorization: credentials('gitlab_ci_token', project.runners_token) } } let(:headers) { { authorization: credentials('gitlab-ci-token', project.runners_token) } }
subject! { get '/jwt/auth', parameters, headers } subject! { get '/jwt/auth', parameters, headers }
......
...@@ -10,7 +10,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do ...@@ -10,7 +10,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
subject { described_class.new(current_project, current_user, current_params).execute } subject { described_class.new(current_project, current_user, current_params).execute }
before do before do
stub_container_registry_config(enabled: true, issuer: 'rspec', key: nil) allow(Gitlab.config.registry).to receive_messages(enabled: true, issuer: 'rspec', key: nil)
allow_any_instance_of(JSONWebToken::RSAToken).to receive(:key).and_return(rsa_key) allow_any_instance_of(JSONWebToken::RSAToken).to receive(:key).and_return(rsa_key)
end end
...@@ -60,6 +60,17 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do ...@@ -60,6 +60,17 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
it { is_expected.to_not include(:token) } it { is_expected.to_not include(:token) }
end end
describe '#full_access_token' do
let(:project) { create(:empty_project) }
let(:token) { described_class.full_access_token(project.path_with_namespace) }
subject { { token: token } }
it_behaves_like 'a accessible' do
let(:actions) { ['*'] }
end
end
context 'user authorization' do context 'user authorization' do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:current_user) { create(:user) } let(:current_user) { create(:user) }
...@@ -116,12 +127,12 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do ...@@ -116,12 +127,12 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
context 'project authorization' do context 'project authorization' do
let(:current_project) { create(:empty_project) } let(:current_project) { create(:empty_project) }
context 'disallow to use offline_token' do context 'allow to use offline_token' do
let(:current_params) do let(:current_params) do
{ offline_token: true } { offline_token: true }
end end
it_behaves_like 'an unauthorized' it_behaves_like 'an authenticated'
end end
context 'allow to pull and push images' do context 'allow to pull and push images' do
......
...@@ -54,7 +54,7 @@ describe Issues::CreateService, services: true do ...@@ -54,7 +54,7 @@ describe Issues::CreateService, services: true do
label_ids: [label.id] } label_ids: [label.id] }
end end
it 'does not assign label'do it 'does not assign label' do
expect(issue.labels).to_not include label expect(issue.labels).to_not include label
end end
end end
......
# .gitignore templates
This directory contains language-specific .gitignore templates that are used by GitLab.
These files were automatically pulled from [this repository](https://github.com/github/gitignore).
Please submit pull requests to that repository. There is no need to edit the files in this directory.
## Bulk Update
To update this directory with the latest changes in the repository, run:
```sh
bundle exec rake gitlab:update_gitignore
```
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment