diff options
53 files changed, 470 insertions, 132 deletions
diff --git a/CHANGELOG b/CHANGELOG index a5d26db7626..1abc4afdddd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,9 +1,11 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.11.0 (unreleased) - Fix of 'Commits being passed to custom hooks are already reachable when using the UI' + - Limit git rev-list output count to one in forced push check v 8.10.0 (unreleased) - Fix profile activity heatmap to show correct day name (eanplatter) + - Speed up ExternalWikiHelper#get_project_wiki_path - Expose {should,force}_remove_source_branch (Ben Boeckel) - Add the functionality to be able to rename a file. !5049 (tiagonbotelho) - Disable PostgreSQL statement timeout during migrations @@ -34,6 +36,7 @@ v 8.10.0 (unreleased) - Added day name to contribution calendar tooltips - Make images fit to the size of the viewport !4810 - Fix check for New Branch button on Issue page !4630 (winniehell) + - Fix GFM autocomplete not working on wiki pages - Fix MR-auto-close text added to description. !4836 - Support U2F devices in Firefox. !5177 - Fix issue, preventing users w/o push access to sort tags !5105 (redetection) @@ -133,6 +136,7 @@ v 8.10.0 (unreleased) - Allow bulk (un)subscription from issues in issue index - Fix MR diff encoding issues exporting GitLab projects - Export and import avatar as part of project import/export + - Fix migration corrupting import data for old version upgrades v 8.9.6 - Fix importing of events under notes for GitLab projects. !5154 diff --git a/Gemfile.lock b/Gemfile.lock index 4bb7a8d0604..363904a4baa 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -578,7 +578,7 @@ GEM railties (>= 4.2.0, < 5.1) rinku (2.0.0) rotp (2.1.2) - rouge (2.0.3) + rouge (2.0.5) rqrcode (0.7.0) chunky_png rqrcode-rails3 (0.1.7) diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 590b8f54363..f87b8a2ad1c 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -49,6 +49,17 @@ border-color: $border-dark; color: $color; } + + svg { + + path { + fill: $color; + } + + use { + stroke: $color; + } + } } @mixin btn-green { @@ -173,6 +184,13 @@ .caret { margin-left: 5px; } + + svg { + height: 15px; + width: auto; + position: relative; + top: 2px; + } } .btn-lg { diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index d52e8f00172..3fa4a22258d 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -198,6 +198,10 @@ header.header-pinned-nav { .sidebar-collapsed-icon { cursor: pointer; + + .btn { + background-color: $gray-light; + } } } diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 542fa244689..ded437625ee 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -122,7 +122,8 @@ button { float: right; - padding: 3px 5px; + padding: 1px 5px; + background-color: $gray-light; } } diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 9807c5a808d..ee3b2d2b801 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -78,6 +78,14 @@ form.edit-issue { } } +.merge-request-ci-status { + svg { + margin-right: 4px; + position: relative; + top: 1px; + } +} + @media (max-width: $screen-xs-max) { .issue-btn-group { width: 100%; diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index fbff0c97355..5254faf723d 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -60,8 +60,10 @@ .ci_widget { border-bottom: 1px solid #eef0f2; - i { + svg { margin-right: 4px; + position: relative; + top: 1px; } &.ci-success { @@ -196,6 +198,16 @@ .merge-request-title { margin-bottom: 2px; + + .ci-status-link { + + svg { + height: 16px; + width: 16px; + position: relative; + top: 3px; + } + } } } diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index a3b72ec9574..a404f108dc4 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -49,6 +49,14 @@ .commit-link { + .ci-status { + + svg { + top: 1px; + margin-right: 0; + } + } + a:hover { text-decoration: none; } @@ -124,6 +132,15 @@ } } + .stage-cell { + + svg { + height: 18px; + width: 18px; + vertical-align: middle; + } + } + .duration, .finished-at { color: $table-text-gray; diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index ea9f7cf0540..63853335fb6 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -129,6 +129,17 @@ color: $layout-link-gray; } + svg { + + path { + fill: $layout-link-gray; + } + + use { + stroke: $layout-link-gray; + } + } + .fa-caret-down { margin-left: 3px; } @@ -486,6 +497,11 @@ pre.light-well { > span { margin-left: 10px; } + + svg { + position: relative; + top: 2px; + } } } diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss index c6b053150be..a22d4b6f6be 100644 --- a/app/assets/stylesheets/pages/status.scss +++ b/app/assets/stylesheets/pages/status.scss @@ -41,6 +41,14 @@ color: $blue-normal; border-color: $blue-normal; } + + svg { + height: 13px; + width: 13px; + position: relative; + top: 1px; + margin: 0 3px; + } } .ci-status-icon-success { diff --git a/app/assets/stylesheets/pages/tags.scss b/app/assets/stylesheets/pages/tags.scss new file mode 100644 index 00000000000..24ebd3e7cfa --- /dev/null +++ b/app/assets/stylesheets/pages/tags.scss @@ -0,0 +1,7 @@ +.tag-buttons { + line-height: 40px; + + .btn:not(.dropdown-toggle) { + margin-left: 10px; + } +} diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index e6c99c9959e..59a8365d60b 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -26,18 +26,20 @@ module CiStatusHelper icon_name = case status when 'success' - 'check' + 'icon_status_success' + when 'success_with_warnings' + 'icon_status_warning' when 'failed' - 'close' + 'icon_status_failed' when 'pending' - 'clock-o' + 'icon_status_pending' when 'running' - 'spinner' + 'icon_status_running' else - 'circle' + 'icon_status_cancel' end - icon(icon_name + ' fw') + custom_icon(icon_name) end def render_commit_status(commit, tooltip_placement: 'auto left', cssclass: '') diff --git a/app/helpers/external_wiki_helper.rb b/app/helpers/external_wiki_helper.rb index 1f3401f2906..defd87d6bbe 100644 --- a/app/helpers/external_wiki_helper.rb +++ b/app/helpers/external_wiki_helper.rb @@ -1,8 +1,7 @@ module ExternalWikiHelper def get_project_wiki_path(project) - external_wiki_service = project.services. - find { |service| service.to_param == 'external_wiki' } - if external_wiki_service.present? && external_wiki_service.active? + external_wiki_service = project.external_wiki + if external_wiki_service external_wiki_service.properties['external_wiki_url'] else namespace_project_wiki_path(project.namespace, project, :home) diff --git a/app/helpers/time_helper.rb b/app/helpers/time_helper.rb index 8cb82c2d5cc..1926f03af07 100644 --- a/app/helpers/time_helper.rb +++ b/app/helpers/time_helper.rb @@ -1,14 +1,4 @@ module TimeHelper - def duration_in_words(finished_at, started_at) - if finished_at && started_at - interval_in_seconds = finished_at.to_i - started_at.to_i - elsif started_at - interval_in_seconds = Time.now.to_i - started_at.to_i - end - - time_interval_in_words(interval_in_seconds) - end - def time_interval_in_words(interval_in_seconds) minutes = interval_in_seconds / 60 seconds = interval_in_seconds - minutes * 60 @@ -25,9 +15,19 @@ module TimeHelper end def duration_in_numbers(finished_at, started_at) - diff_in_seconds = finished_at.to_i - started_at.to_i - time_format = diff_in_seconds < 1.hour ? "%M:%S" : "%H:%M:%S" + interval = interval_in_seconds(started_at, finished_at) + time_format = interval < 1.hour ? "%M:%S" : "%H:%M:%S" - Time.at(diff_in_seconds).utc.strftime(time_format) + Time.at(interval).utc.strftime(time_format) + end + + private + + def interval_in_seconds(started_at, finished_at = nil) + if started_at && finished_at + finished_at.to_i - started_at.to_i + elsif started_at + Time.now.to_i - started_at.to_i + end end end diff --git a/app/models/project.rb b/app/models/project.rb index a805f5d97bc..d08b737e813 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -650,6 +650,22 @@ class Project < ActiveRecord::Base update_column(:has_external_issue_tracker, services.external_issue_trackers.any?) end + def external_wiki + if has_external_wiki.nil? + cache_has_external_wiki # Populate + end + + if has_external_wiki + @external_wiki ||= services.external_wikis.first + else + nil + end + end + + def cache_has_external_wiki + update_column(:has_external_wiki, services.external_wikis.any?) + end + def build_missing_services services_templates = Service.where(template: true) diff --git a/app/models/repository.rb b/app/models/repository.rb index 1a2ac90da51..511df2d67c6 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -392,6 +392,11 @@ class Repository expire_cache if exists? + # expire cache that don't depend on repository data (when expiring) + expire_tags_cache + expire_tag_count_cache + expire_branches_cache + expire_branch_count_cache expire_root_ref_cache expire_emptiness_caches expire_exists_cache diff --git a/app/models/service.rb b/app/models/service.rb index 5432f8c7ab4..4821096379c 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -17,6 +17,7 @@ class Service < ActiveRecord::Base after_commit :reset_updated_properties after_commit :cache_project_has_external_issue_tracker + after_commit :cache_project_has_external_wiki belongs_to :project, inverse_of: :services has_one :service_hook @@ -25,6 +26,7 @@ class Service < ActiveRecord::Base scope :visible, -> { where.not(type: ['GitlabIssueTrackerService', 'GitlabCiService']) } scope :issue_trackers, -> { where(category: 'issue_tracker') } + scope :external_wikis, -> { where(type: 'ExternalWikiService') } scope :active, -> { where(active: true) } scope :without_defaults, -> { where(default: false) } @@ -212,4 +214,10 @@ class Service < ActiveRecord::Base project.cache_has_external_issue_tracker end end + + def cache_project_has_external_wiki + if project && !project.destroyed? + project.cache_has_external_wiki + end + end end diff --git a/app/models/user.rb b/app/models/user.rb index 3d0a033785c..975e935fa20 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -854,7 +854,7 @@ class User < ActiveRecord::Base groups.joins(:shared_projects).select(:project_id)] if min_access_level - scope = { access_level: Gitlab::Access.values.select { |access| access >= min_access_level } } + scope = { access_level: Gitlab::Access.all_values.select { |access| access >= min_access_level } } relations = [relations.shift] + relations.map { |relation| relation.where(members: scope) } end diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml index 12e7ed0e792..351100f3523 100644 --- a/app/views/layouts/_init_auto_complete.html.haml +++ b/app/views/layouts/_init_auto_complete.html.haml @@ -1,7 +1,7 @@ - project = @target_project || @project +- noteable_class = @noteable.class if @noteable.present? -- if @noteable - :javascript - GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_namespace_project_path(project.namespace, project, type: @noteable.class, type_id: params[:id])}" - GitLab.GfmAutoComplete.cachedData = undefined; - GitLab.GfmAutoComplete.setup(); +:javascript + GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_namespace_project_path(project.namespace, project, type: noteable_class, type_id: params[:id])}" + GitLab.GfmAutoComplete.cachedData = undefined; + GitLab.GfmAutoComplete.setup(); diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml index 396cc4ad925..dc57b49f27a 100644 --- a/app/views/projects/builds/_sidebar.html.haml +++ b/app/views/projects/builds/_sidebar.html.haml @@ -49,7 +49,7 @@ - if @build.duration %p.build-detail-row %span.build-light-text Duration: - #{duration_in_words(@build.finished_at, @build.started_at)} + = time_interval_in_words(@build.duration) - if @build.finished_at %p.build-detail-row %span.build-light-text Finished: diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml index a9eaed4c5f6..a098a082854 100644 --- a/app/views/projects/buttons/_fork.html.haml +++ b/app/views/projects/buttons/_fork.html.haml @@ -2,7 +2,7 @@ - if current_user && can?(current_user, :fork_project, @project) - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has-tooltip' do - = icon('code-fork fw') + = custom_icon('icon_fork') Fork %div.count-with-arrow %span.arrow @@ -10,7 +10,7 @@ = @project.forks_count - else = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has-tooltip' do - = icon('code-fork fw') + = custom_icon('icon_fork') Fork %div.count-with-arrow %span.arrow diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 996c9073770..cb0ca7bc8e3 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -35,7 +35,7 @@ - stages_status = pipeline.statuses.latest.stages_status - stages.each do |stage| - %td + %td.stage-cell - status = stages_status[stage] - tooltip = "#{stage.titleize}: #{status || 'not found'}" - if status diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml index dbe9ddfde2f..a1d79bdabda 100644 --- a/app/views/projects/forks/index.html.haml +++ b/app/views/projects/forks/index.html.haml @@ -31,11 +31,11 @@ - if current_user && can?(current_user, :fork_project, @project) - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-new' do - = icon('code-fork fw') + = custom_icon('icon_fork') Fork - else = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn btn-new' do - = icon('code-fork fw') + = custom_icon('icon_fork') Fork 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 542827b2f15..331dc1fcc29 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 @@ -51,7 +51,7 @@ %td.duration - if generic_commit_status.duration = icon("clock-o") - #{duration_in_words(generic_commit_status.finished_at, generic_commit_status.started_at)} + = time_interval_in_words(generic_commit_status.duration) %td.timestamp - if generic_commit_status.finished_at diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index b7d7d5c5382..395d7af6cbb 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -1,36 +1,39 @@ +- @no_container = true - page_title @tag.name, "Tags" = render "projects/commits/head" -.row-content-block - .pull-right - - if can?(current_user, :push_code, @project) - = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn has-tooltip', title: 'Edit release notes' do - = icon("pencil") - = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has-tooltip', title: 'Browse files' do - = icon('files-o') - = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has-tooltip', title: 'Browse commits' do - = icon('history') - - if can? current_user, :download_code, @project - = render 'projects/tags/download', ref: @tag.name, project: @project - - if can?(current_user, :admin_project, @project) - .pull-right - = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do - %i.fa.fa-trash-o - .title - %span.item-title= @tag.name - - if @commit - = render 'projects/branches/commit', commit: @commit, project: @project - - else - Cant find HEAD commit for this tag - - if @tag.message.present? - %pre.body - = strip_gpg_signature(@tag.message) +%div{ class: container_class } + .sub-header-block + .pull-right.tag-buttons + - if can?(current_user, :push_code, @project) + = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Edit release notes' do + = icon("pencil") + = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Browse files' do + = icon('files-o') + = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Browse commits' do + = icon('history') + - if can? current_user, :download_code, @project + = render 'projects/tags/download', ref: @tag.name, project: @project + - if can?(current_user, :admin_project, @project) + .pull-right + = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do + %i.fa.fa-trash-o + .tag-info.append-bottom-10 + .title + %span.item-title= @tag.name + - if @commit + = render 'projects/branches/commit', commit: @commit, project: @project + - else + Cant find HEAD commit for this tag + - if @tag.message.present? + %pre.body + = strip_gpg_signature(@tag.message) -.append-bottom-default.prepend-top-default - - if @release.description.present? - .description - .wiki - = preserve do - = markdown @release.description - - else - This tag has no release notes. + .append-bottom-default.prepend-top-default + - if @release.description.present? + .description + .wiki + = preserve do + = markdown @release.description + - else + This tag has no release notes. diff --git a/app/views/shared/icons/_icon_fork.svg b/app/views/shared/icons/_icon_fork.svg new file mode 100644 index 00000000000..420ffe3a55b --- /dev/null +++ b/app/views/shared/icons/_icon_fork.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><circle id="a" cx="4" cy="4" r="4"/><mask id="d" width="8" height="8" x="0" y="0" fill="#fff"><use xlink:href="#a"/></mask><circle id="b" cx="20" cy="4" r="4"/><mask id="e" width="8" height="8" x="0" y="0" fill="#fff"><use xlink:href="#b"/></mask><circle id="c" cx="12" cy="30" r="4"/><mask id="f" width="8" height="8" x="0" y="0" fill="#fff"><use xlink:href="#c"/></mask></defs><g fill="none" fill-rule="evenodd" transform="translate(8 3)"><path fill="#7E7E7E" d="M10 19.667c-4.14-1.29-7.389-5.878-7.389-5.878C2.274 13.353 2 12.545 2 12.01V6h4v5.509c0 .276.166.65.367.831 0 0 1.136 1.028 1.746 1.574C9.617 15.261 11.048 16 12.09 16c1.028 0 2.41-.723 3.858-2.048.588-.54 1.84-1.742 1.84-1.742a.784.784 0 0 0 .211-.502V6h4v6.008c0 .548-.259 1.349-.601 1.795 0 0-3.21 4.707-7.399 5.916V27h-4v-7.333z"/><use stroke="#7E7E7E" stroke-width="4" mask="url(#d)" xlink:href="#a"/><use stroke="#7E7E7E" stroke-width="4" mask="url(#e)" xlink:href="#b"/><use stroke="#7E7E7E" stroke-width="4" mask="url(#f)" xlink:href="#c"/></g></svg>
\ No newline at end of file diff --git a/app/views/shared/icons/_icon_status_cancel.svg b/app/views/shared/icons/_icon_status_cancel.svg new file mode 100644 index 00000000000..6a0bc1490c4 --- /dev/null +++ b/app/views/shared/icons/_icon_status_cancel.svg @@ -0,0 +1,12 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs> + <circle id="a" cx="7" cy="7" r="7"/> + <mask id="b" width="14" height="14" x="0" y="0" fill="white"> + <use xlink:href="#a"/> + </mask> + </defs> + <g fill="none" fill-rule="evenodd"> + <use stroke="#5C5C5C" stroke-width="2" mask="url(#b)" xlink:href="#a"/> + <rect width="10" height="1" x="2" y="6.5" fill="#5C5C5C" transform="rotate(45 7 7)" rx=".3"/> + </g> +</svg> diff --git a/app/views/shared/icons/_icon_status_failed.svg b/app/views/shared/icons/_icon_status_failed.svg new file mode 100644 index 00000000000..c41ca18cae7 --- /dev/null +++ b/app/views/shared/icons/_icon_status_failed.svg @@ -0,0 +1,12 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs> + <circle id="a" cx="7" cy="7" r="7"/> + <mask id="b" width="14" height="14" x="0" y="0" fill="white"> + <use xlink:href="#a"/> + </mask> + </defs> + <g fill="none" fill-rule="evenodd"> + <use stroke="#D22852" stroke-width="2" mask="url(#b)" xlink:href="#a"/> + <path fill="#D22852" d="M7.5,6.5 L7.5,4.30578971 C7.5,4.12531853 7.36809219,4 7.20537567,4 L6.79462433,4 C6.63904572,4 6.5,4.13690672 6.5,4.30578971 L6.5,6.5 L4.30578971,6.5 C4.12531853,6.5 4,6.63190781 4,6.79462433 L4,7.20537567 C4,7.36095428 4.13690672,7.5 4.30578971,7.5 L6.5,7.5 L6.5,9.69421029 C6.5,9.87468147 6.63190781,10 6.79462433,10 L7.20537567,10 C7.36095428,10 7.5,9.86309328 7.5,9.69421029 L7.5,7.5 L9.69421029,7.5 C9.87468147,7.5 10,7.36809219 10,7.20537567 L10,6.79462433 C10,6.63904572 9.86309328,6.5 9.69421029,6.5 L7.5,6.5 Z" transform="rotate(45 7 7)"/> + </g> +</svg> diff --git a/app/views/shared/icons/_icon_status_pending.svg b/app/views/shared/icons/_icon_status_pending.svg new file mode 100644 index 00000000000..035cd8b4ccc --- /dev/null +++ b/app/views/shared/icons/_icon_status_pending.svg @@ -0,0 +1,13 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs> + <circle id="a" cx="7" cy="7" r="7"/> + <mask id="b" width="14" height="14" x="0" y="0" fill="white"> + <use xlink:href="#a"/> + </mask> + </defs> + <g fill="none" fill-rule="evenodd"> + <use stroke="#E75E40" stroke-width="2" mask="url(#b)" xlink:href="#a"/> + <rect width="1" height="4" x="5" y="5" fill="#E75E40" rx=".3"/> + <rect width="1" height="4" x="8" y="5" fill="#E75E40" rx=".3"/> + </g> +</svg> diff --git a/app/views/shared/icons/_icon_status_running.svg b/app/views/shared/icons/_icon_status_running.svg new file mode 100644 index 00000000000..a48b3a25099 --- /dev/null +++ b/app/views/shared/icons/_icon_status_running.svg @@ -0,0 +1,12 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs> + <circle id="a" cx="7" cy="7" r="7"/> + <mask id="b" width="14" height="14" x="0" y="0" fill="white"> + <use xlink:href="#a"/> + </mask> + </defs> + <g fill="none" fill-rule="evenodd"> + <use stroke="#2D9FD8" stroke-width="2" mask="url(#b)" xlink:href="#a"/> + <path fill="#2D9FD8" d="M7,3.00800862 C9.09023405,3.13960661 10.7448145,4.87657932 10.7448145,7 C10.7448145,9.209139 8.95395346,11 6.74481446,11 C5.4560962,11 4.30972054,10.3905589 3.57817301,9.44416214 L7,7 L7,3.00800862 Z"/> + </g> +</svg> diff --git a/app/views/shared/icons/_icon_status_success.svg b/app/views/shared/icons/_icon_status_success.svg new file mode 100644 index 00000000000..260eab013a3 --- /dev/null +++ b/app/views/shared/icons/_icon_status_success.svg @@ -0,0 +1,15 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs> + <circle id="a" cx="7" cy="7" r="7"/> + <mask id="b" width="14" height="14" x="0" y="0" fill="white"> + <use xlink:href="#a"/> + </mask> + </defs> + <g fill="none" fill-rule="evenodd"> + <use stroke="#31AF64" stroke-width="2" mask="url(#b)" xlink:href="#a"/> + <g fill="#31AF64" transform="rotate(45 -.13 10.953)"> + <rect width="1" height="5" x="2" rx=".3"/> + <rect width="3" height="1" y="4" rx=".3"/> + </g> + </g> +</svg> diff --git a/app/views/shared/icons/_icon_status_warning.svg b/app/views/shared/icons/_icon_status_warning.svg new file mode 100644 index 00000000000..d47e7a1c93f --- /dev/null +++ b/app/views/shared/icons/_icon_status_warning.svg @@ -0,0 +1,15 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs> + <circle id="a" cx="7" cy="7" r="7"/> + <mask id="b" width="14" height="14" x="0" y="0" fill="white"> + <use xlink:href="#a"/> + </mask> + </defs> + <g fill="none" fill-rule="evenodd"> + <g fill="#FF8A24" transform="translate(6 3)"> + <rect width="2" height="5" rx=".5"/> + <rect width="2" height="2" y="6" rx=".5"/> + </g> + <use stroke="#FF8A24" stroke-width="2" mask="url(#b)" xlink:href="#a"/> + </g> +</svg> diff --git a/config/initializers/secure_headers.rb b/config/initializers/secure_headers.rb index 9fd24a667cc..253e3cf7410 100644 --- a/config/initializers/secure_headers.rb +++ b/config/initializers/secure_headers.rb @@ -4,14 +4,7 @@ require 'gitlab/current_settings' include Gitlab::CurrentSettings -# If Sentry is enabled and the Rails app is running in production mode, -# this will construct the Report URI for Sentry. -if Rails.env.production? && current_application_settings.sentry_enabled - uri = URI.parse(current_application_settings.sentry_dsn) - CSP_REPORT_URI = "#{uri.scheme}://#{uri.host}/api#{uri.path}/csp-report/?sentry_key=#{uri.user}" -else - CSP_REPORT_URI = '' -end +CSP_REPORT_URI = '' # Content Security Policy Headers # For more information on CSP see: @@ -71,10 +64,7 @@ SecureHeaders::Configuration.default do |config| upgrade_insecure_requests: true } - # Reports are sent to Sentry if it's enabled. - if current_application_settings.sentry_enabled - config.csp[:report_uri] = %W(#{CSP_REPORT_URI}) - end + config.csp[:report_uri] = %W(#{CSP_REPORT_URI}) # Allow Bootstrap Linter in development mode. if Rails.env.development? diff --git a/config/routes.rb b/config/routes.rb index be651d8903f..2a9fe30b0e6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -89,11 +89,10 @@ Rails.application.routes.draw do mount Grack::AuthSpawner, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\/(info\/lfs|gitlab-lfs)/.match(request.path_info) }, via: [:get, :post, :put] # Help - - get 'help' => 'help#index' - get 'help/*path' => 'help#show', as: :help_page - get 'help/shortcuts' - get 'help/ui' => 'help#ui' + get 'help' => 'help#index' + get 'help/shortcuts' => 'help#shortcuts' + get 'help/ui' => 'help#ui' + get 'help/*path' => 'help#show', as: :help_page # # Global snippets diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index ac7eac0ea7c..611767ac7fe 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -7,7 +7,13 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration class ProjectImportDataFake extend AttrEncrypted attr_accessor :credentials - attr_encrypted :credentials, key: Gitlab::Application.secrets.db_key_base, marshal: true, encode: true, :mode => :per_attribute_iv_and_salt + attr_encrypted :credentials, + key: Gitlab::Application.secrets.db_key_base, + marshal: true, + encode: true, + :mode => :per_attribute_iv_and_salt, + insecure_mode: true, + algorithm: 'aes-256-cbc' end def up diff --git a/db/migrate/20160718153603_add_has_external_wiki_to_projects.rb b/db/migrate/20160718153603_add_has_external_wiki_to_projects.rb new file mode 100644 index 00000000000..55a3e954292 --- /dev/null +++ b/db/migrate/20160718153603_add_has_external_wiki_to_projects.rb @@ -0,0 +1,7 @@ +class AddHasExternalWikiToProjects < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + def change + add_column :projects, :has_external_wiki, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index 39b02d9ba35..66ab0512efc 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: 20160716115710) do +ActiveRecord::Schema.define(version: 20160718153603) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -843,6 +843,7 @@ ActiveRecord::Schema.define(version: 20160716115710) do t.boolean "only_allow_merge_if_build_succeeds", default: false, null: false t.boolean "has_external_issue_tracker" t.string "repository_storage", default: "default", null: false + t.boolean "has_external_wiki" end add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index acb4812b5cf..4df6ca8333e 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -24,7 +24,7 @@ module API pipelines = user_project.pipelines.where(sha: params[:sha]) statuses = ::CommitStatus.where(pipeline: pipelines) - statuses = statuses.latest unless parse_boolean(params[:all]) + statuses = statuses.latest unless to_boolean(params[:all]) statuses = statuses.where(ref: params[:ref]) if params[:ref].present? statuses = statuses.where(stage: params[:stage]) if params[:stage].present? statuses = statuses.where(name: params[:name]) if params[:name].present? diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index d6e4eb2afd7..130509cdad6 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -5,10 +5,6 @@ module API SUDO_HEADER = "HTTP_SUDO" SUDO_PARAM = :sudo - def parse_boolean(value) - [ true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON' ].include?(value) - end - def to_boolean(value) return true if value =~ /^(true|t|yes|y|1|on)$/i return false if value =~ /^(false|f|no|n|0|off)$/i @@ -297,7 +293,7 @@ module API def filter_projects(projects) # If the archived parameter is passed, limit results accordingly if params[:archived].present? - projects = projects.where(archived: parse_boolean(params[:archived])) + projects = projects.where(archived: to_boolean(params[:archived])) end if params[:search].present? diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 4fcdf8968c9..2b685621da9 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -242,7 +242,7 @@ module API should_remove_source_branch: params[:should_remove_source_branch] } - if parse_boolean(params[:merge_when_build_succeeds]) && merge_request.pipeline && merge_request.pipeline.active? + if to_boolean(params[:merge_when_build_succeeds]) && merge_request.pipeline && merge_request.pipeline.active? ::MergeRequests::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user, merge_params). execute(merge_request) else diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 6d2a6f3946c..8fed7db8803 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -8,7 +8,7 @@ module API def map_public_to_visibility_level(attrs) publik = attrs.delete(:public) if publik.present? && !attrs[:visibility_level].present? - publik = parse_boolean(publik) + publik = to_boolean(publik) # Since setting the public attribute to private could mean either # private or internal, use the more conservative option, private. attrs[:visibility_level] = (publik == true) ? Gitlab::VisibilityLevel::PUBLIC : Gitlab::VisibilityLevel::PRIVATE diff --git a/lib/gitlab/checks/force_push.rb b/lib/gitlab/checks/force_push.rb index dfa83a0eab3..5fe86553bd0 100644 --- a/lib/gitlab/checks/force_push.rb +++ b/lib/gitlab/checks/force_push.rb @@ -8,8 +8,8 @@ module Gitlab if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev) false else - missed_refs, _ = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} --git-dir=#{project.repository.path_to_repo} rev-list #{oldrev} ^#{newrev})) - missed_refs.split("\n").size > 0 + missed_ref, _ = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} --git-dir=#{project.repository.path_to_repo} rev-list --max-count=1 #{oldrev} ^#{newrev})) + missed_ref.present? end end end diff --git a/lib/gitlab/diff/position.rb b/lib/gitlab/diff/position.rb index 989fff8918e..2fdcf8d7838 100644 --- a/lib/gitlab/diff/position.rb +++ b/lib/gitlab/diff/position.rb @@ -73,8 +73,8 @@ module Gitlab diff_refs.complete? end - def to_json - JSON.generate(self.to_h) + def to_json(opts = nil) + JSON.generate(self.to_h, opts) end def type diff --git a/lib/gitlab/git_access_status.rb b/lib/gitlab/git_access_status.rb index 5a806ff6e0d..09bb01be694 100644 --- a/lib/gitlab/git_access_status.rb +++ b/lib/gitlab/git_access_status.rb @@ -8,8 +8,8 @@ module Gitlab @message = message end - def to_json - { status: @status, message: @message }.to_json + def to_json(opts = nil) + { status: @status, message: @message }.to_json(opts) end end end diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb index d4f12cb1df9..c5a11148d33 100644 --- a/lib/gitlab/gon_helper.rb +++ b/lib/gitlab/gon_helper.rb @@ -5,7 +5,7 @@ module Gitlab gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s gon.max_file_size = current_application_settings.max_attachment_size gon.relative_url_root = Gitlab.config.gitlab.relative_url_root - gon.shortcuts_path = help_shortcuts_path + gon.shortcuts_path = help_page_path('shortcuts') gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class gon.award_menu_url = emojis_path diff --git a/spec/controllers/help_controller_spec.rb b/spec/controllers/help_controller_spec.rb index 267d511c2db..347bef1e129 100644 --- a/spec/controllers/help_controller_spec.rb +++ b/spec/controllers/help_controller_spec.rb @@ -63,4 +63,13 @@ describe HelpController do end end end + + describe 'GET #ui' do + context 'for UI Development Kit' do + it 'renders found' do + get :ui + expect(response).to have_http_status(200) + end + end + end end diff --git a/spec/helpers/ci_status_helper_spec.rb b/spec/helpers/ci_status_helper_spec.rb index 45199d0f09d..637b02d9388 100644 --- a/spec/helpers/ci_status_helper_spec.rb +++ b/spec/helpers/ci_status_helper_spec.rb @@ -7,7 +7,13 @@ describe CiStatusHelper do let(:failed_commit) { double("Ci::Pipeline", status: 'failed') } describe 'ci_icon_for_status' do - it { expect(helper.ci_icon_for_status(success_commit.status)).to include('fa-check') } - it { expect(helper.ci_icon_for_status(failed_commit.status)).to include('fa-close') } + it 'renders to correct svg on success' do + expect(helper).to receive(:render).with('shared/icons/icon_status_success.svg', anything) + helper.ci_icon_for_status(success_commit.status) + end + it 'renders the correct svg on failure' do + expect(helper).to receive(:render).with('shared/icons/icon_status_failed.svg', anything) + helper.ci_icon_for_status(failed_commit.status) + end end end diff --git a/spec/helpers/time_helper_spec.rb b/spec/helpers/time_helper_spec.rb index 3f62527c5bb..413ead944b9 100644 --- a/spec/helpers/time_helper_spec.rb +++ b/spec/helpers/time_helper_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe TimeHelper do - describe "#duration_in_words" do + describe "#time_interval_in_words" do it "returns minutes and seconds" do intervals_in_words = { 100 => "1 minute 40 seconds", @@ -11,26 +11,23 @@ describe TimeHelper do } intervals_in_words.each do |interval, expectation| - expect(duration_in_words(Time.now + interval, Time.now)).to eq(expectation) + expect(time_interval_in_words(interval)).to eq(expectation) end end - - it "calculates interval from now if there is no finished_at" do - expect(duration_in_words(nil, Time.now - 5)).to eq("5 seconds") - end end - describe "#time_interval_in_words" do + describe "#duration_in_numbers" do it "returns minutes and seconds" do - intervals_in_words = { - 100 => "1 minute 40 seconds", - 121 => "2 minutes 1 second", - 3721 => "62 minutes 1 second", - 0 => "0 seconds" + duration_in_numbers = { + [100, 0] => "01:40", + [121, 0] => "02:01", + [3721, 0] => "01:02:01", + [0, 0] => "00:00", + [nil, Time.now.to_i - 42] => "00:42" } - intervals_in_words.each do |interval, expectation| - expect(time_interval_in_words(interval)).to eq(expectation) + duration_in_numbers.each do |interval, expectation| + expect(duration_in_numbers(*interval)).to eq(expectation) end end end diff --git a/spec/lib/gitlab/diff/position_spec.rb b/spec/lib/gitlab/diff/position_spec.rb index cf28628cb96..10537bea008 100644 --- a/spec/lib/gitlab/diff/position_spec.rb +++ b/spec/lib/gitlab/diff/position_spec.rb @@ -338,4 +338,28 @@ describe Gitlab::Diff::Position, lib: true do end end end + + describe "#to_json" do + let(:hash) do + { + old_path: "files/ruby/popen.rb", + new_path: "files/ruby/popen.rb", + old_line: nil, + new_line: 14, + base_sha: nil, + head_sha: nil, + start_sha: nil + } + end + + let(:diff_position) { described_class.new(hash) } + + it "returns the position as JSON" do + expect(JSON.parse(diff_position.to_json)).to eq(hash.stringify_keys) + end + + it "works when nested under another hash" do + expect(JSON.parse(JSON.generate(pos: diff_position))).to eq('pos' => hash.stringify_keys) + end + end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 9dc34276f18..e3e7319beb2 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -458,6 +458,47 @@ describe Project, models: true do end end + describe "#cache_has_external_wiki" do + let(:project) { create(:project) } + + it "stores true if there is an external wiki" do + services = double(:service, external_wikis: [ExternalWikiService.new]) + expect(project).to receive(:services).and_return(services) + + expect do + project.cache_has_external_wiki + end.to change { project.has_external_wiki }.to(true) + end + + it "stores false if there is no external wiki" do + services = double(:service, external_wikis: []) + expect(project).to receive(:services).and_return(services) + + expect do + project.cache_has_external_wiki + end.to change { project.has_external_wiki }.to(false) + end + + it "changes to true if an external wiki service is created later" do + expect do + project.cache_has_external_wiki + end.to change { project.has_external_wiki }.to(false) + + expect do + create(:service, type: "ExternalWikiService", project: project) + end.to change { project.has_external_wiki }.to(true) + end + + it "changes to false if an external wiki service is destroyed later" do + service = create(:service, type: "ExternalWikiService", project: project) + expect(project.has_external_wiki).to be_truthy + + expect do + service.destroy + end.to change { project.has_external_wiki }.to(false) + end + end + describe '#open_branches' do let(:project) { create(:project) } diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 110df6bbd22..59c5732c075 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -749,6 +749,30 @@ describe Repository, models: true do repository.before_delete end + it 'flushes the tags cache' do + expect(repository).to receive(:expire_tags_cache) + + repository.before_delete + end + + it 'flushes the tag count cache' do + expect(repository).to receive(:expire_tag_count_cache) + + repository.before_delete + end + + it 'flushes the branches cache' do + expect(repository).to receive(:expire_branches_cache) + + repository.before_delete + end + + it 'flushes the branch count cache' do + expect(repository).to receive(:expire_branch_count_cache) + + repository.before_delete + end + it 'flushes the root ref cache' do expect(repository).to receive(:expire_root_ref_cache) @@ -779,6 +803,30 @@ describe Repository, models: true do repository.before_delete end + it 'flushes the tags cache' do + expect(repository).to receive(:expire_tags_cache) + + repository.before_delete + end + + it 'flushes the tag count cache' do + expect(repository).to receive(:expire_tag_count_cache) + + repository.before_delete + end + + it 'flushes the branches cache' do + expect(repository).to receive(:expire_branches_cache) + + repository.before_delete + end + + it 'flushes the branch count cache' do + expect(repository).to receive(:expire_branch_count_cache) + + repository.before_delete + end + it 'flushes the root ref cache' do expect(repository).to receive(:expire_root_ref_cache) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index fc74488ac0e..3bf82cf2668 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -887,16 +887,25 @@ describe User, models: true do end describe '#authorized_projects' do - let!(:user) { create(:user) } - let!(:private_project) { create(:project, :private) } + context 'with a minimum access level' do + it 'includes projects for which the user is an owner' do + user = create(:user) + project = create(:empty_project, :private, namespace: user.namespace) - before do - private_project.team << [user, Gitlab::Access::MASTER] - end + expect(user.authorized_projects(Gitlab::Access::REPORTER)) + .to contain_exactly(project) + end - subject { user.authorized_projects } + it 'includes projects for which the user is a master' do + user = create(:user) + project = create(:empty_project, :private) + + project.team << [user, Gitlab::Access::MASTER] - it { is_expected.to eq([private_project]) } + expect(user.authorized_projects(Gitlab::Access::REPORTER)) + .to contain_exactly(project) + end + end end describe '#ci_authorized_runners' do diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb index 2c755919456..0a52c1ab933 100644 --- a/spec/routing/routing_spec.rb +++ b/spec/routing/routing_spec.rb @@ -116,12 +116,9 @@ describe HelpController, "routing" do expect(get(path)).to route_to('help#show', path: 'workflow/protected_branches/protected_branches1', format: 'png') - path = '/help/shortcuts' - expect(get(path)).to route_to('help#show', - path: 'shortcuts') + path = '/help/ui' - expect(get(path)).to route_to('help#show', - path: 'ui') + expect(get(path)).to route_to('help#ui') end end |