diff options
206 files changed, 2208 insertions, 1271 deletions
diff --git a/.rubocop.yml b/.rubocop.yml index 4e6ced4e1ab..2d8eb4077f3 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -59,7 +59,7 @@ Style/AndOr: # Use `Array#join` instead of `Array#*`. Style/ArrayJoin: - Enabled: false + Enabled: true # Use only ascii symbols in comments. Style/AsciiComments: @@ -71,7 +71,7 @@ Style/AsciiIdentifiers: # Checks for uses of Module#attr. Style/Attr: - Enabled: false + Enabled: true # Avoid the use of BEGIN blocks. Style/BeginBlock: @@ -83,7 +83,7 @@ Style/BarePercentLiterals: # Do not use block comments. Style/BlockComments: - Enabled: false + Enabled: true # Put end statement of multiline block on its own line. Style/BlockEndNewline: @@ -124,7 +124,7 @@ Style/ClassCheck: # Use self when defining module/class methods. Style/ClassMethods: - Enabled: false + Enabled: true # Avoid the use of class variables. Style/ClassVars: @@ -218,7 +218,7 @@ Style/EmptyLiteral: # Avoid the use of END blocks. Style/EndBlock: - Enabled: false + Enabled: true # Use Unix-style line endings. Style/EndOfLine: @@ -226,7 +226,7 @@ Style/EndOfLine: # Favor the use of Fixnum#even? && Fixnum#odd? Style/EvenOdd: - Enabled: false + Enabled: true # Do not use unnecessary spacing. Style/ExtraSpacing: @@ -234,11 +234,16 @@ Style/ExtraSpacing: # Use snake_case for source file names. Style/FileName: - Enabled: false + Enabled: true + +# Checks for a line break before the first parameter in a multi-line method +# parameter definition. +Style/FirstMethodParameterLineBreak: + Enabled: true # Checks for flip flops. Style/FlipFlop: - Enabled: false + Enabled: true # Checks use of for or each in multiline loops. Style/For: @@ -250,7 +255,7 @@ Style/FormatString: # Do not introduce global variables. Style/GlobalVars: - Enabled: false + Enabled: true # Check for conditionals that can be replaced with guard clauses. Style/GuardClause: @@ -271,7 +276,7 @@ Style/IfUnlessModifier: # Do not use if x; .... Use the ternary operator instead. Style/IfWithSemicolon: - Enabled: false + Enabled: true # Checks that conditional statements do not have an identical line at the # end of each branch, which can validly be moved out of the conditional. @@ -309,7 +314,7 @@ Style/Lambda: # Use lambda.call(...) instead of lambda.(...). Style/LambdaCall: - Enabled: false + Enabled: true # Comments should start with a space. Style/LeadingCommentSpace: @@ -329,7 +334,7 @@ Style/MethodDefParentheses: # Use the configured style when naming methods. Style/MethodName: - Enabled: false + Enabled: true # Checks for usage of `extend self` in modules. Style/ModuleFunction: @@ -370,6 +375,11 @@ Style/MultilineMethodCallBraceLayout: Style/MultilineMethodCallIndentation: Enabled: false +# Checks that the closing brace in a method definition is symmetrical with +# respect to the opening brace and the method parameters. +Style/MultilineMethodDefinitionBraceLayout: + Enabled: false + # Checks indentation of binary operations that span more than one line. Style/MultilineOperationIndentation: Enabled: false @@ -392,7 +402,7 @@ Style/NegatedWhile: # Avoid using nested modifiers. Style/NestedModifier: - Enabled: false + Enabled: true # Parenthesize method calls which are nested inside the argument list of # another parenthesized method call. @@ -429,7 +439,7 @@ Style/OneLineConditional: # When defining binary operators, name the argument other. Style/OpMethod: - Enabled: false + Enabled: true # Check for simple usages of parallel assignment. It will only warn when # the number of variables matches on both sides of the assignment. @@ -509,7 +519,8 @@ Style/Semicolon: # Checks for proper usage of fail and raise. Style/SignalException: - Enabled: false + EnforcedStyle: only_raise + Enabled: true # Enforces the names of some block params. Style/SingleLineBlockParams: @@ -534,11 +545,11 @@ Style/SpaceAfterMethodName: # Tracks redundant space after the ! operator. Style/SpaceAfterNot: - Enabled: false + Enabled: true # Use spaces after semicolons. Style/SpaceAfterSemicolon: - Enabled: false + Enabled: true # Checks that the equals signs in parameter default assignments have or don't # have surrounding space depending on configuration. @@ -572,7 +583,7 @@ Style/SpaceBeforeFirstArg: # No spaces before semicolons. Style/SpaceBeforeSemicolon: - Enabled: false + Enabled: true # Checks that block braces have or don't have surrounding space. # For blocks taking parameters, checks that the left brace has or doesn't @@ -594,11 +605,12 @@ Style/SpaceInsideParens: # No spaces inside range literals. Style/SpaceInsideRangeLiteral: - Enabled: false + Enabled: true # Checks for padding/surrounding spaces inside string interpolation. Style/SpaceInsideStringInterpolation: - Enabled: false + EnforcedStyle: no_space + Enabled: true # Avoid Perl-style global variables. Style/SpecialGlobalVars: @@ -606,7 +618,8 @@ Style/SpecialGlobalVars: # Check for the usage of parentheses around stabby lambda arguments. Style/StabbyLambdaParentheses: - Enabled: false + EnforcedStyle: require_parentheses + Enabled: true # Checks if uses of quotes match the configured preference. Style/StringLiterals: @@ -619,7 +632,9 @@ Style/StringLiteralsInInterpolation: # Checks if configured preferred methods are used over non-preferred. Style/StringMethods: - Enabled: false + PreferredMethods: + intern: to_sym + Enabled: true # Use %i or %I for arrays of symbols. Style/SymbolArray: @@ -677,15 +692,16 @@ Style/UnneededPercentQ: # Don't interpolate global, instance and class variables directly in strings. Style/VariableInterpolation: - Enabled: false + Enabled: true # Use the configured style when naming variables. Style/VariableName: - Enabled: false + EnforcedStyle: snake_case + Enabled: true # Use when x then ... for one-line cases. Style/WhenThen: - Enabled: false + Enabled: true # Checks for redundant do after while or until. Style/WhileUntilDo: @@ -693,7 +709,7 @@ Style/WhileUntilDo: # Favor modifier while/until usage when you have a single-line body. Style/WhileUntilModifier: - Enabled: false + Enabled: true # Use %w or %W for arrays of words. Style/WordArray: @@ -1130,7 +1146,7 @@ RSpec/MultipleDescribes: # Enforces the usage of the same method on all negative message expectations. RSpec/NotToNot: EnforcedStyle: not_to - Enabled: false + Enabled: true # Prefer using verifying doubles over normal doubles. RSpec/VerifiedDoubles: diff --git a/CHANGELOG b/CHANGELOG index a79c3048b79..9b8c6ddbeee 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,16 +1,31 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.9.0 (unreleased) + - Allow forking projects with restricted visibility level - Redesign navigation for project pages + - Fix groups API to list only user's accessible projects + - Redesign account and email confirmation emails - Use gitlab-shell v3.0.0 - -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 + - Add rake task 'gitlab:db:configure' for conditionally seeding or migrating the database + - Changed the Slack build message to use the singular duration if necessary (Aran Koning) + - Fix issues filter when ordering by milestone + - Todos will display target state if issuable target is 'Closed' or 'Merged' + - Remove 'main language' feature + +v 8.8.2 + - Added remove due date button. !4209 + - Fix Error 500 when accessing application settings due to nil disabled OAuth sign-in sources. !4242 + - Fix Error 500 in CI charts by gracefully handling commits with no durations. !4245 + - Fix table UI on CI builds page. !4249 + - Fix backups if registry is disabled. !4263 + - Fixed issue with merge button color. !4211 + - Fixed issue with enter key selecting wrong option in dropdown. !4210 + - When creating a .gitignore file a dropdown with templates will be provided. !4075 + - Fix concurrent request when updating build log in browser. !4183 v 8.8.1 - Add documentation for the "Health Check" feature - - Allow anonymous users to access a public project's pipelines + - Allow anonymous users to access a public project's pipelines !4233 - Fix MySQL compatibility in zero downtime migrations helpers - Fix the CI login to Container Registry (the gitlab-ci-token user) @@ -27,6 +42,7 @@ v 8.8.0 - Added inline diff styling for `change_title` system notes. (Adam Butler) - Project#open_branches has been cleaned up and no longer loads entire records into memory. - Escape HTML in commit titles in system note messages + - 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 - Improve multiple branch push performance by memoizing permission checking @@ -88,6 +104,9 @@ v 8.8.0 - Allows MR authors to have the source branch removed when merging the MR. !2801 (Jeroen Jacobs) - When creating a .gitignore file a dropdown with templates will be provided +v 8.7.7 + - Fix import by `Any Git URL` broken if the URL contains a space + v 8.7.6 - Fix links on wiki pages for relative url setups. !4131 (Artem Sidorenko) - Fix import from GitLab.com to a private instance failure. !4181 @@ -121,7 +121,7 @@ group :unicorn do end # State machine -gem "state_machines-activerecord", '~> 0.3.0' +gem "state_machines-activerecord", '~> 0.4.0' # Run events after state machine commits gem 'after_commit_queue' @@ -178,9 +178,6 @@ gem 'ruby-fogbugz', '~> 0.2.1' # d3 gem 'd3_rails', '~> 3.5.0' -#cal-heatmap -gem 'cal-heatmap-rails', '~> 3.6.0' - # underscore-rails gem "underscore-rails", "~> 1.8.0" diff --git a/Gemfile.lock b/Gemfile.lock index 4533aa31d5f..fa2b72b2524 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -102,7 +102,6 @@ GEM bundler (~> 1.2) thor (~> 0.18) byebug (8.2.1) - cal-heatmap-rails (3.6.0) capybara (2.6.2) addressable mime-types (>= 1.16) @@ -790,11 +789,11 @@ GEM activesupport (>= 4.0) sprockets (>= 3.0.0) state_machines (0.4.0) - state_machines-activemodel (0.3.0) - activemodel (~> 4.1) + state_machines-activemodel (0.4.0) + activemodel (>= 4.1, < 5.1) state_machines (>= 0.4.0) - state_machines-activerecord (0.3.0) - activerecord (~> 4.1) + state_machines-activerecord (0.4.0) + activerecord (>= 4.1, < 5.1) state_machines-activemodel (>= 0.3.0) stringex (2.5.2) systemu (2.6.5) @@ -908,7 +907,6 @@ DEPENDENCIES bullet bundler-audit byebug - cal-heatmap-rails (~> 3.6.0) capybara (~> 2.6.2) capybara-screenshot (~> 1.0.0) carrierwave (~> 0.10.0) @@ -1043,7 +1041,7 @@ DEPENDENCIES spring-commands-spinach (~> 1.1.0) spring-commands-teaspoon (~> 0.0.2) sprockets (~> 3.6.0) - state_machines-activerecord (~> 0.3.0) + state_machines-activerecord (~> 0.4.0) task_list (~> 1.0.2) teaspoon (~> 1.1.0) teaspoon-jasmine (~> 2.2.0) diff --git a/app/assets/images/mailers/gitlab_header_logo.png b/app/assets/images/mailers/gitlab_header_logo.png Binary files differnew file mode 100644 index 00000000000..35ca1860887 --- /dev/null +++ b/app/assets/images/mailers/gitlab_header_logo.png diff --git a/app/assets/images/mailers/gitlab_tanuki_2x.png b/app/assets/images/mailers/gitlab_tanuki_2x.png Binary files differnew file mode 100644 index 00000000000..551dd6ce2ce --- /dev/null +++ b/app/assets/images/mailers/gitlab_tanuki_2x.png diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index bffce5a0c0f..7c547ac843b 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -19,7 +19,6 @@ #= require jquery.scrollTo #= require jquery.turbolinks #= require d3 -#= require cal-heatmap #= require turbolinks #= require autosave #= require bootstrap/affix @@ -246,38 +245,6 @@ $ -> if $navIcon.hasClass('fa-angle-left') $navIconToggle.trigger('click') - $(document) - .off 'click', '.js-sidebar-toggle' - .on 'click', '.js-sidebar-toggle', (e, triggered) -> - e.preventDefault() - $this = $(this) - $thisIcon = $this.find 'i' - $allGutterToggleIcons = $('.js-sidebar-toggle i') - if $thisIcon.hasClass('fa-angle-double-right') - $allGutterToggleIcons - .removeClass('fa-angle-double-right') - .addClass('fa-angle-double-left') - $('aside.right-sidebar') - .removeClass('right-sidebar-expanded') - .addClass('right-sidebar-collapsed') - $('.page-with-sidebar') - .removeClass('right-sidebar-expanded') - .addClass('right-sidebar-collapsed') - else - $allGutterToggleIcons - .removeClass('fa-angle-double-left') - .addClass('fa-angle-double-right') - $('aside.right-sidebar') - .removeClass('right-sidebar-collapsed') - .addClass('right-sidebar-expanded') - $('.page-with-sidebar') - .removeClass('right-sidebar-collapsed') - .addClass('right-sidebar-expanded') - if not triggered - $.cookie("collapsed_gutter", - $('.right-sidebar') - .hasClass('right-sidebar-collapsed'), { path: '/' }) - fitSidebarForSize = -> oldBootstrapBreakpoint = bootstrapBreakpoint bootstrapBreakpoint = bp.getBreakpointSize() diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee index d80e0e716ce..f9c7bffdadb 100644 --- a/app/assets/javascripts/calendar.js.coffee +++ b/app/assets/javascripts/calendar.js.coffee @@ -1,34 +1,198 @@ class @Calendar - constructor: (timestamps, starting_year, starting_month, calendar_activities_path) -> - cal = new CalHeatMap() - cal.init - itemName: ["contribution"] - data: timestamps - start: new Date(starting_year, starting_month) - domainLabelFormat: "%b" - id: "cal-heatmap" - domain: "month" - subDomain: "day" - range: 12 - tooltip: true - label: - position: "top" - legend: [ - 0 - 10 - 20 - 30 - ] - legendCellPadding: 3 - cellSize: $('.user-calendar').width() / 73 - onClick: (date, count) -> - formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate() - $.ajax - url: calendar_activities_path - data: - date: formated_date - cache: false - dataType: "html" - success: (data) -> - $(".user-calendar-activities").html data + constructor: (timestamps, @calendar_activities_path) -> + @currentSelectedDate = '' + @daySpace = 1 + @daySize = 15 + @daySizeWithSpace = @daySize + (@daySpace * 2) + @monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] + @months = [] + @highestValue = 0 + # Get the highest value from the timestampes + _.each timestamps, (count) => + if count > @highestValue + @highestValue = count + + # Loop through the timestamps to create a group of objects + # The group of objects will be grouped based on the day of the week they are + @timestampsTmp = [] + i = 0 + group = 0 + _.each timestamps, (count, date) => + newDate = new Date parseInt(date) * 1000 + day = newDate.getDay() + + # Create a new group array if this is the first day of the week + # or if is first object + if (day is 0 and i isnt 0) or i is 0 + @timestampsTmp.push [] + group++ + + innerArray = @timestampsTmp[group-1] + + # Push to the inner array the values that will be used to render map + innerArray.push + count: count + date: newDate + day: day + + i++ + + # Init color functions + @color = @initColor() + @colorKey = @initColorKey() + + # Init the svg element + @renderSvg(group) + @renderDays() + @renderMonths() + @renderDayTitles() + @renderKey() + + @initTooltips() + + renderSvg: (group) -> + @svg = d3.select '.js-contrib-calendar' + .append 'svg' + .attr 'width', (group + 1) * @daySizeWithSpace + .attr 'height', 167 + .attr 'class', 'contrib-calendar' + + renderDays: -> + @svg.selectAll 'g' + .data @timestampsTmp + .enter() + .append 'g' + .attr 'transform', (group, i) => + _.each group, (stamp, a) => + if a is 0 and stamp.day is 0 + month = stamp.date.getMonth() + x = (@daySizeWithSpace * i + 1) + @daySizeWithSpace + lastMonth = _.last(@months) + if lastMonth? + lastMonthX = lastMonth.x + + if !lastMonth? + @months.push + month: month + x: x + else if month isnt lastMonth.month and x - @daySizeWithSpace isnt lastMonthX + @months.push + month: month + x: x + + "translate(#{(@daySizeWithSpace * i + 1) + @daySizeWithSpace}, 18)" + .selectAll 'rect' + .data (stamp) -> + stamp + .enter() + .append 'rect' + .attr 'x', '0' + .attr 'y', (stamp, i) => + (@daySizeWithSpace * stamp.day) + .attr 'width', @daySize + .attr 'height', @daySize + .attr 'title', (stamp) => + contribText = 'No contributions' + + if stamp.count > 0 + contribText = "#{stamp.count} contribution#{if stamp.count > 1 then 's' else ''}" + + date = dateFormat(stamp.date, 'mmm d, yyyy') + + "#{contribText}<br />#{date}" + .attr 'class', 'user-contrib-cell js-tooltip' + .attr 'fill', (stamp) => + if stamp.count isnt 0 + @color(stamp.count) + else + '#ededed' + .attr 'data-container', 'body' + .on 'click', @clickDay + + renderDayTitles: -> + days = [{ + text: 'M' + y: 29 + (@daySizeWithSpace * 1) + }, { + text: 'W' + y: 29 + (@daySizeWithSpace * 3) + }, { + text: 'F' + y: 29 + (@daySizeWithSpace * 5) + }] + @svg.append 'g' + .selectAll 'text' + .data days + .enter() + .append 'text' + .attr 'text-anchor', 'middle' + .attr 'x', 8 + .attr 'y', (day) -> + day.y + .text (day) -> + day.text + .attr 'class', 'user-contrib-text' + + renderMonths: -> + @svg.append 'g' + .selectAll 'text' + .data @months + .enter() + .append 'text' + .attr 'x', (date) -> + date.x + .attr 'y', 10 + .attr 'class', 'user-contrib-text' + .text (date) => + @monthNames[date.month] + + renderKey: -> + keyColors = ['#ededed', @colorKey(0), @colorKey(1), @colorKey(2), @colorKey(3)] + @svg.append 'g' + .attr 'transform', "translate(18, #{@daySizeWithSpace * 8 + 16})" + .selectAll 'rect' + .data keyColors + .enter() + .append 'rect' + .attr 'width', @daySize + .attr 'height', @daySize + .attr 'x', (color, i) => + @daySizeWithSpace * i + .attr 'y', 0 + .attr 'fill', (color) -> + color + + initColor: -> + d3.scale + .linear() + .range(['#acd5f2', '#254e77']) + .domain([0, @highestValue]) + + initColorKey: -> + d3.scale + .linear() + .range(['#acd5f2', '#254e77']) + .domain([0, 3]) + + clickDay: (stamp) -> + if @currentSelectedDate isnt stamp.date + @currentSelectedDate = stamp.date + formatted_date = @currentSelectedDate.getFullYear() + "-" + (@currentSelectedDate.getMonth()+1) + "-" + @currentSelectedDate.getDate() + + $.ajax + url: @calendar_activities_path + data: + date: formatted_date + cache: false + dataType: 'html' + beforeSend: -> + $('.user-calendar-activities').html '<div class="text-center"><i class="fa fa-spinner fa-spin user-calendar-activities-loading"></i></div>' + success: (data) -> + $('.user-calendar-activities').html data + else + $('.user-calendar-activities').html '' + + initTooltips: -> + $('.js-contrib-calendar .js-tooltip').tooltip + html: true diff --git a/app/assets/javascripts/ci/build.coffee b/app/assets/javascripts/ci/build.coffee index fca0c3bae5c..98d05e41273 100644 --- a/app/assets/javascripts/ci/build.coffee +++ b/app/assets/javascripts/ci/build.coffee @@ -28,12 +28,15 @@ class CiBuild # CiBuild.interval = setInterval => if window.location.href.split("#").first() is build_url + last_state = @state $.ajax url: build_url + "/trace.json?state=" + encodeURIComponent(@state) dataType: "json" success: (log) => - @state = log.state - if log.status is "running" + return unless last_state is @state + + if log.state and log.status is "running" + @state = log.state if log.append $('.fa-refresh').before log.html else diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 12c143fdf76..a3185f87640 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -16,7 +16,6 @@ class Dispatcher shortcut_handler = null switch page when 'projects:issues:index' - Issues.init() Issuable.init() shortcut_handler = new ShortcutsNavigation() when 'projects:issues:show' diff --git a/app/assets/javascripts/issuable.js.coffee b/app/assets/javascripts/issuable.js.coffee index afffed63ac5..6504e481102 100644 --- a/app/assets/javascripts/issuable.js.coffee +++ b/app/assets/javascripts/issuable.js.coffee @@ -1,7 +1,11 @@ +issuable_created = false @Issuable = init: -> - Issuable.initTemplates() - Issuable.initSearch() + unless issuable_created + issuable_created = true + Issuable.initTemplates() + Issuable.initSearch() + Issuable.initChecks() initTemplates: -> Issuable.labelRow = _.template( @@ -19,7 +23,16 @@ .on 'keyup', -> clearTimeout(@timer) @timer = setTimeout( -> - Issuable.filterResults $('#issue_search_form') + $search = $('#issue_search') + $form = $('.js-filter-form') + $input = $("input[name='#{$search.attr('name')}']", $form) + + if $input.length is 0 + $form.append "<input type='hidden' name='#{$search.attr('name')}' value='#{_.escape($search.val())}'/>" + else + $input.val $search.val() + + Issuable.filterResults $form , 500) toggleLabelFilters: -> @@ -59,15 +72,22 @@ dataType: "json" reload: -> - if Issues.created - Issues.initChecks() + if Issuable.created + Issuable.initChecks() $('#filter_issue_search').val($('#issue_search').val()) + initChecks: -> + $('.check_all_issues').on 'click', -> + $('.selected_issue').prop('checked', @checked) + Issuable.checkChanged() + + $('.selected_issue').on 'change', Issuable.checkChanged + updateStateFilters: -> - stateFilters = $('.issues-state-filters') + stateFilters = $('.issues-state-filters, .dropdown-menu-sort') newParams = {} - paramKeys = ['author_id', 'milestone_title', 'assignee_id', 'issue_search'] + paramKeys = ['author_id', 'milestone_title', 'assignee_id', 'issue_search', 'issue_search'] for paramKey in paramKeys newParams[paramKey] = gl.utils.getParameterValues(paramKey)[0] or '' @@ -82,3 +102,17 @@ else newUrl = gl.utils.mergeUrlParams(newParams, initialUrl) $(this).attr 'href', newUrl + + checkChanged: -> + checked_issues = $('.selected_issue:checked') + if checked_issues.length > 0 + ids = $.map checked_issues, (value) -> + $(value).data('id') + + $('#update_issues_ids').val ids + $('.issues-other-filters').hide() + $('.issues_bulk_update').show() + else + $('#update_issues_ids').val [] + $('.issues_bulk_update').hide() + $('.issues-other-filters').show() diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee deleted file mode 100644 index 3330e6c68ad..00000000000 --- a/app/assets/javascripts/issues.js.coffee +++ /dev/null @@ -1,38 +0,0 @@ -@Issues = - init: -> - Issues.created = true - Issues.initChecks() - - $("body").on "ajax:success", ".close_issue, .reopen_issue", -> - t = $(this) - totalIssues = undefined - reopen = t.hasClass("reopen_issue") - $(".issue_counter").each -> - issue = $(this) - totalIssues = parseInt($(this).html(), 10) - if reopen and issue.closest(".main_menu").length - $(this).html totalIssues + 1 - else - $(this).html totalIssues - 1 - - initChecks: -> - $(".check_all_issues").click -> - $(".selected_issue").prop("checked", @checked) - Issues.checkChanged() - - $(".selected_issue").bind "change", Issues.checkChanged - - checkChanged: -> - checked_issues = $(".selected_issue:checked") - if checked_issues.length > 0 - ids = [] - $.each checked_issues, (index, value) -> - ids.push $(value).attr("data-id") - - $("#update_issues_ids").val ids - $(".issues-other-filters").hide() - $(".issues_bulk_update").show() - else - $("#update_issues_ids").val [] - $(".issues_bulk_update").hide() - $(".issues-other-filters").show() diff --git a/app/assets/javascripts/layout_nav.js.coffee b/app/assets/javascripts/layout_nav.js.coffee new file mode 100644 index 00000000000..6adac6dac97 --- /dev/null +++ b/app/assets/javascripts/layout_nav.js.coffee @@ -0,0 +1,14 @@ +class @LayoutNav + $ -> + $('.fade-left').addClass('end-scroll') + $('.scrolling-tabs').on 'scroll', (event) -> + $this = $(this) + $el = $(event.target) + currentPosition = $this.scrollLeft() + size = bp.getBreakpointSize() + controlBtnWidth = $('.controls').width() + maxPosition = $this.get(0).scrollWidth - $this.parent().width() + maxPosition += controlBtnWidth if size isnt 'xs' and $('.nav-control').length + + $el.find('.fade-left').toggleClass('end-scroll', currentPosition is 0) + $el.find('.fade-right').toggleClass('end-scroll', currentPosition is maxPosition) diff --git a/app/assets/javascripts/lib/url_utility.js.coffee b/app/assets/javascripts/lib/url_utility.js.coffee index 6a00932c028..e8085e1c2e4 100644 --- a/app/assets/javascripts/lib/url_utility.js.coffee +++ b/app/assets/javascripts/lib/url_utility.js.coffee @@ -26,10 +26,19 @@ newUrl = decodeURIComponent(url) for paramName, paramValue of params pattern = new RegExp "\\b(#{paramName}=).*?(&|$)" - if url.search(pattern) >= 0 + if not paramValue? + newUrl = newUrl.replace pattern, '' + else if url.search(pattern) isnt -1 newUrl = newUrl.replace pattern, "$1#{paramValue}$2" else newUrl = "#{newUrl}#{(if newUrl.indexOf('?') > 0 then '&' else '?')}#{paramName}=#{paramValue}" + + # Remove a trailing ampersand + lastChar = newUrl[newUrl.length - 1] + + if lastChar is '&' + newUrl = newUrl.slice 0, -1 + newUrl # removes parameter query string from url. returns the modified url diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee index 372732d0aac..49a4727205a 100644 --- a/app/assets/javascripts/merge_request_tabs.js.coffee +++ b/app/assets/javascripts/merge_request_tabs.js.coffee @@ -75,6 +75,9 @@ class @MergeRequestTabs @loadDiff($target.attr('href')) if bp? and bp.getBreakpointSize() isnt 'lg' @shrinkView() + + navBarHeight = $('.navbar-gitlab').outerHeight() + $.scrollTo(".merge-request-details .merge-request-tabs", offset: -navBarHeight) else if action == 'builds' @loadBuilds($target.attr('href')) @expandView() diff --git a/app/assets/javascripts/merge_request_widget.js.coffee b/app/assets/javascripts/merge_request_widget.js.coffee index b6d590f681c..d16f44d52f8 100644 --- a/app/assets/javascripts/merge_request_widget.js.coffee +++ b/app/assets/javascripts/merge_request_widget.js.coffee @@ -106,6 +106,7 @@ class @MergeRequestWidget @firstCICheck = false showCIStatus: (state) -> + return if not state? $('.ci_widget').hide() allowed_states = ["failed", "canceled", "running", "pending", "success", "skipped", "not_found"] if state in allowed_states @@ -126,6 +127,6 @@ class @MergeRequestWidget $('.ci_widget:visible .ci-coverage').text(text) setMergeButtonClass: (css_class) -> - $('.js-merge-button') + $('.js-merge-button,.accept-action .dropdown-toggle') .removeClass('btn-danger btn-warning btn-create') .addClass(css_class) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 6d9d6528f45..8131d923677 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -329,7 +329,7 @@ class @Notes @renderDiscussionNote(note) # cleanup after successfully creating a diff/discussion note - @removeDiscussionNoteForm($("#new-discussion-note-form-#{note.discussion_id}")) + @removeDiscussionNoteForm($(xhr.target)) ### Called in response to the edit note form being submitted diff --git a/app/assets/javascripts/right_sidebar.js.coffee b/app/assets/javascripts/right_sidebar.js.coffee index 2d084b76cfe..c9cb0f4bb32 100644 --- a/app/assets/javascripts/right_sidebar.js.coffee +++ b/app/assets/javascripts/right_sidebar.js.coffee @@ -10,6 +10,40 @@ class @Sidebar $('.dropdown').on('loading.gl.dropdown', @sidebarDropdownLoading) $('.dropdown').on('loaded.gl.dropdown', @sidebarDropdownLoaded) + + $(document) + .off 'click', '.js-sidebar-toggle' + .on 'click', '.js-sidebar-toggle', (e, triggered) -> + e.preventDefault() + $this = $(this) + $thisIcon = $this.find 'i' + $allGutterToggleIcons = $('.js-sidebar-toggle i') + if $thisIcon.hasClass('fa-angle-double-right') + $allGutterToggleIcons + .removeClass('fa-angle-double-right') + .addClass('fa-angle-double-left') + $('aside.right-sidebar') + .removeClass('right-sidebar-expanded') + .addClass('right-sidebar-collapsed') + $('.page-with-sidebar') + .removeClass('right-sidebar-expanded') + .addClass('right-sidebar-collapsed') + else + $allGutterToggleIcons + .removeClass('fa-angle-double-left') + .addClass('fa-angle-double-right') + $('aside.right-sidebar') + .removeClass('right-sidebar-collapsed') + .addClass('right-sidebar-expanded') + $('.page-with-sidebar') + .removeClass('right-sidebar-collapsed') + .addClass('right-sidebar-expanded') + if not triggered + $.cookie("collapsed_gutter", + $('.right-sidebar') + .hasClass('right-sidebar-collapsed'), { path: '/' }) + + sidebarDropdownLoading: (e) -> $sidebarCollapsedIcon = $(@).closest('.block').find('.sidebar-collapsed-icon') img = $sidebarCollapsedIcon.find('img') @@ -76,7 +110,7 @@ class @Sidebar @triggerOpenSidebar() if not @isOpen() if action is 'hide' - @triggerOpenSidebar() is @isOpen() + @triggerOpenSidebar() if @isOpen() isOpen: -> @sidebar.is('.right-sidebar-expanded') diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index e2d590f4df4..8b93665d085 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -8,7 +8,6 @@ *= require select2 *= require_self *= require dropzone/basic - *= require cal-heatmap *= require cropper.css */ diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss index 11f39d583bd..8642b7530e2 100644 --- a/app/assets/stylesheets/framework/calendar.scss +++ b/app/assets/stylesheets/framework/calendar.scss @@ -1,70 +1,44 @@ .calender-block { + padding-left: 0; + padding-right: 0; + @media (min-width: $screen-sm-min) and (max-width: $screen-lg-min) { overflow-x: scroll; } } .user-calendar-activities { - .calendar_onclick_hr { - padding: 0; - margin: 10px 0; - } - .str-truncated { max-width: 70%; } - .text-expander { - background: #eee; - color: #555; - padding: 0 5px; - cursor: pointer; - margin-left: 4px; - &:hover { - background-color: #ddd; - } + .user-calendar-activities-loading { + font-size: 24px; } } -/** -* This overwrites the default values of the cal-heatmap gem -*/ -.calendar { - .qi { - fill: #fff; - } - - .q1 { - fill: #ededed !important; - } +.user-calendar { + text-align: center; - .q2 { - fill: #acd5f2 !important; - } - - .q3 { - fill: #7fa8d1 !important; - } - - .q4 { - fill: #49729b !important; - } - - .q5 { - fill: #254e77 !important; + .calendar { + display: inline-block; } +} - .future { - visibility: hidden; +.user-contrib-cell { + &:hover { + cursor: pointer; + stroke: #000; } +} - .domain-background { - fill: none; - shape-rendering: crispedges; - } +.user-contrib-text { + font-size: 12px; + fill: #959494; +} - .ch-tooltip { - padding: 3px; - font-weight: 550; - } +.calendar-hint { + margin-top: -23px; + float: right; + font-size: 12px; } diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 4bf3a050403..79a37d87e82 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -162,6 +162,10 @@ } } +.dropdown-menu-full-width { + width: 100%; +} + .dropdown-menu-paging { .dropdown-page-two, .dropdown-menu-back { diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index 8c96c7a9c31..71a9f79be3e 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -5,6 +5,10 @@ .file-holder { border: 1px solid $border-color; + &.file-holder-no-border { + border: 0; + } + &.readme-holder { margin: $gl-padding-top 0; } @@ -23,8 +27,17 @@ word-wrap: break-word; border-radius: 3px 3px 0 0; + &.file-title-clear { + padding-left: 0; + padding-right: 0; + background-color: transparent; + + .file-actions { + right: 0; + } + } + .file-actions { - float: right; position: absolute; top: 5px; right: 15px; diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index c70be58f48f..adfe5540704 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -1,3 +1,34 @@ +@mixin fade($gradient-direction, $rgba, $gradient-color) { + visibility: visible; + opacity: 1; + position: absolute; + bottom: 12px; + width: 43px; + height: 30px; + transition-duration: .3s; + -webkit-transform: translateZ(0); + background: -webkit-linear-gradient($gradient-direction, $rgba, $gradient-color 45%); + background: -o-linear-gradient($gradient-direction, $rgba, $gradient-color 45%); + background: -moz-linear-gradient($gradient-direction, $rgba, $gradient-color 45%); + background: linear-gradient($gradient-direction, $rgba, $gradient-color 45%); + + &.end-scroll { + visibility: hidden; + opacity: 0; + transition-duration: .3s; + } +} + +@mixin scrolling-links() { + white-space: nowrap; + overflow-x: auto; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + &::-webkit-scrollbar { + display: none; + } +} + .nav-links { padding: 0; margin: 0; @@ -119,7 +150,7 @@ } input { - height: 34px; + height: 35px; display: inline-block; position: relative; top: 2px; @@ -196,7 +227,7 @@ position: fixed; top: $header-height; width: 100%; - z-index: 3; + z-index: 11; background: $background-color; border-bottom: 1px solid $border-color; transition-duration: .3s; @@ -209,13 +240,8 @@ float: right; padding: 7px 0 0; - @media (max-width: $screen-xs-min) { - float: none; - padding: 0 9px; - - .dropdown-new { - width: 100%; - } + @media (max-width: $screen-xs-max) { + display: none; } i { @@ -246,14 +272,18 @@ } .nav-links { + @include scrolling-links(); border-bottom: none; height: 51px; - white-space: nowrap; - overflow-x: auto; - overflow-y: hidden; - -webkit-overflow-scrolling: touch; - &::-webkit-scrollbar { - display: none; + + .fade-right { + @include fade(left, rgba(250, 250, 250, 0.4), $background-color); + right: 0; + } + + .fade-left { + @include fade(right, rgba(250, 250, 250, 0.4), $background-color); + left: 0; } li { @@ -278,16 +308,39 @@ } } + .nav-control { + .fade-right { + + @media (min-width: $screen-xs-max) { + right: 67px; + } + @media (max-width: $screen-xs-min) { + right: 0; + } + } + } } -.page-with-layout-nav { - margin-top: 50px; +.nav-block { + position: relative; - &.controls-dropdown-visible { - @media (max-width: $screen-xs-min) { - margin-top: 96px; + .nav-links { + @include scrolling-links(); + + .fade-right { + @include fade(left, rgba(255, 255, 255, 0.4), $white-light); + right: 0; + } + + .fade-left { + @include fade(right, rgba(255, 255, 255, 0.4), $white-light); + left: 0; } } +} + +.page-with-layout-nav { + margin-top: $header-height + 2; .right-sidebar { top: ($header-height * 2) + 2; diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index f90d7a806d3..67f491b6d9c 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -324,7 +324,7 @@ .layout-nav { @media (max-width: $screen-xs-min) { - padding-right: 0;; + padding-right: 0; } @media (min-width: $screen-xs-min) and (max-width: $screen-md-min) { diff --git a/app/assets/stylesheets/mailers/devise.scss b/app/assets/stylesheets/mailers/devise.scss new file mode 100644 index 00000000000..28611a5ec81 --- /dev/null +++ b/app/assets/stylesheets/mailers/devise.scss @@ -0,0 +1,134 @@ +// NOTE: This stylesheet is for the exclusive use of the `devise_mailer` layout +// used for Devise email templates, and _should not_ be included in any +// application stylesheets. +// +// Styles defined here are embedded directly into the resulting email HTML via +// the `premailer` gem. + +$body-background-color: #363636; +$message-background-color: #fafafa; + +$header-color: #6b4fbb; +$body-color: #444; +$cta-color: #e14329; +$footer-link-color: #7e7e7e; + +$font-family: Helvetica, Arial, sans-serif; + +body { + background-color: $body-background-color; + font-family: $font-family; + margin: 0; + padding: 0; +} + +table { + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + + border: 0; + border-collapse: separate; + + &#wrapper { + background-color: $body-background-color; + width: 100%; + } + + &#header { + margin: 0 auto; + text-align: left; + width: 600px; + } + + &#body { + background-color: $message-background-color; + border: 1px solid #000; + border-radius: 4px; + margin: 0 auto; + width: 600px; + } + + &#footer { + color: $footer-link-color; + font-size: 14px; + text-align: center; + width: 100%; + } + + td { + &#body-container { + padding: 20px 40px; + } + } +} + +.center { + text-align: center; +} + +#logo { + border: none; + outline: none; + min-height: 88px; + width: 134px; +} + +#content { + h2 { + color: $header-color; + font-size: 30px; + font-weight: 400; + line-height: 34px; + margin-top: 0; + } + + p { + color: $body-color; + font-size: 17px; + line-height: 24px; + margin-bottom: 0; + } +} + +#cta { + border: 1px solid $cta-color; + border-radius: 3px; + display: inline-block; + margin: 20px 0; + padding: 12px 24px; + + a { + background-color: $message-background-color; + color: $cta-color; + display: inline-block; + text-decoration: none; + } +} + +#tanuki { + padding: 40px 0 0; + + img { + border: none; + outline: none; + width: 37px; + min-height: 36px; + } +} + +#tagline { + font-size: 22px; + font-weight: 100; + padding: 4px 0 40px; +} + +#social { + padding: 0 10px 20px; + width: 600px; + word-spacing: 20px; + + a { + color: $footer-link-color; + text-decoration: none; + } +} diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index fc9db97132d..4e35ca329e4 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -40,11 +40,6 @@ } } -.issue-search-form { - margin: 0; - height: 24px; -} - form.edit-issue { margin: 0; } @@ -96,8 +91,3 @@ form.edit-issue { .issue-form .select2-container { width: 250px !important; } - -.issue-closed-by-widget { - color: $gl-text-color; - margin-left: 52px; -} diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index c4005ba1e69..4f8a8748d3f 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -280,11 +280,5 @@ background-color: $white-light; color: $gl-placeholder-color; } - - th, - td { - padding: 16px; - } } } - diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 546176b65e4..6128868b670 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -1,4 +1,24 @@ -.pipeline-stage { - overflow: hidden; - text-overflow: ellipsis; +.pipelines { + .stage { + max-width: 100px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .duration, .finished_at { + margin: 4px 0; + } + + .commit-title { + margin: 0; + } + + .controls { + white-space: nowrap; + } + + .btn { + margin: 4px; + } } diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 843379a3f54..167ab40d881 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -66,12 +66,6 @@ } } -.calendar-hint { - margin-top: -12px; - float: right; - font-size: 12px; -} - .profile-link-holder { display: inline; @@ -134,14 +128,6 @@ } } -.change-username-title { - color: $gl-warning; -} - -.remove-account-title { - color: $gl-danger; -} - .provider-btn-group { display: inline-block; margin-right: 10px; diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index b7653a833ec..edef336481d 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -10,7 +10,7 @@ margin-bottom: 0; } .new_project, -.edit_project { +.edit-project { fieldset.features { .control-label { font-weight: normal; diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss index 3fb70085713..2e8f356298d 100644 --- a/app/assets/stylesheets/pages/settings.scss +++ b/app/assets/stylesheets/pages/settings.scss @@ -12,3 +12,11 @@ border: 1px solid $warning-message-border; border-radius: $border-radius-base; } + +.warning-title { + color: $gl-warning; +} + +.danger-title { + color: $gl-danger; +} diff --git a/app/assets/stylesheets/pages/snippets.scss b/app/assets/stylesheets/pages/snippets.scss index 639d639d5b0..2aa939b7dc3 100644 --- a/app/assets/stylesheets/pages/snippets.scss +++ b/app/assets/stylesheets/pages/snippets.scss @@ -16,19 +16,6 @@ } } -.snippet-box { - @include border-radius(2px); - - display: block; - float: left; - padding: 0 $gl-padding; - font-weight: normal; - margin-right: 10px; - font-size: $gl-font-size; - border: 1px solid; - line-height: 32px; -} - .markdown-snippet-copy { position: fixed; top: -10px; @@ -36,3 +23,34 @@ max-height: 0; max-width: 0; } + +.file-holder.snippet-file-content { + padding-bottom: $gl-padding; + border-bottom: 1px solid $border-color; + + .file-title { + padding-top: $gl-padding; + padding-bottom: $gl-padding; + } + + .file-actions { + top: 12px; + } + + .file-content { + border-left: 1px solid $border-color; + border-right: 1px solid $border-color; + border-bottom: 1px solid $border-color; + } +} + +.snippet-title { + font-size: 24px; + font-weight: normal; +} + +.snippet-actions { + @media (min-width: $screen-sm-min) { + float: right; + } +} diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss index e51c3491dae..afc00a68572 100644 --- a/app/assets/stylesheets/pages/todos.scss +++ b/app/assets/stylesheets/pages/todos.scss @@ -29,6 +29,17 @@ .todo-item { .todo-title { @include str-truncated(calc(100% - 174px)); + overflow: visible; + } + + .status-box { + margin: 0; + float: none; + display: inline-block; + font-weight: normal; + padding: 0 5px; + line-height: inherit; + font-size: 14px; } .todo-body { @@ -76,12 +87,11 @@ @media (max-width: $screen-xs-max) { .todo-item { - padding-left: $gl-padding; - .todo-title { white-space: normal; overflow: visible; max-width: 100%; + margin-bottom: 10px; } .avatar { diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index f137c12d215..17ce8e2ad20 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -229,6 +229,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController if ci_commit status = ci_commit.status coverage = ci_commit.try(:coverage) + + status ||= "preparing" else ci_service = @merge_request.source_project.ci_service status = ci_service.commit_status(merge_request.last_commit.sha, merge_request.source_branch) if ci_service @@ -238,8 +240,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController end end - status = "preparing" if status.nil? - response = { title: merge_request.title, sha: merge_request.last_commit_short_sha, diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 799421c185b..a99632454d9 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -74,8 +74,6 @@ class UsersController < ApplicationController def calendar calendar = contributions_calendar @timestamps = calendar.timestamps - @starting_year = calendar.starting_year - @starting_month = calendar.starting_month render 'calendar', layout: false end diff --git a/app/finders/group_projects_finder.rb b/app/finders/group_projects_finder.rb index 3b9a421b118..aa8f4c1d0e4 100644 --- a/app/finders/group_projects_finder.rb +++ b/app/finders/group_projects_finder.rb @@ -18,7 +18,7 @@ class GroupProjectsFinder < UnionFinder projects = [] if current_user - if @group.users.include?(current_user) + if @group.users.include?(current_user) || current_user.admin? projects << @group.projects unless only_shared projects << @group.shared_projects unless only_owned else diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 5849e00662b..8ed3ccf1c02 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -250,12 +250,12 @@ class IssuableFinder def by_milestone(items) if milestones? if filter_by_no_milestone? - items = items.where(milestone_id: [-1, nil]) + items = items.left_joins_milestones.where(milestone_id: [-1, nil]) elsif filter_by_upcoming_milestone? upcoming_ids = Milestone.upcoming_ids_by_projects(projects) - items = items.joins(:milestone).where(milestone_id: upcoming_ids) + items = items.left_joins_milestones.where(milestone_id: upcoming_ids) else - items = items.joins(:milestone).where(milestones: { title: params[:milestone_title] }) + items = items.with_milestone(params[:milestone_title]) if projects items = items.where(milestones: { project_id: projects }) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e6e2546b92f..439b015b3b8 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -262,6 +262,8 @@ module ApplicationHelper assignee_id: params[:assignee_id], author_id: params[:author_id], sort: params[:sort], + issue_search: params[:issue_search], + label_name: params[:label_name] } options = exist_opts.merge(options) @@ -272,16 +274,11 @@ module ApplicationHelper end end - path = request.path - path << "?#{options.to_param}" - if add_label - if params[:label_name].present? and params[:label_name].respond_to?('any?') - params[:label_name].each do |label| - path << "&label_name[]=#{label}" - end - end - end - path + params = options.compact + + params.delete(:label_name) unless add_label + + "#{request.path}?#{params.to_param}" end def outdated_browser? diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index e1489381706..bfedcb1c42b 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -159,28 +159,6 @@ module EventsHelper "--broken encoding" end - def event_to_atom(xml, event) - if event.visible_to_user?(current_user) - xml.entry do - event_link = event_feed_url(event) - event_title = event_feed_title(event) - event_summary = event_feed_summary(event) - - xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}" - xml.link href: event_link - xml.title truncate(event_title, length: 80) - xml.updated event.created_at.xmlschema - xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(event.author_email)) - xml.author do |author| - xml.name event.author_name - xml.email event.author_email - end - - xml.summary(type: "xhtml") { |x| x << event_summary unless event_summary.nil? } - end - end - end - def event_row_class(event) if event.body? "event-block" diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 198d39455d7..551409e8855 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -105,23 +105,6 @@ module IssuesHelper return 'hidden' if issue.closed? == closed end - def issue_to_atom(xml, issue) - xml.entry do - xml.id namespace_project_issue_url(issue.project.namespace, - issue.project, issue) - xml.link href: namespace_project_issue_url(issue.project.namespace, - issue.project, issue) - xml.title truncate(issue.title, length: 80) - xml.updated issue.created_at.xmlschema - xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(issue.author_email)) - xml.author do |author| - xml.name issue.author_name - xml.email issue.author_email - end - xml.summary issue.title - end - end - def merge_requests_sentence(merge_requests) # Sorting based on the `!123` or `group/project!123` reference will sort # local merge requests first. diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index fbb799eecd3..f685e547537 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -48,7 +48,7 @@ module NavHelper "page-with-layout-nav" if defined?(nav) && nav end - def layout_dropdown_class - "controls-dropdown-visible" if current_user + def nav_control_class + "nav-control" if current_user end end diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index 81b9b5d7052..b9d7edb4185 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -37,6 +37,16 @@ module TodosHelper end end + def todo_target_state_pill(todo) + return unless show_todo_state?(todo) + + content_tag(:span, nil, class: 'target-status') do + content_tag(:span, nil, class: "status-box status-box-#{todo.target.state.dasherize}") do + todo.target.state.capitalize + end + end + end + def todos_filter_params { state: params[:state], @@ -95,4 +105,10 @@ module TodosHelper options_from_collection_for_select(types, 'name', 'title', params[:type]) end + + private + + def show_todo_state?(todo) + (todo.target.is_a?(MergeRequest) || todo.target.is_a?(Issue)) && ['closed', 'merged'].include?(todo.target.state) + end end diff --git a/app/mailers/devise_mailer.rb b/app/mailers/devise_mailer.rb index b616add283a..415f6e12885 100644 --- a/app/mailers/devise_mailer.rb +++ b/app/mailers/devise_mailer.rb @@ -1,4 +1,6 @@ class DeviseMailer < Devise::Mailer default from: "#{Gitlab.config.gitlab.email_display_name} <#{Gitlab.config.gitlab.email_from}>" default reply_to: Gitlab.config.gitlab.email_reply_to + + layout 'devise_mailer' end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index c1248b53031..91315b3459f 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -31,18 +31,21 @@ module Issuable scope :unassigned, -> { where("assignee_id IS NULL") } scope :of_projects, ->(ids) { where(project_id: ids) } scope :of_milestones, ->(ids) { where(milestone_id: ids) } + scope :with_milestone, ->(title) { left_joins_milestones.where(milestones: { title: title }) } scope :opened, -> { with_state(:opened, :reopened) } scope :only_opened, -> { with_state(:opened) } scope :only_reopened, -> { with_state(:reopened) } scope :closed, -> { with_state(:closed) } - scope :order_milestone_due_desc, -> { outer_join_milestone.reorder('milestones.due_date IS NULL ASC, milestones.due_date DESC, milestones.id DESC') } - scope :order_milestone_due_asc, -> { outer_join_milestone.reorder('milestones.due_date IS NULL ASC, milestones.due_date ASC, milestones.id ASC') } - scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) } + scope :left_joins_milestones, -> { joins("LEFT OUTER JOIN milestones ON #{table_name}.milestone_id = milestones.id") } + scope :order_milestone_due_desc, -> { left_joins_milestones.reorder('milestones.due_date IS NULL, milestones.id IS NULL, milestones.due_date DESC') } + scope :order_milestone_due_asc, -> { left_joins_milestones.reorder('milestones.due_date IS NULL, milestones.id IS NULL, milestones.due_date ASC') } + + scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) } scope :join_project, -> { joins(:project) } scope :references_project, -> { references(:project) } scope :non_archived, -> { join_project.where(projects: { archived: false }) } - scope :outer_join_milestone, -> { joins("LEFT OUTER JOIN milestones ON milestones.id = #{table_name}.milestone_id") } + delegate :name, :email, diff --git a/app/models/project.rb b/app/models/project.rb index 37de1dfe4d5..f3a56b86d5a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -431,7 +431,13 @@ class Project < ActiveRecord::Base def check_limit unless creator.can_create_project? or namespace.kind == 'group' - self.errors.add(:limit_reached, "Your project limit is #{creator.projects_limit} projects! Please contact your administrator to increase it") + projects_limit = creator.projects_limit + + if projects_limit == 0 + self.errors.add(:limit_reached, "Personal project creation is not allowed. Please contact your administrator with questions") + else + self.errors.add(:limit_reached, "Your project limit is #{projects_limit} projects! Please contact your administrator to increase it") + end end rescue self.errors.add(:base, "Can't check your ability to create project") diff --git a/app/models/project_services/slack_service/build_message.rb b/app/models/project_services/slack_service/build_message.rb index c124cad4afd..69c21b3fc38 100644 --- a/app/models/project_services/slack_service/build_message.rb +++ b/app/models/project_services/slack_service/build_message.rb @@ -35,8 +35,8 @@ class SlackService private def message - "#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status} in #{duration} second(s)" - end + "#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status} in #{duration} #{'second'.pluralize(duration)}" + end def format(string) Slack::Notifier::LinkFormatter.format(string) diff --git a/app/models/repository.rb b/app/models/repository.rb index ecc8795c954..1ab163510bf 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -972,12 +972,6 @@ class Repository end end - def main_language - return unless head_exists? - - Linguist::Repository.new(rugged, rugged.head.target_id).language - end - def avatar return nil unless exists? diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index 66136b62617..a886f35981f 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -53,10 +53,6 @@ class GitPushService < BaseService # could cause the last commit of a merge request to change. update_merge_requests - # Checks if the main language has changed in the project and if so - # it updates it accordingly - update_main_language - perform_housekeeping end @@ -64,19 +60,6 @@ class GitPushService < BaseService @project.repository.copy_gitattributes(params[:ref]) end - def update_main_language - # Performance can be bad so for now only check main_language once - # See https://gitlab.com/gitlab-org/gitlab-ce/issues/14937 - return if @project.main_language.present? - - return unless is_default_branch? - return unless push_to_new_branch? || push_to_existing_branch? - - current_language = @project.repository.main_language - @project.update_attributes(main_language: current_language) - true - end - protected def update_merge_requests diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index 0577ae778d5..de6dc38cc8e 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -3,7 +3,7 @@ module Projects def execute new_params = { forked_from_project_id: @project.id, - visibility_level: @project.visibility_level, + visibility_level: allowed_visibility_level, description: @project.description, name: @project.name, path: @project.path, @@ -19,5 +19,17 @@ module Projects new_project = CreateService.new(current_user, new_params).execute new_project end + + private + + def allowed_visibility_level + project_level = @project.visibility_level + + if Gitlab::VisibilityLevel.non_restricted_level?(project_level) + project_level + else + Gitlab::VisibilityLevel.highest_allowed_level + end + end end end diff --git a/app/views/admin/builds/index.html.haml b/app/views/admin/builds/index.html.haml index ed24757087b..d74cf8598e8 100644 --- a/app/views/admin/builds/index.html.haml +++ b/app/views/admin/builds/index.html.haml @@ -47,4 +47,3 @@ = render "admin/builds/build", build: build = paginate @builds, theme: 'gitlab' - diff --git a/app/views/dashboard/issues.atom.builder b/app/views/dashboard/issues.atom.builder index 0d7b1b30dc3..83c0c6da21b 100644 --- a/app/views/dashboard/issues.atom.builder +++ b/app/views/dashboard/issues.atom.builder @@ -6,8 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.id issues_dashboard_url xml.updated @issues.first.created_at.xmlschema if @issues.any? - @issues.each do |issue| - issue_to_atom(xml, issue) - end + xml << render(partial: 'issues/issue', collection: @issues) if @issues.any? end - diff --git a/app/views/dashboard/projects/index.atom.builder b/app/views/dashboard/projects/index.atom.builder index d4daf07c6c0..fb5be63b472 100644 --- a/app/views/dashboard/projects/index.atom.builder +++ b/app/views/dashboard/projects/index.atom.builder @@ -6,7 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.id dashboard_projects_url xml.updated @events[0].updated_at.xmlschema if @events[0] - @events.each do |event| - event_to_atom(xml, event) - end + xml << render(partial: 'events/event', collection: @events) if @events.any? end diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml index 539f1dc6036..98f302d2f93 100644 --- a/app/views/dashboard/todos/_todo.html.haml +++ b/app/views/dashboard/todos/_todo.html.haml @@ -3,6 +3,8 @@ = image_tag avatar_icon(todo.author_email, 40), class: 'avatar s40', alt:'' .todo-title.title - unless todo.build_failed? + = todo_target_state_pill(todo) + %span.author-name - if todo.author = link_to_author(todo) diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb deleted file mode 100644 index c6fa8f0ee36..00000000000 --- a/app/views/devise/mailer/confirmation_instructions.html.erb +++ /dev/null @@ -1,9 +0,0 @@ -<p>Welcome <%= @resource.name %>!</p> - -<% if @resource.unconfirmed_email.present? %> - <p>You can confirm your email (<%= @resource.unconfirmed_email %>) through the link below:</p> -<% else %> - <p>You can confirm your account through the link below:</p> -<% end %> - -<p><%= link_to 'Confirm your account', confirmation_url(@resource, confirmation_token: @token) %></p> diff --git a/app/views/devise/mailer/confirmation_instructions.html.haml b/app/views/devise/mailer/confirmation_instructions.html.haml new file mode 100644 index 00000000000..086bb8e083d --- /dev/null +++ b/app/views/devise/mailer/confirmation_instructions.html.haml @@ -0,0 +1,16 @@ +.center + - if @resource.unconfirmed_email.present? + #content + %h2= @resource.unconfirmed_email + %p Click the link below to confirm your email address. + #cta + = link_to 'Confirm your email address', confirmation_url(@resource, confirmation_token: @token) + - else + #content + - if Gitlab.com? + %h2 Thanks for signing up to GitLab! + - else + %h2 Welcome, #{@resource.name}! + %p To get started, click the link below to confirm your account. + #cta + = link_to 'Confirm your account', confirmation_url(@resource, confirmation_token: @token) diff --git a/app/views/devise/mailer/confirmation_instructions.text.erb b/app/views/devise/mailer/confirmation_instructions.text.erb new file mode 100644 index 00000000000..9f76edb76a4 --- /dev/null +++ b/app/views/devise/mailer/confirmation_instructions.text.erb @@ -0,0 +1,9 @@ +Welcome, <%= @resource.name %>! + +<% if @resource.unconfirmed_email.present? %> +You can confirm your email (<%= @resource.unconfirmed_email %>) through the link below: +<% else %> +You can confirm your account through the link below: +<% end %> + +<%= confirmation_url(@resource, confirmation_token: @token) %> diff --git a/app/views/doorkeeper/authorizations/new.html.haml b/app/views/doorkeeper/authorizations/new.html.haml index eae80e5210f..ce050007204 100644 --- a/app/views/doorkeeper/authorizations/new.html.haml +++ b/app/views/doorkeeper/authorizations/new.html.haml @@ -1,4 +1,4 @@ -%h3.page-title Authorize required +%h3.page-title Authorization required %main{:role => "main"} %p.h4 Authorize diff --git a/app/views/events/_event.atom.builder b/app/views/events/_event.atom.builder new file mode 100644 index 00000000000..7890e717aa7 --- /dev/null +++ b/app/views/events/_event.atom.builder @@ -0,0 +1,20 @@ +return unless event.visible_to_user?(current_user) + +xml.entry do + xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}" + xml.link href: event_feed_url(event) + xml.title truncate(event_feed_title(event), length: 80) + xml.updated event.created_at.xmlschema + xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(event.author_email)) + + xml.author do + xml.name event.author_name + xml.email event.author_email + end + + xml.summary(type: "xhtml") do |summary| + event_summary = event_feed_summary(event) + + summary << event_summary unless event_summary.nil? + end +end diff --git a/app/views/groups/issues.atom.builder b/app/views/groups/issues.atom.builder index a6eb9abada6..c19671295af 100644 --- a/app/views/groups/issues.atom.builder +++ b/app/views/groups/issues.atom.builder @@ -6,8 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.id issues_group_url xml.updated @issues.first.created_at.xmlschema if @issues.any? - @issues.each do |issue| - issue_to_atom(xml, issue) - end + xml << render(partial: 'issues/issue', collection: @issues) if @issues.any? end - diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder index c66b82bb484..b68bf444d27 100644 --- a/app/views/groups/show.atom.builder +++ b/app/views/groups/show.atom.builder @@ -6,7 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.id group_url(@group) xml.updated @events[0].updated_at.xmlschema if @events[0] - @events.each do |event| - event_to_atom(xml, event) - end + xml << render(@events) if @events.any? end diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml index e3a356b5379..aedb8468eca 100644 --- a/app/views/import/gitlab/status.html.haml +++ b/app/views/import/gitlab/status.html.haml @@ -47,7 +47,7 @@ %td.import-target = repo["path_with_namespace"] %td.import-actions.job-status - = button_tag class: "btn js-add-to-import" do + = button_tag class: "btn btn-import js-add-to-import" do Import = icon("spinner spin", class: "loading-icon") diff --git a/app/views/issues/_issue.atom.builder b/app/views/issues/_issue.atom.builder new file mode 100644 index 00000000000..68a2d19e58d --- /dev/null +++ b/app/views/issues/_issue.atom.builder @@ -0,0 +1,14 @@ +xml.entry do + xml.id namespace_project_issue_url(issue.project.namespace, issue.project, issue) + xml.link href: namespace_project_issue_url(issue.project.namespace, issue.project, issue) + xml.title truncate(issue.title, length: 80) + xml.updated issue.created_at.xmlschema + xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(issue.author_email)) + + xml.author do |author| + xml.name issue.author_name + xml.email issue.author_email + end + + xml.summary issue.title +end diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 5be0b546a62..1e961853c70 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -25,7 +25,7 @@ .layout-nav .container-fluid = render "layouts/nav/#{nav}" - .content-wrapper{ class: "#{layout_nav_class} #{layout_dropdown_class}" } + .content-wrapper{ class: "#{layout_nav_class}" } = render "layouts/broadcast" = render "layouts/flash" = yield :flash_message diff --git a/app/views/layouts/devise_mailer.html.haml b/app/views/layouts/devise_mailer.html.haml new file mode 100644 index 00000000000..c258eafdd51 --- /dev/null +++ b/app/views/layouts/devise_mailer.html.haml @@ -0,0 +1,34 @@ +!!! 5 +%html + %head + %meta(content='text/html; charset=UTF-8' http-equiv='Content-Type') + = stylesheet_link_tag 'mailers/devise' + + %body + %table#wrapper + %tr + %td + %table#header + %td{valign: "top"} + = image_tag('mailers/gitlab_header_logo.png', id: 'logo', alt: 'GitLab Wordmark') + + %table#body + %tr + %td#body-container + = yield + + - if Gitlab.com? + %table#footer + %tr + %td#tanuki + = image_tag('mailers/gitlab_tanuki_2x.png', alt: 'GitLab Logo') + %tr + %td#tagline + Everyone can contribute + %tr + %td#social + = link_to 'Blog', 'https://about.gitlab.com/blog/' + = link_to 'Twitter', 'https://twitter.com/gitlab' + = link_to 'Facebook', 'https://www.facebook.com/gitlab/' + = link_to 'YouTube', 'https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg' + = link_to 'LinkedIn', 'https://www.linkedin.com/company/gitlab-com' diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index 3438005863a..de15add3617 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -1,37 +1,40 @@ -= render 'layouts/nav/group_settings' +%div{ class: nav_control_class } + = render 'layouts/nav/group_settings' -%ul.nav-links - = nav_link(path: 'groups#show', html_options: {class: 'home'}) do - = link_to group_path(@group), title: 'Home' do - = icon('group fw') - %span - Group - = nav_link(path: 'groups#activity') do - = link_to activity_group_path(@group), title: 'Activity' do - = icon('dashboard fw') - %span - Activity - = nav_link(controller: [:group, :milestones]) do - = link_to group_milestones_path(@group), title: 'Milestones' do - = icon('clock-o fw') - %span - Milestones - = nav_link(path: 'groups#issues') do - = link_to issues_group_path(@group), title: 'Issues' do - = icon('exclamation-circle fw') - %span - Issues - - issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute - %span.badge.count= number_with_delimiter(issues.count) - = nav_link(path: 'groups#merge_requests') do - = link_to merge_requests_group_path(@group), title: 'Merge Requests' do - = icon('tasks fw') - %span - Merge Requests - - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute - %span.badge.count= number_with_delimiter(merge_requests.count) - = nav_link(controller: [:group_members]) do - = link_to group_group_members_path(@group), title: 'Members' do - = icon('users fw') - %span - Members + %ul.nav-links.scrolling-tabs + .fade-left + = nav_link(path: 'groups#show', html_options: {class: 'home'}) do + = link_to group_path(@group), title: 'Home' do + = icon('group fw') + %span + Group + = nav_link(path: 'groups#activity') do + = link_to activity_group_path(@group), title: 'Activity' do + = icon('dashboard fw') + %span + Activity + = nav_link(controller: [:group, :milestones]) do + = link_to group_milestones_path(@group), title: 'Milestones' do + = icon('clock-o fw') + %span + Milestones + = nav_link(path: 'groups#issues') do + = link_to issues_group_path(@group), title: 'Issues' do + = icon('exclamation-circle fw') + %span + Issues + - issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute + %span.badge.count= number_with_delimiter(issues.count) + = nav_link(path: 'groups#merge_requests') do + = link_to merge_requests_group_path(@group), title: 'Merge Requests' do + = icon('tasks fw') + %span + Merge Requests + - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute + %span.badge.count= number_with_delimiter(merge_requests.count) + = nav_link(controller: [:group_members]) do + = link_to group_group_members_path(@group), title: 'Members' do + = icon('users fw') + %span + Members + .fade-right diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index d730840d63a..2efc6c48a48 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -1,4 +1,5 @@ -%ul.nav-links +%ul.nav-links.scrolling-tabs + .fade-left = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do = link_to profile_path, title: 'Profile Settings' do = icon('user fw') @@ -47,3 +48,4 @@ = icon('history fw') %span Audit Log + .fade-right diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 33ba654bbee..087b7472701 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -19,113 +19,117 @@ 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 - = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do - = icon('bookmark fw') - %span - Project - = nav_link(path: 'projects#activity') do - = link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do - = icon('dashboard fw') - %span - Activity - - if project_nav_tab? :files - = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do - = link_to project_files_path(@project), title: 'Files', class: 'shortcuts-tree' do - = icon('files-o fw') +%div{ class: nav_control_class } + %ul.nav-links.scrolling-tabs + .fade-left + = nav_link(path: 'projects#show', html_options: {class: 'home'}) do + = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do + = icon('bookmark fw') %span - Files - - - if project_nav_tab? :commits - = nav_link(controller: %w(commit commits compare repositories tags branches releases network)) do - = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do - = icon('history fw') + Project + = nav_link(path: 'projects#activity') do + = link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do + = icon('dashboard fw') %span - Commits + Activity + - if project_nav_tab? :files + = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do + = link_to project_files_path(@project), title: 'Files', class: 'shortcuts-tree' do + = icon('files-o fw') + %span + Files - - if project_nav_tab? :pipelines - = nav_link(controller: :pipelines) do - = link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do - = icon('ship fw') - %span - Pipelines - %span.badge.count.ci_counter= number_with_delimiter(@project.ci_commits.running_or_pending.count) + - if project_nav_tab? :commits + = nav_link(controller: %w(commit commits compare repositories tags branches releases network)) do + = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do + = icon('history fw') + %span + Commits - - if project_nav_tab? :builds - = nav_link(controller: %w(builds)) do - = link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do - = icon('cubes fw') - %span - Builds - %span.badge.count.builds_counter= number_with_delimiter(@project.builds.running_or_pending.count(:all)) + - if project_nav_tab? :pipelines + = nav_link(controller: :pipelines) do + = link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do + = icon('ship fw') + %span + Pipelines + %span.badge.count.ci_counter= number_with_delimiter(@project.ci_commits.running_or_pending.count) - - if project_nav_tab? :container_registry - = nav_link(controller: %w(container_registry)) do - = link_to project_container_registry_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do - = icon('hdd-o fw') - %span - Container Registry + - if project_nav_tab? :builds + = nav_link(controller: %w(builds)) do + = link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do + = icon('cubes fw') + %span + Builds + %span.badge.count.builds_counter= number_with_delimiter(@project.builds.running_or_pending.count(:all)) - - if project_nav_tab? :graphs - = nav_link(controller: %w(graphs)) do - = link_to namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Graphs', class: 'shortcuts-graphs' do - = icon('area-chart fw') - %span - Graphs + - if project_nav_tab? :container_registry + = nav_link(controller: %w(container_registry)) do + = link_to project_container_registry_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do + = icon('hdd-o fw') + %span + Container Registry - - if project_nav_tab? :milestones - = nav_link(controller: :milestones) do - = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do - = icon('clock-o fw') - %span - Milestones + - if project_nav_tab? :graphs + = nav_link(controller: %w(graphs)) do + = link_to namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Graphs', class: 'shortcuts-graphs' do + = icon('area-chart fw') + %span + Graphs - - if project_nav_tab? :issues - = nav_link(controller: :issues) do - = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues' do - = icon('exclamation-circle fw') - %span - Issues - - if @project.default_issues_tracker? - %span.badge.count.issue_counter= number_with_delimiter(@project.issues.visible_to_user(current_user).opened.count) + - if project_nav_tab? :milestones + = nav_link(controller: :milestones) do + = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do + = icon('clock-o fw') + %span + Milestones - - if project_nav_tab? :merge_requests - = nav_link(controller: :merge_requests) do - = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do - = icon('tasks fw') - %span - Merge Requests - %span.badge.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count) + - if project_nav_tab? :issues + = nav_link(controller: :issues) do + = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues' do + = icon('exclamation-circle fw') + %span + Issues + - if @project.default_issues_tracker? + %span.badge.count.issue_counter= number_with_delimiter(@project.issues.visible_to_user(current_user).opened.count) - - if project_nav_tab? :labels - = nav_link(controller: :labels) do - = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do - = icon('tags fw') - %span - Labels + - if project_nav_tab? :merge_requests + = nav_link(controller: :merge_requests) do + = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do + = icon('tasks fw') + %span + Merge Requests + %span.badge.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count) - - if project_nav_tab? :wiki - = nav_link(controller: :wikis) do - = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do - = icon('book fw') - %span - Wiki + - if project_nav_tab? :labels + = nav_link(controller: :labels) do + = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do + = icon('tags fw') + %span + Labels - - if project_nav_tab? :snippets - = nav_link(controller: :snippets) do - = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do - = icon('clipboard fw') - %span - Snippets + - if project_nav_tab? :wiki + = nav_link(controller: :wikis) do + = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do + = icon('book fw') + %span + Wiki + + - if project_nav_tab? :snippets + = nav_link(controller: :snippets) do + = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do + = icon('clipboard fw') + %span + Snippets + + -# Global shortcut to network page for compatibility + - if project_nav_tab? :network + %li.hidden + = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network' do + Network - -# Global shortcut to network page for compatibility - - if project_nav_tab? :network + -# Shortcut to create a new issue %li.hidden - = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network' do - Network + = link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'shortcuts-new-issue' do + Create a new issue - -# Shortcut to create a new issue - %li.hidden - = link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'shortcuts-new-issue' do - Create a new issue + .fade-right diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index afd3d79321f..01ac8161945 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -70,7 +70,7 @@ - if current_user.can_change_username? .row.prepend-top-default .col-lg-3.profile-settings-sidebar - %h4.prepend-top-0.change-username-title + %h4.prepend-top-0.warning-title Change username %p Changing your username will change path to all personal projects! @@ -94,7 +94,7 @@ - if signup_enabled? .row.prepend-top-default .col-lg-3.profile-settings-sidebar - %h4.prepend-top-0.remove-account-title + %h4.prepend-top-0.danger-title Remove account .col-lg-9 - if @user.can_be_removed? diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index bfe53be6854..1b1b16d656f 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -5,7 +5,7 @@ %h4.prepend-top-0 Application theme %p - This setting allows you to customize the appearance of the site, ex. sidebar. + This setting allows you to customize the appearance of the site, e.g. the sidebar. .col-lg-9.application-theme - Gitlab::Themes.each do |theme| = label_tag do diff --git a/app/views/projects/_builds_settings.html.haml b/app/views/projects/_builds_settings.html.haml index 0de019983ca..0568c2d305e 100644 --- a/app/views/projects/_builds_settings.html.haml +++ b/app/views/projects/_builds_settings.html.haml @@ -1,74 +1,65 @@ %fieldset.builds-feature - %legend - Builds: - + %h5.prepend-top-0 + Builds - unless @repository.gitlab_ci_yml .form-group - .col-sm-offset-2.col-sm-10 - %p Builds need to be configured before you can begin using Continuous Integration. - = link_to 'Get started with Builds', help_page_path('ci/quick_start', 'README'), class: 'btn btn-info' - %hr - + %p Builds need to be configured before you can begin using Continuous Integration. + = link_to 'Get started with Builds', help_page_path('ci/quick_start', 'README'), class: 'btn btn-info' .form-group - .col-sm-offset-2.col-sm-10 - %p Get recent application code using the following command: - .radio - = f.label :build_allow_git_fetch_false do - = f.radio_button :build_allow_git_fetch, 'false' - %strong git clone - %br - %span.descr Slower but makes sure you have a clean dir before every build - .radio - = f.label :build_allow_git_fetch_true do - = f.radio_button :build_allow_git_fetch, 'true' - %strong git fetch - %br - %span.descr Faster + %p Get recent application code using the following command: + .radio + = f.label :build_allow_git_fetch_false do + = f.radio_button :build_allow_git_fetch, 'false' + %strong git clone + %br + %span.descr Slower but makes sure you have a clean dir before every build + .radio + = f.label :build_allow_git_fetch_true do + = f.radio_button :build_allow_git_fetch, 'true' + %strong git fetch + %br + %span.descr Faster .form-group - = f.label :build_timeout_in_minutes, 'Timeout', class: 'control-label' - .col-sm-10 - = f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0' - %p.help-block per build in minutes + = f.label :build_timeout_in_minutes, 'Timeout', class: 'label-light' + = f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0' + %p.help-block per build in minutes .form-group - = f.label :build_coverage_regex, "Test coverage parsing", class: 'control-label' - .col-sm-10 - .input-group - %span.input-group-addon / - = f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered' - %span.input-group-addon / - %p.help-block - We will use this regular expression to find test coverage output in build trace. - Leave blank if you want to disable this feature - .bs-callout.bs-callout-info - %p Below are examples of regex for existing tools: - %ul - %li - Simplecov (Ruby) - - %code \(\d+.\d+\%\) covered - %li - pytest-cov (Python) - - %code \d+\%\s*$ - %li - phpunit --coverage-text --colors=never (PHP) - - %code ^\s*Lines:\s*\d+.\d+\% - %li - gcovr (C/C++) - - %code ^TOTAL.*\s+(\d+\%)$ - %li - tap --coverage-report=text-summary (Node.js) - - %code ^Statements\s*:\s*([^%]+) + = f.label :build_coverage_regex, "Test coverage parsing", class: 'label-light' + .input-group + %span.input-group-addon / + = f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered' + %span.input-group-addon / + %p.help-block + We will use this regular expression to find test coverage output in build trace. + Leave blank if you want to disable this feature + .bs-callout.bs-callout-info + %p Below are examples of regex for existing tools: + %ul + %li + Simplecov (Ruby) - + %code \(\d+.\d+\%\) covered + %li + pytest-cov (Python) - + %code \d+\%\s*$ + %li + phpunit --coverage-text --colors=never (PHP) - + %code ^\s*Lines:\s*\d+.\d+\% + %li + gcovr (C/C++) - + %code ^TOTAL.*\s+(\d+\%)$ + %li + tap --coverage-report=text-summary (Node.js) - + %code ^Statements\s*:\s*([^%]+) .form-group - .col-sm-offset-2.col-sm-10 - .checkbox - = f.label :public_builds do - = f.check_box :public_builds - %strong Public builds - .help-block Allow everyone to access builds for Public and Internal projects + .checkbox + = f.label :public_builds do + = f.check_box :public_builds + %strong Public builds + .help-block Allow everyone to access builds for Public and Internal projects - .form-group - = f.label :runners_token, "Runners token", class: 'control-label' - .col-sm-10 - = f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89' - %p.help-block The secure token used to checkout project. + .form-group.append-bottom-0 + = f.label :runners_token, "Runners token", class: 'label-light' + = f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89' + %p.help-block The secure token used to checkout project. diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml index 98f4a9416e5..7c0bec264ab 100644 --- a/app/views/projects/builds/index.html.haml +++ b/app/views/projects/builds/index.html.haml @@ -35,9 +35,6 @@ = icon('wrench') %span CI Lint -.row-content-block - #{(@scope || 'all').capitalize} builds from this project - %ul.content-list - if @builds.blank? %li diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml index e23a3782c6b..5bd6e3f0ebc 100644 --- a/app/views/projects/ci/builds/_build.html.haml +++ b/app/views/projects/ci/builds/_build.html.haml @@ -57,14 +57,10 @@ %td.duration - if build.duration - = icon("clock-o") - #{duration_in_words(build.finished_at, build.started_at)} %td.timestamp - if build.finished_at - = icon("calendar") - %span #{time_ago_with_tooltip(build.finished_at)} - if defined?(coverage) && coverage diff --git a/app/views/projects/ci/commits/_commit.html.haml b/app/views/projects/ci/commits/_commit.html.haml index 13162b41f9b..5b6b940a0c4 100644 --- a/app/views/projects/ci/commits/_commit.html.haml +++ b/app/views/projects/ci/commits/_commit.html.haml @@ -12,10 +12,10 @@ · = link_to commit.short_sha, namespace_project_commit_path(@project.namespace, @project, commit.sha), class: "commit-id monospace" - - if commit.latest? - %span.label.label-success latest - if commit.tag? %span.label.label-primary tag + - elsif commit.latest? + %span.label.label-success.has-tooltip{ title: 'Latest build for this branch' } latest - if commit.triggered? %span.label.label-primary triggered - if commit.yaml_errors.present? @@ -23,33 +23,29 @@ - if commit.builds.any?(&:stuck?) %span.label.label-warning stuck - %p - %span - - if commit_data = commit.commit_data - = link_to_gfm commit_data.title, namespace_project_commit_path(@project.namespace, @project, commit_data.id), class: "commit-row-message" - - else - Cant find HEAD commit for this branch + %p.commit-title + - if commit_data = commit.commit_data + = link_to_gfm truncate(commit_data.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit_data.id), class: "commit-row-message" + - else + Cant find HEAD commit for this branch - stages_status = commit.statuses.stages_status - stages.each do |stage| %td - - if status = stages_status[stage] - - tooltip = "#{stage.titleize}: #{status}" - %span.has-tooltip{ title: "#{tooltip}", class: "ci-status-icon-#{status}" } + - status = stages_status[stage] + - tooltip = "#{stage.titleize}: #{status || 'not found'}" + - if status + = link_to namespace_project_pipeline_path(@project.namespace, @project, commit.id, anchor: stage), class: "has-tooltip ci-status-icon-#{status}", title: tooltip do = ci_icon_for_status(status) + - else + .light.has-tooltip{ title: tooltip } + \- %td - if commit.started_at && commit.finished_at - %p - = icon("clock-o") - + %p.duration #{duration_in_words(commit.finished_at, commit.started_at)} - - if commit.finished_at - %p - = icon("calendar") - - #{time_ago_with_tooltip(commit.finished_at)} %td .controls.hidden-xs.pull-right @@ -67,11 +63,9 @@ %span #{build.name} - if can?(current_user, :update_pipeline, @project) - - if commit.retryable? && commit.builds.failed.any? = link_to retry_namespace_project_pipeline_path(@project.namespace, @project, commit.id), class: 'btn has-tooltip', title: "Retry", method: :post do = icon("repeat") - - if commit.active? = link_to cancel_namespace_project_pipeline_path(@project.namespace, @project, commit.id), class: 'btn btn-remove has-tooltip', title: "Cancel", method: :post do = icon("remove") diff --git a/app/views/projects/commit/_ci_stage.html.haml b/app/views/projects/commit/_ci_stage.html.haml index aaa318e1eb3..ae7bb01223e 100644 --- a/app/views/projects/commit/_ci_stage.html.haml +++ b/app/views/projects/commit/_ci_stage.html.haml @@ -1,6 +1,7 @@ %tr %th{colspan: 10} %strong + %a{name: stage} - status = statuses.latest.status %span{class: "ci-status-link ci-status-icon-#{status}"} = ci_icon_for_status(status) diff --git a/app/views/projects/commits/_commit.atom.builder b/app/views/projects/commits/_commit.atom.builder new file mode 100644 index 00000000000..1657fb46163 --- /dev/null +++ b/app/views/projects/commits/_commit.atom.builder @@ -0,0 +1,14 @@ +xml.entry do + xml.id namespace_project_commit_url(@project.namespace, @project, id: commit.id) + xml.link href: namespace_project_commit_url(@project.namespace, @project, id: commit.id) + xml.title truncate(commit.title, length: 80) + xml.updated commit.committed_date.xmlschema + xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(commit.author_email)) + + xml.author do |author| + xml.name commit.author_name + xml.email commit.author_email + end + + xml.summary markdown(commit.description, pipeline: :single_line) +end diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder index e310fafd82c..30bb7412073 100644 --- a/app/views/projects/commits/show.atom.builder +++ b/app/views/projects/commits/show.atom.builder @@ -6,18 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.id namespace_project_commits_url(@project.namespace, @project, @ref) xml.updated @commits.first.committed_date.xmlschema if @commits.any? - @commits.each do |commit| - xml.entry do - xml.id namespace_project_commit_url(@project.namespace, @project, id: commit.id) - xml.link href: namespace_project_commit_url(@project.namespace, @project, id: commit.id) - xml.title truncate(commit.title, length: 80) - xml.updated commit.committed_date.xmlschema - xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(commit.author_email)) - xml.author do |author| - xml.name commit.author_name - xml.email commit.author_email - end - xml.summary markdown(commit.description, pipeline: :single_line) - end - end + xml << render(@commits) if @commits.any? end diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index f6a53fddf17..18b125ff9d4 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -1,261 +1,221 @@ -.project-edit-container.prepend-top-default - .project-edit-errors - .project-edit-content - .panel.panel-default - .panel-heading +.project-edit-container + .row.prepend-top-default + .col-lg-3.profile-settings-sidebar + %h4.prepend-top-0 Project settings - .panel-body - = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit_project form-horizontal fieldset-form" }, authenticity_token: true do |f| - - %fieldset - .form-group.project_name_holder - = f.label :name, class: 'control-label' do - Project name - .col-sm-10 - = f.text_field :name, class: "form-control", id: "project_name_edit" - - - .form-group - = f.label :description, class: 'control-label' do - Project description - %span.light (optional) - .col-sm-10 - = f.text_area :description, class: "form-control", rows: 3, maxlength: 250 - - - unless @project.empty_repo? - .form-group - = f.label :default_branch, "Default Branch", class: 'control-label' - .col-sm-10= f.select(:default_branch, @project.repository.branch_names, {}, {class: 'select2 select-wide'}) - - - = render 'shared/visibility_level', f: f, visibility_level: @project.visibility_level, can_change_visibility_level: can_change_visibility_level?(@project, current_user), form_model: @project - + .col-lg-9 + = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit-project" }, authenticity_token: true do |f| + %fieldset.append-bottom-0 .form-group - = f.label :tag_list, "Tags", class: 'control-label' - .col-sm-10 - = f.text_field :tag_list, value: @project.tag_list.to_s, maxlength: 2000, class: "form-control" - %p.help-block Separate tags with commas. - - %fieldset.features - %legend - Features: - .form-group - .col-sm-offset-2.col-sm-10 - .checkbox - = f.label :issues_enabled do - = f.check_box :issues_enabled - %strong Issues - %br - %span.descr Lightweight issue tracking system for this project - - .form-group - .col-sm-offset-2.col-sm-10 - .checkbox - = f.label :merge_requests_enabled do - = f.check_box :merge_requests_enabled - %strong Merge Requests - %br - %span.descr Submit changes to be merged upstream - - .form-group - .col-sm-offset-2.col-sm-10 - .checkbox - = f.label :builds_enabled do - = f.check_box :builds_enabled - %strong Builds - %br - %span.descr Test and deploy your changes before merge - - .form-group - .col-sm-offset-2.col-sm-10 - .checkbox - = f.label :wiki_enabled do - = f.check_box :wiki_enabled - %strong Wiki - %br - %span.descr Pages for project documentation - - .form-group - .col-sm-offset-2.col-sm-10 - .checkbox - = f.label :snippets_enabled do - = f.check_box :snippets_enabled - %strong Snippets - %br - %span.descr Share code pastes with others out of git repository - - - if Gitlab.config.registry.enabled - .form-group - .col-sm-offset-2.col-sm-10 - .checkbox - = f.label :container_registry_enabled do - = f.check_box :container_registry_enabled - %strong Container Registry - %br - %span.descr Enable Container Registry for this repository - - = render 'builds_settings', f: f + = f.label :name, class: 'label-light' do + Project name + = f.text_field :name, class: "form-control", id: "project_name_edit" + .form-group + = f.label :description, class: 'label-light' do + Project description + %span.light (optional) + = f.text_area :description, class: "form-control", rows: 3, maxlength: 250 - %fieldset.features - %legend - Project avatar: + - unless @project.empty_repo? .form-group - .col-sm-offset-2.col-sm-10 - - if @project.avatar? - = project_icon("#{@project.namespace.to_param}/#{@project.to_param}", alt: '', class: 'avatar project-avatar s160') - %p.light - - if @project.avatar_in_git - Project avatar in repository: #{ @project.avatar_in_git } - %p.light - - if @project.avatar? - You can change your project avatar here - - else - You can upload a project avatar here - %a.choose-btn.btn.btn-sm.js-choose-project-avatar-button - %i.icon-paper-clip - %span Choose File ... - - %span.file_name.js-avatar-filename File name... - = f.file_field :avatar, class: "js-project-avatar-input hidden" - .light The maximum file size allowed is 200KB. - - if @project.avatar? - %hr - = link_to 'Remove avatar', namespace_project_avatar_path(@project.namespace, @project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar" - - - .form-actions - = f.submit 'Save changes', class: "btn btn-save" - - - - .danger-settings - .panel.panel-default - .panel-heading Housekeeping - .errors-holder - .panel-body - %p - Runs a number of housekeeping tasks within the current repository, - such as compressing file revisions and removing unreachable objects. - %br - - .form-actions - = link_to 'Housekeeping', housekeeping_namespace_project_path(@project.namespace, @project), - method: :post, class: "btn btn-default" - - - if can? current_user, :archive_project, @project - - if @project.archived? - .panel.panel-success - .panel-heading - Unarchive project - .panel-body - %p - Unarchiving the project will mark its repository as active. + = f.label :default_branch, "Default Branch", class: 'label-light' + = f.select(:default_branch, @project.repository.branch_names, {}, {class: 'select2 select-wide'}) + .form-group.project-visibility-level-holder + = f.label :visibility_level, class: 'label-light' do + Visibility Level + = link_to "(?)", help_page_path("public_access", "public_access") + - if can_change_visibility_level?(@project, current_user) + = render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: @project.visibility_level, form_model: @project) + - else + .info + = visibility_level_icon(@project.visibility_level) + %strong + = visibility_level_label(@project.visibility_level) + .light= visibility_level_description(@project.visibility_level, @project) + .form-group + = f.label :tag_list, "Tags", class: 'label-light' + = f.text_field :tag_list, value: @project.tag_list.to_s, maxlength: 2000, class: "form-control" + %p.help-block Separate tags with commas. + %hr + %fieldset.features.append-bottom-0 + %h5.prepend-top-0 + Features + .form-group + .checkbox + = f.label :issues_enabled do + = f.check_box :issues_enabled + %strong Issues %br - The project can be committed to. + %span.descr Lightweight issue tracking system for this project + .form-group + .checkbox + = f.label :merge_requests_enabled do + = f.check_box :merge_requests_enabled + %strong Merge Requests %br - %strong Once active this project shows up in the search and on the dashboard. - - .form-actions - = link_to 'Unarchive project', unarchive_namespace_project_path(@project.namespace, @project), - data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be committed to again." }, - method: :post, class: "btn btn-success" - - else - .panel.panel-warning - .panel-heading - Archive project - .panel-body - %p - Archiving the project will mark its repository as read-only. + %span.descr Submit changes to be merged upstream + .form-group + .checkbox + = f.label :builds_enabled do + = f.check_box :builds_enabled + %strong Builds + %br + %span.descr Test and deploy your changes before merge + .form-group + .checkbox + = f.label :wiki_enabled do + = f.check_box :wiki_enabled + %strong Wiki %br - It is hidden from the dashboard and doesn't show up in searches. + %span.descr Pages for project documentation + .form-group + .checkbox + = f.label :snippets_enabled do + = f.check_box :snippets_enabled + %strong Snippets %br - %strong Archived projects cannot be committed to! - - .form-actions - = link_to 'Archive project', archive_namespace_project_path(@project.namespace, @project), - data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." }, - method: :post, class: "btn btn-warning" - - else - .nothing-here-block Only the project owner can archive a project - - .panel.panel-default.panel.panel-warning - .panel-heading Rename repository - .errors-holder - .panel-body - = form_for([@project.namespace.becomes(Namespace), @project], html: { class: 'form-horizontal' }) do |f| - .form-group.project_name_holder - = f.label :name, class: 'control-label' do - Project name - .col-sm-9 - .form-group - = f.text_field :name, class: "form-control" + %span.descr Share code pastes with others out of git repository + - if Gitlab.config.registry.enabled .form-group - = f.label :path, class: 'control-label' do - %span Path - .col-sm-9 - .form-group - .input-group - .input-group-addon - #{URI.join(root_url, @project.namespace.path)}/ - = f.text_field :path, class: 'form-control' - %ul - %li Be careful. Renaming a project's repository can have unintended side effects. - %li You will need to update your local repositories to point to the new location. - .form-actions - = f.submit 'Rename project', class: "btn btn-warning" - - - if can?(current_user, :change_namespace, @project) - .panel.panel-default.panel.panel-danger - .panel-heading Transfer project - .errors-holder - .panel-body - = form_for([@project.namespace.becomes(Namespace), @project], url: transfer_namespace_project_path(@project.namespace, @project), method: :put, remote: true, html: { class: 'transfer-project form-horizontal' }) do |f| - .form-group - = label_tag :new_namespace_id, nil, class: 'control-label' do - %span Namespace - .col-sm-9 - .form-group - = select_tag :new_namespace_id, namespaces_options(@project.namespace_id), { prompt: 'Choose a project namespace', class: 'select2' } - %ul - %li Be careful. Changing the project's namespace can have unintended side effects. - %li You can only transfer the project to namespaces you manage. - %li You will need to update your local repositories to point to the new location. - %li Project visibility level will be changed to match namespace rules when transfering to a group. - .form-actions - = f.submit 'Transfer project', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => transfer_project_message(@project) } - - else - .nothing-here-block Only the project owner can transfer a project - - - if @project.forked? - - if can?(current_user, :remove_fork_project, @project) - = form_for([@project.namespace.becomes(Namespace), @project], url: remove_fork_namespace_project_path(@project.namespace, @project), method: :delete, remote: true, html: { class: 'transfer-project form-horizontal' }) do |f| - .panel.panel-default.panel.panel-danger - .panel-heading Remove fork relationship - .panel-body - %p - This will remove the fork relationship to source project - #{link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)}. + .checkbox + = f.label :container_registry_enabled do + = f.check_box :container_registry_enabled + %strong Container Registry %br - %strong Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source. - .form-actions - = button_to 'Remove fork relationship', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_message(@project) } + %span.descr Enable Container Registry for this repository + %hr + = render 'builds_settings', f: f + %hr + %fieldset.features.append-bottom-default + %h5.prepend-top-0 + Project avatar + .form-group + - if @project.avatar? + = project_icon("#{@project.namespace.to_param}/#{@project.to_param}", alt: '', class: 'avatar project-avatar s160') + %p.light + - if @project.avatar_in_git + Project avatar in repository: #{ @project.avatar_in_git } + %a.choose-btn.btn.js-choose-project-avatar-button + Browse file... + %span.file_name.prepend-left-default.js-avatar-filename No file chosen + = f.file_field :avatar, class: "js-project-avatar-input hidden" + .help-block The maximum file size allowed is 200KB. + - if @project.avatar? + %hr + = link_to 'Remove avatar', namespace_project_avatar_path(@project.namespace, @project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar" + = f.submit 'Save changes', class: "btn btn-save" + .row.prepend-top-default + %hr + .row.prepend-top-default + .col-lg-3 + %h4.prepend-top-0 + Housekeeping + %p.append-bottom-0 + %p + Runs a number of housekeeping tasks within the current repository, + such as compressing file revisions and removing unreachable objects. + .col-lg-9 + = link_to 'Housekeeping', housekeeping_namespace_project_path(@project.namespace, @project), + method: :post, class: "btn btn-save" + %hr + - if can? current_user, :archive_project, @project + .row.prepend-top-default + .col-lg-3 + %h4.warning-title.prepend-top-0 + - if @project.archived? + Unarchive project + - else + Archive project + %p.append-bottom-0 + - if @project.archived? + Unarchiving the project will mark its repository as active. The project can be committed to. + - else + Archiving the project will mark its repository as read-only. It is hidden from the dashboard and doesn't show up in searches. + .col-lg-9 + - if @project.archived? + %p + %strong Once active this project shows up in the search and on the dashboard. + = link_to 'Unarchive project', unarchive_namespace_project_path(@project.namespace, @project), + data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be committed to again." }, + method: :post, class: "btn btn-success" - else - .nothing-here-block Only the project owner can remove the fork relationship. - - - if can?(current_user, :remove_project, @project) - .panel.panel-default.panel.panel-danger - .panel-heading Remove project - .panel-body - = form_tag(namespace_project_path(@project.namespace, @project), method: :delete, class: 'form-horizontal') do - %p - Removing the project will delete its repository and all related resources including issues, merge requests etc. - %br - %strong Removed projects cannot be restored! - .form-actions - = button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) } - - else - .nothing-here-block Only the project owner can remove a project. - + %p + %strong Archived projects cannot be committed to! + = link_to 'Archive project', archive_namespace_project_path(@project.namespace, @project), + data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." }, + method: :post, class: "btn btn-warning" + %hr + .row.prepend-top-default + .col-lg-3 + %h4.prepend-top-0.warning-title + Rename repository + .col-lg-9 + = form_for([@project.namespace.becomes(Namespace), @project]) do |f| + .form-group.project_name_holder + = f.label :name, class: 'label-light' do + Project name + .form-group + = f.text_field :name, class: "form-control" + .form-group + = f.label :path, class: 'label-light' do + %span Path + .form-group + .input-group + .input-group-addon + #{URI.join(root_url, @project.namespace.path)}/ + = f.text_field :path, class: 'form-control' + %ul + %li Be careful. Renaming a project's repository can have unintended side effects. + %li You will need to update your local repositories to point to the new location. + = f.submit 'Rename project', class: "btn btn-warning" + - if can?(current_user, :change_namespace, @project) + %hr + .row.prepend-top-default + .col-lg-3 + %h4.prepend-top-0.danger-title + Transfer project + .col-lg-9 + = form_for([@project.namespace.becomes(Namespace), @project], url: transfer_namespace_project_path(@project.namespace, @project), method: :put, remote: true) do |f| + .form-group + = label_tag :new_namespace_id, nil, class: 'label-light' do + %span Namespace + .form-group + = select_tag :new_namespace_id, namespaces_options(@project.namespace_id), { prompt: 'Choose a project namespace', class: 'select2' } + %ul + %li Be careful. Changing the project's namespace can have unintended side effects. + %li You can only transfer the project to namespaces you manage. + %li You will need to update your local repositories to point to the new location. + %li Project visibility level will be changed to match namespace rules when transfering to a group. + = f.submit 'Transfer project', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => transfer_project_message(@project) } + - if @project.forked? && can?(current_user, :remove_fork_project, @project) + %hr + .row.prepend-top-default.append-bottom-default + .col-lg-3 + %h4.prepend-top-0.danger-title + Remove fork relationship + %p.append-bottom-0 + %p + This will remove the fork relationship to source project + = succeed "." do + = link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project) + .col-lg-9 + = form_for([@project.namespace.becomes(Namespace), @project], url: remove_fork_namespace_project_path(@project.namespace, @project), method: :delete, remote: true, html: { class: 'transfer-project' }) do |f| + %p + %strong Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source. + = button_to 'Remove fork relationship', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_message(@project) } + - if can?(current_user, :remove_project, @project) + %hr + .row.prepend-top-default.append-bottom-default + .col-lg-3 + %h4.prepend-top-0.danger-title + Remove project + %p.append-bottom-0 + Removing the project will delete its repository and all related resources including issues, merge requests etc. + .col-lg-9 + = form_tag(namespace_project_path(@project.namespace, @project), method: :delete) do + %p + %strong Removed projects cannot be restored! + = button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) } .save-project-loader.hide .center @@ -264,5 +224,4 @@ Saving project. %p Please wait a moment, this page will automatically refresh when ready. - = render 'shared/confirm_modal', phrase: @project.path diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml index 8129514964a..5bc5c71283e 100644 --- a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml +++ b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml @@ -40,24 +40,20 @@ %td = generic_commit_status.name - .pull-right - - if generic_commit_status.tags.any? - - generic_commit_status.tags.each do |tag| - %span.label.label-primary - = tag - - if defined?(retried) && retried - %span.label.label-warning retried + %td + - if generic_commit_status.tags.any? + - generic_commit_status.tags.each do |tag| + %span.label.label-primary + = tag + - if defined?(retried) && retried + %span.label.label-warning retried %td.duration - if generic_commit_status.duration - = icon("clock-o") - #{duration_in_words(generic_commit_status.finished_at, generic_commit_status.started_at)} %td.timestamp - if generic_commit_status.finished_at - = icon("calendar") - %span #{time_ago_with_tooltip(generic_commit_status.finished_at)} - if defined?(coverage) && coverage diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml index ad4a932d391..f82c47301f4 100644 --- a/app/views/projects/graphs/show.html.haml +++ b/app/views/projects/graphs/show.html.haml @@ -19,7 +19,7 @@ .header.clearfix %h3#date_header.page-title %p.light - Commits to #{@ref}, excluding merge commits. Limited by 6,000 commits + Commits to #{@ref}, excluding merge commits. Limited to 6,000 commits. %input#brush_change{:type => "hidden"} .graphs #contributors-master diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 5cf70ea3bb7..78f64150601 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -6,7 +6,7 @@ .issue-title.title %span.issue-title-text = confidential_icon(issue) - = link_to_gfm issue.title, issue_path(issue) + = link_to issue.title, issue_path(issue) %ul.controls - if issue.closed? %li diff --git a/app/views/projects/issues/_merge_requests.html.haml b/app/views/projects/issues/_merge_requests.html.haml index e953353567e..2f9dc867d0d 100644 --- a/app/views/projects/issues/_merge_requests.html.haml +++ b/app/views/projects/issues/_merge_requests.html.haml @@ -24,5 +24,8 @@ MERGED - elsif merge_request.closed? CLOSED - - if @closed_by_merge_requests.present? + %li = render partial: 'projects/issues/closed_by_box', locals: {merge_request_count: @merge_requests.count} + - if @closed_by_merge_requests.present? + %li + = render partial: 'projects/issues/closed_by_box', locals: {merge_request_count: @merge_requests.count} diff --git a/app/views/projects/issues/index.atom.builder b/app/views/projects/issues/index.atom.builder index ee8a9414657..7ad7c9c87e8 100644 --- a/app/views/projects/issues/index.atom.builder +++ b/app/views/projects/issues/index.atom.builder @@ -6,7 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.id namespace_project_issues_url(@project.namespace, @project) xml.updated @issues.first.created_at.xmlschema if @issues.any? - @issues.each do |issue| - issue_to_atom(xml, issue) - end + xml << render(partial: 'issues/issue', collection: @issues) if @issues.any? end diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index cc41130a9dc..05382b9cee7 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -18,6 +18,6 @@ - else .nothing-here-block - if can? current_user, :admin_label, @project - Create first label or #{link_to 'generate', generate_namespace_project_labels_path(@project.namespace, @project), method: :post} default set of labels + Create a label or #{link_to 'generate a default set of labels', generate_namespace_project_labels_path(@project.namespace, @project), method: :post}. - else No labels created diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 2c54171c6bd..c02f94490a0 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -1,7 +1,7 @@ %li{ class: mr_css_classes(merge_request) } .merge-request-title.title %span.merge-request-title-text - = link_to_gfm merge_request.title, merge_request_path(merge_request) + = link_to merge_request.title, merge_request_path(merge_request) %ul.controls - if merge_request.merged? %li diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml index 9d5b6d367c9..a6c12814adf 100644 --- a/app/views/projects/pipelines/index.html.haml +++ b/app/views/projects/pipelines/index.html.haml @@ -36,15 +36,7 @@ = icon('wrench') %span CI Lint -.row-content-block - - if @scope == 'running' - Running pipelines for this project - - elsif @scope.nil? - Pipelines for this project - - else - #{@scope.titleize} for this project - -%ul.content-list +%ul.content-list.pipelines - stages = @pipelines.stages - if @pipelines.blank? %li @@ -56,10 +48,10 @@ %th ID %th Commit - stages.each do |stage| - %th - %span.pipeline-stage.has-tooltip{ title: "#{stage.titleize}" } + %th.stage + %span.has-tooltip{ title: "#{stage.titleize}" } = stage.titleize.pluralize - %th + %th Duration %th = render @pipelines, commit_sha: true, stage: true, allow_retry: true, stages: stages diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml index 30cd1263a12..8ae9f0d95f7 100644 --- a/app/views/projects/runners/_specific_runners.html.haml +++ b/app/views/projects/runners/_specific_runners.html.haml @@ -8,7 +8,7 @@ Install GitLab Runner software. Checkout the #{link_to 'GitLab Runner section', 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'} to install it %li - Specify following URL during runner setup: + Specify the following URL during runner setup: %code #{ci_root_url(only_path: false)} %li Use the following registration token during setup: diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index 1b70880043a..1f13ea28b4e 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -1,18 +1,16 @@ -%h3.page-title - = @service.title - = boolean_to_icon @service.activated? +.row.prepend-top-default.append-bottom-default + .col-lg-3 + %h4.prepend-top-0 + = @service.title + = boolean_to_icon @service.activated? -%p= @service.description - -%hr - -= form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |form| - = render 'shared/service_settings', form: form - - .form-actions - = form.submit 'Save changes', class: 'btn btn-save' - - - if @service.valid? && @service.activated? - - disabled = @service.can_test? ? '':'disabled' - = link_to 'Test settings', test_namespace_project_service_path(@project.namespace, @project, @service.to_param), class: "btn #{disabled}" - = link_to "Cancel", namespace_project_services_path(@project.namespace, @project), class: "btn btn-cancel" + %p= @service.description + .col-lg-9 + = form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |form| + = render 'shared/service_settings', form: form + = form.submit 'Save changes', class: 'btn btn-save' + + - if @service.valid? && @service.activated? + - disabled = @service.can_test? ? '':'disabled' + = link_to 'Test settings', test_namespace_project_service_path(@project.namespace, @project, @service.to_param), class: "btn #{disabled}" + = link_to "Cancel", namespace_project_services_path(@project.namespace, @project), class: "btn btn-cancel" diff --git a/app/views/projects/services/index.html.haml b/app/views/projects/services/index.html.haml index c1356f6db02..4a33a5bc6f6 100644 --- a/app/views/projects/services/index.html.haml +++ b/app/views/projects/services/index.html.haml @@ -1,24 +1,32 @@ - page_title "Services" -%h3.page-title Project services -%p.light Project services allow you to integrate GitLab with other applications -.table-holder - %table.table - %thead - %tr - %th - %th Service - %th Description - %th Last edit - - @services.sort_by(&:title).each do |service| - %tr - %td - = boolean_to_icon service.activated? - %td - = link_to edit_namespace_project_service_path(@project.namespace, @project, service.to_param) do - %strong= service.title - %td - = service.description - %td.light - = time_ago_in_words service.updated_at - ago +.row.prepend-top-default.append-bottom-default + .col-lg-3 + %h4.prepend-top-0 + Project services + %p Project services allow you to integrate GitLab with other applications + .col-lg-9 + %table.table + %colgroup + %col + %col + %col.hidden-xs + %col{ width: "120" } + %thead + %tr + %th + %th Service + %th.hidden-xs Description + %th Last edit + - @services.sort_by(&:title).each do |service| + %tr + %td + = boolean_to_icon service.activated? + %td + = link_to edit_namespace_project_service_path(@project.namespace, @project, service.to_param) do + %strong= service.title + %td.hidden-xs + = service.description + %td.light + = time_ago_in_words service.updated_at + ago diff --git a/app/views/projects/show.atom.builder b/app/views/projects/show.atom.builder index 9b3d3f069d9..11310d5e1e1 100644 --- a/app/views/projects/show.atom.builder +++ b/app/views/projects/show.atom.builder @@ -6,7 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.id namespace_project_url(@project.namespace, @project) xml.updated @events[0].updated_at.xmlschema if @events[0] - @events.each do |event| - event_to_atom(xml, event) - end + xml << render(@events) if @events.any? end diff --git a/app/views/projects/snippets/_actions.html.haml b/app/views/projects/snippets/_actions.html.haml index 4a515469422..bf57beb9d07 100644 --- a/app/views/projects/snippets/_actions.html.haml +++ b/app/views/projects/snippets/_actions.html.haml @@ -1,11 +1,27 @@ -= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do - = icon('plus') - New Snippet -- if can?(current_user, :admin_project_snippet, @snippet) - = link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' do - = icon('trash-o') - Delete -- if can?(current_user, :update_project_snippet, @snippet) - = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped snippable-edit" do - = icon('pencil-square-o') - Edit +.hidden-xs + = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped btn-create new-snippet-link', title: "New Snippet" do + = icon('plus') + New Snippet + - if can?(current_user, :update_project_snippet, @snippet) + = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped snippable-edit" do + Edit + - if can?(current_user, :update_project_snippet, @snippet) + = link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-warning", title: 'Delete Snippet' do + Delete +.visible-xs-block.dropdown + %button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } } + Options + %span.caret + .dropdown-menu.dropdown-menu-full-width + %ul + %li + = link_to new_namespace_project_snippet_path(@project.namespace, @project), title: "New Snippet" do + New Snippet + - if can?(current_user, :update_project_snippet, @snippet) + %li + = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet) do + Edit + - if can?(current_user, :update_project_snippet, @snippet) + %li + = link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do + Delete diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml index 7c599563ce4..6f7bdcb29e8 100644 --- a/app/views/projects/snippets/show.html.haml +++ b/app/views/projects/snippets/show.html.haml @@ -4,15 +4,13 @@ .snippet-holder = render 'shared/snippets/header' - %article.file-holder - .file-title + %article.file-holder.file-holder-no-border.snippet-file-content + .file-title.file-title-clear = blob_icon 0, @snippet.file_name - %strong - = @snippet.file_name + = @snippet.file_name .file-actions.hidden-xs = clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{@snippet.id}']") = link_to 'Raw', raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank" - = render 'shared/snippets/blob' %div#notes= render "projects/notes/notes_with_form" diff --git a/app/views/projects/triggers/index.html.haml b/app/views/projects/triggers/index.html.haml index d73ac987161..7f3de47d7df 100644 --- a/app/views/projects/triggers/index.html.haml +++ b/app/views/projects/triggers/index.html.haml @@ -5,7 +5,7 @@ %h4.prepend-top-0 = page_title %p - Triggers can be used to force a rebuild of a specific branch or tag with an API call. + Triggers can force a specific branch or tag to rebuild with an API call. .col-lg-9 %h5.prepend-top-0 Your triggers @@ -19,7 +19,7 @@ = render partial: 'trigger', collection: @triggers, as: :trigger - else %p.settings-message.text-center.append-bottom-default - There are no triggers to use, add one by the button below. + No triggers have been created yet. Add one using the button below. = form_for @trigger, url: url_for(controller: 'projects/triggers', action: 'create') do |f| = f.submit "Add Trigger", class: 'btn btn-success' @@ -28,8 +28,7 @@ Use CURL %p.light - Copy the token above and set your branch or tag name. This is the reference that will be rebuild. - + Copy the token above, set your branch or tag name, and that reference will be rebuilt. %pre :plain @@ -41,10 +40,10 @@ Use .gitlab-ci.yml %p.light - Copy the snippet to - %i .gitlab-ci.yml - of dependent project. - At the end of your build it will trigger this project to rebuilt. + In the + %code .gitlab-ci.yml + of the dependent project, include the following snippet. + The project will rebuild at the end of the build. %pre :plain @@ -57,9 +56,8 @@ %p.light Add - %strong variables[VARIABLE]=VALUE - to API request. - The value of variable could then be used to distinguish triggered build from normal one. + %code variables[VARIABLE]=VALUE + to an API request. Variable values can be used to distinguish between triggered builds and normal builds. %pre.append-bottom-0 :plain diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml index c38d9313dba..30055002213 100644 --- a/app/views/shared/_event_filter.html.haml +++ b/app/views/shared/_event_filter.html.haml @@ -1,5 +1,7 @@ -%ul.nav-links.event-filter +%ul.nav-links.event-filter.scrolling-tabs + .fade-left = event_filter_link EventFilter.push, 'Push events' = event_filter_link EventFilter.merged, 'Merge events' = event_filter_link EventFilter.comments, 'Comments' = event_filter_link EventFilter.team, 'Team' + .fade-right diff --git a/app/views/shared/_sort_dropdown.html.haml b/app/views/shared/_sort_dropdown.html.haml index d327bd0a96f..1e0f075b303 100644 --- a/app/views/shared/_sort_dropdown.html.haml +++ b/app/views/shared/_sort_dropdown.html.haml @@ -6,7 +6,7 @@ - else = sort_title_recently_created %b.caret - %ul.dropdown-menu.dropdown-menu-align-right + %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-sort %li = link_to page_filter_path(sort: sort_value_recently_created) do = sort_title_recently_created diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index 9474462cbd1..323d563cd4a 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -1,6 +1,8 @@ .issues-filters .issues-details-filters.row-content-block.second-block - = form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name]), method: :get, class: 'filter-form' do + = form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name, :issue_search]), method: :get, class: 'filter-form js-filter-form' do + - if params[:issue_search].present? + = hidden_field_tag :issue_search, params[:issue_search] - if controller.controller_name == 'issues' && can?(current_user, :admin_issue, @project) .check-all-holder = check_box_tag "check_all_issues", nil, false, diff --git a/app/views/shared/issuable/_search_form.html.haml b/app/views/shared/issuable/_search_form.html.haml index afad48499b7..186963b32b8 100644 --- a/app/views/shared/issuable/_search_form.html.haml +++ b/app/views/shared/issuable/_search_form.html.haml @@ -1,8 +1,2 @@ = form_tag(path, method: :get, id: "issue_search_form", class: 'issue-search-form') do = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by name ...', class: 'form-control issue_search search-text-input input-short', spellcheck: false } - = hidden_field_tag :state, params['state'] - = hidden_field_tag :scope, params['scope'] - = hidden_field_tag :assignee_id, params['assignee_id'] - = hidden_field_tag :author_id, params['author_id'] - = hidden_field_tag :milestone_id, params['milestone_id'] - = hidden_field_tag :label_id, params['label_id'] diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index 9ef021747a5..b8b66d08db8 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -12,9 +12,6 @@ %li.project-row{ class: css_class } = cache(cache_key) do .controls - - if project.main_language - %span - = project.main_language - if project.commit.try(:status) %span = render_commit_status(project.commit) diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index e65b1814872..897aa5b4462 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -1,25 +1,24 @@ -.detail-page-header - .snippet-box.has-tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: 'body' }} +.detail-page-header.clearfix + .snippet-box.has-tooltip.inline.append-right-5{ title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: "body" } } + %span.sr-only + = visibility_level_label(@snippet.visibility_level) = visibility_level_icon(@snippet.visibility_level, fw: false) - = visibility_level_label(@snippet.visibility_level) - %span.identifier + %strong.item-title Snippet ##{@snippet.id} %span.creator - · created by #{link_to_member(@project, @snippet.author, size: 24)} - · + created by #{link_to_member(@project, @snippet.author, size: 24, author_class: "author item-title")} = time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago') - if @snippet.updated_at != @snippet.created_at %span - · = icon('edit', title: 'edited') = time_ago_with_tooltip(@snippet.updated_at, placement: 'bottom', html_class: 'snippet_edited_ago') - .pull-right + .snippet-actions - if @snippet.project_id? = render "projects/snippets/actions" - else = render "snippets/actions" -.detail-page-description.row-content-block.second-block - %h2.title +.content-block.second-block + %h2.snippet-title.prepend-top-0.append-bottom-0 = markdown escape_once(@snippet.title), pipeline: :single_line diff --git a/app/views/snippets/_actions.html.haml b/app/views/snippets/_actions.html.haml index 1979ae6d5bc..a7769654b61 100644 --- a/app/views/snippets/_actions.html.haml +++ b/app/views/snippets/_actions.html.haml @@ -1,11 +1,27 @@ -= link_to new_snippet_path, class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do - = icon('plus') - New Snippet -- if can?(current_user, :update_personal_snippet, @snippet) - = link_to edit_snippet_path(@snippet), class: "btn btn-grouped snippable-edit" do - = icon('pencil-square-o') - Edit -- if can?(current_user, :admin_personal_snippet, @snippet) - = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' do - = icon('trash-o') - Delete +.hidden-xs + = link_to new_snippet_path, class: "btn btn-grouped btn-create new-snippet-link", title: "New Snippet" do + = icon('plus') + New Snippet + - if can?(current_user, :update_personal_snippet, @snippet) + = link_to edit_snippet_path(@snippet), class: "btn btn-grouped snippable-edit" do + Edit + - if can?(current_user, :admin_personal_snippet, @snippet) + = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-warning", title: 'Delete Snippet' do + Delete +.visible-xs-block.dropdown + %button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } } + Options + %span.caret + .dropdown-menu.dropdown-menu-full-width + %ul + %li + = link_to new_snippet_path, title: "New Snippet" do + New Snippet + - if can?(current_user, :update_personal_snippet, @snippet) + %li + = link_to edit_snippet_path(@snippet) do + Edit + - if can?(current_user, :admin_personal_snippet, @snippet) + %li + = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do + Delete diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index a2b36568770..ed3992650d4 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -3,11 +3,10 @@ .snippet-holder = render 'shared/snippets/header' - %article.file-holder - .file-title + %article.file-holder.file-holder-no-border.snippet-file-content + .file-title.file-title-clear = blob_icon 0, @snippet.file_name - %strong - = @snippet.file_name + = @snippet.file_name .file-actions.hidden-xs = clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{@snippet.id}']") = link_to 'Raw', raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank" diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml index 1de71f37d1a..77f2ddefb1e 100644 --- a/app/views/users/calendar.html.haml +++ b/app/views/users/calendar.html.haml @@ -1,10 +1,9 @@ -#cal-heatmap.calendar - :javascript - new Calendar( - #{@timestamps.to_json}, - #{@starting_year}, - #{@starting_month}, - '#{user_calendar_activities_path}' - ); - -.calendar-hint Summary of issues, merge requests, and push events +.clearfix.calendar + .js-contrib-calendar + .calendar-hint + Summary of issues, merge requests, and push events +:javascript + new Calendar( + #{@timestamps.to_json}, + '#{user_calendar_activities_path}' + ); diff --git a/app/views/users/calendar_activities.html.haml b/app/views/users/calendar_activities.html.haml index 027a93a75fc..630d97e339d 100644 --- a/app/views/users/calendar_activities.html.haml +++ b/app/views/users/calendar_activities.html.haml @@ -1,23 +1,27 @@ %h4.prepend-top-20 - %span.light Contributions for + Contributions for %strong #{@calendar_date.to_s(:short)} -%ul.bordered-list - - @events.sort_by(&:created_at).each do |event| - %li - %span.light - %i.fa.fa-clock-o - = event.created_at.to_s(:time) - - if event.push? - #{event.action_name} #{event.ref_type} #{event.ref_name} - - else - = event_action_name(event) - - if event.target - %strong= link_to "##{event.target_iid}", [event.project.namespace.becomes(Namespace), event.project, event.target] - - at - %strong - - if event.project - = link_to_project event.project +- if @events.any? + %ul.bordered-list + - @events.sort_by(&:created_at).each do |event| + %li + %span.light + %i.fa.fa-clock-o + = event.created_at.to_s(:time) + - if event.push? + #{event.action_name} #{event.ref_type} #{event.ref_name} - else - = event.project_name + = event_action_name(event) + - if event.target + %strong= link_to "##{event.target_iid}", [event.project.namespace.becomes(Namespace), event.project, event.target] + + at + %strong + - if event.project + = link_to_project event.project + - else + = event.project_name +- else + %p + No contributions found for #{@calendar_date.to_s(:short)} diff --git a/app/views/users/show.atom.builder b/app/views/users/show.atom.builder index e9e466c6350..6c85e5f9fbd 100644 --- a/app/views/users/show.atom.builder +++ b/app/views/users/show.atom.builder @@ -6,7 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.id user_url(@user) xml.updated @events[0].updated_at.xmlschema if @events[0] - @events.each do |event| - event_to_atom(xml, event) - end + xml << render(@events) if @events.any? end diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 9017fd54fcc..0c513308308 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -89,10 +89,9 @@ .tab-content #activity.tab-pane .row-content-block.calender-block.white.second-block.hidden-xs - %div{ class: container_class } - .user-calendar{data: {href: user_calendar_path}} - %h4.center.light - %i.fa.fa-spinner.fa-spin + .user-calendar{data: {href: user_calendar_path}} + %h4.center.light + %i.fa.fa-spinner.fa-spin .user-calendar-activities .content_list{ data: {href: user_path} } diff --git a/config/application.rb b/config/application.rb index 0e5a77285e5..de2bb08c978 100644 --- a/config/application.rb +++ b/config/application.rb @@ -80,7 +80,7 @@ module Gitlab config.assets.precompile << "*.png" config.assets.precompile << "print.css" config.assets.precompile << "notify.css" - config.assets.precompile << "mailers/repository_push_email.css" + config.assets.precompile << "mailers/*.css" # Version of your assets, change this if you want to expire all your assets config.assets.version = '1.0' diff --git a/config/environments/development.rb b/config/environments/development.rb index 4f39016bfa4..8cca0039b4a 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -39,6 +39,7 @@ Rails.application.configure do config.action_mailer.delivery_method = :letter_opener_web # Don't make a mess when bootstrapping a development environment config.action_mailer.perform_deliveries = (ENV['BOOTSTRAP'] != '1') + config.action_mailer.preview_path = 'spec/mailers/previews' config.eager_load = false end diff --git a/config/initializers/premailer.rb b/config/initializers/premailer.rb index b9176688bc4..cb00d3cfe95 100644 --- a/config/initializers/premailer.rb +++ b/config/initializers/premailer.rb @@ -3,6 +3,6 @@ Premailer::Rails.config.merge!( generate_text_part: false, preserve_styles: true, remove_comments: true, - remove_ids: true, + remove_ids: false, remove_scripts: false ) diff --git a/db/migrate/20160525205328_remove_main_language_from_projects.rb b/db/migrate/20160525205328_remove_main_language_from_projects.rb new file mode 100644 index 00000000000..0f9d60c385f --- /dev/null +++ b/db/migrate/20160525205328_remove_main_language_from_projects.rb @@ -0,0 +1,21 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class RemoveMainLanguageFromProjects < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # When using the methods "add_concurrent_index" or "add_column_with_default" + # you must disable the use of transactions as these methods can not run in an + # existing transaction. When using "add_concurrent_index" make sure that this + # method is the _only_ method called in the migration, any other changes + # should go in a separate migration. This ensures that upon failure _only_ the + # index creation fails and can be retried or reverted easily. + # + # To disable transactions uncomment the following line and remove these + # comments: + # disable_ddl_transaction! + + def change + remove_column :projects, :main_language + end +end diff --git a/db/schema.rb b/db/schema.rb index aee5fb90937..b42eef08a00 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160516174813) do +ActiveRecord::Schema.define(version: 20160525205328) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -760,7 +760,6 @@ ActiveRecord::Schema.define(version: 20160516174813) do t.integer "build_timeout", default: 3600, null: false t.boolean "pending_delete", default: false t.boolean "public_builds", default: true, null: false - t.string "main_language" t.integer "pushes_since_gc", default: 0 t.boolean "last_repository_check_failed" t.datetime "last_repository_check_at" diff --git a/doc/administration/high_availability/README.md b/doc/administration/high_availability/README.md index 43d85ffb775..d74a786ac24 100644 --- a/doc/administration/high_availability/README.md +++ b/doc/administration/high_availability/README.md @@ -19,6 +19,8 @@ Components/Servers Required: - 2 servers/virtual machines (one active/one passive) +![Active/Passive HA Diagram](../img/high_availability/active-passive-diagram.png) + ### Active/Active This architecture scales easily because all application servers handle @@ -26,6 +28,8 @@ user requests simultaneously. The database, Redis, and GitLab application are all deployed on separate servers. The configuration is **only** highly-available if the database, Redis and storage are also configured as such. +![Active/Active HA Diagram](../img/high_availability/active-active-diagram.png) + **Steps to configure active/active:** 1. [Configure the database](database.md) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index d89a1e582ca..f6153216f33 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -26,7 +26,7 @@ that runs Redis. ```ruby external_url 'https://gitlab.example.com' - # Disable all components except PostgreSQL + # Disable all components except Redis redis['enable'] = true bootstrap['enable'] = false nginx['enable'] = false diff --git a/doc/administration/img/high_availability/active-active-diagram.png b/doc/administration/img/high_availability/active-active-diagram.png Binary files differnew file mode 100644 index 00000000000..81259e0ae93 --- /dev/null +++ b/doc/administration/img/high_availability/active-active-diagram.png diff --git a/doc/administration/img/high_availability/active-passive-diagram.png b/doc/administration/img/high_availability/active-passive-diagram.png Binary files differnew file mode 100644 index 00000000000..f69ff1d0357 --- /dev/null +++ b/doc/administration/img/high_availability/active-passive-diagram.png diff --git a/doc/development/testing.md b/doc/development/testing.md index 33eed29ba5c..513457d203a 100644 --- a/doc/development/testing.md +++ b/doc/development/testing.md @@ -65,7 +65,7 @@ the command line via `bundle exec teaspoon`, or via a web browser at - Use `context` to test branching logic. - Don't `describe` symbols (see [Gotchas](gotchas.md#dont-describe-symbols)). - Don't supply the `:each` argument to hooks since it's the default. -- Prefer `not_to` to `to_not`. +- Prefer `not_to` to `to_not` (_this is enforced by Rubocop_). - Try to match the ordering of tests to the ordering within the class. - Try to follow the [Four-Phase Test][four-phase-test] pattern, using newlines to separate phases. diff --git a/doc/development/ui_guide.md b/doc/development/ui_guide.md index a3e260a5f89..b4dcb748351 100644 --- a/doc/development/ui_guide.md +++ b/doc/development/ui_guide.md @@ -6,3 +6,31 @@ We created a page inside GitLab where you can check commonly used html and css e When you run GitLab instance locally - just visit http://localhost:3000/help/ui page to see UI examples you can use during GitLab development. + +## Design repository + +All design files are stored in the [gitlab-design](https://gitlab.com/gitlab-org/gitlab-design) +repository and maintained by GitLab UX designers. + +## Navigation + +GitLab's layout contains 2 sections: the left sidebar and the content. The left sidebar contains a static navigation menu. +This menu will be visible regardless of what page you visit. The left sidebar also contains the GitLab logo +and the current user's profile picture. The content section contains a header and the content itself. +The header describes the current GitLab page and what navigation is +available to user in this area. Depending on the area (project, group, profile setting) the header name and navigation may change. For example when user visits one of the +project pages the header will contain a project name and navigation for that project. When the user visits a group page it will contain a group name and navigation related to this group. + +### Adding new tab to header navigation + +We try to keep the amount of tabs in the header navigation between 5 and 10 so that it fits on a typical laptop screen. We also try not to confuse the user with too many options. Ideally each +tab should represent separate functionality. Everything related to the issue +tracker should be under the 'Issues' tab while everything related to the wiki should +be under 'Wiki' tab and so on and so forth. + +## Mobile screen size + +We want GitLab to work well on small mobile screens as well. Size limitations make it is impossible to fit everything on a mobile screen. In this case it is OK to hide +part of the UI for smaller resolutions in favor of a better user experience. +However core functionality like browsing files, creating issues, writing comments, should +be available on all resolutions.
\ No newline at end of file diff --git a/doc/install/installation.md b/doc/install/installation.md index 526f127178d..108ceb593d0 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -271,7 +271,7 @@ sudo usermod -aG redis git # Clone GitLab repository sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-8-stable gitlab -**Note:** You can change `8-7-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! +**Note:** You can change `8-8-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! ### Configure It diff --git a/doc/monitoring/health_check.md b/doc/monitoring/health_check.md index defbf37ac19..0d17799372f 100644 --- a/doc/monitoring/health_check.md +++ b/doc/monitoring/health_check.md @@ -11,7 +11,7 @@ and access the cache. This endpoint can be provided to uptime monitoring service ## 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. +accepted token can be found on the `admin/health_check` page of your GitLab instance. ![access token](img/health_check_token.png) diff --git a/features/steps/admin/users.rb b/features/steps/admin/users.rb index 4bc290b6bdf..8fb8a86d58b 100644 --- a/features/steps/admin/users.rb +++ b/features/steps/admin/users.rb @@ -158,7 +158,7 @@ class Spinach::Features::AdminUsers < Spinach::FeatureSteps step 'I should not see twitter details' do expect(page).to have_content 'Pete' - expect(page).to_not have_content 'twitter' + expect(page).not_to have_content 'twitter' end step 'click on ssh keys tab' do diff --git a/features/steps/dashboard/todos.rb b/features/steps/dashboard/todos.rb index 2b23df6764b..d3b6c7f6a15 100644 --- a/features/steps/dashboard/todos.rb +++ b/features/steps/dashboard/todos.rb @@ -106,7 +106,7 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps if pending expect(page).to have_link 'Done' else - expect(page).to_not have_link 'Done' + expect(page).not_to have_link 'Done' end end end diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb index f33f37be951..e1b29f1e57a 100644 --- a/features/steps/project/commits/commits.rb +++ b/features/steps/project/commits/commits.rb @@ -105,7 +105,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps end step 'I should not see button to create a new merge request' do - expect(page).to_not have_link 'Create Merge Request' + expect(page).not_to have_link 'Create Merge Request' end step 'I should see button to the merge request' do diff --git a/features/steps/project/issues/award_emoji.rb b/features/steps/project/issues/award_emoji.rb index c5d45709b44..1b14659b4df 100644 --- a/features/steps/project/issues/award_emoji.rb +++ b/features/steps/project/issues/award_emoji.rb @@ -39,8 +39,8 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps step 'I can see the activity and food categories' do page.within '.emoji-menu' do - expect(page).to_not have_selector 'Activity' - expect(page).to_not have_selector 'Food' + expect(page).not_to have_selector 'Activity' + expect(page).not_to have_selector 'Food' end end diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb index fc12843ea5c..f2c68f007ef 100644 --- a/features/steps/project/issues/issues.rb +++ b/features/steps/project/issues/issues.rb @@ -216,7 +216,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps page.within 'li.issue:nth-child(3)' do expect(page).to have_content 'Bugfix' - expect(page).to_not have_content '0 0' + expect(page).not_to have_content '0 0' end end end @@ -235,7 +235,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps page.within 'li.issue:nth-child(3)' do expect(page).to have_content 'Bugfix' - expect(page).to_not have_content '0 0' + expect(page).not_to have_content '0 0' end end end diff --git a/features/steps/project/issues/labels.rb b/features/steps/project/issues/labels.rb index 0ca2d6257c3..8d87f6a7a58 100644 --- a/features/steps/project/issues/labels.rb +++ b/features/steps/project/issues/labels.rb @@ -24,8 +24,8 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps step 'I should see labels help message' do page.within '.labels' do - expect(page).to have_content 'Create first label or generate default set of '\ - 'labels' + expect(page).to have_content 'Create a label or generate a default set '\ + 'of labels' end end diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index b79d19f1c58..cfa586f859f 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -203,7 +203,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps page.within 'li.merge-request:nth-child(3)' do expect(page).to have_content 'Bug NS-05' - expect(page).to_not have_content '0 0' + expect(page).not_to have_content '0 0' end end end @@ -222,7 +222,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps page.within 'li.merge-request:nth-child(3)' do expect(page).to have_content 'Bug NS-05' - expect(page).to_not have_content '0 0' + expect(page).not_to have_content '0 0' end end end diff --git a/features/steps/project/snippets.rb b/features/steps/project/snippets.rb index 786a0cad975..beb8ecfc799 100644 --- a/features/steps/project/snippets.rb +++ b/features/steps/project/snippets.rb @@ -43,12 +43,12 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps step 'I click link "Edit"' do page.within ".detail-page-header" do - click_link "Edit" + first(:link, "Edit").click end end step 'I click link "Delete"' do - click_link "Delete" + first(:link, "Delete").click end step 'I submit new snippet "Snippet three"' do diff --git a/features/steps/shared/note.rb b/features/steps/shared/note.rb index a3c3887ab46..3d7c6ef9d2d 100644 --- a/features/steps/shared/note.rb +++ b/features/steps/shared/note.rb @@ -107,7 +107,7 @@ module SharedNote end step 'I should see no notes at all' do - expect(page).to_not have_css('.note') + expect(page).not_to have_css('.note') end # Markdown diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index ea5f9580308..ce9ea7ee18a 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -95,7 +95,7 @@ module SharedProject step 'I should see project settings' do expect(current_path).to eq edit_namespace_project_path(@project.namespace, @project) expect(page).to have_content("Project name") - expect(page).to have_content("Features:") + expect(page).to have_content("Features") end def current_project diff --git a/features/steps/snippets/snippets.rb b/features/steps/snippets/snippets.rb index 023032e679f..19366b11071 100644 --- a/features/steps/snippets/snippets.rb +++ b/features/steps/snippets/snippets.rb @@ -14,12 +14,12 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps step 'I click link "Edit"' do page.within ".detail-page-header" do - click_link "Edit" + first(:link, "Edit").click end end step 'I click link "Delete"' do - click_link "Delete" + first(:link, "Delete").click end step 'I submit new snippet "Personal snippet three"' do diff --git a/features/steps/user.rb b/features/steps/user.rb index b1d088f07f9..59385a6ab59 100644 --- a/features/steps/user.rb +++ b/features/steps/user.rb @@ -34,7 +34,7 @@ class Spinach::Features::User < Spinach::FeatureSteps end step 'I should see contributions calendar' do - expect(page).to have_css('.cal-heatmap-container') + expect(page).to have_css('.js-contrib-calendar') end def contributed_project diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 91e420832f3..9d8b8d737a9 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -95,8 +95,7 @@ module API # GET /groups/:id/projects get ":id/projects" do group = find_group(params[:id]) - projects = group.projects - projects = filter_projects(projects) + projects = GroupProjectsFinder.new(group).execute(current_user) projects = paginate projects present projects, with: Entities::Project end diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index 7d0608f09da..3e07096e6cc 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -1,5 +1,8 @@ module Backup class Manager + ARCHIVES_TO_BACKUP = %w[uploads builds artifacts lfs registry] + FOLDERS_TO_BACKUP = %w[repositories db] + def pack # Make sure there is a connection ActiveRecord::Base.connection.reconnect! @@ -147,7 +150,7 @@ module Backup end def skipped?(item) - settings[:skipped] && settings[:skipped].include?(item) + settings[:skipped] && settings[:skipped].include?(item) || disabled_features.include?(item) end private @@ -157,11 +160,17 @@ module Backup end def archives_to_backup - %w{uploads builds artifacts lfs registry}.map{ |name| (name + ".tar.gz") unless skipped?(name) }.compact + ARCHIVES_TO_BACKUP.map{ |name| (name + ".tar.gz") unless skipped?(name) }.compact end def folders_to_backup - %w{repositories db}.reject{ |name| skipped?(name) } + FOLDERS_TO_BACKUP.reject{ |name| skipped?(name) } + end + + def disabled_features + features = [] + features << 'registry' unless Gitlab.config.registry.enabled + features end def settings diff --git a/lib/ci/ansi2html.rb b/lib/ci/ansi2html.rb index c628257e3f4..229050151d3 100644 --- a/lib/ci/ansi2html.rb +++ b/lib/ci/ansi2html.rb @@ -90,7 +90,7 @@ module Ci def convert(raw, new_state) reset_state - restore_state(raw, new_state) if new_state + restore_state(raw, new_state) if new_state.present? start = @offset ansi = raw[@offset..-1] @@ -105,6 +105,8 @@ module Ci break elsif s.scan(/</) @out << '<' + elsif s.scan(/\n/) + @out << '<br>' else @out << s.scan(/./m) end diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 504d3df9d34..e4b4760c53b 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -1,6 +1,6 @@ module Ci class GitlabCiYamlProcessor - class ValidationError < StandardError;end + class ValidationError < StandardError; end DEFAULT_STAGES = %w(build test deploy) DEFAULT_STAGE = 'test' diff --git a/lib/gitlab/contributions_calendar.rb b/lib/gitlab/contributions_calendar.rb index 85583dce9ee..9dc2602867e 100644 --- a/lib/gitlab/contributions_calendar.rb +++ b/lib/gitlab/contributions_calendar.rb @@ -19,7 +19,7 @@ module Gitlab select('date(created_at) as date, count(id) as total_amount'). map(&:attributes) - dates = (1.year.ago.to_date..(Date.today + 1.day)).to_a + dates = (1.year.ago.to_date..Date.today).to_a dates.each do |date| date_id = date.to_time.to_i.to_s diff --git a/lib/gitlab/url_sanitizer.rb b/lib/gitlab/url_sanitizer.rb index c59d53b941a..7d02fe3c971 100644 --- a/lib/gitlab/url_sanitizer.rb +++ b/lib/gitlab/url_sanitizer.rb @@ -7,7 +7,7 @@ module Gitlab end def initialize(url, credentials: nil) - @url = Addressable::URI.parse(URI.encode(url)) + @url = Addressable::URI.parse(url) @credentials = credentials end diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb index a1ee1cba216..9462f3368e6 100644 --- a/lib/gitlab/visibility_level.rb +++ b/lib/gitlab/visibility_level.rb @@ -32,6 +32,13 @@ module Gitlab } end + def highest_allowed_level + restricted_levels = current_application_settings.restricted_visibility_levels + + allowed_levels = self.values - restricted_levels + allowed_levels.max || PRIVATE + end + def allowed_for?(user, level) user.is_admin? || allowed_level?(level.to_i) end diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake index d97d974ec20..596eaca6d0d 100644 --- a/lib/tasks/gitlab/backup.rake +++ b/lib/tasks/gitlab/backup.rake @@ -179,18 +179,26 @@ namespace :gitlab do task create: :environment do $progress.puts "Dumping container registry images ... ".blue - if ENV["SKIP"] && ENV["SKIP"].include?("registry") - $progress.puts "[SKIPPED]".cyan + if Gitlab.config.registry.enabled + if ENV["SKIP"] && ENV["SKIP"].include?("registry") + $progress.puts "[SKIPPED]".cyan + else + Backup::Registry.new.dump + $progress.puts "done".green + end else - Backup::Registry.new.dump - $progress.puts "done".green + $progress.puts "[DISABLED]".cyan end end task restore: :environment do $progress.puts "Restoring container registry images ... ".blue - Backup::Registry.new.restore - $progress.puts "done".green + if Gitlab.config.registry.enabled + Backup::Registry.new.restore + $progress.puts "done".green + else + $progress.puts "[DISABLED]".cyan + end end end diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake index e473b756023..86f5d65f128 100644 --- a/lib/tasks/gitlab/db.rake +++ b/lib/tasks/gitlab/db.rake @@ -36,5 +36,15 @@ namespace :gitlab do # Add `IF EXISTS` because cascade could have already deleted a table. tables.each { |t| connection.execute("DROP TABLE IF EXISTS #{t} CASCADE") } end + + desc 'Configures the database by running migrate, or by loading the schema and seeding if needed' + task configure: :environment do + if ActiveRecord::Base.connection.tables.any? + Rake::Task['db:migrate'].invoke + else + Rake::Task['db:schema:load'].invoke + Rake::Task['db:seed_fu'].invoke + end + end end end diff --git a/spec/controllers/admin/projects_controller_spec.rb b/spec/controllers/admin/projects_controller_spec.rb index 2ba0d489197..4cb8b8da150 100644 --- a/spec/controllers/admin/projects_controller_spec.rb +++ b/spec/controllers/admin/projects_controller_spec.rb @@ -17,7 +17,7 @@ describe Admin::ProjectsController do it 'does not retrieve the project' do get :index, visibility_levels: [Gitlab::VisibilityLevel::INTERNAL] - expect(response.body).to_not match(project.name) + expect(response.body).not_to match(project.name) end end end diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb index 788a609ee40..4018dac95a2 100644 --- a/spec/controllers/projects/compare_controller_spec.rb +++ b/spec/controllers/projects/compare_controller_spec.rb @@ -19,7 +19,7 @@ describe Projects::CompareController do to: ref_to) expect(response).to be_success - expect(assigns(:diffs).first).to_not be_nil + expect(assigns(:diffs).first).not_to be_nil expect(assigns(:commits).length).to be >= 1 end @@ -32,7 +32,7 @@ describe Projects::CompareController do w: 1) expect(response).to be_success - expect(assigns(:diffs).first).to_not be_nil + expect(assigns(:diffs).first).not_to be_nil expect(assigns(:commits).length).to be >= 1 # without whitespace option, there are more than 2 diff_splits diff_splits = assigns(:diffs).first.diff.split("\n") diff --git a/spec/controllers/projects/group_links_controller_spec.rb b/spec/controllers/projects/group_links_controller_spec.rb index 1bd1fc5189e..fbe8758dda7 100644 --- a/spec/controllers/projects/group_links_controller_spec.rb +++ b/spec/controllers/projects/group_links_controller_spec.rb @@ -43,7 +43,7 @@ describe Projects::GroupLinksController do end it 'does not share project with that group' do - expect(group.shared_projects).to_not include project + expect(group.shared_projects).not_to include project end end end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 2b2ad3b9412..c469480b086 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -56,7 +56,7 @@ describe Projects::IssuesController do move_issue expect(response).to have_http_status :found - expect(another_project.issues).to_not be_empty + expect(another_project.issues).not_to be_empty end end diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index ed64e7cf9af..750fbecdd07 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -38,7 +38,7 @@ describe Projects::ProjectMembersController do include_context 'import applied' it 'does not import team members' do - expect(project.team_members).to_not include member + expect(project.team_members).not_to include member end it 'responds with not found' do diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb index df70a589a89..fbe50d10ec5 100644 --- a/spec/controllers/registrations_controller_spec.rb +++ b/spec/controllers/registrations_controller_spec.rb @@ -16,7 +16,7 @@ describe RegistrationsController do it 'logs user in directly' do post(:create, user_params) expect(ActionMailer::Base.deliveries.last).to be_nil - expect(subject.current_user).to_not be_nil + expect(subject.current_user).not_to be_nil end end diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index 83cc8ec6d26..ab57c52c7cd 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -47,7 +47,7 @@ describe SessionsController do authenticate_2fa(login: another_user.username, otp_attempt: another_user.current_otp) - expect(subject.current_user).to_not eq another_user + expect(subject.current_user).not_to eq another_user end end @@ -56,7 +56,7 @@ describe SessionsController do authenticate_2fa(login: another_user.username, otp_attempt: 'invalid') - expect(subject.current_user).to_not eq another_user + expect(subject.current_user).not_to eq another_user end end @@ -73,7 +73,7 @@ describe SessionsController do before { authenticate_2fa(otp_attempt: 'invalid') } it 'does not authenticate' do - expect(subject.current_user).to_not eq user + expect(subject.current_user).not_to eq user end it 'warns about invalid OTP code' do diff --git a/spec/factories_spec.rb b/spec/factories_spec.rb index a073a1f9d7c..675d9bd18b7 100644 --- a/spec/factories_spec.rb +++ b/spec/factories_spec.rb @@ -6,7 +6,7 @@ describe 'factories' do let(:entity) { build(factory.name) } it 'does not raise error when created' do - expect { entity }.to_not raise_error + expect { entity }.not_to raise_error end it 'should be valid', if: factory.build_class < ActiveRecord::Base do diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb index 26d03944b8a..8ebd4a6808e 100644 --- a/spec/features/admin/admin_runners_spec.rb +++ b/spec/features/admin/admin_runners_spec.rb @@ -79,7 +79,7 @@ describe "Admin Runners" do end it 'changes registration token' do - expect(page_token).to_not eq token + expect(page_token).not_to eq token end end end diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index 6dee0cd8d47..96621843b30 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -152,7 +152,7 @@ describe "Admin::Users", feature: true do it 'sees impersonation log out icon' do icon = first('.fa.fa-user-secret') - expect(icon).to_not eql nil + expect(icon).not_to eql nil end it 'can log out of impersonated user back to original user' do diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb index f83a78308e3..7a05d30e8b5 100644 --- a/spec/features/builds_spec.rb +++ b/spec/features/builds_spec.rb @@ -43,11 +43,10 @@ describe "Builds" do end it { expect(page).to have_selector('.nav-links li.active', text: 'All') } - it { expect(page).to have_selector('.row-content-block', text: 'All builds from this project') } it { expect(page).to have_content @build.short_sha } it { expect(page).to have_content @build.ref } it { expect(page).to have_content @build.name } - it { expect(page).to_not have_link 'Cancel running' } + it { expect(page).not_to have_link 'Cancel running' } end end @@ -63,7 +62,7 @@ describe "Builds" do it { expect(page).to have_content @build.short_sha } it { expect(page).to have_content @build.ref } it { expect(page).to have_content @build.name } - it { expect(page).to_not have_link 'Cancel running' } + it { expect(page).not_to have_link 'Cancel running' } end describe "GET /:project/builds/:id" do diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index dacaa96d760..20f0b27bcc1 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -137,8 +137,8 @@ describe 'Commits' do expect(page).to have_content commit.git_commit_message expect(page).to have_content commit.git_author_name expect(page).to have_link('Download artifacts') - expect(page).to_not have_link('Cancel running') - expect(page).to_not have_link('Retry failed') + expect(page).not_to have_link('Cancel running') + expect(page).not_to have_link('Retry failed') end end @@ -155,9 +155,9 @@ describe 'Commits' do expect(page).to have_content commit.sha[0..7] expect(page).to have_content commit.git_commit_message expect(page).to have_content commit.git_author_name - expect(page).to_not have_link('Download artifacts') - expect(page).to_not have_link('Cancel running') - expect(page).to_not have_link('Retry failed') + expect(page).not_to have_link('Download artifacts') + expect(page).not_to have_link('Cancel running') + expect(page).not_to have_link('Retry failed') end end end diff --git a/spec/features/issues/filter_issues_spec.rb b/spec/features/issues/filter_issues_spec.rb index 192e3619375..1f0594e6b02 100644 --- a/spec/features/issues/filter_issues_spec.rb +++ b/spec/features/issues/filter_issues_spec.rb @@ -154,4 +154,144 @@ describe 'Filter issues', feature: true do end end end + + describe 'filter issues by text' do + before do + create(:issue, title: "Bug", project: project) + + bug_label = create(:label, project: project, title: 'bug') + milestone = create(:milestone, title: "8", project: project) + + issue = create(:issue, + title: "Bug 2", + project: project, + milestone: milestone, + author: user, + assignee: user) + issue.labels << bug_label + + visit namespace_project_issues_path(project.namespace, project) + end + + context 'only text', js: true do + it 'should filter issues by searched text' do + fill_in 'issue_search', with: 'Bug' + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 2) + end + end + + it 'should not show any issues' do + fill_in 'issue_search', with: 'testing' + + page.within '.issues-list' do + expect(page).not_to have_selector('.issue') + end + end + end + + context 'text and dropdown options', js: true do + it 'should filter by text and label' do + fill_in 'issue_search', with: 'Bug' + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 2) + end + + click_button 'Label' + page.within '.labels-filter' do + click_link 'bug' + end + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 1) + end + end + + it 'should filter by text and milestone' do + fill_in 'issue_search', with: 'Bug' + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 2) + end + + click_button 'Milestone' + page.within '.milestone-filter' do + click_link '8' + end + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 1) + end + end + + it 'should filter by text and assignee' do + fill_in 'issue_search', with: 'Bug' + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 2) + end + + click_button 'Assignee' + page.within '.dropdown-menu-assignee' do + click_link user.name + end + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 1) + end + end + + it 'should filter by text and author' do + fill_in 'issue_search', with: 'Bug' + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 2) + end + + click_button 'Author' + page.within '.dropdown-menu-author' do + click_link user.name + end + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 1) + end + end + end + end + + describe 'filter issues and sort', js: true do + before do + bug_label = create(:label, project: project, title: 'bug') + bug_one = create(:issue, title: "Frontend", project: project) + bug_two = create(:issue, title: "Bug 2", project: project) + + bug_one.labels << bug_label + bug_two.labels << bug_label + + visit namespace_project_issues_path(project.namespace, project) + end + + it 'should be able to filter and sort issues' do + click_button 'Label' + page.within '.labels-filter' do + click_link 'bug' + end + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 2) + end + + click_button 'Last created' + page.within '.dropdown-menu-sort' do + click_link 'Oldest created' + end + + page.within '.issues-list' do + expect(first('.issue')).to have_content('Frontend') + end + end + end end diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb index b03dd0f666d..466a6f7dfa7 100644 --- a/spec/features/issues/update_issues_spec.rb +++ b/spec/features/issues/update_issues_spec.rb @@ -95,7 +95,7 @@ feature 'Multiple issue updating from issues#index', feature: true do find('.dropdown-menu-milestone a', text: "No Milestone").click click_update_issues_button - expect(first('.issue')).to_not have_content milestone.title + expect(first('.issue')).not_to have_content milestone.title end end diff --git a/spec/features/pipelines_spec.rb b/spec/features/pipelines_spec.rb index 1d6f4485c81..bef0578a9bb 100644 --- a/spec/features/pipelines_spec.rb +++ b/spec/features/pipelines_spec.rb @@ -41,7 +41,7 @@ describe "Pipelines" do context 'when canceling' do before { click_link('Cancel') } - it { expect(page).to_not have_link('Cancel') } + it { expect(page).not_to have_link('Cancel') } it { expect(page).to have_selector('.ci-canceled') } end end @@ -57,7 +57,7 @@ describe "Pipelines" do context 'when retrying' do before { click_link('Retry') } - it { expect(page).to_not have_link('Retry') } + it { expect(page).not_to have_link('Retry') } it { expect(page).to have_selector('.ci-pending') } end end @@ -75,7 +75,7 @@ describe "Pipelines" do context 'without artifacts' do let!(:without_artifacts) { create(:ci_build, :success, commit: pipeline, name: 'rspec', stage: 'test') } - it { expect(page).to_not have_selector('.build-artifacts') } + it { expect(page).not_to have_selector('.build-artifacts') } end end end @@ -104,23 +104,23 @@ describe "Pipelines" do end context 'retrying builds' do - it { expect(page).to_not have_content('retried') } + it { expect(page).not_to have_content('retried') } context 'when retrying' do before { click_on 'Retry failed' } - it { expect(page).to_not have_content('Retry failed') } + it { expect(page).not_to have_content('Retry failed') } it { expect(page).to have_content('retried') } end end context 'canceling builds' do - it { expect(page).to_not have_selector('.ci-canceled') } + it { expect(page).not_to have_selector('.ci-canceled') } context 'when canceling' do before { click_on 'Cancel running' } - it { expect(page).to_not have_content('Cancel running') } + it { expect(page).not_to have_content('Cancel running') } it { expect(page).to have_selector('.ci-canceled') } end end diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index 49156130ad3..a5ed3595b0a 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -29,8 +29,8 @@ describe "Runners" do end before do - expect(page).to_not have_content(@specific_runner3.display_name) - expect(page).to_not have_content(@specific_runner3.display_name) + expect(page).not_to have_content(@specific_runner3.display_name) + expect(page).not_to have_content(@specific_runner3.display_name) end it "places runners in right places" do diff --git a/spec/features/todos/target_state_spec.rb b/spec/features/todos/target_state_spec.rb new file mode 100644 index 00000000000..72491ac7e61 --- /dev/null +++ b/spec/features/todos/target_state_spec.rb @@ -0,0 +1,65 @@ +require 'rails_helper' + +feature 'Todo target states', feature: true do + let(:user) { create(:user) } + let(:author) { create(:user) } + let(:project) { create(:project) } + + before do + login_as user + end + + scenario 'on a closed issue todo has closed label' do + issue_closed = create(:issue, state: 'closed') + create_todo issue_closed + visit dashboard_todos_path + + page.within '.todos-list' do + expect(page).to have_content('Closed') + end + end + + scenario 'on an open issue todo does not have an open label' do + issue_open = create(:issue) + create_todo issue_open + visit dashboard_todos_path + + page.within '.todos-list' do + expect(page).not_to have_content('Open') + end + end + + scenario 'on a merged merge request todo has merged label' do + mr_merged = create(:merge_request, :simple, author: user, state: 'merged') + create_todo mr_merged + visit dashboard_todos_path + + page.within '.todos-list' do + expect(page).to have_content('Merged') + end + end + + scenario 'on a closed merge request todo has closed label' do + mr_closed = create(:merge_request, :simple, author: user, state: 'closed') + create_todo mr_closed + visit dashboard_todos_path + + page.within '.todos-list' do + expect(page).to have_content('Closed') + end + end + + scenario 'on an open merge request todo does not have an open label' do + mr_open = create(:merge_request, :simple, author: user) + create_todo mr_open + visit dashboard_todos_path + + page.within '.todos-list' do + expect(page).not_to have_content('Open') + end + end + + def create_todo(target) + create(:todo, :mentioned, user: user, project: project, target: target, author: author) + end +end diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb index 48e2dae4884..a2b8f7b6931 100644 --- a/spec/features/variables_spec.rb +++ b/spec/features/variables_spec.rb @@ -34,7 +34,7 @@ describe 'Project variables', js: true do find('.btn-variable-delete').click end - expect(page).to_not have_selector('variables-table') + expect(page).not_to have_selector('variables-table') end it 'should edit variable' do diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb index 16fbb5dcecb..49ea4fa6d3e 100644 --- a/spec/helpers/auth_helper_spec.rb +++ b/spec/helpers/auth_helper_spec.rb @@ -36,7 +36,7 @@ describe AuthHelper do ) expect(helper.enabled_button_based_providers).to include('twitter') - expect(helper.enabled_button_based_providers).to_not include('github') + expect(helper.enabled_button_based_providers).not_to include('github') end end end diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb index 600e1c4e9ec..8e7ed42e883 100644 --- a/spec/helpers/merge_requests_helper_spec.rb +++ b/spec/helpers/merge_requests_helper_spec.rb @@ -17,7 +17,7 @@ describe MergeRequestsHelper do it 'does not include api credentials in a link' do allow(ci_service). to receive(:build_page).and_return("http://secretuser:secretpass@jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c") - expect(helper.ci_build_details_path(merge_request)).to_not match("secret") + expect(helper.ci_build_details_path(merge_request)).not_to match("secret") end end diff --git a/spec/javascripts/fixtures/right_sidebar.html.haml b/spec/javascripts/fixtures/right_sidebar.html.haml new file mode 100644 index 00000000000..95efaff4b69 --- /dev/null +++ b/spec/javascripts/fixtures/right_sidebar.html.haml @@ -0,0 +1,13 @@ +%div + %div.page-gutter.page-with-sidebar + + %aside.right-sidebar + %div.block.issuable-sidebar-header + %a.gutter-toggle.pull-right.js-sidebar-toggle + %i.fa.fa-angle-double-left + + %form.issuable-context-form + %div.block.labels + %div.sidebar-collapsed-icon + %i.fa.fa-tags + %span 1 diff --git a/spec/javascripts/right_sidebar_spec.js.coffee b/spec/javascripts/right_sidebar_spec.js.coffee new file mode 100644 index 00000000000..2075cacdb67 --- /dev/null +++ b/spec/javascripts/right_sidebar_spec.js.coffee @@ -0,0 +1,69 @@ +#= require right_sidebar +#= require jquery +#= require jquery.cookie + +@sidebar = null +$aside = null +$toggle = null +$icon = null +$page = null +$labelsIcon = null + + +assertSidebarState = (state) -> + + shouldBeExpanded = state is 'expanded' + shouldBeCollapsed = state is 'collapsed' + + expect($aside.hasClass('right-sidebar-expanded')).toBe shouldBeExpanded + expect($page.hasClass('right-sidebar-expanded')).toBe shouldBeExpanded + expect($icon.hasClass('fa-angle-double-right')).toBe shouldBeExpanded + + expect($aside.hasClass('right-sidebar-collapsed')).toBe shouldBeCollapsed + expect($page.hasClass('right-sidebar-collapsed')).toBe shouldBeCollapsed + expect($icon.hasClass('fa-angle-double-left')).toBe shouldBeCollapsed + + +describe 'RightSidebar', -> + + fixture.preload 'right_sidebar.html' + + beforeEach -> + fixture.load 'right_sidebar.html' + + @sidebar = new Sidebar + $aside = $ '.right-sidebar' + $page = $ '.page-with-sidebar' + $icon = $aside.find 'i' + $toggle = $aside.find '.js-sidebar-toggle' + $labelsIcon = $aside.find '.sidebar-collapsed-icon' + + + it 'should expand the sidebar when arrow is clicked', -> + + $toggle.click() + assertSidebarState 'expanded' + + + it 'should collapse the sidebar when arrow is clicked', -> + + $toggle.click() + assertSidebarState 'expanded' + + $toggle.click() + assertSidebarState 'collapsed' + + + it 'should float over the page and when sidebar icons clicked', -> + + $labelsIcon.click() + assertSidebarState 'expanded' + + + it 'should collapse when the icon arrow clicked while it is floating on page', -> + + $labelsIcon.click() + assertSidebarState 'expanded' + + $toggle.click() + assertSidebarState 'collapsed' diff --git a/spec/lib/award_emoji_spec.rb b/spec/lib/award_emoji_spec.rb index 88c22912950..c3098574292 100644 --- a/spec/lib/award_emoji_spec.rb +++ b/spec/lib/award_emoji_spec.rb @@ -5,7 +5,7 @@ describe AwardEmoji do subject { AwardEmoji.urls } it { is_expected.to be_an_instance_of(Array) } - it { is_expected.to_not be_empty } + it { is_expected.not_to be_empty } context 'every Hash in the Array' do it 'has the correct keys and values' do diff --git a/spec/lib/ci/ansi2html_spec.rb b/spec/lib/ci/ansi2html_spec.rb index 04afbd06929..898f1e84ab0 100644 --- a/spec/lib/ci/ansi2html_spec.rb +++ b/spec/lib/ci/ansi2html_spec.rb @@ -175,5 +175,14 @@ describe Ci::Ansi2html, lib: true do it_behaves_like 'stateable converter' end + + context 'with new line' do + let(:pre_text) { "Hello\r" } + let(:pre_html) { "Hello\r" } + let(:text) { "\nWorld" } + let(:html) { "<br>World" } + + it_behaves_like 'stateable converter' + end end end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 9eef8ea0976..7375539cf17 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -619,19 +619,19 @@ module Ci context 'no dependencies' do let(:dependencies) { } - it { expect { subject }.to_not raise_error } + it { expect { subject }.not_to raise_error } end context 'dependencies to builds' do let(:dependencies) { ['build1', 'build2'] } - it { expect { subject }.to_not raise_error } + it { expect { subject }.not_to raise_error } end context 'dependencies to builds defined as symbols' do let(:dependencies) { [:build1, :build2] } - it { expect { subject }.to_not raise_error } + it { expect { subject }.not_to raise_error } end context 'undefined dependency' do diff --git a/spec/lib/container_registry/registry_spec.rb b/spec/lib/container_registry/registry_spec.rb index 2638401ae6e..4f3f8b24fc4 100644 --- a/spec/lib/container_registry/registry_spec.rb +++ b/spec/lib/container_registry/registry_spec.rb @@ -10,7 +10,7 @@ describe ContainerRegistry::Registry do it { is_expected.to respond_to(:uri) } it { is_expected.to respond_to(:path) } - it { expect(subject.repository('test')).to_not be_nil } + it { expect(subject.repository('test')).not_to be_nil } context '#path' do subject { registry.path } diff --git a/spec/lib/container_registry/repository_spec.rb b/spec/lib/container_registry/repository_spec.rb index e6d66b11e4e..279709521c9 100644 --- a/spec/lib/container_registry/repository_spec.rb +++ b/spec/lib/container_registry/repository_spec.rb @@ -6,7 +6,7 @@ describe ContainerRegistry::Repository do it { expect(repository).to respond_to(:registry) } it { expect(repository).to delegate_method(:client).to(:registry) } - it { expect(repository.tag('test')).to_not be_nil } + it { expect(repository.tag('test')).not_to be_nil } context '#path' do subject { repository.path } @@ -27,7 +27,7 @@ describe ContainerRegistry::Repository do context '#manifest' do subject { repository.manifest } - it { is_expected.to_not be_nil } + it { is_expected.not_to be_nil } end context '#valid?' do @@ -39,7 +39,7 @@ describe ContainerRegistry::Repository do context '#tags' do subject { repository.tags } - it { is_expected.to_not be_empty } + it { is_expected.not_to be_empty } end end diff --git a/spec/lib/container_registry/tag_spec.rb b/spec/lib/container_registry/tag_spec.rb index 12cf91127ed..858cb0bb134 100644 --- a/spec/lib/container_registry/tag_spec.rb +++ b/spec/lib/container_registry/tag_spec.rb @@ -50,13 +50,13 @@ describe ContainerRegistry::Tag do context '#config' do subject { tag.config } - it { is_expected.to_not be_nil } + it { is_expected.not_to be_nil } end context '#created_at' do subject { tag.created_at } - it { is_expected.to_not be_nil } + it { is_expected.not_to be_nil } end end end diff --git a/spec/lib/gitlab/bitbucket_import/client_spec.rb b/spec/lib/gitlab/bitbucket_import/client_spec.rb index af839f42421..7718689e6d4 100644 --- a/spec/lib/gitlab/bitbucket_import/client_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/client_spec.rb @@ -59,7 +59,7 @@ describe Gitlab::BitbucketImport::Client, lib: true do bitbucket_access_token_secret: "test" } }) project.import_url = "ssh://git@bitbucket.org/test/test.git" - expect { described_class.from_project(project) }.to_not raise_error + expect { described_class.from_project(project) }.not_to raise_error end end end diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb index 46a5b7fce65..711a3e1c7d4 100644 --- a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb +++ b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb @@ -122,7 +122,7 @@ describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do describe 'empty path', path: '' do subject { |example| path(example) } - it { is_expected.to_not have_parent } + it { is_expected.not_to have_parent } describe '#children' do subject { |example| path(example).children } diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb index 0a7ca3ec848..0af249d8690 100644 --- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb +++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb @@ -33,8 +33,8 @@ describe Gitlab::Gfm::ReferenceRewriter do end it { is_expected.to include issue_first.to_reference(new_project) } - it { is_expected.to_not include issue_second.to_reference(new_project) } - it { is_expected.to_not include merge_request.to_reference(new_project) } + it { is_expected.not_to include issue_second.to_reference(new_project) } + it { is_expected.not_to include merge_request.to_reference(new_project) } end context 'description ambigous elements' do diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb index eda956e6f0a..6eca33f9fee 100644 --- a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb +++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb @@ -32,13 +32,13 @@ describe Gitlab::Gfm::UploadsRewriter do let(:new_paths) { new_files.map(&:path) } it 'rewrites content' do - expect(new_text).to_not eq text + expect(new_text).not_to eq text expect(new_text.length).to eq text.length end it 'copies files' do expect(new_files).to all(exist) - expect(old_paths).to_not match_array new_paths + expect(old_paths).not_to match_array new_paths expect(old_paths).to all(include(old_project.path_with_namespace)) expect(new_paths).to all(include(new_project.path_with_namespace)) end @@ -48,8 +48,8 @@ describe Gitlab::Gfm::UploadsRewriter do end it 'generates a new secret for each file' do - expect(new_paths).to_not include image_uploader.secret - expect(new_paths).to_not include zip_uploader.secret + expect(new_paths).not_to include image_uploader.secret + expect(new_paths).not_to include zip_uploader.secret end end diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb index 3325190789b..88814bc474d 100644 --- a/spec/lib/gitlab/lfs/lfs_router_spec.rb +++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb @@ -368,7 +368,7 @@ describe Gitlab::Lfs::Router, lib: true do expect(response['objects']).to be_kind_of(Array) expect(response['objects'].first['oid']).to eq(sample_oid) expect(response['objects'].first['size']).to eq(sample_size) - expect(lfs_object.projects.pluck(:id)).to_not include(project.id) + expect(lfs_object.projects.pluck(:id)).not_to include(project.id) expect(lfs_object.projects.pluck(:id)).to include(public_project.id) expect(response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}") expect(response['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth) @@ -430,7 +430,7 @@ describe Gitlab::Lfs::Router, lib: true do expect(response_body['objects'].last['oid']).to eq(sample_oid) expect(response_body['objects'].last['size']).to eq(sample_size) - expect(response_body['objects'].last).to_not have_key('actions') + expect(response_body['objects'].last).not_to have_key('actions') end end end diff --git a/spec/lib/gitlab/metrics/instrumentation_spec.rb b/spec/lib/gitlab/metrics/instrumentation_spec.rb index 7b86450a223..220e86924a2 100644 --- a/spec/lib/gitlab/metrics/instrumentation_spec.rb +++ b/spec/lib/gitlab/metrics/instrumentation_spec.rb @@ -67,7 +67,7 @@ describe Gitlab::Metrics::Instrumentation do allow(Gitlab::Metrics).to receive(:method_call_threshold). and_return(100) - expect(transaction).to_not receive(:add_metric) + expect(transaction).not_to receive(:add_metric) @dummy.foo end @@ -147,7 +147,7 @@ describe Gitlab::Metrics::Instrumentation do allow(Gitlab::Metrics).to receive(:method_call_threshold). and_return(100) - expect(transaction).to_not receive(:add_metric) + expect(transaction).not_to receive(:add_metric) @dummy.new.bar end @@ -220,7 +220,7 @@ describe Gitlab::Metrics::Instrumentation do described_class.instrument_methods(@dummy) - expect(@dummy).to_not respond_to(:_original_kittens) + expect(@dummy).not_to respond_to(:_original_kittens) end it 'can take a block to determine if a method should be instrumented' do @@ -228,7 +228,7 @@ describe Gitlab::Metrics::Instrumentation do false end - expect(@dummy).to_not respond_to(:_original_foo) + expect(@dummy).not_to respond_to(:_original_foo) end end diff --git a/spec/lib/gitlab/metrics/sampler_spec.rb b/spec/lib/gitlab/metrics/sampler_spec.rb index 38da77adc9f..59db127674a 100644 --- a/spec/lib/gitlab/metrics/sampler_spec.rb +++ b/spec/lib/gitlab/metrics/sampler_spec.rb @@ -130,7 +130,7 @@ describe Gitlab::Metrics::Sampler do 100.times do interval = sampler.sleep_interval - expect(interval).to_not eq(last) + expect(interval).not_to eq(last) last = interval end diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb index e3293a01207..49699ffe28f 100644 --- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb @@ -13,7 +13,7 @@ describe Gitlab::Metrics::Subscribers::ActiveRecord do describe 'without a current transaction' do it 'simply returns' do expect_any_instance_of(Gitlab::Metrics::Transaction). - to_not receive(:increment) + not_to receive(:increment) subscriber.sql(event) end diff --git a/spec/lib/gitlab/sherlock/collection_spec.rb b/spec/lib/gitlab/sherlock/collection_spec.rb index de6bb86c5dd..2ae79b50e77 100644 --- a/spec/lib/gitlab/sherlock/collection_spec.rb +++ b/spec/lib/gitlab/sherlock/collection_spec.rb @@ -11,13 +11,13 @@ describe Gitlab::Sherlock::Collection, lib: true do it 'adds a new transaction' do collection.add(transaction) - expect(collection).to_not be_empty + expect(collection).not_to be_empty end it 'is aliased as <<' do collection << transaction - expect(collection).to_not be_empty + expect(collection).not_to be_empty end end @@ -47,7 +47,7 @@ describe Gitlab::Sherlock::Collection, lib: true do it 'returns false for a collection with a transaction' do collection.add(transaction) - expect(collection).to_not be_empty + expect(collection).not_to be_empty end end diff --git a/spec/lib/gitlab/sherlock/query_spec.rb b/spec/lib/gitlab/sherlock/query_spec.rb index 05da915ccfd..0a620428138 100644 --- a/spec/lib/gitlab/sherlock/query_spec.rb +++ b/spec/lib/gitlab/sherlock/query_spec.rb @@ -85,7 +85,7 @@ FROM users; frames = query.application_backtrace expect(frames).to be_an_instance_of(Array) - expect(frames).to_not be_empty + expect(frames).not_to be_empty frames.each do |frame| expect(frame.path).to start_with(Rails.root.to_s) diff --git a/spec/lib/gitlab/sherlock/transaction_spec.rb b/spec/lib/gitlab/sherlock/transaction_spec.rb index 7553f2a045f..9fe18f253f0 100644 --- a/spec/lib/gitlab/sherlock/transaction_spec.rb +++ b/spec/lib/gitlab/sherlock/transaction_spec.rb @@ -203,7 +203,7 @@ describe Gitlab::Sherlock::Transaction, lib: true do end it 'only tracks queries triggered from the transaction thread' do - expect(transaction).to_not receive(:track_query) + expect(transaction).not_to receive(:track_query) Thread.new { subscription.publish('test', time, time, nil, query_data) }. join @@ -226,7 +226,7 @@ describe Gitlab::Sherlock::Transaction, lib: true do end it 'only tracks views rendered from the transaction thread' do - expect(transaction).to_not receive(:track_view) + expect(transaction).not_to receive(:track_view) Thread.new { subscription.publish('test', time, time, nil, view_data) }. join diff --git a/spec/lib/json_web_token/rsa_token_spec.rb b/spec/lib/json_web_token/rsa_token_spec.rb index 0c3d3ea7019..18726754517 100644 --- a/spec/lib/json_web_token/rsa_token_spec.rb +++ b/spec/lib/json_web_token/rsa_token_spec.rb @@ -23,7 +23,7 @@ describe JSONWebToken::RSAToken do subject { JWT.decode(rsa_encoded, rsa_key) } - it { expect{subject}.to_not raise_error } + it { expect{subject}.not_to raise_error } it { expect(subject.first).to include('key' => 'value') } it do expect(subject.second).to eq( diff --git a/spec/mailers/previews/devise_mailer_preview.rb b/spec/mailers/previews/devise_mailer_preview.rb new file mode 100644 index 00000000000..dc3062a4332 --- /dev/null +++ b/spec/mailers/previews/devise_mailer_preview.rb @@ -0,0 +1,11 @@ +class DeviseMailerPreview < ActionMailer::Preview + def confirmation_instructions_for_signup + user = User.new(name: 'Jane Doe', email: 'signup@example.com') + DeviseMailer.confirmation_instructions(user, 'faketoken', {}) + end + + def confirmation_instructions_for_new_email + user = User.last + DeviseMailer.confirmation_instructions(user, 'faketoken', {}) + end +end diff --git a/spec/mailers/shared/notify.rb b/spec/mailers/shared/notify.rb index 5a85cb501dd..93de5850ba2 100644 --- a/spec/mailers/shared/notify.rb +++ b/spec/mailers/shared/notify.rb @@ -146,8 +146,8 @@ shared_examples 'it should have Gmail Actions links' do end shared_examples 'it should not have Gmail Actions links' do - it { is_expected.to_not have_body_text '<script type="application/ld+json">' } - it { is_expected.to_not have_body_text /ViewAction/ } + it { is_expected.not_to have_body_text '<script type="application/ld+json">' } + it { is_expected.not_to have_body_text /ViewAction/ } end shared_examples 'it should show Gmail Actions View Issue link' do diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index abae3271a5c..55b7af441d6 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -90,7 +90,7 @@ describe Ci::Build, models: true do build.update_attributes(trace: token) end - it { is_expected.to_not include(token) } + it { is_expected.not_to include(token) } end end @@ -317,7 +317,7 @@ describe Ci::Build, models: true do context 'when build does not have tags' do subject { create(:ci_build, tag_list: []) } - it { is_expected.to_not have_tags } + it { is_expected.not_to have_tags } end end @@ -534,7 +534,7 @@ describe Ci::Build, models: true do end it 'should set erase date' do - expect(build.erased_at).to_not be_falsy + expect(build.erased_at).not_to be_falsy end end @@ -606,7 +606,7 @@ describe Ci::Build, models: true do describe '#erase' do it 'should not raise error' do - expect { build.erase }.to_not raise_error + expect { build.erase }.not_to raise_error end end end diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index 1b5940ad5a8..22f8639e5ab 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -247,7 +247,7 @@ describe Ci::Commit, models: true do expect(commit.builds.pluck(:status)).to contain_exactly('pending') commit.builds.running_or_pending.each(&:success) - expect(commit.builds.running_or_pending).to_not be_empty + expect(commit.builds.running_or_pending).not_to be_empty expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test') expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending') diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 7c6e39419ed..5d04d8ffcff 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -140,7 +140,7 @@ describe Ci::Runner, models: true do context 'when runner does not have tags' do subject { create(:ci_runner, tag_list: []) } - it { is_expected.to_not have_tags } + it { is_expected.not_to have_tags } end end diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 4a4cd093435..583827cdf42 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -114,6 +114,35 @@ describe Issue, "Issuable" do end end + describe "#sort" do + let(:project) { build_stubbed(:empty_project) } + + context "by milestone due date" do + #Correct order is: + #Issues/MRs with milestones ordered by date + #Issues/MRs with milestones without dates + #Issues/MRs without milestones + + let!(:issue) { create(:issue, project: project) } + let!(:early_milestone) { create(:milestone, project: project, due_date: 10.days.from_now) } + let!(:late_milestone) { create(:milestone, project: project, due_date: 30.days.from_now) } + let!(:issue1) { create(:issue, project: project, milestone: early_milestone) } + let!(:issue2) { create(:issue, project: project, milestone: late_milestone) } + let!(:issue3) { create(:issue, project: project) } + + it "sorts desc" do + issues = project.issues.sort('milestone_due_desc') + expect(issues).to match_array([issue2, issue1, issue, issue3]) + end + + it "sorts asc" do + issues = project.issues.sort('milestone_due_asc') + expect(issues).to match_array([issue1, issue2, issue, issue3]) + end + end + end + + describe '#subscribed?' do context 'user is not a participant in the issue' do before { allow(issue).to receive(:participants).with(user).and_return([]) } @@ -165,7 +194,7 @@ describe Issue, "Issuable" do expect(data[:object_kind]).to eq("issue") expect(data[:user]).to eq(user.hook_attrs) expect(data[:object_attributes]).to eq(issue.hook_attrs) - expect(data).to_not have_key(:assignee) + expect(data).not_to have_key(:assignee) end context "issue is assigned" do diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb index b6adc2bf247..9e8ebc56a31 100644 --- a/spec/models/concerns/token_authenticatable_spec.rb +++ b/spec/models/concerns/token_authenticatable_spec.rb @@ -28,14 +28,14 @@ describe ApplicationSetting, 'TokenAuthenticatable' do context 'token is not generated yet' do describe 'token field accessor' do subject { described_class.new.send(token_field) } - it { is_expected.to_not be_blank } + it { is_expected.not_to be_blank } end describe 'ensured token' do subject { described_class.new.send("ensure_#{token_field}") } it { is_expected.to be_a String } - it { is_expected.to_not be_blank } + it { is_expected.not_to be_blank } end describe 'ensured! token' do diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb index 0caf5869c24..d0e02618b6b 100644 --- a/spec/models/generic_commit_status_spec.rb +++ b/spec/models/generic_commit_status_spec.rb @@ -27,13 +27,13 @@ describe GenericCommitStatus, models: true do describe :context do subject { generic_commit_status.context } - it { is_expected.to_not be_nil } + it { is_expected.not_to be_nil } end describe :stage do subject { generic_commit_status.stage } - it { is_expected.to_not be_nil } + it { is_expected.not_to be_nil } end end end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 8ab00c70f9d..6540d77fbc0 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -192,7 +192,7 @@ describe Issue, models: true do source_project: subject.project, source_branch: "#{subject.iid}-branch" }) merge_request.create_cross_references!(user) - expect(subject.referenced_merge_requests).to_not be_empty + expect(subject.referenced_merge_requests).not_to be_empty expect(subject.related_branches(user)).to eq([subject.to_branch_name]) end diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index 6fb5cad5011..f887ed4bafc 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -303,7 +303,7 @@ describe HipchatService, models: true do it "should notify only broken" do hipchat.notify_only_broken_builds = true hipchat.execute(data) - expect(WebMock).to_not have_requested(:post, api_url).once + expect(WebMock).not_to have_requested(:post, api_url).once end end end diff --git a/spec/models/project_services/slack_service/build_message_spec.rb b/spec/models/project_services/slack_service/build_message_spec.rb index 621c83c0cda..7fcfdf0eacd 100644 --- a/spec/models/project_services/slack_service/build_message_spec.rb +++ b/spec/models/project_services/slack_service/build_message_spec.rb @@ -15,7 +15,7 @@ describe SlackService::BuildMessage do commit: { status: status, author_name: 'hacker', - duration: 10, + duration: duration, }, } end @@ -23,9 +23,10 @@ describe SlackService::BuildMessage do context 'succeeded' do let(:status) { 'success' } let(:color) { 'good' } - + let(:duration) { 10 } + it 'returns a message with information about succeeded build' do - message = '<somewhere.com|project_name>: Commit <somewhere.com/commit/97de212e80737a608d939f648d959671fb0a0142/builds|97de212e> of <somewhere.com/commits/develop|develop> branch by hacker passed in 10 second(s)' + message = '<somewhere.com|project_name>: Commit <somewhere.com/commit/97de212e80737a608d939f648d959671fb0a0142/builds|97de212e> of <somewhere.com/commits/develop|develop> branch by hacker passed in 10 seconds' expect(subject.pretext).to be_empty expect(subject.fallback).to eq(message) expect(subject.attachments).to eq([text: message, color: color]) @@ -35,9 +36,23 @@ describe SlackService::BuildMessage do context 'failed' do let(:status) { 'failed' } let(:color) { 'danger' } + let(:duration) { 10 } it 'returns a message with information about failed build' do - message = '<somewhere.com|project_name>: Commit <somewhere.com/commit/97de212e80737a608d939f648d959671fb0a0142/builds|97de212e> of <somewhere.com/commits/develop|develop> branch by hacker failed in 10 second(s)' + message = '<somewhere.com|project_name>: Commit <somewhere.com/commit/97de212e80737a608d939f648d959671fb0a0142/builds|97de212e> of <somewhere.com/commits/develop|develop> branch by hacker failed in 10 seconds' + expect(subject.pretext).to be_empty + expect(subject.fallback).to eq(message) + expect(subject.attachments).to eq([text: message, color: color]) + end + end + + describe '#seconds_name' do + let(:status) { 'failed' } + let(:color) { 'danger' } + let(:duration) { 1 } + + it 'returns seconds as singular when there is only one' do + message = '<somewhere.com|project_name>: Commit <somewhere.com/commit/97de212e80737a608d939f648d959671fb0a0142/builds|97de212e> of <somewhere.com/commits/develop|develop> branch by hacker failed in 1 second' expect(subject.pretext).to be_empty expect(subject.fallback).to eq(message) expect(subject.attachments).to eq([text: message, color: color]) diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 60e1ec43f2b..6c1b0393682 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -60,7 +60,7 @@ describe Project, models: true do project2 = build(:project) allow(project2).to receive(:creator).and_return(double(can_create_project?: false, projects_limit: 0).as_null_object) expect(project2).not_to be_valid - expect(project2.errors[:limit_reached].first).to match(/Your project limit is 0/) + expect(project2.errors[:limit_reached].first).to match(/Personal project creation is not allowed/) end end @@ -791,7 +791,7 @@ describe Project, models: true do subject { project.container_registry_repository } - it { is_expected.to_not be_nil } + it { is_expected.not_to be_nil } end describe '#container_registry_repository_url' do @@ -809,7 +809,7 @@ describe Project, models: true do } end - it { is_expected.to_not be_nil } + it { is_expected.not_to be_nil } end context 'for disabled registry' do diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 583151023b6..8c2347992f1 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -443,7 +443,7 @@ describe Repository, models: true do end it 'does nothing' do - expect(repository.raw_repository).to_not receive(:autocrlf=). + expect(repository.raw_repository).not_to receive(:autocrlf=). with(:input) repository.update_autocrlf_option @@ -511,7 +511,7 @@ describe Repository, models: true do it 'does not expire the emptiness caches for a non-empty repository' do expect(repository).to receive(:empty?).and_return(false) - expect(repository).to_not receive(:expire_emptiness_caches) + expect(repository).not_to receive(:expire_emptiness_caches) repository.expire_cache end @@ -674,7 +674,7 @@ describe Repository, models: true do end it 'does not flush caches that depend on repository data' do - expect(repository).to_not receive(:expire_cache) + expect(repository).not_to receive(:expire_cache) repository.before_delete end @@ -829,18 +829,6 @@ describe Repository, models: true do end end - describe "#main_language" do - it 'shows the main language of the project' do - expect(repository.main_language).to eq("Ruby") - end - - it 'returns nil when the repository is empty' do - allow(repository).to receive(:empty?).and_return(true) - - expect(repository.main_language).to be_nil - end - end - describe '#before_remove_tag' do it 'flushes the tag cache' do expect(repository).to receive(:expire_tag_count_cache) @@ -951,7 +939,7 @@ describe Repository, models: true do expect(repository.avatar).to eq('logo.png') - expect(repository).to_not receive(:blob_at_branch) + expect(repository).not_to receive(:blob_at_branch) expect(repository.avatar).to eq('logo.png') end end @@ -1045,7 +1033,7 @@ describe Repository, models: true do and_return(true) repository.cache_keys.each do |key| - expect(repository).to_not receive(key) + expect(repository).not_to receive(key) end repository.build_cache diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 37ddab83c30..7ecefce80d6 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -12,6 +12,7 @@ describe API::API, api: true do let!(:group2) { create(:group, :private) } let!(:project1) { create(:project, namespace: group1) } let!(:project2) { create(:project, namespace: group2) } + let!(:project3) { create(:project, namespace: group1, path: 'test', visibility_level: Gitlab::VisibilityLevel::PRIVATE) } before do group1.add_owner(user1) @@ -147,9 +148,11 @@ describe API::API, api: true do context "when authenticated as user" do it "should return the group's projects" do get api("/groups/#{group1.id}/projects", user1) + expect(response.status).to eq(200) - expect(json_response.length).to eq(1) - expect(json_response.first['name']).to eq(project1.name) + expect(json_response.length).to eq(2) + project_names = json_response.map { |proj| proj['name' ] } + expect(project_names).to match_array([project1.name, project3.name]) end it "should not return a non existing group" do @@ -162,6 +165,16 @@ describe API::API, api: true do expect(response.status).to eq(404) end + + it "should only return projects to which user has access" do + project3.team << [user3, :developer] + + get api("/groups/#{group1.id}/projects", user3) + + expect(response.status).to eq(200) + expect(json_response.length).to eq(1) + expect(json_response.first['name']).to eq(project3.name) + end end context "when authenticated as admin" do @@ -181,8 +194,10 @@ describe API::API, api: true do context 'when using group path in URL' do it 'should return any existing group' do get api("/groups/#{group1.path}/projects", admin) + expect(response.status).to eq(200) - expect(json_response.first['name']).to eq(project1.name) + project_names = json_response.map { |proj| proj['name' ] } + expect(project_names).to match_array([project1.name, project3.name]) end it 'should not return a non existing group' do diff --git a/spec/requests/api/project_members_spec.rb b/spec/requests/api/project_members_spec.rb index c112ca5e3ca..44b532b10e1 100644 --- a/spec/requests/api/project_members_spec.rb +++ b/spec/requests/api/project_members_spec.rb @@ -133,7 +133,7 @@ describe API::API, api: true do delete api("/projects/#{project.id}/members/#{user3.id}", user) expect do delete api("/projects/#{project.id}/members/#{user3.id}", user) - end.to_not change { ProjectMember.count } + end.not_to change { ProjectMember.count } expect(response.status).to eq(200) end diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb index 3e676515488..94eebc48ec8 100644 --- a/spec/requests/api/system_hooks_spec.rb +++ b/spec/requests/api/system_hooks_spec.rb @@ -49,7 +49,7 @@ describe API::API, api: true do it "should not create new hook without url" do expect do post api("/hooks", admin) - end.to_not change { SystemHook.count } + end.not_to change { SystemHook.count } end end diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 7ebf8e41f3b..e5124ea5ea7 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -253,13 +253,13 @@ describe Ci::API::API do it "using token as parameter" do post authorize_url, { token: build.token }, headers expect(response.status).to eq(200) - expect(json_response["TempPath"]).to_not be_nil + expect(json_response["TempPath"]).not_to be_nil end it "using token as header" do post authorize_url, {}, headers_with_token expect(response.status).to eq(200) - expect(json_response["TempPath"]).to_not be_nil + expect(json_response["TempPath"]).not_to be_nil end end diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb index 3f4a1ced2b6..98ef9d21035 100644 --- a/spec/services/auth/container_registry_authentication_service_spec.rb +++ b/spec/services/auth/container_registry_authentication_service_spec.rb @@ -52,12 +52,12 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do shared_examples 'an unauthorized' do it { is_expected.to include(http_status: 401) } - it { is_expected.to_not include(:token) } + it { is_expected.not_to include(:token) } end shared_examples 'a forbidden' do it { is_expected.to include(http_status: 403) } - it { is_expected.to_not include(:token) } + it { is_expected.not_to include(:token) } end describe '#full_access_token' do diff --git a/spec/services/create_commit_builds_service_spec.rb b/spec/services/create_commit_builds_service_spec.rb index ea5dcfa068a..9ae8f31b372 100644 --- a/spec/services/create_commit_builds_service_spec.rb +++ b/spec/services/create_commit_builds_service_spec.rb @@ -78,7 +78,7 @@ describe CreateCommitBuildsService, services: true do expect(commit).to be_persisted expect(commit.builds.any?).to be false expect(commit.status).to eq('failed') - expect(commit.yaml_errors).to_not be_nil + expect(commit.yaml_errors).not_to be_nil end describe :ci_skip? do diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index eeab540c2fd..18692f1279a 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -158,49 +158,6 @@ describe GitPushService, services: true do end end - describe "Updates main language" do - context "before push" do - it { expect(project.main_language).to eq(nil) } - end - - context "after push" do - def execute - execute_service(project, user, @oldrev, @newrev, ref) - end - - context "to master" do - let(:ref) { @ref } - - context 'when main_language is nil' do - it 'obtains the language from the repository' do - expect(project.repository).to receive(:main_language) - execute - end - - it 'sets the project main language' do - execute - expect(project.main_language).to eq("Ruby") - end - end - - context 'when main_language is already set' do - it 'does not check the repository' do - execute # do an initial run to simulate lang being preset - expect(project.repository).not_to receive(:main_language) - execute - end - end - end - - context "to other branch" do - let(:ref) { 'refs/heads/feature/branch' } - - it { expect(project.main_language).to eq(nil) } - end - end - end - - describe "Updates git attributes" do context "for default branch" do it "calls the copy attributes method for the first push to the default branch" do diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index 6aefb48a4e8..a97ac3ead45 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -14,7 +14,7 @@ describe Groups::CreateService, services: true do context "cannot create group with restricted visibility level" do before { allow(current_application_settings).to receive(:restricted_visibility_levels).and_return([Gitlab::VisibilityLevel::PUBLIC]) } - it { is_expected.to_not be_persisted } + it { is_expected.not_to be_persisted } end end end diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb index 6aa0a89f893..1ee9f3aae4d 100644 --- a/spec/services/issues/create_service_spec.rb +++ b/spec/services/issues/create_service_spec.rb @@ -55,7 +55,7 @@ describe Issues::CreateService, services: true do end it 'does not assign label' do - expect(issue.labels).to_not include label + expect(issue.labels).not_to include label end end @@ -69,7 +69,7 @@ describe Issues::CreateService, services: true do end it 'does not assign milestone' do - expect(issue.milestone).to_not eq milestone + expect(issue.milestone).not_to eq milestone end end end diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index c15e26189a5..95fe6c2400a 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -194,10 +194,10 @@ describe Issues::MoveService, services: true do include_context 'issue move executed' it 'rewrites uploads in description' do - expect(new_issue.description).to_not eq description + expect(new_issue.description).not_to eq description expect(new_issue.description) .to match(/Text and #{FileUploader::MARKDOWN_PATTERN}/) - expect(new_issue.description).to_not include uploader.secret + expect(new_issue.description).not_to include uploader.secret end end end @@ -231,7 +231,7 @@ describe Issues::MoveService, services: true do context 'user is reporter in both projects' do include_context 'user can move issue' - it { expect { move }.to_not raise_error } + it { expect { move }.not_to raise_error } end context 'user is reporter only in new project' do diff --git a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb index 52a302e0e1a..82247849814 100644 --- a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb +++ b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb @@ -75,7 +75,7 @@ describe MergeRequests::MergeWhenBuildSucceedsService do allow(ci_commit).to receive(:success?).and_return(true) allow(old_build).to receive(:sha).and_return('1234abcdef') - expect(MergeWorker).to_not receive(:perform_async) + expect(MergeWorker).not_to receive(:perform_async) service.trigger(old_build) end end @@ -88,7 +88,7 @@ describe MergeRequests::MergeWhenBuildSucceedsService do it "doesn't merge a requests for status on other branch" do allow(project.repository).to receive(:branch_names_contains).with(commit_status.sha).and_return([]) - expect(MergeWorker).to_not receive(:perform_async) + expect(MergeWorker).not_to receive(:perform_async) service.trigger(commit_status) end @@ -122,7 +122,7 @@ describe MergeRequests::MergeWhenBuildSucceedsService do end it "doesn't merge if some stages failed" do - expect(MergeWorker).to_not receive(:perform_async) + expect(MergeWorker).not_to receive(:perform_async) build.success test.drop end diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index d1ee60a0aea..31bb7120d84 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -42,6 +42,33 @@ describe Projects::ForkService, services: true do expect(@to_project.builds_enabled?).to be_truthy end end + + context "when project has restricted visibility level" do + context "and only one visibility level is restricted" do + before do + @from_project.update_attributes(visibility_level: Gitlab::VisibilityLevel::INTERNAL) + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::INTERNAL]) + end + + it "creates fork with highest allowed level" do + forked_project = fork_project(@from_project, @to_user) + + expect(forked_project.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC) + end + end + + context "and all visibility levels are restricted" do + before do + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC, Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PRIVATE]) + end + + it "creates fork with private visibility levels" do + forked_project = fork_project(@from_project, @to_user) + + expect(forked_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) + end + end + end end describe :fork_to_namespace do diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 8aeb013eec6..25da0917134 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -2,6 +2,8 @@ require 'spec_helper' require 'rake' describe 'gitlab:app namespace rake task' do + let(:enable_registry) { true } + before :all do Rake.application.rake_require 'tasks/gitlab/task_helpers' Rake.application.rake_require 'tasks/gitlab/backup' @@ -15,6 +17,10 @@ describe 'gitlab:app namespace rake task' do FileUtils.mkdir_p('public/uploads') end + before do + stub_container_registry_config(enabled: enable_registry) + end + def run_rake_task(task_name) Rake::Task[task_name].reenable Rake.application.invoke_task task_name @@ -143,6 +149,18 @@ describe 'gitlab:app namespace rake task' do expect(temp_dirs).to be_empty end + + context 'registry disabled' do + let(:enable_registry) { false } + + it 'should not create registry.tar.gz' do + tar_contents, exit_status = Gitlab::Popen.popen( + %W{tar -tvf #{@backup_tar}} + ) + expect(exit_status).to eq(0) + expect(tar_contents).not_to match('registry.tar.gz') + end + end end # backup_create task describe "Skipping items" do diff --git a/spec/tasks/gitlab/db_rake_spec.rb b/spec/tasks/gitlab/db_rake_spec.rb new file mode 100644 index 00000000000..36d03a224e4 --- /dev/null +++ b/spec/tasks/gitlab/db_rake_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' +require 'rake' + +describe 'gitlab:db namespace rake task' do + before :all do + Rake.application.rake_require 'active_record/railties/databases' + Rake.application.rake_require 'tasks/seed_fu' + Rake.application.rake_require 'tasks/gitlab/db' + + # empty task as env is already loaded + Rake::Task.define_task :environment + end + + before do + # Stub out db tasks + allow(Rake::Task['db:migrate']).to receive(:invoke).and_return(true) + allow(Rake::Task['db:schema:load']).to receive(:invoke).and_return(true) + allow(Rake::Task['db:seed_fu']).to receive(:invoke).and_return(true) + end + + describe 'configure' do + it 'should invoke db:migrate when schema has already been loaded' do + allow(ActiveRecord::Base.connection).to receive(:tables).and_return(['default']) + expect(Rake::Task['db:migrate']).to receive(:invoke) + expect(Rake::Task['db:schema:load']).not_to receive(:invoke) + expect(Rake::Task['db:seed_fu']).not_to receive(:invoke) + expect { run_rake_task('gitlab:db:configure') }.not_to raise_error + end + + it 'should invoke db:shema:load and db:seed_fu when schema is not loaded' do + allow(ActiveRecord::Base.connection).to receive(:tables).and_return([]) + expect(Rake::Task['db:schema:load']).to receive(:invoke) + expect(Rake::Task['db:seed_fu']).to receive(:invoke) + expect(Rake::Task['db:migrate']).not_to receive(:invoke) + expect { run_rake_task('gitlab:db:configure') }.not_to raise_error + end + + it 'should not invoke any other rake tasks during an error' do + allow(ActiveRecord::Base).to receive(:connection).and_raise(RuntimeError, 'error') + expect(Rake::Task['db:migrate']).not_to receive(:invoke) + expect(Rake::Task['db:schema:load']).not_to receive(:invoke) + expect(Rake::Task['db:seed_fu']).not_to receive(:invoke) + expect { run_rake_task('gitlab:db:configure') }.to raise_error(RuntimeError, 'error') + # unstub connection so that the database cleaner still works + allow(ActiveRecord::Base).to receive(:connection).and_call_original + end + + it 'should not invoke seed after a failed schema_load' do + allow(ActiveRecord::Base.connection).to receive(:tables).and_return([]) + allow(Rake::Task['db:schema:load']).to receive(:invoke).and_raise(RuntimeError, 'error') + expect(Rake::Task['db:schema:load']).to receive(:invoke) + expect(Rake::Task['db:seed_fu']).not_to receive(:invoke) + expect(Rake::Task['db:migrate']).not_to receive(:invoke) + expect { run_rake_task('gitlab:db:configure') }.to raise_error(RuntimeError, 'error') + end + end + + def run_rake_task(task_name) + Rake::Task[task_name].reenable + Rake.application.invoke_task task_name + end +end diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb index 2f465bcf1e3..20d3dfb42b3 100644 --- a/spec/workers/post_receive_spec.rb +++ b/spec/workers/post_receive_spec.rb @@ -61,7 +61,7 @@ describe PostReceive do context "does not create a Ci::Commit" do before { stub_ci_commit_yaml_file(nil) } - it { expect{ subject }.to_not change{ Ci::Commit.count } } + it { expect{ subject }.not_to change{ Ci::Commit.count } } end end end |