diff options
33 files changed, 460 insertions, 219 deletions
diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md index b676916fdf4..ac38f0c9521 100644 --- a/.gitlab/issue_templates/Bug.md +++ b/.gitlab/issue_templates/Bug.md @@ -10,7 +10,7 @@ (What you should see instead) -### Actual behaviour +### Actual behavior (What actually happens) diff --git a/CHANGELOG b/CHANGELOG index a655db6e236..9ce58719999 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.12.0 (unreleased) + - Prepend blank line to `Closes` message on merge request linked to issue (lukehowell) - Filter tags by name !6121 - Make push events have equal vertical spacing. - Add two-factor recovery endpoint to internal API !5510 @@ -15,6 +16,7 @@ v 8.12.0 (unreleased) - Set path for all JavaScript cookies to honor GitLab's subdirectory setting !5627 (Mike Greiling) - Fix bug where pagination is still displayed despite all todos marked as done (ClemMakesApps) - Center build stage columns in pipeline overview (ClemMakesApps) + - Rename behaviour to behavior in bug issue template for consistency (ClemMakesApps) - Shorten task status phrase (ClemMakesApps) - Add hover color to emoji icon (ClemMakesApps) - Fix branches page dropdown sort alignment (ClemMakesApps) @@ -47,6 +49,7 @@ v 8.12.0 (unreleased) - Expire commit info views after one day, instead of two weeks, to allow for user email updates - Add delimiter to project stars and forks count (ClemMakesApps) - Fix badge count alignment (ClemMakesApps) + - Remove green outline from `New branch unavailable` button on issue page !5858 (winniehell) - Fix repo title alignment (ClemMakesApps) - Fix branch title trailing space on hover (ClemMakesApps) - Award emoji tooltips containing more than 10 usernames are now truncated !4780 (jlogandavison) @@ -64,6 +67,7 @@ v 8.12.0 (unreleased) - Replace play icon font with svg (ClemMakesApps) - Added 'only_allow_merge_if_build_succeeds' project setting in the API. !5930 (Duck) - Reduce number of database queries on builds tab + - Wrap text in commit message containers - Capitalize mentioned issue timeline notes (ClemMakesApps) - Fix inconsistent checkbox alignment (ClemMakesApps) - Use the default branch for displaying the project icon instead of master !5792 (Hannes Rosenögger) @@ -71,10 +75,13 @@ v 8.12.0 (unreleased) - Fix hover leading space bug in pipeline graph !5980 - User can edit closed MR with deleted fork (Katarzyna Kobierska Ula Budziszewska) !5496 - Fixed invisible scroll controls on build page on iPhone + - Fix error on raw build trace download for old builds stored in database !4822 v 8.11.5 (unreleased) - Optimize branch lookups and force a repository reload for Repository#find_branch + - Fix member expiration date picker after update - Fix suggested colors options for new labels in the admin area. !6138 + - Fix GitLab import button v 8.11.4 - Fix resolving conflicts on forks. !6082 diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js index 6838d9d8da1..e6422602ce8 100644 --- a/app/assets/javascripts/issue.js +++ b/app/assets/javascripts/issue.js @@ -127,7 +127,7 @@ Issue.prototype.initCanCreateBranch = function() { var $container; - $container = $('div#new-branch'); + $container = $('#new-branch'); if ($container.length === 0) { return; } @@ -139,7 +139,6 @@ if (data.can_create_branch) { $container.find('.checking').hide(); $container.find('.available').show(); - return $container.find('a').attr('disabled', false); } else { $container.find('.checking').hide(); return $container.find('.unavailable').show(); diff --git a/app/assets/javascripts/project_new.js b/app/assets/javascripts/project_new.js index 798f15e40a0..a787b11f2a9 100644 --- a/app/assets/javascripts/project_new.js +++ b/app/assets/javascripts/project_new.js @@ -4,6 +4,8 @@ this.ProjectNew = (function() { function ProjectNew() { this.toggleSettings = bind(this.toggleSettings, this); + this.$selects = $('.features select'); + $('.project-edit-container').on('ajax:before', (function(_this) { return function() { $('.project-edit-container').hide(); @@ -15,18 +17,24 @@ } ProjectNew.prototype.toggleSettings = function() { - this._showOrHide('#project_builds_enabled', '.builds-feature'); - return this._showOrHide('#project_merge_requests_enabled', '.merge-requests-feature'); + var self = this; + + this.$selects.each(function () { + var $select = $(this), + className = $select.data('field').replace(/_/g, '-') + .replace('access-level', 'feature'); + self._showOrHide($select, '.' + className); + }); }; ProjectNew.prototype.toggleSettingsOnclick = function() { - return $('#project_builds_enabled, #project_merge_requests_enabled').on('click', this.toggleSettings); + this.$selects.on('change', this.toggleSettings); }; ProjectNew.prototype._showOrHide = function(checkElement, container) { - var $container; - $container = $(container); - if ($(checkElement).prop('checked')) { + var $container = $(container); + + if ($(checkElement).val() !== '0') { return $container.show(); } else { return $container.hide(); diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 8984bce616c..5957dce89bc 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -53,7 +53,7 @@ pre { &.well-pre { border: 1px solid #eee; - background: #f9f9f9; + background: $gray-light; border-radius: 0; color: #555; } @@ -225,7 +225,7 @@ li.note { .milestone { &.milestone-closed { - background: #f9f9f9; + background: $gray-light; } .progress { margin-bottom: 0; diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index d3e3fc50736..e3be45ba1dc 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -115,7 +115,7 @@ padding: 0; } td.blame-commit { - background: #f9f9f9; + background: $gray-light; min-width: 350px; .commit-author-link { diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index 62dc2cb3fdc..00f92cef9a4 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -44,7 +44,7 @@ } &.active { - background: #f9f9f9; + background: $gray-light; a { font-weight: 600; } diff --git a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss index 371c1bf17e1..915aa631ef8 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss @@ -125,7 +125,7 @@ $panel-inner-border: $border-color; // //## -$well-bg: #f9f9f9; +$well-bg: $gray-light; $well-border: #eee; //== Code diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 5da390118c6..e4a3ca3a9ea 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -10,12 +10,78 @@ $sidebar-transition-duration: .15s; $sidebar-breakpoint: 1024px; /* + * Color schema + */ +$white-light: #fff; +$white-normal: #ededed; +$white-dark: #ececec; + +$gray-light: #fafafa; +$gray-normal: #f5f5f5; +$gray-dark: #ededed; +$gray-darkest: #c9c9c9; + +$green-light: #38ae67; +$green-normal: #2faa60; +$green-dark: #2ca05b; + +$blue-light: #2ea8e5; +$blue-normal: #2d9fd8; +$blue-dark: #2897ce; + +$blue-medium-light: #3498cb; +$blue-medium: #2f8ebf; +$blue-medium-dark: #2d86b4; + +$orange-light: #fc8a51; +$orange-normal: #e75e40; +$orange-dark: #ce5237; + +$red-light: #e52c5a; +$red-normal: #d22852; +$red-dark: darken($red-normal, 5%); + +$black: #000; +$black-transparent: rgba(0, 0, 0, 0.3); + +$border-white-light: #f1f2f4; +$border-white-normal: #d6dae2; +$border-white-dark: #c6cacf; + +$border-gray-light: #dcdcdc; +$border-gray-normal: #d7d7d7; +$border-gray-dark: #c6cacf; + +$border-green-light: #2faa60; +$border-green-normal: #2ca05b; +$border-green-dark: #279654; + +$border-blue-light: #2d9fd8; +$border-blue-normal: #2897ce; +$border-blue-dark: #258dc1; + +$border-orange-light: #fc6d26; +$border-orange-normal: #ce5237; +$border-orange-dark: #c14e35; + +$border-red-light: #d22852; +$border-red-normal: #ca264f; +$border-red-dark: darken($border-red-normal, 5%); + +$help-well-bg: $gray-light; +$help-well-border: #e5e5e5; + +$warning-message-bg: #fbf2d9; +$warning-message-color: #9e8e60; +$warning-message-border: #f0e2bb; + +/* * UI elements */ $border-color: #e5e5e5; $focus-border-color: #3aabf0; $table-border-color: #f0f0f0; -$background-color: #fafafa; +$background-color: $gray-light; $dark-background-color: #f5f5f5; $table-text-gray: #8f8f8f; @@ -90,73 +156,6 @@ $btn-side-margin: 10px; $btn-sm-side-margin: 7px; $btn-xs-side-margin: 5px; -/* - * Color schema - */ - -$white-light: #fff; -$white-normal: #ededed; -$white-dark: #ececec; - -$gray-light: #faf9f9; -$gray-normal: #f5f5f5; -$gray-dark: #ededed; -$gray-darkest: #c9c9c9; - -$green-light: #38ae67; -$green-normal: #2faa60; -$green-dark: #2ca05b; - -$blue-light: #2ea8e5; -$blue-normal: #2d9fd8; -$blue-dark: #2897ce; - -$blue-medium-light: #3498cb; -$blue-medium: #2f8ebf; -$blue-medium-dark: #2d86b4; - -$orange-light: #fc8a51; -$orange-normal: #e75e40; -$orange-dark: #ce5237; - -$red-light: #e52c5a; -$red-normal: #d22852; -$red-dark: darken($red-normal, 5%); - -$black: #000; -$black-transparent: rgba(0, 0, 0, 0.3); - -$border-white-light: #f1f2f4; -$border-white-normal: #d6dae2; -$border-white-dark: #c6cacf; - -$border-gray-light: #dcdcdc; -$border-gray-normal: #d7d7d7; -$border-gray-dark: #c6cacf; - -$border-green-light: #2faa60; -$border-green-normal: #2ca05b; -$border-green-dark: #279654; - -$border-blue-light: #2d9fd8; -$border-blue-normal: #2897ce; -$border-blue-dark: #258dc1; - -$border-orange-light: #fc6d26; -$border-orange-normal: #ce5237; -$border-orange-dark: #c14e35; - -$border-red-light: #d22852; -$border-red-normal: #ca264f; -$border-red-dark: darken($border-red-normal, 5%); - -$help-well-bg: #fafafa; -$help-well-border: #e5e5e5; - -$warning-message-bg: #fbf2d9; -$warning-message-color: #9e8e60; -$warning-message-border: #f0e2bb; - /* tanuki logo colors */ $tanuki-red: #e24329; $tanuki-orange: #fc6d26; @@ -186,7 +185,7 @@ $line-removed-dark: #fac5cd; $line-number-old: #f9d7dc; $line-number-new: #ddfbe6; $line-number-select: #fbf2da; -$match-line: #fafafa; +$match-line: $gray-light; $table-border-gray: #f0f0f0; $line-target-blue: #eaf3fc; $line-select-yellow: #fcf8e7; @@ -267,7 +266,7 @@ $zen-control-hover-color: #111; $calendar-header-color: #b8b8b8; $calendar-hover-bg: #ecf3fe; $calendar-border-color: rgba(#000, .1); -$calendar-unselectable-bg: #faf9f9; +$calendar-unselectable-bg: $gray-light; /* * Personal Access Tokens diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index cee198691c2..23255f34710 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -108,7 +108,7 @@ } .blocks-container { - padding: $gl-padding; + padding: 0 $gl-padding; } .block { @@ -123,6 +123,13 @@ } } + .retry-link { + color: $gl-link-color; + &:hover { + text-decoration: underline; + } + } + .stage-item { cursor: pointer; @@ -132,7 +139,7 @@ } .build-dropdown { - padding: 0 $gl-padding; + padding: $gl-padding 0; .dropdown-menu-toggle { margin-top: 8px; @@ -146,7 +153,6 @@ } .builds-container { - margin-top: $gl-padding; background-color: $white-light; border-top: 1px solid $border-color; border-bottom: 1px solid $border-color; diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 6a58b445afa..b369f5c0805 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -113,11 +113,13 @@ .commit-row-description { font-size: 14px; - border-left: 1px solid #eee; + border-left: 1px solid $btn-gray-hover; padding: 10px 15px; margin: 10px 0; - background: #f9f9f9; + background: $gray-light; display: none; + white-space: pre-line; + word-break: normal; pre { border: none; diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index 0cd45fb90bf..1d00da1266c 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -60,7 +60,7 @@ pre { border: none; - background: #f9f9f9; + background: $gray-light; border-radius: 0; color: #777; margin: 0 20px; @@ -92,7 +92,7 @@ border: 1px solid #eee; padding: 5px; @include border-radius(5px); - background: #f9f9f9; + background: $gray-light; margin-left: 10px; top: -6px; img { diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 3a3bb10feac..910700b0206 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -72,12 +72,12 @@ form.edit-issue { } &.closed { - background: #f9f9f9; + background: $gray-light; border-color: #e5e5e5; } &.merged { - background: #f9f9f9; + background: $gray-light; border-color: #e5e5e5; } } diff --git a/app/assets/stylesheets/pages/merge_conflicts.scss b/app/assets/stylesheets/pages/merge_conflicts.scss index 1f499897c16..5ec660799e3 100644 --- a/app/assets/stylesheets/pages/merge_conflicts.scss +++ b/app/assets/stylesheets/pages/merge_conflicts.scss @@ -16,7 +16,7 @@ $colors: ( white_button_origin_chosen : #268ced, white_header_not_chosen : #f0f0f0, - white_line_not_chosen : #f9f9f9, + white_line_not_chosen : $gray-light, dark_header_head_neutral : rgba(#3f3, .2), diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss index 0340526a53a..68a5d1ae06c 100644 --- a/app/assets/stylesheets/pages/todos.scss +++ b/app/assets/stylesheets/pages/todos.scss @@ -99,7 +99,7 @@ pre { border: none; - background: #f9f9f9; + background: $gray-light; border-radius: 0; color: #777; margin: 0 20px; diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb index 12195c3cbb8..77934ff9962 100644 --- a/app/controllers/projects/builds_controller.rb +++ b/app/controllers/projects/builds_controller.rb @@ -78,8 +78,8 @@ class Projects::BuildsController < Projects::ApplicationController end def raw - if @build.has_trace? - send_file @build.path_to_trace, type: 'text/plain; charset=utf-8', disposition: 'inline' + if @build.has_trace_file? + send_file @build.trace_file_path, type: 'text/plain; charset=utf-8', disposition: 'inline' else render_404 end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index d6efa603223..4c685b97c03 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -431,6 +431,6 @@ module ProjectsHelper options.delete('Everyone with access') if @project.private? && level != ProjectFeature::ENABLED options = options_for_select(options, selected: @project.project_feature.public_send(field) || ProjectFeature::ENABLED) - content_tag(:select, options, name: "project[project_feature_attributes][#{field.to_s}]", id: "project_project_feature_attributes_#{field.to_s}", class: "pull-right form-control").html_safe + content_tag(:select, options, name: "project[project_feature_attributes][#{field.to_s}]", id: "project_project_feature_attributes_#{field.to_s}", class: "pull-right form-control", data: { field: field }).html_safe end end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 23c8de6f650..61052437318 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -208,22 +208,31 @@ module Ci end end + def has_trace_file? + File.exist?(path_to_trace) || has_old_trace_file? + end + def has_trace? raw_trace.present? end def raw_trace - if File.file?(path_to_trace) - File.read(path_to_trace) - elsif project.ci_id && File.file?(old_path_to_trace) - # Temporary fix for build trace data integrity - File.read(old_path_to_trace) + if File.exist?(trace_file_path) + File.read(trace_file_path) else # backward compatibility read_attribute :trace end end + ## + # Deprecated + # + # This is a hotfix for CI build data integrity, see #4246 + def has_old_trace_file? + project.ci_id && File.exist?(old_path_to_trace) + end + def trace trace = raw_trace if project && trace.present? && project.runners_token.present? @@ -262,6 +271,14 @@ module Ci end end + def trace_file_path + if has_old_trace_file? + old_path_to_trace + else + path_to_trace + end + end + def dir_to_trace File.join( Settings.gitlab_ci.builds_path, diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb index 290742f1506..e57791f6818 100644 --- a/app/services/merge_requests/build_service.rb +++ b/app/services/merge_requests/build_service.rb @@ -83,7 +83,7 @@ module MergeRequests closes_issue = "Closes ##{iid}" if merge_request.description.present? - merge_request.description += closes_issue.prepend("\n") + merge_request.description += closes_issue.prepend("\n\n") else merge_request.description = closes_issue end diff --git a/app/views/groups/group_members/update.js.haml b/app/views/groups/group_members/update.js.haml index 742f9d7a433..3be7ed8432c 100644 --- a/app/views/groups/group_members/update.js.haml +++ b/app/views/groups/group_members/update.js.haml @@ -1,3 +1,3 @@ :plain $("##{dom_id(@group_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @group_member))}'); - new MemberExpirationDate(); + new gl.MemberExpirationDate(); diff --git a/app/views/projects/_merge_request_settings.html.haml b/app/views/projects/_merge_request_settings.html.haml index 19b4249374b..14eb47089b1 100644 --- a/app/views/projects/_merge_request_settings.html.haml +++ b/app/views/projects/_merge_request_settings.html.haml @@ -1,11 +1,14 @@ -%fieldset.builds-feature - %h5.prepend-top-0 - Merge Requests - .form-group - .checkbox - = f.label :only_allow_merge_if_build_succeeds do - = f.check_box :only_allow_merge_if_build_succeeds - %strong Only allow merge requests to be merged if the build succeeds - .help-block - Builds need to be configured to enable this feature. - = link_to icon('question-circle'), help_page_path('workflow/merge_requests', anchor: 'only-allow-merge-requests-to-be-merged-if-the-build-succeeds') +.merge-requests-feature + %fieldset.builds-feature + %hr + %h5.prepend-top-0 + Merge Requests + .form-group + .checkbox + = f.label :only_allow_merge_if_build_succeeds do + = f.check_box :only_allow_merge_if_build_succeeds + %strong Only allow merge requests to be merged if the build succeeds + %br + %span.descr + Builds need to be configured to enable this feature. + = link_to icon('question-circle'), help_page_path('workflow/merge_requests', anchor: 'only-allow-merge-requests-to-be-merged-if-the-build-succeeds') diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml index 5b0b58e087b..5ce36a475a9 100644 --- a/app/views/projects/builds/_sidebar.html.haml +++ b/app/views/projects/builds/_sidebar.html.haml @@ -1,3 +1,6 @@ +- builds = @build.pipeline.builds.latest.to_a +- statuses = ["failed", "pending", "running", "canceled", "success", "skipped"] + %aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar .block.build-sidebar-header.visible-xs-block.visible-sm-block.append-bottom-default Build @@ -11,40 +14,6 @@ %p.build-detail-row #{@build.coverage}% - - builds = @build.pipeline.builds.latest.to_a - - statuses = ["failed", "pending", "running", "canceled", "success", "skipped"] - - if builds.size > 1 - .dropdown.build-dropdown - .build-light-text Stage - %button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'} - %span.stage-selection More - = icon('caret-down') - %ul.dropdown-menu - - builds.map(&:stage).uniq.each do |stage| - %li - %a.stage-item= stage - - .builds-container - - statuses.each do |build_status| - - builds.select{|build| build.status == build_status}.each do |build| - .build-job{class: ('active' if build == @build), data: {stage: build.stage}} - = link_to namespace_project_build_path(@project.namespace, @project, build) do - = icon('check') - = ci_icon_for_status(build.status) - %span - - if build.name - = build.name - - else - = build.id - - - if @build.retried? - %li.active - %a - Build ##{@build.id} - · - %i.fa.fa-warning - This build was retried. - .blocks-container - if can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?) .block{ class: ("block-first" if !@build.coverage) } @@ -76,7 +45,7 @@ .title Build details - if can?(current_user, :update_build, @build) && @build.retryable? - = link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right', method: :post + = link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right retry-link', method: :post - if @build.merge_request %p.build-detail-row %span.build-light-text Merge Request: @@ -100,7 +69,7 @@ - elsif @build.runner \##{@build.runner.id} .btn-group.btn-group-justified{ role: :group } - - if @build.has_trace? + - if @build.has_trace_file? = link_to 'Raw', raw_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default' - if @build.active? = link_to "Cancel", cancel_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default', method: :post @@ -141,3 +110,35 @@ - @build.tag_list.each do |tag| %span.label.label-primary = tag + + - if builds.size > 1 + .dropdown.build-dropdown + .title Stage + %button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'} + %span.stage-selection More + = icon('caret-down') + %ul.dropdown-menu + - builds.map(&:stage).uniq.each do |stage| + %li + %a.stage-item= stage + + .builds-container + - statuses.each do |build_status| + - builds.select{|build| build.status == build_status}.each do |build| + .build-job{class: ('active' if build == @build), data: {stage: build.stage}} + = link_to namespace_project_build_path(@project.namespace, @project, build) do + = icon('check') + = ci_icon_for_status(build.status) + %span + - if build.name + = build.name + - else + = build.id + + - if @build.retried? + %li.active + %a + Build ##{@build.id} + · + %i.fa.fa-warning + This build was retried. diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml index 24749699c6d..33556a1a2b3 100644 --- a/app/views/projects/issues/_new_branch.html.haml +++ b/app/views/projects/issues/_new_branch.html.haml @@ -1,13 +1,12 @@ - if can?(current_user, :push_code, @project) .pull-right #new-branch{'data-path' => can_create_branch_namespace_project_issue_path(@project.namespace, @project, @issue)} + = link_to '#', class: 'checking btn btn-grouped', disabled: 'disabled' do + = icon('spinner spin') + Checking branches = link_to namespace_project_branches_path(@project.namespace, @project, branch_name: @issue.to_branch_name, issue_iid: @issue.iid), - method: :post, class: 'btn btn-new btn-inverted has-tooltip', title: @issue.to_branch_name, disabled: 'disabled' do - .checking - = icon('spinner spin') - Checking branches - .available.hide - New branch - .unavailable.hide - = icon('exclamation-triangle') - New branch unavailable + method: :post, class: 'btn btn-new btn-inverted btn-grouped has-tooltip available hide', title: @issue.to_branch_name do + New branch + = link_to '#', class: 'unavailable btn btn-grouped hide', disabled: 'disabled' do + = icon('exclamation-triangle') + New branch unavailable diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 0a1e2bb2cc6..fda0592dd41 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -55,7 +55,7 @@ = render 'bitbucket_import_modal' %div - if gitlab_import_enabled? - = link_to status_import_gitlab_path, class: "btn import_gitlab #{'how_to_import_link' unless bitbucket_import_configured?}" do + = link_to status_import_gitlab_path, class: "btn import_gitlab #{'how_to_import_link' unless gitlab_import_configured?}" do = icon('gitlab', text: 'GitLab.com') - unless gitlab_import_configured? = render 'gitlab_import_modal' diff --git a/app/views/projects/project_members/update.js.haml b/app/views/projects/project_members/update.js.haml index 833954bc039..37e55dc72a3 100644 --- a/app/views/projects/project_members/update.js.haml +++ b/app/views/projects/project_members/update.js.haml @@ -1,3 +1,3 @@ :plain $("##{dom_id(@project_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @project_member))}'); - new MemberExpirationDate(); + new gl.MemberExpirationDate(); diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md index 37bb59e112c..39b801f761d 100644 --- a/doc/development/doc_styleguide.md +++ b/doc/development/doc_styleguide.md @@ -155,15 +155,30 @@ Inside the document: - Every piece of documentation that comes with a new feature should declare the GitLab version that feature got introduced. Right below the heading add a - note: `> Introduced in GitLab 8.3.`. + note: + + ``` + > Introduced in GitLab 8.3. + ``` + - If possible every feature should have a link to the MR that introduced it. The above note would be then transformed to: - `> [Introduced][ce-1242] in GitLab 8.3.`, where - the [link identifier](#links) is named after the repository (CE) and the MR - number. -- If the feature is only in GitLab EE, don't forget to mention it, like: - `> Introduced in GitLab EE 8.3.`. Otherwise, leave - this mention out. + + ``` + > [Introduced][ce-1242] in GitLab 8.3. + ``` + + , where the [link identifier](#links) is named after the repository (CE) and + the MR number. + +- If the feature is only in GitLab Enterprise Edition, don't forget to mention + it, like: + + ``` + > Introduced in GitLab Enterprise Edition 8.3. + ``` + + Otherwise, leave this mention out. ## References diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 571f1a38358..9799e0a3730 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -63,24 +63,24 @@ If you have enough RAM memory and a recent CPU the speed of GitLab is mainly lim ### Memory -You need at least 2GB of addressable memory (RAM + swap) to install and use GitLab! +You need at least 4GB of addressable memory (RAM + swap) to install and use GitLab! The operating system and any other running applications will also be using memory -so keep in mind that you need at least 2GB available before running GitLab. With +so keep in mind that you need at least 4GB available before running GitLab. With less memory GitLab will give strange errors during the reconfigure run and 500 errors during usage. -- 512MB RAM + 1.5GB of swap is the absolute minimum but we strongly **advise against** this amount of memory. See the unicorn worker section below for more advice. -- 1GB RAM + 1GB swap supports up to 100 users but it will be very slow -- **2GB RAM** is the **recommended** memory size for all installations and supports up to 100 users -- 4GB RAM supports up to 1,000 users -- 8GB RAM supports up to 2,000 users -- 16GB RAM supports up to 4,000 users -- 32GB RAM supports up to 8,000 users -- 64GB RAM supports up to 16,000 users -- 128GB RAM supports up to 32,000 users +- 1GB RAM + 3GB of swap is the absolute minimum but we strongly **advise against** this amount of memory. See the unicorn worker section below for more advice. +- 2GB RAM + 2GB swap supports up to 100 users but it will be very slow +- **4GB RAM** is the **recommended** memory size for all installations and supports up to 100 users +- 8GB RAM supports up to 1,000 users +- 16GB RAM supports up to 2,000 users +- 32GB RAM supports up to 4,000 users +- 64GB RAM supports up to 8,000 users +- 128GB RAM supports up to 16,000 users +- 256GB RAM supports up to 32,000 users - More users? Run it on [multiple application servers](https://about.gitlab.com/high-availability/) -We recommend having at least 1GB of swap on your server, even if you currently have +We recommend having at least 2GB of swap on your server, even if you currently have enough available RAM. Having swap will help reduce the chance of errors occurring if your available memory changes. @@ -113,10 +113,8 @@ It's possible to increase the amount of unicorn workers and this will usually he For most instances we recommend using: CPU cores + 1 = unicorn workers. So for a machine with 2 cores, 3 unicorn workers is ideal. -For all machines that have 1GB and up we recommend a minimum of three unicorn workers. -If you have a 512MB machine with a magnetic (non-SSD) swap drive we recommend to configure only one Unicorn worker to prevent excessive swapping. -With one Unicorn worker only git over ssh access will work because the git over HTTP access requires two running workers (one worker to receive the user request and one worker for the authorization check). -If you have a 512MB machine with a SSD drive you can use two Unicorn workers, this will allow HTTP access although it will be slow due to swapping. +For all machines that have 2GB and up we recommend a minimum of three unicorn workers. +If you have a 1GB machine we recommend to configure only two Unicorn workers to prevent excessive swapping. To change the Unicorn workers when you have the Omnibus package please see [the Unicorn settings in the Omnibus GitLab documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/unicorn.md#unicorn-settings). diff --git a/lib/api/internal.rb b/lib/api/internal.rb index 5b54c11ef62..6e6efece7c4 100644 --- a/lib/api/internal.rb +++ b/lib/api/internal.rb @@ -105,15 +105,19 @@ module API post '/two_factor_recovery_codes' do status 200 - key = Key.find(params[:key_id]) - user = key.user + key = Key.find_by(id: params[:key_id]) + + unless key + return { 'success' => false, 'message' => 'Could not find the given key' } + end - # Make sure this isn't a deploy key - unless key.type.nil? + if key.is_a?(DeployKey) return { success: false, message: 'Deploy keys cannot be used to retrieve recovery codes' } end - unless user.present? + user = key.user + + unless user return { success: false, message: 'Could not find a user for the given key' } end diff --git a/spec/features/projects/builds_spec.rb b/spec/features/projects/builds_spec.rb index 0cfeb2e57d8..d5d571e49bc 100644 --- a/spec/features/projects/builds_spec.rb +++ b/spec/features/projects/builds_spec.rb @@ -1,4 +1,5 @@ require 'spec_helper' +require 'tempfile' describe "Builds" do let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } @@ -6,7 +7,7 @@ describe "Builds" do before do login_as(:user) @commit = FactoryGirl.create :ci_pipeline - @build = FactoryGirl.create :ci_build, pipeline: @commit + @build = FactoryGirl.create :ci_build, :trace, pipeline: @commit @build2 = FactoryGirl.create :ci_build @project = @commit.project @project.team << [@user, :developer] @@ -156,7 +157,6 @@ describe "Builds" do context 'Build raw trace' do before do @build.run! - @build.trace = 'BUILD TRACE' visit namespace_project_build_path(@project.namespace, @project, @build) end @@ -255,35 +255,101 @@ describe "Builds" do end end - describe "GET /:project/builds/:id/raw" do - context "Build from project" do - before do - Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') - @build.run! - @build.trace = 'BUILD TRACE' - visit namespace_project_build_path(@project.namespace, @project, @build) - page.within('.js-build-sidebar') { click_link 'Raw' } + describe 'GET /:project/builds/:id/raw' do + context 'access source' do + context 'build from project' do + before do + Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') + @build.run! + visit namespace_project_build_path(@project.namespace, @project, @build) + page.within('.js-build-sidebar') { click_link 'Raw' } + end + + it 'sends the right headers' do + expect(page.status_code).to eq(200) + expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8') + expect(page.response_headers['X-Sendfile']).to eq(@build.path_to_trace) + end end - it 'sends the right headers' do - expect(page.status_code).to eq(200) - expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8') - expect(page.response_headers['X-Sendfile']).to eq(@build.path_to_trace) + context 'build from other project' do + before do + Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') + @build2.run! + visit raw_namespace_project_build_path(@project.namespace, @project, @build2) + end + + it 'sends the right headers' do + expect(page.status_code).to eq(404) + end end end - context "Build from other project" do - before do - Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') - @build2.run! - @build2.trace = 'BUILD TRACE' - visit raw_namespace_project_build_path(@project.namespace, @project, @build2) - puts page.status_code - puts current_url + context 'storage form' do + let(:existing_file) { Tempfile.new('existing-trace-file').path } + let(:non_existing_file) do + file = Tempfile.new('non-existing-trace-file') + path = file.path + file.unlink + path end - it 'sends the right headers' do - expect(page.status_code).to eq(404) + context 'when build has trace in file' do + before do + Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') + @build.run! + visit namespace_project_build_path(@project.namespace, @project, @build) + + allow_any_instance_of(Project).to receive(:ci_id).and_return(nil) + allow_any_instance_of(Ci::Build).to receive(:path_to_trace).and_return(existing_file) + allow_any_instance_of(Ci::Build).to receive(:old_path_to_trace).and_return(non_existing_file) + + page.within('.js-build-sidebar') { click_link 'Raw' } + end + + it 'sends the right headers' do + expect(page.status_code).to eq(200) + expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8') + expect(page.response_headers['X-Sendfile']).to eq(existing_file) + end + end + + context 'when build has trace in old file' do + before do + Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') + @build.run! + visit namespace_project_build_path(@project.namespace, @project, @build) + + allow_any_instance_of(Project).to receive(:ci_id).and_return(999) + allow_any_instance_of(Ci::Build).to receive(:path_to_trace).and_return(non_existing_file) + allow_any_instance_of(Ci::Build).to receive(:old_path_to_trace).and_return(existing_file) + + page.within('.js-build-sidebar') { click_link 'Raw' } + end + + it 'sends the right headers' do + expect(page.status_code).to eq(200) + expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8') + expect(page.response_headers['X-Sendfile']).to eq(existing_file) + end + end + + context 'when build has trace in DB' do + before do + Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') + @build.run! + visit namespace_project_build_path(@project.namespace, @project, @build) + + allow_any_instance_of(Project).to receive(:ci_id).and_return(nil) + allow_any_instance_of(Ci::Build).to receive(:path_to_trace).and_return(non_existing_file) + allow_any_instance_of(Ci::Build).to receive(:old_path_to_trace).and_return(non_existing_file) + + page.within('.js-build-sidebar') { click_link 'Raw' } + end + + it 'sends the right headers' do + expect(page.status_code).to eq(404) + end end end end diff --git a/spec/features/projects/edit_spec.rb b/spec/features/projects/edit_spec.rb new file mode 100644 index 00000000000..a1643fd1f43 --- /dev/null +++ b/spec/features/projects/edit_spec.rb @@ -0,0 +1,57 @@ +require 'rails_helper' + +feature 'Project edit', feature: true, js: true do + include WaitForAjax + + let(:user) { create(:user) } + let(:project) { create(:project) } + + before do + project.team << [user, :master] + login_as(user) + + visit edit_namespace_project_path(project.namespace, project) + end + + context 'feature visibility' do + context 'merge requests select' do + it 'hides merge requests section' do + select('Disabled', from: 'project_project_feature_attributes_merge_requests_access_level') + + expect(page).to have_selector('.merge-requests-feature', visible: false) + end + + it 'hides merge requests section after save' do + select('Disabled', from: 'project_project_feature_attributes_merge_requests_access_level') + + expect(page).to have_selector('.merge-requests-feature', visible: false) + + click_button 'Save changes' + + wait_for_ajax + + expect(page).to have_selector('.merge-requests-feature', visible: false) + end + end + + context 'builds select' do + it 'hides merge requests section' do + select('Disabled', from: 'project_project_feature_attributes_builds_access_level') + + expect(page).to have_selector('.builds-feature', visible: false) + end + + it 'hides merge requests section after save' do + select('Disabled', from: 'project_project_feature_attributes_builds_access_level') + + expect(page).to have_selector('.builds-feature', visible: false) + + click_button 'Save changes' + + wait_for_ajax + + expect(page).to have_selector('.builds-feature', visible: false) + end + end + end +end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 36d10636ae9..bce18b4e99e 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -19,4 +19,64 @@ describe Ci::Build, models: true do expect(build.trace).to eq(test_trace) end end + + describe '#has_trace_file?' do + context 'when there is no trace' do + it { expect(build.has_trace_file?).to be_falsey } + it { expect(build.trace).to be_nil } + end + + context 'when there is a trace' do + context 'when trace is stored in file' do + let(:build_with_trace) { create(:ci_build, :trace) } + + it { expect(build_with_trace.has_trace_file?).to be_truthy } + it { expect(build_with_trace.trace).to eq('BUILD TRACE') } + end + + context 'when trace is stored in old file' do + before do + allow(build.project).to receive(:ci_id).and_return(999) + allow(File).to receive(:exist?).with(build.path_to_trace).and_return(false) + allow(File).to receive(:exist?).with(build.old_path_to_trace).and_return(true) + allow(File).to receive(:read).with(build.old_path_to_trace).and_return(test_trace) + end + + it { expect(build.has_trace_file?).to be_truthy } + it { expect(build.trace).to eq(test_trace) } + end + + context 'when trace is stored in DB' do + before do + allow(build.project).to receive(:ci_id).and_return(nil) + allow(build).to receive(:read_attribute).with(:trace).and_return(test_trace) + allow(File).to receive(:exist?).with(build.path_to_trace).and_return(false) + allow(File).to receive(:exist?).with(build.old_path_to_trace).and_return(false) + end + + it { expect(build.has_trace_file?).to be_falsey } + it { expect(build.trace).to eq(test_trace) } + end + end + end + + describe '#trace_file_path' do + context 'when trace is stored in file' do + before do + allow(build).to receive(:has_trace_file?).and_return(true) + allow(build).to receive(:has_old_trace_file?).and_return(false) + end + + it { expect(build.trace_file_path).to eq(build.path_to_trace) } + end + + context 'when trace is stored in old file' do + before do + allow(build).to receive(:has_trace_file?).and_return(true) + allow(build).to receive(:has_old_trace_file?).and_return(true) + end + + it { expect(build.trace_file_path).to eq(build.old_path_to_trace) } + end + end end diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb index 5d06abcfeb3..46d1b868782 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal_spec.rb @@ -44,8 +44,8 @@ describe API::API, api: true do secret_token: secret_token, key_id: 12345 - expect(response).to have_http_status(404) - expect(json_response['message']).to eq('404 Not found') + expect(json_response['success']).to be_falsey + expect(json_response['message']).to eq('Could not find the given key') end it 'returns an error message when the key is a deploy key' do diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb index 232508cda23..0d586e2216b 100644 --- a/spec/services/merge_requests/build_service_spec.rb +++ b/spec/services/merge_requests/build_service_spec.rb @@ -99,14 +99,14 @@ describe MergeRequests::BuildService, services: true do let(:source_branch) { "#{issue.iid}-fix-issue" } it 'appends "Closes #$issue-iid" to the description' do - expect(merge_request.description).to eq("#{commit_1.safe_message.split(/\n+/, 2).last}\nCloses ##{issue.iid}") + expect(merge_request.description).to eq("#{commit_1.safe_message.split(/\n+/, 2).last}\n\nCloses ##{issue.iid}") end context 'merge request already has a description set' do let(:description) { 'Merge request description' } it 'appends "Closes #$issue-iid" to the description' do - expect(merge_request.description).to eq("#{description}\nCloses ##{issue.iid}") + expect(merge_request.description).to eq("#{description}\n\nCloses ##{issue.iid}") end end |