diff options
52 files changed, 440 insertions, 237 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a11c4705e82..5d2fad03f19 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,9 +1,8 @@ -image: "ruby:2.3.1" +image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3-git-2.7-phantomjs-2.1" cache: key: "ruby-231" paths: - - vendor/apt - vendor/ruby variables: @@ -141,14 +140,13 @@ spinach 9 10: *spinach-knapsack # Execute all testing suites against Ruby 2.1 .ruby-21: &ruby-21 - image: "ruby:2.1" + image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.1-git-2.7-phantomjs-2.1" <<: *use-db only: - master cache: key: "ruby21" paths: - - vendor/apt - vendor/ruby .rspec-knapsack-ruby21: &rspec-knapsack-ruby21 diff --git a/CHANGELOG b/CHANGELOG index 851b7d5013a..58f1e4a59c2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,30 +1,38 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.13.0 (unreleased) + - Add link from system note to compare with previous version - Use gitlab-shell v3.6.2 (GIT TRACE logging) - Fix centering of custom header logos (Ashley Dumaine) - AbstractReferenceFilter caches project_refs on RequestStore when active + - Replaced the check sign to arrow in the show build view. !6501 + - Add a /wip slash command to toggle the Work In Progress status of a merge request. !6259 (tbalthazar) - Speed-up group milestones show page - Log LDAP lookup errors and don't swallow unrelated exceptions. !6103 (Markus Koller) - Add more tests for calendar contribution (ClemMakesApps) - Avoid database queries on Banzai::ReferenceParser::BaseParser for nodes without references - Fix permission for setting an issue's due date - Expose expires_at field when sharing project on API + - Fix issue with page scrolling to top when closing or pinning sidebar (lukehowell) - Allow the Koding integration to be configured through the API - Added soft wrap button to repository file/blob editor - Add word-wrap to issue title on issue and milestone boards (ClemMakesApps) - Fix robots.txt disallowing access to groups starting with "s" (Matt Harrison) - Close open merge request without source project (Katarzyna Kobierska Ula Budziszewska) + - Add configurable email subject suffix (Fu Xu) - Use a ConnectionPool for Rails.cache on Sidekiq servers - Replace `alias_method_chain` with `Module#prepend` + - Enable GitLab Import/Export for non-admin users. - Preserve label filters when sorting !6136 (Joseph Frazier) - Only update issuable labels if they have been changed - Take filters in account in issuable counters. !6496 + - Use custom Ruby images to test builds (registry.dev.gitlab.org/gitlab/gitlab-build-images:*) - Revoke button in Applications Settings underlines on hover. - Add missing values to linter !6276 (Katarzyna Kobierska Ula Budziszewska) - Fix Long commit messages overflow viewport in file tree - Revert avoid touching file system on Build#artifacts? - Add broadcast messages and alerts below sub-nav + - Better empty state for Groups view - Update ruby-prof to 0.16.2. !6026 (Elan Ruusamäe) - Fix unnecessary escaping of reserved HTML characters in milestone title. !6533 - Add organization field to user profile diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index b72762837ea..4a788a01dad 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -3.6.2 +3.6.3 @@ -99,17 +99,17 @@ gem 'unf', '~> 0.1.4' gem 'seed-fu', '~> 2.3.5' # Markdown and HTML processing -gem 'html-pipeline', '~> 1.11.0' -gem 'task_list', '~> 1.0.2', require: 'task_list/railtie' -gem 'github-markup', '~> 1.4' -gem 'redcarpet', '~> 3.3.3' -gem 'RedCloth', '~> 4.3.2' -gem 'rdoc', '~>3.6' -gem 'org-ruby', '~> 0.9.12' -gem 'creole', '~> 0.5.0' -gem 'wikicloth', '0.8.1' -gem 'asciidoctor', '~> 1.5.2' -gem 'rouge', '~> 2.0' +gem 'html-pipeline', '~> 1.11.0' +gem 'deckar01-task_list', '1.0.5', require: 'task_list/railtie' +gem 'github-markup', '~> 1.4' +gem 'redcarpet', '~> 3.3.3' +gem 'RedCloth', '~> 4.3.2' +gem 'rdoc', '~>3.6' +gem 'org-ruby', '~> 0.9.12' +gem 'creole', '~> 0.5.0' +gem 'wikicloth', '0.8.1' +gem 'asciidoctor', '~> 1.5.2' +gem 'rouge', '~> 2.0' # See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s # and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM diff --git a/Gemfile.lock b/Gemfile.lock index f15715a20ff..ca06b21ae65 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -157,6 +157,10 @@ GEM database_cleaner (1.5.3) debug_inspector (0.0.2) debugger-ruby_core_source (1.3.8) + deckar01-task_list (1.0.5) + activesupport (~> 4.0) + html-pipeline + rack (~> 1.0) default_value_for (3.0.2) activerecord (>= 3.2.0, < 5.1) descendants_tracker (0.0.4) @@ -725,8 +729,6 @@ GEM ffi sysexits (1.2.0) systemu (2.6.5) - task_list (1.0.2) - html-pipeline teaspoon (1.1.5) railties (>= 3.2.5, < 6) teaspoon-jasmine (2.2.0) @@ -831,6 +833,7 @@ DEPENDENCIES creole (~> 0.5.0) d3_rails (~> 3.5.0) database_cleaner (~> 1.5.0) + deckar01-task_list (= 1.0.5) default_value_for (~> 3.0.0) devise (~> 4.2) devise-two-factor (~> 3.0.0) @@ -963,7 +966,6 @@ DEPENDENCIES sprockets-es6 (~> 0.9.2) state_machines-activerecord (~> 0.4.0) sys-filesystem (~> 1.1.6) - task_list (~> 1.0.2) teaspoon (~> 1.1.0) teaspoon-jasmine (~> 2.2.0) test_after_commit (~> 0.4.2) diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 3b7de4b57bb..557ef7291cf 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -142,6 +142,7 @@ transition-duration: .3s; position: absolute; top: 0; + cursor: pointer; &:hover, &:focus { diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index 732dc645c66..185ce970e71 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -57,7 +57,6 @@ } .groups-header { - @media (min-width: $screen-sm-min) { .nav-links { width: 35%; @@ -68,3 +67,38 @@ } } } + +.groups-empty-state { + padding: 50px 100px; + overflow: hidden; + + @media (max-width: $screen-md-min) { + padding: 50px 0; + } + + svg { + float: right; + + @media (max-width: $screen-md-min) { + float: none; + display: block; + width: 250px; + position: relative; + left: 50%; + margin-left: -125px; + } + } + + .text-content { + float: left; + width: 460px; + margin-top: 120px; + + @media (max-width: $screen-md-min) { + float: none; + margin-top: 60px; + width: auto; + text-align: center; + } + } +} diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb index 7d0eff37635..3ec173abcdb 100644 --- a/app/controllers/import/gitlab_projects_controller.rb +++ b/app/controllers/import/gitlab_projects_controller.rb @@ -1,6 +1,5 @@ class Import::GitlabProjectsController < Import::BaseController before_action :verify_gitlab_project_import_enabled - before_action :authenticate_admin! def new @namespace_id = project_params[:namespace_id] @@ -48,8 +47,4 @@ class Import::GitlabProjectsController < Import::BaseController :path, :namespace_id, :file ) end - - def authenticate_admin! - render_404 unless current_user.is_admin? - end end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 68bb4232f5b..8c8c56228ad 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -276,7 +276,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def remove_wip - MergeRequests::UpdateService.new(project, current_user, title: @merge_request.wipless_title).execute(@merge_request) + MergeRequests::UpdateService.new(project, current_user, wip_event: 'unwip').execute(@merge_request) redirect_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), notice: "The merge request can now be merged." diff --git a/app/mailers/devise_mailer.rb b/app/mailers/devise_mailer.rb index 415f6e12885..f7ed61625f4 100644 --- a/app/mailers/devise_mailer.rb +++ b/app/mailers/devise_mailer.rb @@ -3,4 +3,12 @@ class DeviseMailer < Devise::Mailer default reply_to: Gitlab.config.gitlab.email_reply_to layout 'devise_mailer' + + protected + + def subject_for(key) + subject = super + subject << " | #{Gitlab.config.gitlab.email_subject_suffix}" if Gitlab.config.gitlab.email_subject_suffix.present? + subject + end end diff --git a/app/mailers/emails/members.rb b/app/mailers/emails/members.rb index 45311690293..7b617b359ea 100644 --- a/app/mailers/emails/members.rb +++ b/app/mailers/emails/members.rb @@ -45,7 +45,7 @@ module Emails @token = token mail(to: member.invite_email, - subject: "Invitation to join the #{member_source.human_name} #{member_source.model_name.singular}") + subject: subject("Invitation to join the #{member_source.human_name} #{member_source.model_name.singular}")) end def member_invite_accepted_email(member_source_type, member_id) diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index 29f1c527776..2444702104e 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -92,6 +92,7 @@ class Notify < BaseMailer subject = "" subject << "#{@project.name} | " if @project subject << extra.join(' | ') if extra.present? + subject << " | #{Gitlab.config.gitlab.email_subject_suffix}" if Gitlab.config.gitlab.email_subject_suffix.present? subject end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index aec555dcec0..a431d46cc9e 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -155,6 +155,20 @@ class MergeRequest < ActiveRecord::Base where("merge_requests.id IN (#{union.to_sql})") end + WIP_REGEX = /\A\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i.freeze + + def self.work_in_progress?(title) + !!(title =~ WIP_REGEX) + end + + def self.wipless_title(title) + title.sub(WIP_REGEX, "") + end + + def self.wip_title(title) + work_in_progress?(title) ? title : "WIP: #{title}" + end + def to_reference(from_project = nil) reference = "#{self.class.reference_prefix}#{iid}" @@ -389,14 +403,16 @@ class MergeRequest < ActiveRecord::Base @closed_event ||= target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last end - WIP_REGEX = /\A\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i.freeze - def work_in_progress? - !!(title =~ WIP_REGEX) + self.class.work_in_progress?(title) end def wipless_title - self.title.sub(WIP_REGEX, "") + self.class.wipless_title(self.title) + end + + def wip_title + self.class.wip_title(self.title) end def mergeable?(skip_ci_check: false) diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb index ba424b09463..d0d155b7ee1 100644 --- a/app/services/merge_requests/base_service.rb +++ b/app/services/merge_requests/base_service.rb @@ -5,16 +5,17 @@ module MergeRequests end def create_title_change_note(issuable, old_title) - removed_wip = old_title =~ MergeRequest::WIP_REGEX && !issuable.work_in_progress? - added_wip = old_title !~ MergeRequest::WIP_REGEX && issuable.work_in_progress? + removed_wip = MergeRequest.work_in_progress?(old_title) && !issuable.work_in_progress? + added_wip = !MergeRequest.work_in_progress?(old_title) && issuable.work_in_progress? + changed_title = MergeRequest.wipless_title(old_title) != issuable.wipless_title if removed_wip SystemNoteService.remove_merge_request_wip(issuable, issuable.project, current_user) elsif added_wip SystemNoteService.add_merge_request_wip(issuable, issuable.project, current_user) - else - super end + + super if changed_title end def hook_data(merge_request, action, oldrev = nil) diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb index f14f9e4b327..9dbec49d163 100644 --- a/app/services/merge_requests/update_service.rb +++ b/app/services/merge_requests/update_service.rb @@ -16,7 +16,7 @@ module MergeRequests end merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch) - + handle_wip_event(merge_request) update(merge_request) end @@ -81,5 +81,18 @@ module MergeRequests def after_update(issuable) issuable.cache_merge_request_closes_issues!(current_user) end + + private + + def handle_wip_event(merge_request) + if wip_event = params.delete(:wip_event) + # We update the title that is provided in the params or we use the mr title + title = params[:title] || merge_request.title + params[:title] = case wip_event + when 'wip' then MergeRequest.wip_title(title) + when 'unwip' then MergeRequest.wipless_title(title) + end + end + end end end diff --git a/app/services/slash_commands/interpret_service.rb b/app/services/slash_commands/interpret_service.rb index ffcad5b3a87..1725a30fae5 100644 --- a/app/services/slash_commands/interpret_service.rb +++ b/app/services/slash_commands/interpret_service.rb @@ -214,6 +214,18 @@ module SlashCommands @updates[:due_date] = nil end + desc do + "Toggle the Work In Progress status" + end + condition do + issuable.persisted? && + issuable.respond_to?(:work_in_progress?) && + current_user.can?(:"update_#{issuable.to_ability_name}", issuable) + end + command :wip do + @updates[:wip_event] = issuable.work_in_progress? ? 'unwip' : 'wip' + end + # This is a dummy command, so that it appears in the autocomplete commands desc 'CC' params '@user' diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 0c8446e7c3d..5ccaa5275b7 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -24,6 +24,7 @@ module SystemNoteService body = "Added #{commits_text}:\n\n" body << existing_commit_summary(noteable, existing_commits, oldrev) body << new_commit_summary(new_commits).join("\n") + body << "\n\n[Compare with previous version](#{diff_comparison_url(noteable, project, oldrev)})" create_note(noteable: noteable, project: project, author: author, note: body) end @@ -254,8 +255,7 @@ module SystemNoteService # # "Started branch `201-issue-branch-button`" def new_issue_branch(issue, project, author, branch) - h = Gitlab::Routing.url_helpers - link = h.namespace_project_compare_url(project.namespace, project, from: project.default_branch, to: branch) + link = url_helpers.namespace_project_compare_url(project.namespace, project, from: project.default_branch, to: branch) body = "Started branch [`#{branch}`](#{link})" create_note(noteable: issue, project: project, author: author, note: body) @@ -466,4 +466,20 @@ module SystemNoteService def escape_html(text) Rack::Utils.escape_html(text) end + + def url_helpers + @url_helpers ||= Gitlab::Routing.url_helpers + end + + def diff_comparison_url(merge_request, project, oldrev) + diff_id = merge_request.merge_request_diff.id + + url_helpers.diffs_namespace_project_merge_request_url( + project.namespace, + project, + merge_request.iid, + diff_id: diff_id, + start_sha: oldrev + ) + end end diff --git a/app/views/dashboard/groups/_empty_state.html.haml b/app/views/dashboard/groups/_empty_state.html.haml new file mode 100644 index 00000000000..f5222fe631e --- /dev/null +++ b/app/views/dashboard/groups/_empty_state.html.haml @@ -0,0 +1,7 @@ +.groups-empty-state + = custom_icon("icon_empty_groups") + + .text-content + %h4 A group is a collection of several projects. + %p If you organize your projects under a group, it works like a folder. + %p You can manage your group member’s permissions and access to each project in the group. diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml index caca91af536..1a679c51774 100644 --- a/app/views/dashboard/groups/index.html.haml +++ b/app/views/dashboard/groups/index.html.haml @@ -2,9 +2,12 @@ - header_title "Groups", dashboard_groups_path = render 'dashboard/groups_head' -%ul.content-list - - @group_members.each do |group_member| - - group = group_member.group - = render 'shared/groups/group', group: group, group_member: group_member +- if @group_members.empty? + = render 'empty_state' +- else + %ul.content-list + - @group_members.each do |group_member| + - group = group_member.group + = render 'shared/groups/group', group: group, group_member: group_member -= paginate @group_members, theme: 'gitlab' + = paginate @group_members, theme: 'gitlab' diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 3950b4f53e7..8aefdcb3d9b 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -1,10 +1,11 @@ .page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" } .sidebar-wrapper.nicescroll .sidebar-action-buttons - = link_to '#', class: 'nav-header-btn toggle-nav-collapse', title: "Open/Close" do + .nav-header-btn.toggle-nav-collapse{ title: "Open/Close" } %span.sr-only Toggle navigation = icon('bars') - = link_to '#', class: "nav-header-btn pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: {placement: 'right', container: 'body'} do + + %div{ class: "nav-header-btn pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: { placement: 'right', container: 'body' } } %span.sr-only Toggle navigation pinning = icon('fw thumb-tack') diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml index 8846cf8577c..f5344091cae 100644 --- a/app/views/projects/builds/_sidebar.html.haml +++ b/app/views/projects/builds/_sidebar.html.haml @@ -128,7 +128,7 @@ - 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') + = icon('right-arrow') = ci_icon_for_status(build.status) %span - if build.name diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 636beb73ec2..7a39064adc5 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -23,6 +23,8 @@ or a = link_to '.gitignore', add_special_file_path(@project, file_name: '.gitignore'), class: 'underlined-link' to this project. + %p + You will need to be owner or have the master permission level for the initial push, as the master branch is automatically protected. - if can?(current_user, :push_code, @project) %div{ class: container_class } diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index fda0592dd41..cc8cb134fb8 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -72,7 +72,7 @@ = link_to "#", class: 'btn js-toggle-button import_git' do = icon('git', text: 'Repo by URL') %div{ class: 'import_gitlab_project' } - - if gitlab_project_import_enabled? && current_user.is_admin? + - if gitlab_project_import_enabled? = link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do = icon('gitlab', text: 'GitLab export') diff --git a/app/views/shared/icons/_icon_empty_groups.svg b/app/views/shared/icons/_icon_empty_groups.svg new file mode 100644 index 00000000000..9228be05f03 --- /dev/null +++ b/app/views/shared/icons/_icon_empty_groups.svg @@ -0,0 +1 @@ +<svg width="249" height="368" viewBox="891 156 249 368" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><rect id="a" width="131" height="162" rx="10"/><mask id="e" x="0" y="0" width="131" height="162" fill="#fff"><use xlink:href="#a"/></mask><path d="M223.616 127.958V108.96c0-4.416-3.584-8-8.005-8h-23.985c-2.778 0-5.98 2.014-7.18 4.5l-5.07 10.5h-49.763c-5.527 0-9.996 4.475-9.996 9.997v53.005c0 5.513 4.475 9.997 9.996 9.997h84.01c5.525 0 9.994-4.477 9.994-9.998v-51.004z" id="b"/><mask id="f" x="0" y="0" width="104" height="88" fill="#fff"><use xlink:href="#b"/></mask><path d="M47 25h.996C53.52 25 58 29.472 58 34.99v20.02C58 60.526 53.52 65 47.996 65H10.004C4.48 65 0 60.528 0 55.01V34.99C0 29.474 4.48 25 10.004 25H11v-7c0-9.94 8.06-18 18-18s18 8.06 18 18v7zm-6 0H17v-7c0-6.627 5.373-12 12-12s12 5.373 12 12v7z" id="c"/><mask id="g" x="0" y="0" width="58" height="65" fill="#fff"><use xlink:href="#c"/></mask><path d="M0 10.008C0 4.48 4.476 0 10 0h218c5.523 0 10 4.473 10 10.008v140.94c0 5.53-4.062 11.882-9.08 14.196l-100.84 46.5c-5.015 2.31-13.142 2.312-18.16 0l-100.84-46.5C4.064 162.832 0 156.484 0 150.95V10.007z" id="d"/><mask id="h" x="0" y="0" width="238" height="213.417" fill="#fff"><use xlink:href="#d"/></mask></defs><g fill="none" fill-rule="evenodd" transform="translate(891 156)"><g transform="rotate(8 -266.528 490.3)"><use stroke="#E5E5E5" mask="url(#e)" stroke-width="8" fill="#FFF" xlink:href="#a"/><rect fill="#FC8A51" x="20" y="31" width="12" height="4" rx="2"/><rect fill="#FC8A51" x="60" y="31" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="36" y="31" width="20" height="4" rx="2"/><rect fill="#6B4FBB" x="20" y="65" width="20" height="4" rx="2"/><rect fill="#FDE5D8" x="44" y="65" width="20" height="4" rx="2"/><rect fill="#FC8A51" x="36" y="80" width="20" height="4" rx="2"/><rect fill="#FDE5D8" x="20" y="80" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="20" y="48" width="12" height="4" rx="2"/><rect fill="#FC8A51" x="36" y="48" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="60" y="80" width="12" height="4" rx="2"/><rect fill="#6B4FBB" x="52" y="48" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="68" y="48" width="12" height="4" rx="2"/></g><use stroke="#B5A7DD" mask="url(#f)" stroke-width="8" fill="#FFF" transform="rotate(5 171.616 144.96)" xlink:href="#b"/><path d="M58 132c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18-18-8.06-18-18z" fill="#C1E7D0"/><path d="M90.143 132c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M74.686 133.875l-3.18-3.18c-.29-.29-.77-.296-1.06-.005l-1.55 1.55c-.287.287-.29.766.004 1.06l4.92 4.92c.504.504 1.32.504 1.823 0l.654-.653 7.804-7.804c.3-.3.29-.77-.005-1.067l-1.578-1.58c-.302-.3-.775-.298-1.068-.004l-6.764 6.763z" fill="#31AF64"/><path d="M4 66c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18S4 75.94 4 66z" fill="#D5ECF7"/><path d="M36.143 66c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M22 55.714c5.68 0 10.286 4.605 10.286 10.286 0 5.68-4.605 10.286-10.286 10.286-3.45 0-6.505-1.7-8.37-4.307L22 66V55.714z" fill="#2D9FD8"/><g transform="rotate(-8 748.533 18.147)"><use stroke="#FDE5D8" mask="url(#g)" stroke-width="8" fill="#FFF" xlink:href="#c"/><path d="M31 46.584c1.766-.772 3-2.534 3-4.584 0-2.76-2.24-5-5-5s-5 2.24-5 5c0 2.05 1.234 3.812 3 4.584v3.42c0 1.1.895 1.996 2 1.996 1.112 0 2-.894 2-1.997v-3.42z" fill="#FC8A51"/></g><g transform="translate(0 154)"><use stroke="#E5E5E5" mask="url(#h)" stroke-width="8" fill="#FFF" xlink:href="#d"/><g opacity=".3"><path d="M141.837 104.53l-2.56-7.993-5.074-15.843c-.26-.815-1.398-.815-1.66 0l-5.074 15.843h-16.85l-5.075-15.843c-.26-.815-1.398-.815-1.66 0l-5.073 15.843-2.56 7.993c-.234.73.022 1.528.633 1.98l22.16 16.33 22.16-16.33c.61-.452.866-1.25.632-1.98" fill="#A1A1A1"/><path fill="#5C5C5C" d="M119.044 122.84l8.425-26.303h-16.85l8.424 26.304"/><path fill="#787878" d="M119.044 122.84l-8.425-26.303H98.81l20.232 26.304"/><path fill="#787878" d="M119.044 122.84l8.425-26.303h11.807l-20.233 26.304"/><path d="M98.812 96.537l-2.56 7.993c-.234.73.022 1.528.633 1.98l22.16 16.33L98.81 96.538z" fill="#A1A1A1"/><path d="M98.812 96.537h11.807l-5.075-15.843c-.26-.815-1.398-.815-1.66 0l-5.073 15.843z" fill="#5C5C5C"/><path d="M139.277 96.537l2.56 7.993c.234.73-.022 1.528-.634 1.98l-22.16 16.33 20.234-26.303z" fill="#A1A1A1"/><path d="M139.277 96.537H127.47l5.074-15.843c.26-.815 1.398-.815 1.66 0l5.073 15.843z" fill="#5C5C5C"/></g><path d="M57 18.29c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83H41c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83H77c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83h-16c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83h-16c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83h-16c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm17 24.693c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V28.35c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.633zm-202 0c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V28.35c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.633zm202 32.923c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V61.274c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm-202 0c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V61.274c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm202 32.923c0 1.01.895 1.828 2 1.828s2-.82 2-1.83V94.2c0-1.012-.895-1.83-2-1.83s-2 .818-2 1.83v14.63zm-202 0c0 1.01.895 1.828 2 1.828s2-.82 2-1.83V94.2c0-1.012-.895-1.83-2-1.83s-2 .818-2 1.83v14.63zm202 32.922c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V127.12c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm-202 0c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V127.12c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm179.023 19.555c-.988.452-1.388 1.55-.894 2.454.493.904 1.694 1.27 2.682.82l14.31-6.545c.99-.452 1.39-1.55.896-2.454-.494-.902-1.696-1.27-2.684-.817l-14.31 6.544zm-32.2 14.723c-.987.452-1.388 1.55-.894 2.454.493.904 1.695 1.27 2.683.818l14.31-6.544c.99-.45 1.39-1.55.895-2.454-.494-.903-1.695-1.27-2.683-.818l-14.31 6.544zm-32.2 14.724c-.987.45-1.387 1.55-.893 2.454.494.903 1.695 1.27 2.683.818l14.31-6.544c.99-.452 1.39-1.55.896-2.454-.495-.904-1.697-1.27-2.685-.818l-14.31 6.544zm-23.67-2.023l-12.186-5.57c-.987-.452-2.19-.086-2.683.817-.494.904-.093 2.003.895 2.454l12.185 5.573c.754.345 1.57.645 2.438.898 1.052.307 2.177-.224 2.513-1.187.335-.962-.246-1.99-1.298-2.298-.677-.197-1.302-.426-1.864-.684zM62.57 168.437c-.988-.452-2.19-.086-2.683.818-.494.903-.094 2.002.894 2.454l14.31 6.544c.988.45 2.19.085 2.683-.818.494-.904.094-2.003-.894-2.454l-14.312-6.544zm-32.2-14.723c-.988-.452-2.19-.086-2.683.818-.494.904-.093 2.003.895 2.454l14.31 6.544c.988.452 2.19.086 2.684-.818.494-.903.093-2.002-.895-2.454l-14.312-6.543z" fill="#EEE"/></g><g><path d="M104 18c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18-18-8.06-18-18z" fill="#FADFD9"/><path d="M136.143 18c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M119.43 8.994c0-.707.57-1.28 1.283-1.28h2.574c.71 0 1.284.57 1.284 1.28v10.298c0 .706-.57 1.28-1.283 1.28h-2.574c-.71 0-1.284-.57-1.284-1.28V8.994zm0 15.433c0-.71.57-1.284 1.283-1.284h2.574c.71 0 1.284.57 1.284 1.284V27c0 .71-.57 1.286-1.283 1.286h-2.574c-.71 0-1.284-.57-1.284-1.285v-2.573z" fill="#E75E40"/></g><g><path d="M213 89c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18-18-8.06-18-18z" fill="#F6D4DC"/><path d="M245.143 89c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M231 86.348l-3.603-3.602c-.288-.29-.766-.286-1.063.01l-1.578 1.578c-.3.302-.3.773-.01 1.063L228.348 89l-3.602 3.603c-.29.288-.286.766.01 1.063l1.578 1.578c.302.3.773.3 1.063.01L231 91.652l3.603 3.602c.288.29.766.286 1.063-.01l1.578-1.578c.3-.302.3-.773.01-1.063L233.652 89l3.602-3.603c.29-.288.286-.766-.01-1.063l-1.578-1.578c-.302-.3-.773-.3-1.063-.01L231 86.348z" fill="#D22852"/></g></g></svg>
\ No newline at end of file diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 1470a6e2550..a79356923b2 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -70,6 +70,7 @@ production: &base email_from: example@example.com email_display_name: GitLab email_reply_to: noreply@example.com + email_subject_suffix: '' # Email server smtp settings are in config/initializers/smtp_settings.rb.sample diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 195108b921b..c5ed2162c92 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -186,6 +186,7 @@ Settings.gitlab['email_enabled'] ||= true if Settings.gitlab['email_enabled'].ni Settings.gitlab['email_from'] ||= ENV['GITLAB_EMAIL_FROM'] || "gitlab@#{Settings.gitlab.host}" Settings.gitlab['email_display_name'] ||= ENV['GITLAB_EMAIL_DISPLAY_NAME'] || 'GitLab' Settings.gitlab['email_reply_to'] ||= ENV['GITLAB_EMAIL_REPLY_TO'] || "noreply@#{Settings.gitlab.host}" +Settings.gitlab['email_subject_suffix'] ||= ENV['GITLAB_EMAIL_SUBJECT_SUFFIX'] || "" Settings.gitlab['base_url'] ||= Settings.send(:build_base_gitlab_url) Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url) Settings.gitlab['user'] ||= 'git' diff --git a/doc/administration/environment_variables.md b/doc/administration/environment_variables.md index 7f53915a4d7..b4a953d1ccc 100644 --- a/doc/administration/environment_variables.md +++ b/doc/administration/environment_variables.md @@ -13,15 +13,17 @@ override certain values. Variable | Type | Description -------- | ---- | ----------- -`GITLAB_ROOT_PASSWORD` | string | Sets the password for the `root` user on installation -`GITLAB_HOST` | string | The full URL of the GitLab server (including `http://` or `https://`) -`RAILS_ENV` | string | The Rails environment; can be one of `production`, `development`, `staging` or `test` -`DATABASE_URL` | string | The database URL; is of the form: `postgresql://localhost/blog_development` -`GITLAB_EMAIL_FROM` | string | The e-mail address used in the "From" field in e-mails sent by GitLab -`GITLAB_EMAIL_DISPLAY_NAME` | string | The name used in the "From" field in e-mails sent by GitLab -`GITLAB_EMAIL_REPLY_TO` | string | The e-mail address used in the "Reply-To" field in e-mails sent by GitLab -`GITLAB_UNICORN_MEMORY_MIN` | integer | The minimum memory threshold (in bytes) for the Unicorn worker killer -`GITLAB_UNICORN_MEMORY_MAX` | integer | The maximum memory threshold (in bytes) for the Unicorn worker killer +`GITLAB_ROOT_PASSWORD` | string | Sets the password for the `root` user on installation +`GITLAB_HOST` | string | The full URL of the GitLab server (including `http://` or `https://`) +`RAILS_ENV` | string | The Rails environment; can be one of `production`, `development`, `staging` or `test` +`DATABASE_URL` | string | The database URL; is of the form: `postgresql://localhost/blog_development` +`GITLAB_EMAIL_FROM` | string | The e-mail address used in the "From" field in e-mails sent by GitLab +`GITLAB_EMAIL_DISPLAY_NAME` | string | The name used in the "From" field in e-mails sent by GitLab +`GITLAB_EMAIL_REPLY_TO` | string | The e-mail address used in the "Reply-To" field in e-mails sent by GitLab +`GITLAB_EMAIL_REPLY_TO` | string | The e-mail address used in the "Reply-To" field in e-mails sent by GitLab +`GITLAB_EMAIL_SUBJECT_SUFFIX` | string | The e-mail subject suffix used in e-mails sent by GitLab +`GITLAB_UNICORN_MEMORY_MIN` | integer | The minimum memory threshold (in bytes) for the Unicorn worker killer +`GITLAB_UNICORN_MEMORY_MAX` | integer | The maximum memory threshold (in bytes) for the Unicorn worker killer ## Complete database variables diff --git a/doc/update/8.12-to-8.13.md b/doc/update/8.12-to-8.13.md index d61c76673f7..411e4837e20 100644 --- a/doc/update/8.12-to-8.13.md +++ b/doc/update/8.12-to-8.13.md @@ -72,7 +72,7 @@ sudo -u git -H git checkout 8-13-stable-ee ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch --all --tags -sudo -u git -H git checkout v3.6.2 +sudo -u git -H git checkout v3.6.3 ``` ### 6. Update gitlab-workhorse diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md index 445c0ee8333..65ed9fae4ec 100644 --- a/doc/user/project/settings/import_export.md +++ b/doc/user/project/settings/import_export.md @@ -7,7 +7,8 @@ > that of the exporter. > - For existing installations, the project import option has to be enabled in > application settings (`/admin/application_settings`) under 'Import sources'. -> You will have to be an administrator to enable and use the import functionality. +> Ask your administrator if you don't see the **GitLab export** button when +> creating a new project. > - You can find some useful raketasks if you are an administrator in the > [import_export](../../../administration/raketasks/project_import_export.md) > raketask. diff --git a/doc/user/project/slash_commands.md b/doc/user/project/slash_commands.md index 1792a0c501d..5f6a6c6503e 100644 --- a/doc/user/project/slash_commands.md +++ b/doc/user/project/slash_commands.md @@ -27,4 +27,5 @@ do. | `/subscribe` | Subscribe | | `/unsubscribe` | Unsubscribe | | <code>/due <in 2 days | this Friday | December 31st></code> | Set due date | -| `/remove_due_date` | Remove due date | +| `/remove_due_date` | Remove due date | +| `/wip` | Toggle the Work In Progress status | diff --git a/features/profile/ssh_keys.feature b/features/profile/ssh_keys.feature deleted file mode 100644 index b0d5b748916..00000000000 --- a/features/profile/ssh_keys.feature +++ /dev/null @@ -1,20 +0,0 @@ -@profile -Feature: Profile SSH Keys - Background: - Given I sign in as a user - And I have ssh key "ssh-rsa Work" - And I visit profile keys page - - Scenario: I should see ssh keys - Then I should see my ssh keys - - Scenario: Add new ssh key - Given I should see new ssh key form - And I submit new ssh key "Laptop" - Then I should see new ssh key "Laptop" - - Scenario: Remove ssh key - Given I click link "Work" - And I click link "Remove" - Then I visit profile keys page - And I should not see "Work" ssh key diff --git a/features/steps/dashboard/new_project.rb b/features/steps/dashboard/new_project.rb index 2f0941e4113..cb36d6ae1a9 100644 --- a/features/steps/dashboard/new_project.rb +++ b/features/steps/dashboard/new_project.rb @@ -20,6 +20,7 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps expect(page).to have_link('GitLab.com') expect(page).to have_link('Google Code') expect(page).to have_link('Repo by URL') + expect(page).to have_link('GitLab export') end step 'I click on "Import project from GitHub"' do diff --git a/features/steps/profile/ssh_keys.rb b/features/steps/profile/ssh_keys.rb deleted file mode 100644 index a400488a532..00000000000 --- a/features/steps/profile/ssh_keys.rb +++ /dev/null @@ -1,46 +0,0 @@ -class Spinach::Features::ProfileSshKeys < Spinach::FeatureSteps - include SharedAuthentication - - step 'I should see my ssh keys' do - @user.keys.each do |key| - expect(page).to have_content(key.title) - end - end - - step 'I should see new ssh key form' do - expect(page).to have_content("Add an SSH key") - end - - step 'I submit new ssh key "Laptop"' do - fill_in "key_title", with: "Laptop" - fill_in "key_key", with: "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAzrEJUIR6Y03TCE9rIJ+GqTBvgb8t1jI9h5UBzCLuK4VawOmkLornPqLDrGbm6tcwM/wBrrLvVOqi2HwmkKEIecVO0a64A4rIYScVsXIniHRS6w5twyn1MD3sIbN+socBDcaldECQa2u1dI3tnNVcs8wi77fiRe7RSxePsJceGoheRQgC8AZ510UdIlO+9rjIHUdVN7LLyz512auAfYsgx1OfablkQ/XJcdEwDNgi9imI6nAXhmoKUm1IPLT2yKajTIC64AjLOnE0YyCh6+7RFMpiMyu1qiOCpdjYwTgBRiciNRZCH8xIedyCoAmiUgkUT40XYHwLuwiPJICpkAzp7Q== user@laptop" - click_button "Add key" - end - - step 'I should see new ssh key "Laptop"' do - key = Key.find_by(title: "Laptop") - expect(page).to have_content(key.title) - expect(page).to have_content(key.key) - expect(current_path).to eq profile_key_path(key) - end - - step 'I click link "Work"' do - click_link "Work" - end - - step 'I click link "Remove"' do - click_link "Remove" - end - - step 'I visit profile keys page' do - visit profile_keys_path - end - - step 'I should not see "Work" ssh key' do - expect(page).not_to have_content "Work" - end - - step 'I have ssh key "ssh-rsa Work"' do - create(:key, user: @user, title: "ssh-rsa Work", key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+L3TbFegm3k8QjejSwemk4HhlRh+DuN679Pc5ckqE/MPhVtE/+kZQDYCTB284GiT2aIoGzmZ8ee9TkaoejAsBwlA+Wz2Q3vhz65X6sMgalRwpdJx8kSEUYV8ZPV3MZvPo8KdNg993o4jL6G36GDW4BPIyO6FPZhfsawdf6liVD0Xo5kibIK7B9VoE178cdLQtLpS2YolRwf5yy6XR6hbbBGQR+6xrGOdP16eGZDb1CE2bMvvJijjloFqPscGktWOqW+nfh5txwFfBzlfARDTBsS8WZtg3Yoj1kn33kPsWRlgHfNutFRAIynDuDdQzQq8tTtVwm+Yi75RfcPHW8y3P Work") - end -end diff --git a/lib/api/award_emoji.rb b/lib/api/award_emoji.rb index 2461a783ea8..e9ccba3b465 100644 --- a/lib/api/award_emoji.rb +++ b/lib/api/award_emoji.rb @@ -8,16 +8,19 @@ module API awardable_string = awardable_type.pluralize awardable_id_string = "#{awardable_type}_id" + params do + requires :id, type: String, desc: 'The ID of a project' + requires :"#{awardable_id_string}", type: Integer, desc: "The ID of an Issue, Merge Request or Snippet" + end + [ ":id/#{awardable_string}/:#{awardable_id_string}/award_emoji", ":id/#{awardable_string}/:#{awardable_id_string}/notes/:note_id/award_emoji" ].each do |endpoint| - # Get a list of project +awardable+ award emoji - # - # Parameters: - # id (required) - The ID of a project - # awardable_id (required) - The ID of an issue or MR - # Example Request: - # GET /projects/:id/issues/:awardable_id/award_emoji + + desc 'Get a list of project +awardable+ award emoji' do + detail 'This feature was introduced in 8.9' + success Entities::AwardEmoji + end get endpoint do if can_read_awardable? awards = paginate(awardable.award_emoji) @@ -27,14 +30,13 @@ module API end end - # Get a specific award emoji - # - # Parameters: - # id (required) - The ID of a project - # awardable_id (required) - The ID of an issue or MR - # award_id (required) - The ID of the award - # Example Request: - # GET /projects/:id/issues/:awardable_id/award_emoji/:award_id + desc 'Get a specific award emoji' do + detail 'This feature was introduced in 8.9' + success Entities::AwardEmoji + end + params do + requires :award_id, type: Integer, desc: 'The ID of the award' + end get "#{endpoint}/:award_id" do if can_read_awardable? present awardable.award_emoji.find(params[:award_id]), with: Entities::AwardEmoji @@ -43,17 +45,14 @@ module API end end - # Award a new Emoji - # - # Parameters: - # id (required) - The ID of a project - # awardable_id (required) - The ID of an issue or mr - # name (required) - The name of a award_emoji (without colons) - # Example Request: - # POST /projects/:id/issues/:awardable_id/award_emoji + desc 'Award a new Emoji' do + detail 'This feature was introduced in 8.9' + success Entities::AwardEmoji + end + params do + requires :name, type: String, desc: 'The name of a award_emoji (without colons)' + end post endpoint do - required_attributes! [:name] - not_found!('Award Emoji') unless can_read_awardable? && can_award_awardable? award = awardable.create_award_emoji(params[:name], current_user) @@ -65,14 +64,13 @@ module API end end - # Delete a +awardables+ award emoji - # - # Parameters: - # id (required) - The ID of a project - # awardable_id (required) - The ID of an issue or MR - # award_emoji_id (required) - The ID of an award emoji - # Example Request: - # DELETE /projects/:id/issues/:issue_id/notes/:note_id/award_emoji/:award_id + desc 'Delete a +awardables+ award emoji' do + detail 'This feature was introduced in 8.9' + success Entities::AwardEmoji + end + params do + requires :award_id, type: Integer, desc: 'The ID of an award emoji' + end delete "#{endpoint}/:award_id" do award = awardable.award_emoji.find(params[:award_id]) diff --git a/lib/api/keys.rb b/lib/api/keys.rb index 2b723b79504..767f27ef334 100644 --- a/lib/api/keys.rb +++ b/lib/api/keys.rb @@ -4,10 +4,9 @@ module API before { authenticate! } resource :keys do - # Get single ssh key by id. Only available to admin users. - # - # Example Request: - # GET /keys/:id + desc 'Get single ssh key by id. Only available to admin users' do + success Entities::SSHKeyWithUser + end get ":id" do authenticated_as_admin! diff --git a/lib/banzai/filter/task_list_filter.rb b/lib/banzai/filter/task_list_filter.rb index 4efbcaf5c7f..9fa5f589f3e 100644 --- a/lib/banzai/filter/task_list_filter.rb +++ b/lib/banzai/filter/task_list_filter.rb @@ -2,29 +2,7 @@ require 'task_list/filter' module Banzai module Filter - # Work around a bug in the default TaskList::Filter that adds a `task-list` - # class to every list element, regardless of whether or not it contains a - # task list. - # - # This is a (hopefully) temporary fix, pending a new release of the - # task_list gem. - # - # See https://github.com/github/task_list/pull/60 - module ClassNamesFilter - def add_css_class(node, *new_class_names) - if new_class_names.include?('task-list') - # Don't add class to all lists - return - elsif new_class_names.include?('task-list-item') - super(node.parent, 'task-list') - end - - super(node, *new_class_names) - end - end - class TaskListFilter < TaskList::Filter - prepend ClassNamesFilter end end end diff --git a/lib/gitlab/sidekiq_middleware/arguments_logger.rb b/lib/gitlab/sidekiq_middleware/arguments_logger.rb index 7813091ec7b..82a59a7a87e 100644 --- a/lib/gitlab/sidekiq_middleware/arguments_logger.rb +++ b/lib/gitlab/sidekiq_middleware/arguments_logger.rb @@ -2,7 +2,7 @@ module Gitlab module SidekiqMiddleware class ArgumentsLogger def call(worker, job, queue) - Sidekiq.logger.info "arguments: #{job['args']}" + Sidekiq.logger.info "arguments: #{JSON.dump(job['args'])}" yield end end diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh index 6e987e7d9c9..1eaafdce389 100755 --- a/scripts/prepare_build.sh +++ b/scripts/prepare_build.sh @@ -16,21 +16,6 @@ retry() { } if [ -f /.dockerenv ] || [ -f ./dockerinit ]; then - mkdir -p vendor/apt - - # Install phantomjs package - pushd vendor/apt - PHANTOMJS_FILE="phantomjs-$PHANTOMJS_VERSION-linux-x86_64" - if [ ! -d "$PHANTOMJS_FILE" ]; then - curl -q -L "https://s3.amazonaws.com/gitlab-build-helpers/$PHANTOMJS_FILE.tar.bz2" | tar jx - fi - cp "$PHANTOMJS_FILE/bin/phantomjs" "/usr/bin/" - popd - - # Try to install packages - retry 'apt-get update -yqqq; apt-get -o dir::cache::archives="vendor/apt" install -y -qq --force-yes \ - libicu-dev libkrb5-dev cmake nodejs postgresql-client mysql-client unzip' - cp config/database.yml.mysql config/database.yml sed -i 's/username:.*/username: root/g' config/database.yml sed -i 's/password:.*/password:/g' config/database.yml diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 94c9edc91fe..742edd8ba3d 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -644,6 +644,20 @@ describe Projects::MergeRequestsController do end end + context 'POST remove_wip' do + it 'removes the wip status' do + merge_request.title = merge_request.wip_title + merge_request.save + + post :remove_wip, + namespace_id: merge_request.project.namespace.to_param, + project_id: merge_request.project.to_param, + id: merge_request.iid + + expect(merge_request.reload.title).to eq(merge_request.wipless_title) + end + end + context 'POST resolve_conflicts' do let(:json_response) { JSON.parse(response.body) } let!(:original_head_sha) { merge_request_with_conflicts.diff_head_sha } diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb index bf2b93c92fb..3f2da1c380c 100644 --- a/spec/features/issues/user_uses_slash_commands_spec.rb +++ b/spec/features/issues/user_uses_slash_commands_spec.rb @@ -99,5 +99,15 @@ feature 'Issues > User uses slash commands', feature: true, js: true do end end end + + describe 'toggling the WIP prefix from the title from note' do + let(:issue) { create(:issue, project: project) } + + it 'does not recognize the command nor create a note' do + write_note("/wip") + + expect(page).not_to have_content '/wip' + end + end end end diff --git a/spec/features/merge_requests/merge_request_versions_spec.rb b/spec/features/merge_requests/merge_request_versions_spec.rb index 22d9e42119d..23cee891bac 100644 --- a/spec/features/merge_requests/merge_request_versions_spec.rb +++ b/spec/features/merge_requests/merge_request_versions_spec.rb @@ -1,12 +1,13 @@ require 'spec_helper' feature 'Merge Request versions', js: true, feature: true do + let(:merge_request) { create(:merge_request, importing: true) } + let(:project) { merge_request.source_project } + before do login_as :admin - merge_request = create(:merge_request, importing: true) merge_request.merge_request_diffs.create(head_commit_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') merge_request.merge_request_diffs.create(head_commit_sha: '5937ac0a7beb003549fc5fd26fc247adbce4a52e') - project = merge_request.source_project visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request) end @@ -47,6 +48,16 @@ feature 'Merge Request versions', js: true, feature: true do end end + it 'has a path with comparison context' do + expect(page).to have_current_path diffs_namespace_project_merge_request_path( + project.namespace, + project, + merge_request.iid, + diff_id: 2, + start_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9' + ) + end + it 'should have correct value in the compare dropdown' do page.within '.mr-version-compare-dropdown' do expect(page).to have_content 'version 1' @@ -61,10 +72,6 @@ feature 'Merge Request versions', js: true, feature: true do expect(page).to have_content '4 changed files with 15 additions and 6 deletions' end - it 'show diff between new and old version' do - expect(page).to have_content '4 changed files with 15 additions and 6 deletions' - end - it 'should return to latest version when "Show latest version" button is clicked' do click_link 'Show latest version' page.within '.mr-version-dropdown' do diff --git a/spec/features/merge_requests/user_uses_slash_commands_spec.rb b/spec/features/merge_requests/user_uses_slash_commands_spec.rb index 22d9d1b9fd5..cb3cea3fd51 100644 --- a/spec/features/merge_requests/user_uses_slash_commands_spec.rb +++ b/spec/features/merge_requests/user_uses_slash_commands_spec.rb @@ -14,21 +14,66 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do let(:new_url_opts) { { merge_request: { source_branch: 'feature' } } } end - describe 'adding a due date from note' do + describe 'merge-request-only commands' do before do project.team << [user, :master] login_with(user) visit namespace_project_merge_request_path(project.namespace, project, merge_request) end - + after do wait_for_ajax end - it 'does not recognize the command nor create a note' do - write_note("/due 2016-08-28") + describe 'toggling the WIP prefix in the title from note' do + context 'when the current user can toggle the WIP prefix' do + it 'adds the WIP: prefix to the title' do + write_note("/wip") + + expect(page).not_to have_content '/wip' + expect(page).to have_content 'Your commands have been executed!' + + expect(merge_request.reload.work_in_progress?).to eq true + end + + it 'removes the WIP: prefix from the title' do + merge_request.title = merge_request.wip_title + merge_request.save + write_note("/wip") + + expect(page).not_to have_content '/wip' + expect(page).to have_content 'Your commands have been executed!' + + expect(merge_request.reload.work_in_progress?).to eq false + end + end + + context 'when the current user cannot toggle the WIP prefix' do + let(:guest) { create(:user) } + before do + project.team << [guest, :guest] + logout + login_with(guest) + visit namespace_project_merge_request_path(project.namespace, project, merge_request) + end + + it 'does not change the WIP prefix' do + write_note("/wip") + + expect(page).not_to have_content '/wip' + expect(page).not_to have_content 'Your commands have been executed!' + + expect(merge_request.reload.work_in_progress?).to eq false + end + end + end + + describe 'adding a due date from note' do + it 'does not recognize the command nor create a note' do + write_note('/due 2016-08-28') - expect(page).not_to have_content '/due 2016-08-28' + expect(page).not_to have_content '/due 2016-08-28' + end end end end diff --git a/spec/features/profiles/keys_spec.rb b/spec/features/profiles/keys_spec.rb index 3b20d38c520..eb1050d21c6 100644 --- a/spec/features/profiles/keys_spec.rb +++ b/spec/features/profiles/keys_spec.rb @@ -1,18 +1,57 @@ require 'rails_helper' -describe 'Profile > SSH Keys', feature: true do +feature 'Profile > SSH Keys', feature: true do let(:user) { create(:user) } before do login_as(user) - visit profile_keys_path end - describe 'User adds an SSH key' do - it 'auto-populates the title', js: true do + describe 'User adds a key' do + before do + visit profile_keys_path + end + + scenario 'auto-populates the title', js: true do fill_in('Key', with: attributes_for(:key).fetch(:key)) expect(find_field('Title').value).to eq 'dummy@gitlab.com' end + + scenario 'saves the new key' do + attrs = attributes_for(:key) + + fill_in('Key', with: attrs[:key]) + fill_in('Title', with: attrs[:title]) + click_button('Add key') + + expect(page).to have_content("Title: #{attrs[:title]}") + expect(page).to have_content(attrs[:key]) + end + end + + scenario 'User sees their keys' do + key = create(:key, user: user) + visit profile_keys_path + + expect(page).to have_content(key.title) + end + + scenario 'User removes a key via the key index' do + create(:key, user: user) + visit profile_keys_path + + click_link('Remove') + + expect(page).to have_content('Your SSH keys (0)') + end + + scenario 'User removes a key via its details page' do + key = create(:key, user: user) + visit profile_key_path(key) + + click_link('Remove') + + expect(page).to have_content('Your SSH keys (0)') end end diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb index 09cd6369881..f32834801a0 100644 --- a/spec/features/projects/import_export/import_file_spec.rb +++ b/spec/features/projects/import_export/import_file_spec.rb @@ -86,14 +86,14 @@ feature 'Import/Export - project import integration test', feature: true, js: tr login_as(normal_user) end - scenario 'non-admin user is not allowed to import a project' do + scenario 'non-admin user is allowed to import a project' do expect(Project.all.count).to be_zero visit new_project_path fill_in :project_path, with: 'test-project-path', visible: true - expect(page).not_to have_content('GitLab export') + expect(page).to have_content('GitLab export') end end diff --git a/spec/finders/access_requests_finder_spec.rb b/spec/finders/access_requests_finder_spec.rb index 6cc90299417..626513200e4 100644 --- a/spec/finders/access_requests_finder_spec.rb +++ b/spec/finders/access_requests_finder_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' describe AccessRequestsFinder, services: true do let(:user) { create(:user) } let(:access_requester) { create(:user) } - let(:project) { create(:project) } - let(:group) { create(:group) } + let(:project) { create(:project, :public) } + let(:group) { create(:group, :public) } before do project.request_access(access_requester) diff --git a/spec/lib/banzai/filter/task_list_filter_spec.rb b/spec/lib/banzai/filter/task_list_filter_spec.rb deleted file mode 100644 index 569cbc885c7..00000000000 --- a/spec/lib/banzai/filter/task_list_filter_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'spec_helper' - -describe Banzai::Filter::TaskListFilter, lib: true do - include FilterSpecHelper - - it 'does not apply `task-list` class to non-task lists' do - exp = act = %(<ul><li>Item</li></ul>) - expect(filter(act).to_html).to eq exp - end - - it 'applies `task-list` to single-item task lists' do - act = filter('<ul><li>[ ] Task 1</li></ul>') - - expect(act.to_html).to start_with '<ul class="task-list">' - end -end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index cd8578b6f49..0e4130e8a3a 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -831,6 +831,7 @@ describe Notify do let(:user) { create(:user, email: 'old-email@mail.com') } before do + stub_config_setting(email_subject_suffix: 'A Nice Suffix') perform_enqueued_jobs do user.email = "new-email@mail.com" user.save @@ -847,7 +848,7 @@ describe Notify do end it 'has the correct subject' do - is_expected.to have_subject "Confirmation instructions" + is_expected.to have_subject /^Confirmation instructions/ end it 'includes a link to the site' do diff --git a/spec/mailers/shared/notify.rb b/spec/mailers/shared/notify.rb index 5c9851f14c7..3956d05060b 100644 --- a/spec/mailers/shared/notify.rb +++ b/spec/mailers/shared/notify.rb @@ -37,6 +37,16 @@ shared_examples 'an email sent from GitLab' do reply_to = subject.header[:reply_to].addresses expect(reply_to).to eq([gitlab_sender_reply_to]) end + + context 'when custom suffix for email subject is set' do + before do + stub_config_setting(email_subject_suffix: 'A Nice Suffix') + end + + it 'ends the subject with the suffix' do + is_expected.to have_subject /\ \| A Nice Suffix$/ + end + end end shared_examples 'an email that contains a header with author username' do diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index bda23eaed43..485121701af 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -174,7 +174,7 @@ describe Member, models: true do describe '.add_user' do %w[project group].each do |source_type| context "when source is a #{source_type}" do - let!(:source) { create(source_type) } + let!(:source) { create(source_type, :public) } let!(:user) { create(:user) } let!(:admin) { create(:admin) } diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 580a3235127..9d7be2429ed 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -287,6 +287,46 @@ describe MergeRequest, models: true do end end + describe "#wipless_title" do + ['WIP ', 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', ' [WIP] WIP [WIP] WIP: WIP '].each do |wip_prefix| + it "removes the '#{wip_prefix}' prefix" do + wipless_title = subject.title + subject.title = "#{wip_prefix}#{subject.title}" + + expect(subject.wipless_title).to eq wipless_title + end + + it "is satisfies the #work_in_progress? method" do + subject.title = "#{wip_prefix}#{subject.title}" + subject.title = subject.wipless_title + + expect(subject.work_in_progress?).to eq false + end + end + end + + describe "#wip_title" do + it "adds the WIP: prefix to the title" do + wip_title = "WIP: #{subject.title}" + + expect(subject.wip_title).to eq wip_title + end + + it "does not add the WIP: prefix multiple times" do + wip_title = "WIP: #{subject.title}" + subject.title = subject.wip_title + subject.title = subject.wip_title + + expect(subject.wip_title).to eq wip_title + end + + it "is satisfies the #work_in_progress? method" do + subject.title = subject.wip_title + + expect(subject.work_in_progress?).to eq true + end + end + describe '#can_remove_source_branch?' do let(:user) { create(:user) } let(:user2) { create(:user) } diff --git a/spec/services/slash_commands/interpret_service_spec.rb b/spec/services/slash_commands/interpret_service_spec.rb index 5b1edba87a1..ae4d286d250 100644 --- a/spec/services/slash_commands/interpret_service_spec.rb +++ b/spec/services/slash_commands/interpret_service_spec.rb @@ -165,6 +165,23 @@ describe SlashCommands::InterpretService, services: true do end end + shared_examples 'wip command' do + it 'returns wip_event: "wip" if content contains /wip' do + _, updates = service.execute(content, issuable) + + expect(updates).to eq(wip_event: 'wip') + end + end + + shared_examples 'unwip command' do + it 'returns wip_event: "unwip" if content contains /wip' do + issuable.update(title: issuable.wip_title) + _, updates = service.execute(content, issuable) + + expect(updates).to eq(wip_event: 'unwip') + end + end + shared_examples 'empty command' do it 'populates {} if content contains an unsupported command' do _, updates = service.execute(content, issuable) @@ -376,6 +393,16 @@ describe SlashCommands::InterpretService, services: true do let(:issuable) { issue } end + it_behaves_like 'wip command' do + let(:content) { '/wip' } + let(:issuable) { merge_request } + end + + it_behaves_like 'unwip command' do + let(:content) { '/wip' } + let(:issuable) { merge_request } + end + it_behaves_like 'empty command' do let(:content) { '/remove_due_date' } let(:issuable) { merge_request } diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 3d854a959f3..b16840a1238 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -40,6 +40,12 @@ describe SystemNoteService, services: true do describe 'note body' do let(:note_lines) { subject.note.split("\n").reject(&:blank?) } + describe 'comparison diff link line' do + it 'adds the comparison text' do + expect(note_lines[2]).to match "[Compare with previous version]" + end + end + context 'without existing commits' do it 'adds a message header' do expect(note_lines[0]).to eq "Added #{new_commits.size} commits:" |