diff options
91 files changed, 571 insertions, 233 deletions
diff --git a/CHANGELOG b/CHANGELOG index 2d760c2f53b..f7c278823ee 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.4.0 (unreleased) + - The default GitLab logo now acts as a loading indicator + - Accept 2xx status codes for successful Web hook triggers (Stan Hu) - Fix missing date of month in network graph when commits span a month (Stan Hu) - Expire view caches when application settings change (e.g. Gravatar disabled) (Stan Hu) - Don't notify users twice if they are both project watchers and subscribers (Stan Hu) @@ -14,24 +16,29 @@ v 8.4.0 (unreleased) - Add "Frequently used" category to emoji picker - Add CAS support (tduehr) - Add link to merge request on build detail page + - Fix: Problem with projects ending with .keys (Jose Corcuera) - Revert back upvote and downvote button to the issue and MR pages - Swap position of Assignee and Author selector on Issuables (Zeger-Jan van de Weg) - Add system hook messages for project rename and transfer (Steve Norman) - Fix version check image in Safari - Show 'All' tab by default in the builds page + - Add Open Graph and Twitter Card data to all pages - Fix API project lookups when querying with a namespace with dots (Stan Hu) - Update version check images to use SVG - Validate README format before displaying - Enable Microsoft Azure OAuth2 support (Janis Meybohm) - Properly set task-list class on single item task lists - - Add file finder feature in tree view (koreamic) + - Add file finder feature in tree view (Kyungchul Shin) - Ajax filter by message for commits page + - API: Add support for deleting a tag via the API (Robert Schilling) v 8.3.3 (unreleased) - Preserve CE behavior with JIRA integration by only calling API if URL is set - Get "Merge when build succeeds" to work when commits were pushed to MR target branch while builds were running + - Suppress e-mails on failed builds if allow_failure is set (Stan Hu) - Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu) - Enable "Add key" button when user fills in a proper key (Stan Hu) + - Fix error in processing reply-by-email messages (Jason Lee) v 8.3.2 - Change single user API endpoint to return more detailed data (Michael Potthoff) @@ -67,10 +67,6 @@ gem 'grape', '~> 0.13.0' gem 'grape-entity', '~> 0.4.2' gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' -# Format dates and times -# based on human-friendly examples -gem "stamp", '~> 0.6.0' - # Pagination gem "kaminari", "~> 0.16.3" diff --git a/Gemfile.lock b/Gemfile.lock index f5391ef9e48..96bf2bd1a2e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -734,7 +734,6 @@ GEM actionpack (>= 3.0) activesupport (>= 3.0) sprockets (>= 2.8, < 4.0) - stamp (0.6.0) state_machines (0.4.0) state_machines-activemodel (0.3.0) activemodel (~> 4.1) @@ -978,7 +977,6 @@ DEPENDENCIES spring-commands-spinach (~> 1.0.0) spring-commands-teaspoon (~> 0.0.2) sprockets (~> 2.12.3) - stamp (~> 0.6.0) state_machines-activerecord (~> 0.3.0) task_list (~> 1.0.2) teaspoon (~> 1.0.0) diff --git a/app/assets/javascripts/dropzone_input.js.coffee b/app/assets/javascripts/dropzone_input.js.coffee index 30a35a04339..c714c0fa939 100644 --- a/app/assets/javascripts/dropzone_input.js.coffee +++ b/app/assets/javascripts/dropzone_input.js.coffee @@ -66,7 +66,7 @@ class @DropzoneInput success: (header, response) -> child = $(dropzone[0]).children("textarea") - $(child).val $(child).val() + formatLink(response.link) + "\n" + $(child).val $(child).val() + response.link.markdown + "\n" return error: (temp, errorMessage) -> @@ -99,11 +99,6 @@ class @DropzoneInput child = $(dropzone[0]).children("textarea") - formatLink = (link) -> - text = "[#{link.alt}](#{link.url})" - text = "!#{text}" if link.is_image - text - handlePaste = (event) -> pasteEvent = event.originalEvent if pasteEvent.clipboardData and pasteEvent.clipboardData.items @@ -162,7 +157,7 @@ class @DropzoneInput closeAlertMessage() success: (e, textStatus, response) -> - insertToTextArea(filename, formatLink(response.responseJSON.link)) + insertToTextArea(filename, response.responseJSON.link.markdown) error: (response) -> showError(response.responseJSON.message) @@ -202,8 +197,3 @@ class @DropzoneInput e.preventDefault() $(@).closest('.gfm-form').find('.div-dropzone').click() return - - formatLink: (link) -> - text = "[#{link.alt}](#{link.url})" - text = "!#{text}" if link.is_image - text diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index 282aaf2219b..984b4b91216 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -138,6 +138,7 @@ */ .event-last-push { overflow: auto; + width: 100%; .event-last-push-text { @include str-truncated(100%); padding: 5px 0; diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 9da273a0b6b..d4b44004f4f 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -94,8 +94,16 @@ } .cross-project-reference { - font-weight: bold; color: $gl-link-color; + + span { + white-space: nowrap; + width: 85%; + overflow: hidden; + position: relative; + display: inline-block; + text-overflow: ellipsis; + } button { float: right; diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index be6ef43e49c..0133a0d6822 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -415,6 +415,7 @@ ul.nav.nav-projects-tabs { border-bottom: 1px solid #EEE; margin: 0 -16px; padding: 0 $gl-padding; + height: 57px; ul.left-top-menu { display: inline-block; diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index f7f7a1a02d3..2b9bad9c9ea 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -206,7 +206,7 @@ module ApplicationHelper element = content_tag :time, time.to_s, class: "#{html_class} js-timeago js-timeago-pending", datetime: time.getutc.iso8601, - title: time.in_time_zone.stamp('Aug 21, 2011 9:23pm'), + title: time.in_time_zone.to_s(:medium), data: { toggle: 'tooltip', placement: placement, container: 'body' } unless skip_js diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index c12456a187f..a7080ddfefb 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -80,7 +80,7 @@ module IssuesHelper xml.link href: namespace_project_issue_url(issue.project.namespace, issue.project, issue) xml.title truncate(issue.title, length: 80) - xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") + xml.updated issue.created_at.xmlschema xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(issue.author_email)) xml.author do |author| xml.name issue.author_name diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index bb12d43f397..99d7df64a83 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -19,7 +19,7 @@ module SortingHelper end def sort_title_recently_updated - 'Recently updated' + 'Last updated' end def sort_title_oldest_created @@ -27,7 +27,7 @@ module SortingHelper end def sort_title_recently_created - 'Recently created' + 'Last created' end def sort_title_milestone_soon diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index af1d7562ebe..7ee276255a0 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -121,9 +121,9 @@ class GlobalMilestone def expires_at if due_date if due_date.past? - "expired at #{due_date.stamp("Aug 21, 2011")}" + "expired on #{due_date.to_s(:medium)}" else - "expires at #{due_date.stamp("Aug 21, 2011")}" + "expires on #{due_date.to_s(:medium)}" end end end diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index 7164c0e1e90..d0aadfc330a 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -61,7 +61,7 @@ class WebHook < ActiveRecord::Base basic_auth: auth) end - [response.code == 200, ActionView::Base.full_sanitizer.sanitize(response.to_s)] + [(response.code >= 200 && response.code < 300), ActionView::Base.full_sanitizer.sanitize(response.to_s)] rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout => e logger.error("WebHook Error => #{e}") [false, e.to_s] diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 550d14d4c39..c9a0ad8b9b6 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -112,9 +112,9 @@ class Milestone < ActiveRecord::Base def expires_at if due_date if due_date.past? - "expired at #{due_date.stamp("Aug 21, 2011")}" + "expired on #{due_date.to_s(:medium)}" else - "expires at #{due_date.stamp("Aug 21, 2011")}" + "expires on #{due_date.to_s(:medium)}" end end end diff --git a/app/models/project_services/builds_email_service.rb b/app/models/project_services/builds_email_service.rb index 92c9b13c9b9..f6313255cbb 100644 --- a/app/models/project_services/builds_email_service.rb +++ b/app/models/project_services/builds_email_service.rb @@ -73,12 +73,16 @@ class BuildsEmailService < Service when 'success' !notify_only_broken_builds? when 'failed' - true + !allow_failure?(data) else false end end + def allow_failure?(data) + data[:build_allow_failure] == true + end + def all_recipients(data) all_recipients = recipients.split(',') diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index b6c01c32d9a..f6571fc063e 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -40,15 +40,10 @@ class JiraService < IssueTrackerService end def help - line1 = 'Setting `project_url`, `issues_url` and `new_issue_url` will '\ + 'Setting `project_url`, `issues_url` and `new_issue_url` will '\ 'allow a user to easily navigate to the Jira issue tracker. See the '\ '[integration doc](http://doc.gitlab.com/ce/integration/external-issue-tracker.html) '\ 'for details.' - - line2 = 'Support for referencing commits and automatic closing of Jira issues directly '\ - 'from GitLab is [available in GitLab EE.](http://doc.gitlab.com/ee/integration/jira.html)' - - [line1, line2].join("\n\n") end def title diff --git a/app/services/projects/download_service.rb b/app/services/projects/download_service.rb index 99f22293d0d..6386f57fb0d 100644 --- a/app/services/projects/download_service.rb +++ b/app/services/projects/download_service.rb @@ -16,13 +16,7 @@ module Projects uploader.download!(@url) uploader.store! - filename = uploader.image? ? uploader.file.basename : uploader.file.filename - - { - 'alt' => filename, - 'url' => uploader.secure_url, - 'is_image' => uploader.image? - } + uploader.to_h end private diff --git a/app/services/projects/upload_service.rb b/app/services/projects/upload_service.rb index 279550d6f4a..012e82a7704 100644 --- a/app/services/projects/upload_service.rb +++ b/app/services/projects/upload_service.rb @@ -10,13 +10,7 @@ module Projects uploader = FileUploader.new(@project) uploader.store!(@file) - filename = uploader.image? ? uploader.file.basename : uploader.file.filename - - { - alt: filename, - url: uploader.secure_url, - is_image: uploader.image? - } + uploader.to_h end private diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb index ac920119a85..86d24469e05 100644 --- a/app/uploaders/file_uploader.rb +++ b/app/uploaders/file_uploader.rb @@ -30,4 +30,19 @@ class FileUploader < CarrierWave::Uploader::Base def secure_url File.join("/uploads", @secret, file.filename) end + + def to_h + filename = image? ? self.file.basename : self.file.filename + escaped_filename = filename.gsub("]", "\\]") + + markdown = "[#{escaped_filename}](#{self.secure_url})" + markdown.prepend("!") if image? + + { + alt: filename, + url: self.secure_url, + is_image: image?, + markdown: markdown + } + end end diff --git a/app/validators/namespace_validator.rb b/app/validators/namespace_validator.rb index 10e35ce665a..7a35958cc5f 100644 --- a/app/validators/namespace_validator.rb +++ b/app/validators/namespace_validator.rb @@ -17,6 +17,7 @@ class NamespaceValidator < ActiveModel::EachValidator hooks issues merge_requests + new notes profile projects diff --git a/app/views/admin/abuse_reports/_abuse_report.html.haml b/app/views/admin/abuse_reports/_abuse_report.html.haml index cf50a376e11..2ab01704b77 100644 --- a/app/views/admin/abuse_reports/_abuse_report.html.haml +++ b/app/views/admin/abuse_reports/_abuse_report.html.haml @@ -23,6 +23,9 @@ data: { confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?" }, remote: true, method: :delete, class: "btn btn-xs btn-remove js-remove-tr" %td - - if user + - if user && !user.blocked? = link_to 'Block user', block_admin_user_path(user), data: {confirm: 'USER WILL BE BLOCKED! Are you sure?'}, method: :put, class: "btn btn-xs" + - else + .btn.btn-xs.disabled + Already Blocked = link_to 'Remove report', [:admin, abuse_report], remote: true, method: :delete, class: "btn btn-xs btn-close js-remove-tr" diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index 296497a4cd4..f7fd156b84a 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -30,7 +30,7 @@ %li %span.light Created on: %strong - = @group.created_at.stamp("March 1, 1999") + = @group.created_at.to_s(:medium) .panel.panel-default .panel-heading diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index 5260eadf95b..0c986af4a95 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -38,7 +38,7 @@ %li %span.light Created on: %strong - = @project.created_at.stamp("March 1, 1999") + = @project.created_at.to_s(:medium) %li %span.light http: diff --git a/app/views/admin/users/_profile.html.haml b/app/views/admin/users/_profile.html.haml index 7d11edc79e2..6bc217f84cc 100644 --- a/app/views/admin/users/_profile.html.haml +++ b/app/views/admin/users/_profile.html.haml @@ -4,7 +4,7 @@ %ul.well-list %li %span.light Member since - %strong= user.created_at.stamp("Aug 21, 2011") + %strong= user.created_at.to_s(:medium) - unless user.public_email.blank? %li %span.light E-mail: diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index 0848504b7a6..2c2450d4117 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -58,12 +58,12 @@ %li %span.light Member since: %strong - = @user.created_at.stamp("Nov 12, 2031") + = @user.created_at.to_s(:medium) - if @user.confirmed_at %li %span.light Confirmed at: %strong - = @user.confirmed_at.stamp("Nov 12, 2031") + = @user.confirmed_at.to_s(:medium) - else %li %span.light Confirmed: @@ -74,7 +74,7 @@ %span.light Current sign-in at: %strong - if @user.current_sign_in_at - = @user.current_sign_in_at.stamp("Nov 12, 2031") + = @user.current_sign_in_at.to_s(:medium) - else never @@ -82,7 +82,7 @@ %span.light Last sign-in at: %strong - if @user.last_sign_in_at - = @user.last_sign_in_at.stamp("Nov 12, 2031") + = @user.last_sign_in_at.to_s(:medium) - else never diff --git a/app/views/dashboard/issues.atom.builder b/app/views/dashboard/issues.atom.builder index 07bda1c77f8..0d7b1b30dc3 100644 --- a/app/views/dashboard/issues.atom.builder +++ b/app/views/dashboard/issues.atom.builder @@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link href: issues_dashboard_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: issues_dashboard_url, rel: "alternate", type: "text/html" xml.id issues_dashboard_url - xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any? + xml.updated @issues.first.created_at.xmlschema if @issues.any? @issues.each do |issue| issue_to_atom(xml, issue) diff --git a/app/views/dashboard/projects/index.atom.builder b/app/views/dashboard/projects/index.atom.builder index c8c219f4cca..2e2712c5146 100644 --- a/app/views/dashboard/projects/index.atom.builder +++ b/app/views/dashboard/projects/index.atom.builder @@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link href: dashboard_projects_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: dashboard_projects_url, rel: "alternate", type: "text/html" xml.id dashboard_projects_url - xml.updated @events.latest_update_time.strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? + xml.updated @events.latest_update_time.xmlschema if @events.any? @events.each do |event| event_to_atom(xml, event) diff --git a/app/views/groups/issues.atom.builder b/app/views/groups/issues.atom.builder index 66fe7e25871..486d1d8587a 100644 --- a/app/views/groups/issues.atom.builder +++ b/app/views/groups/issues.atom.builder @@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link href: issues_dashboard_url(format: :atom, private_token: @user.private_token), rel: "self", type: "application/atom+xml" xml.link href: issues_dashboard_url, rel: "alternate", type: "text/html" xml.id issues_dashboard_url - xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any? + xml.updated @issues.first.created_at.xmlschema if @issues.any? @issues.each do |issue| issue_to_atom(xml, issue) diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder index 7ea574434c3..5cc0f5e1d2e 100644 --- a/app/views/groups/show.atom.builder +++ b/app/views/groups/show.atom.builder @@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link href: group_url(@group, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: group_url(@group), rel: "alternate", type: "text/html" xml.id group_url(@group) - xml.updated @events.latest_update_time.strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? + xml.updated @events.latest_update_time.xmlschema if @events.any? @events.each do |event| event_to_atom(xml, event) diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml index 4361f67a74d..3dd2595f1ad 100644 --- a/app/views/notify/repository_push_email.html.haml +++ b/app/views/notify/repository_push_email.html.haml @@ -17,7 +17,7 @@ %strong #{link_to(commit.short_id, namespace_project_commit_url(@message.project_namespace, @message.project, commit))} %div %span by #{commit.author_name} - %i at #{commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")} + %i at #{commit.committed_date.to_s(:iso8601)} %pre.commit-message = commit.safe_message diff --git a/app/views/notify/repository_push_email.text.haml b/app/views/notify/repository_push_email.text.haml index aa0e263b6df..53869e36b28 100644 --- a/app/views/notify/repository_push_email.text.haml +++ b/app/views/notify/repository_push_email.text.haml @@ -8,7 +8,7 @@ \ = @message.reverse_compare? ? "Deleted commits:" : "Commits:" - @message.commits.each do |commit| - #{commit.short_id} by #{commit.author_name} at #{commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")} + #{commit.short_id} by #{commit.author_name} at #{commit.committed_date.to_s(:iso8601)} #{commit.safe_message} \- - - - - \ diff --git a/app/views/profiles/keys/_key_details.html.haml b/app/views/profiles/keys/_key_details.html.haml index 0ca8bd95157..3bd1f1af162 100644 --- a/app/views/profiles/keys/_key_details.html.haml +++ b/app/views/profiles/keys/_key_details.html.haml @@ -10,7 +10,7 @@ %strong= @key.title %li %span.light Created on: - %strong= @key.created_at.stamp("Aug 21, 2011") + %strong= @key.created_at.to_s(:medium) .col-md-8 %p diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml index 0cd9ce1f371..6c631228002 100644 --- a/app/views/projects/commits/_commits.html.haml +++ b/app/views/projects/commits/_commits.html.haml @@ -6,7 +6,7 @@ .col-md-2.hidden-xs.hidden-sm %h5.commits-row-date %i.fa.fa-calendar - %span= day.stamp("28 Aug, 2010") + %span= day.strftime('%d %b, %Y') .light = pluralize(commits.count, 'commit') .col-md-10.col-sm-12 diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder index 7ffa7317196..e310fafd82c 100644 --- a/app/views/projects/commits/show.atom.builder +++ b/app/views/projects/commits/show.atom.builder @@ -4,14 +4,14 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link href: namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: namespace_project_commits_url(@project.namespace, @project, @ref), rel: "alternate", type: "text/html" xml.id namespace_project_commits_url(@project.namespace, @project, @ref) - xml.updated @commits.first.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ") if @commits.any? + xml.updated @commits.first.committed_date.xmlschema if @commits.any? @commits.each do |commit| xml.entry do xml.id namespace_project_commit_url(@project.namespace, @project, id: commit.id) xml.link href: namespace_project_commit_url(@project.namespace, @project, id: commit.id) xml.title truncate(commit.title, length: 80) - xml.updated commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ") + xml.updated commit.committed_date.xmlschema xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(commit.author_email)) xml.author do |author| xml.name commit.author_name diff --git a/app/views/projects/issues/index.atom.builder b/app/views/projects/issues/index.atom.builder index dc8e477185b..ee8a9414657 100644 --- a/app/views/projects/issues/index.atom.builder +++ b/app/views/projects/issues/index.atom.builder @@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link href: namespace_project_issues_url(@project.namespace, @project, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: namespace_project_issues_url(@project.namespace, @project), rel: "alternate", type: "text/html" xml.id namespace_project_issues_url(@project.namespace, @project) - xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any? + xml.updated @issues.first.created_at.xmlschema if @issues.any? @issues.each do |issue| issue_to_atom(xml, issue) diff --git a/app/views/projects/show.atom.builder b/app/views/projects/show.atom.builder index 15c49767556..d6762219108 100644 --- a/app/views/projects/show.atom.builder +++ b/app/views/projects/show.atom.builder @@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link href: namespace_project_url(@project.namespace, @project, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: namespace_project_url(@project.namespace, @project), rel: "alternate", type: "text/html" xml.id namespace_project_url(@project.namespace, @project) - xml.updated @events.latest_update_time.strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? + xml.updated @events.latest_update_time.xmlschema if @events.any? @events.each do |event| event_to_atom(xml, event) diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 79c5cc7f40a..2299112bec7 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -54,14 +54,6 @@ = f.collection_select :label_ids, issuable.project.labels.all, :id, :name, { selected: issuable.label_ids }, multiple: true, class: 'select2 js-select2', data: { placeholder: "Select labels" } - .block - .title - Cross-project reference - .cross-project-reference - %span#cross-project-reference - = cross_project_reference(@project, issuable) - = clipboard_button(clipboard_target: 'span#cross-project-reference') - = render "shared/issuable/participants", participants: issuable.participants(current_user) - if current_user @@ -77,7 +69,16 @@ You're not receiving notifications from this thread. .subscribed{class: ( 'hidden' unless subscribed )} You're receiving notifications because you're subscribed to this thread. + - project_ref = cross_project_reference(@project, issuable) + .block + .title + .cross-project-reference + %span#cross-project-reference + Reference: + %a{href: '#', title:project_ref} + = project_ref + = clipboard_button(clipboard_target: 'span#cross-project-reference') :javascript new Subscription("#{toggle_subscription_path(issuable)}"); - new IssuableContext(); + new IssuableContext();
\ No newline at end of file diff --git a/app/views/users/show.atom.builder b/app/views/users/show.atom.builder index 2fe5b7fac83..114d1e7a379 100644 --- a/app/views/users/show.atom.builder +++ b/app/views/users/show.atom.builder @@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link href: user_url(@user, :atom), rel: "self", type: "application/atom+xml" xml.link href: user_url(@user), rel: "alternate", type: "text/html" xml.id user_url(@user) - xml.updated @events.latest_update_time.strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? + xml.updated @events.latest_update_time.xmlschema if @events.any? @events.each do |event| event_to_atom(xml, event) diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 0bca8177e14..ce17fc7bca1 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -21,7 +21,7 @@ %span #{@user.bio}. %span - Member since #{@user.created_at.stamp("Aug 21, 2011")} + Member since #{@user.created_at.to_s(:medium)} .cover-desc - unless @user.public_email.blank? diff --git a/config/initializers/date_time_formats.rb b/config/initializers/date_time_formats.rb new file mode 100644 index 00000000000..57568203cab --- /dev/null +++ b/config/initializers/date_time_formats.rb @@ -0,0 +1,9 @@ +# :short - 10 Nov +# :medium - Nov 10, 2007 +# :long - November 10, 2007 +Date::DATE_FORMATS[:medium] = '%b %-d, %Y' + +# :short - 18 Jan 06:10 +# :medium - Jan 18, 2007 6:10am +# :long - January 18, 2007 06:10 +Time::DATE_FORMATS[:medium] = '%b %-d, %Y %-I:%M%P' diff --git a/config/routes.rb b/config/routes.rb index 5b69d06eb76..4fcea1185c0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -52,9 +52,6 @@ Rails.application.routes.draw do API::API.logger Rails.logger mount API::API => '/api' - # Get all keys of user - get ':username.keys' => 'profiles/keys#get_keys' , constraints: { username: /.*/ } - constraint = lambda { |request| request.env['warden'].authenticate? and request.env['warden'].user.admin? } constraints constraint do mount Sidekiq::Web, at: '/admin/sidekiq', as: :sidekiq @@ -686,5 +683,8 @@ Rails.application.routes.draw do end end + # Get all keys of user + get ':username.keys' => 'profiles/keys#get_keys' , constraints: { username: /.*/ } + get ':id' => 'namespaces#show', constraints: { id: /(?:[^.]|\.(?!atom$))+/, format: /atom/ } end diff --git a/doc/api/projects.md b/doc/api/projects.md index 0ca81ffd49e..37d74216c1b 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -482,6 +482,34 @@ Parameters: - `id` (required) - The ID of a project +## Uploads + +### Upload a file + +Uploads a file to the specified project to be used in an issue or merge request description, or a comment. + +``` +POST /projects/:id/uploads +``` + +Parameters: + +- `id` (required) - The ID of the project +- `file` (required) - The file to be uploaded + +```json +{ + "alt": "dk", + "url": "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png", + "is_image": true, + "markdown": "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)" +} +``` + +**Note**: The returned `url` is relative to the project path. +In Markdown contexts, the link is automatically expanded when the format in `markdown` is used. + + ## Team members ### List project team members diff --git a/doc/api/tags.md b/doc/api/tags.md index 085d387e824..17d12e9cc62 100644 --- a/doc/api/tags.md +++ b/doc/api/tags.md @@ -83,6 +83,26 @@ it will contain the annotation. It returns 200 if the operation succeed. In case of an error, 405 with an explaining error message is returned. +## Delete a tag + +Deletes a tag of a repository with given name. On success, this API method +returns 200 with the name of the deleted tag. If the tag does not exist, the +API returns 404. + +``` +DELETE /projects/:id/repository/tags/:tag_name +``` + +Parameters: + +- `id` (required) - The ID of a project +- `tag_name` (required) - The name of a tag + +```json +{ + "tag_name": "v4.3.0" +} +``` ## Create a new release diff --git a/doc/integration/README.md b/doc/integration/README.md index 2a9f76533b7..5edac746c7b 100644 --- a/doc/integration/README.md +++ b/doc/integration/README.md @@ -1,6 +1,7 @@ # GitLab Integration -GitLab integrates with multiple third-party services to allow external issue trackers and external authentication. +GitLab integrates with multiple third-party services to allow external issue +trackers and external authentication. See the documentation below for details on how to configure these services. @@ -15,13 +16,25 @@ See the documentation below for details on how to configure these services. - [Gmail actions buttons](gmail_action_buttons_for_gitlab.md) Adds GitLab actions to messages - [reCAPTCHA](recaptcha.md) Configure GitLab to use Google reCAPTCHA for new users -GitLab Enterprise Edition contains [advanced JIRA support](http://doc.gitlab.com/ee/integration/jira.html) and [advanced Jenkins support](http://doc.gitlab.com/ee/integration/jenkins.html). +GitLab Enterprise Edition contains [advanced Jenkins support][jenkins]. ## Project services -Integration with services such as Campfire, Flowdock, Gemnasium, HipChat, Pivotal Tracker, and Slack are available in the form of a Project Service. -You can find these within GitLab in the Services page under Project Settings if you are at least a master on the project. -Project Services are a bit like plugins in that they allow a lot of freedom in adding functionality to GitLab, for example there is also a service that can send an email every time someone pushes new commits. -Because GitLab is open source we can ship with the code and tests for all plugins. -This allows the community to keep the plugins up to date so that they always work in newer GitLab versions. -For an overview of what projects services are available without logging in please see the [project_services directory](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/models/project_services). +Integration with services such as Campfire, Flowdock, Gemnasium, HipChat, +Pivotal Tracker, and Slack are available in the form of a [Project Service][]. +You can find these within GitLab in the Services page under Project Settings if +you are at least a master on the project. +Project Services are a bit like plugins in that they allow a lot of freedom in +adding functionality to GitLab. For example there is also a service that can +send an email every time someone pushes new commits. + +Because GitLab is open source we can ship with the code and tests for all +plugins. This allows the community to keep the plugins up to date so that they +always work in newer GitLab versions. + +For an overview of what projects services are available without logging in, +please see the [project_services directory][projects-code]. + +[jenkins]: http://doc.gitlab.com/ee/integration/jenkins.html +[Project Service]: ../project_services/project_services.md +[projects-code]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/models/project_services diff --git a/doc/integration/external-issue-tracker.md b/doc/integration/external-issue-tracker.md index 3e660cfba1e..3543a67dd49 100644 --- a/doc/integration/external-issue-tracker.md +++ b/doc/integration/external-issue-tracker.md @@ -1,44 +1,30 @@ # External issue tracker -GitLab has a great issue tracker but you can also use an external issue tracker such as Jira, Bugzilla or Redmine. You can configure issue trackers per GitLab project. For instance, if you configure Jira it allows you to do the following: +GitLab has a great issue tracker but you can also use an external one such as +Jira or Redmine. Issue trackers are configurable per GitLab project and allow +you to do the following: -- the 'Issues' link on the GitLab project pages takes you to the appropriate Jira issue index; -- clicking 'New issue' on the project dashboard creates a new Jira issue; -- To reference Jira issue PROJECT-1234 in comments, use syntax PROJECT-1234. Commit messages get turned into HTML links to the corresponding Jira issue. - -![Jira screenshot](jira-integration-points.png) - -GitLab Enterprise Edition contains [advanced JIRA support](http://doc.gitlab.com/ee/integration/jira.html). +- the **Issues** link on the GitLab project pages takes you to the appropriate + issue index of the external tracker +- clicking **New issue** on the project dashboard creates a new issue on the + external tracker ## Configuration -### Project Service +The configuration is done via a project's **Services**. -You can enable an external issue tracker per project. As an example, we will configure `Redmine` for project named gitlab-ci. - -Fill in the required details on the page: +### Project Service -![redmine configuration](redmine_configuration.png) +To enable an external issue tracker you must configure the appropriate **Service**. +Visit the links below for details: -* `description` A name for the issue tracker (to differentiate between instances, for example). -* `project_url` The URL to the project in Redmine which is being linked to this GitLab project. -* `issues_url` The URL to the issue in Redmine project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the url. This id is used by GitLab as a placeholder to replace the issue number. -* `new_issue_url` This is the URL to create a new issue in Redmine for the project linked to this GitLab project. +- [Redmine](../project_services/redmine.md) +- [Jira](jira.md) ### Service Template -It is necessary to configure the external issue tracker per project, because project specific details are needed for the integration with GitLab. -The admin can add a service template that sets a default for each project. This makes it much easier to configure individual projects. - -In GitLab Admin section, navigate to `Service Templates` and choose the service template you want to create: - -![redmine service template](redmine_service_template.png) - -After the template is created, the template details will be pre-filled on the project service page. - -NOTE: For each project, you will still need to configure the issue tracking URLs by replacing `:issues_tracker_id` in the above screenshot -with the ID used by your external issue tracker. Prior to GitLab v7.8, this ID was configured in the project settings, and GitLab would automatically -update the URL configured in `gitlab.yml`. This behavior is now depecated, and all issue tracker URLs must be configured directly -within the project's Services settings. +To save you the hassle from configuring each project's service individually, +GitLab provides the ability to set Service Templates which can then be +overridden in each project's settings. -Support to add your commits to the Jira ticket automatically is [available in GitLab EE](http://doc.gitlab.com/ee/integration/jira.html). +Read more on [Services Templates](../project_services/services_templates.md). diff --git a/doc/integration/jira_issue_reference.png b/doc/integration/img/jira_issue_reference.png Binary files differindex 15739a22dc7..15739a22dc7 100644 --- a/doc/integration/jira_issue_reference.png +++ b/doc/integration/img/jira_issue_reference.png diff --git a/doc/integration/img/jira_merge_request_close.png b/doc/integration/img/jira_merge_request_close.png Binary files differnew file mode 100644 index 00000000000..1e78daf105f --- /dev/null +++ b/doc/integration/img/jira_merge_request_close.png diff --git a/doc/integration/jira_project_name.png b/doc/integration/img/jira_project_name.png Binary files differindex 5986fdb63fb..5986fdb63fb 100644 --- a/doc/integration/jira_project_name.png +++ b/doc/integration/img/jira_project_name.png diff --git a/doc/integration/jira_service.png b/doc/integration/img/jira_service.png Binary files differindex 1f6628c4371..1f6628c4371 100644 --- a/doc/integration/jira_service.png +++ b/doc/integration/img/jira_service.png diff --git a/doc/integration/jira_service_close_issue.png b/doc/integration/img/jira_service_close_issue.png Binary files differindex 67dfc6144c4..67dfc6144c4 100644 --- a/doc/integration/jira_service_close_issue.png +++ b/doc/integration/img/jira_service_close_issue.png diff --git a/doc/integration/img/jira_service_page.png b/doc/integration/img/jira_service_page.png Binary files differnew file mode 100644 index 00000000000..2b37eda3520 --- /dev/null +++ b/doc/integration/img/jira_service_page.png diff --git a/doc/integration/jira_workflow_screenshot.png b/doc/integration/img/jira_workflow_screenshot.png Binary files differindex 8635a32eb68..8635a32eb68 100644 --- a/doc/integration/jira_workflow_screenshot.png +++ b/doc/integration/img/jira_workflow_screenshot.png diff --git a/doc/integration/jira.md b/doc/integration/jira.md index 624601d0fac..de574d53410 100644 --- a/doc/integration/jira.md +++ b/doc/integration/jira.md @@ -1,14 +1,15 @@ # GitLab Jira integration -GitLab can be configured to interact with Jira. -Configuration happens via username and password. -Connecting to a Jira server via CAS is not possible. +GitLab can be configured to interact with Jira. Configuration happens via +username and password. Connecting to a Jira server via CAS is not possible. -Each project can be configured to connect to a different Jira instance, configuration is explained [here](#configuration). -If you have one Jira instance you can pre-fill the settings page with a default template. To configure the template [see external issue tracker document](external-issue-tracker.md#service-template)). - -Once the project is connected to Jira, you can reference and close the issues in Jira directly from GitLab. +Each project can be configured to connect to a different Jira instance, see the +[configuration](#configuration) section. If you have one Jira instance you can +pre-fill the settings page with a default template. To configure the template +see the [Services Templates][services-templates] document. +Once the project is connected to Jira, you can reference and close the issues +in Jira directly from GitLab. ## Table of Contents @@ -18,8 +19,11 @@ Once the project is connected to Jira, you can reference and close the issues in ### Referencing Jira Issues -When GitLab project has Jira issue tracker configured and enabled, mentioning Jira issue in GitLab will automatically add a comment in Jira issue with the link back to GitLab. This means that in comments in merge requests and commits referencing an issue, eg. `PROJECT-7`, will add a comment in Jira issue in the format: - +When GitLab project has Jira issue tracker configured and enabled, mentioning +Jira issue in GitLab will automatically add a comment in Jira issue with the +link back to GitLab. This means that in comments in merge requests and commits +referencing an issue, eg. `PROJECT-7`, will add a comment in Jira issue in the +format: ``` USER mentioned this issue in LINK_TO_THE_MENTION @@ -29,85 +33,117 @@ When GitLab project has Jira issue tracker configured and enabled, mentioning Ji * `LINK_TO_THE_MENTION` Link to the origin of mention with a name of the entity where Jira issue was mentioned. Can be commit or merge request. +![example of mentioning or closing the Jira issue](img/jira_issue_reference.png) -![example of mentioning or closing the Jira issue](jira_issue_reference.png) - +--- ### Closing Jira Issues -Jira issues can be closed directly from GitLab by using trigger words, eg. `Resolves PROJECT-1`, `Closes PROJECT-1` or `Fixes PROJECT-1`, in commits and merge requests. -When a commit which contains the trigger word in the commit message is pushed, GitLab will add a comment in the mentioned Jira issue. +Jira issues can be closed directly from GitLab by using trigger words, eg. +`Resolves PROJECT-1`, `Closes PROJECT-1` or `Fixes PROJECT-1`, in commits and +merge requests. When a commit which contains the trigger word in the commit +message is pushed, GitLab will add a comment in the mentioned Jira issue. -For example, for project named PROJECT in Jira, we implemented a new feature and created a merge request in GitLab. +For example, for project named `PROJECT` in Jira, we implemented a new feature +and created a merge request in GitLab. -This feature was requested in Jira issue PROJECT-7. Merge request in GitLab contains the improvement and in merge request description we say that this merge request `Closes PROJECT-7` issue. +This feature was requested in Jira issue `PROJECT-7`. Merge request in GitLab +contains the improvement and in merge request description we say that this +merge request `Closes PROJECT-7` issue. -Once this merge request is merged, Jira issue will be automatically closed with a link to the commit that resolved the issue. +Once this merge request is merged, the Jira issue will be automatically closed +with a link to the commit that resolved the issue. -![A Git commit that causes the Jira issue to be closed](merge_request_close_jira.png) +![A Git commit that causes the Jira issue to be closed](img/jira_merge_request_close.png) +--- -![The GitLab integration user leaves a comment on Jira](jira_service_close_issue.png) +![The GitLab integration user leaves a comment on Jira](img/jira_service_close_issue.png) +--- ## Configuration ### Configuring JIRA -We need to create a user in JIRA which will have access to all projects that need to integrate with GitLab. -Login to your JIRA instance as admin and under Administration go to User Management and create a new user. -As an example, we'll create a user named `gitlab` and add it to `jira-developers` group. +We need to create a user in JIRA which will have access to all projects that +need to integrate with GitLab. Login to your JIRA instance as admin and under +Administration go to User Management and create a new user. + +As an example, we'll create a user named `gitlab` and add it to `jira-developers` +group. **It is important that the user `gitlab` has write-access to projects in JIRA** ### Configuring GitLab -### GitLab 7.8 EE and up with JIRA v6.x +JIRA configuration in GitLab is done via a project's **Services**. -To enable JIRA integration in a project, navigate to the project Settings page and go to Services. Here you will find JIRA. +#### GitLab 7.8 and up with JIRA v6.x -Fill in the required details on the page: +See next section. -![Jira service page](jira_service_page.png) +#### GitLab 7.8 and up -* `description` A name for the issue tracker (to differentiate between instances, for instance). -* `project url` The URL to the JIRA project which is being linked to this GitLab project. -* `issues url` The URL to the JIRA project issues overview for the project that is linked to this GitLab project. -* `new issue url` This is the URL to create a new issue in JIRA for the project linked to this GitLab project. -* `api url` The base URL of the JIRA API. It may be omitted, in which case GitLab will automatically use API version `2` based on the `project url`, i.e. `https://jira.example.com/rest/api/2`. -* `username` The username of the user created in [configuring JIRA step](#configuring-jira). -* `password` The password of the user created in [configuring JIRA step](#configuring-jira). -* `Jira issue transition` This is the id of a transition that moves issues to a closed state. You can find this number under [JIRA workflow administration, see screenshot](jira_workflow_screenshot.png). By default, this id is `2`. (In the example image, this is `2` as well) +_The currently supported JIRA versions are v6.x and v7.x._ -After saving the configuration, your GitLab project will be able to interact with the linked JIRA project. +To enable JIRA integration in a project, navigate to the project's +**Settings > Services > JIRA**. +Fill in the required details on the page as described in the table below. -### GitLab 6.x-7.7 with JIRA v6.x +| Field | Description | +| ----- | ----------- | +| `description` | A name for the issue tracker (to differentiate between instances, for instance). | +| `project url` | The URL to the JIRA project which is being linked to this GitLab project. | +| `issues url` | The URL to the JIRA project issues overview for the project that is linked to this GitLab project. | +| `new issue url` | This is the URL to create a new issue in JIRA for the project linked to this GitLab project. | +| `api url` | The base URL of the JIRA API. It may be omitted, in which case GitLab will automatically use API version `2` based on the `project url`, i.e. `https://jira.example.com/rest/api/2`. | +| `username` | The username of the user created in [configuring JIRA step](#configuring-jira). | +| `password` |The password of the user created in [configuring JIRA step](#configuring-jira). | +| `Jira issue transition` | This is the ID of a transition that moves issues to a closed state. You can find this number under JIRA workflow administration ([see screenshot](img/jira_workflow_screenshot.png)). By default, this ID is `2` (in the example image, this is `2` as well) | -**Note: GitLab 7.8 and up contain various integration improvements. We strongly recommend upgrading.** +After saving the configuration, your GitLab project will be able to interact +with the linked JIRA project. +![Jira service page](img/jira_service_page.png) -In `gitlab.yml` enable [JIRA issue tracker section by uncommenting the lines](https://gitlab.com/subscribers/gitlab-ee/blob/6-8-stable-ee/config/gitlab.yml.example#L111-115). -This will make sure that all issues within GitLab are pointing to the JIRA issue tracker. +--- -We can also enable JIRA service that will allow us to interact with JIRA issues. +#### GitLab 6.x-7.7 with JIRA v6.x -For example, we can close issues in JIRA by a commit in GitLab. +_**Note:** GitLab versions 7.8 and up contain various integration improvements. +We strongly recommend upgrading._ -Go to project settings page and fill in the project name for the JIRA project: +In `gitlab.yml` enable the JIRA issue tracker section by +[uncommenting these lines][jira-gitlab-yml]. This will make sure that all +issues within GitLab are pointing to the JIRA issue tracker. -![Set the JIRA project name in GitLab to 'NEW'](jira_project_name.png) +After you set this, you will be able to close issues in JIRA by a commit in +GitLab. -Next, go to the services page and find JIRA. +Go to your project's **Settings** page and fill in the project name for the +JIRA project: -![Jira services page](jira_service.png) +![Set the JIRA project name in GitLab to 'NEW'](img/jira_project_name.png) -1. Tick the active check box to enable the service. -1. Supply the url to JIRA server, for example http://jira.sample -1. Supply the username of a user we created under `Configuring JIRA` section, for example `gitlab` +--- + +You can also enable the JIRA service that will allow you to interact with JIRA +issues. Go to the **Settings > Services > JIRA** and: + +1. Tick the active check box to enable the service +1. Supply the URL to JIRA server, for example http://jira.example.com +1. Supply the username of a user we created under `Configuring JIRA` section, + for example `gitlab` 1. Supply the password of the user -1. Optional: supply the JIRA api version, default is version -1. Optional: supply the JIRA issue transition ID (issue transition to closed). This is dependant on JIRA settings, default is 2 -1. Save +1. Optional: supply the JIRA API version, default is version `2` +1. Optional: supply the JIRA issue transition ID (issue transition to closed). + This is dependent on JIRA settings, default is `2` +1. Hit save + + +![Jira services page](img/jira_service.png) -Now we should be able to interact with JIRA issues. +[services-templates]: ../project_services/services_templates.md +[jira-gitlab-yml]: https://gitlab.com/subscribers/gitlab-ee/blob/6-8-stable-ee/config/gitlab.yml.example#L111-115 diff --git a/doc/integration/jira_service_page.png b/doc/integration/jira_service_page.png Binary files differdeleted file mode 100644 index 69ec44e826f..00000000000 --- a/doc/integration/jira_service_page.png +++ /dev/null diff --git a/doc/integration/redmine_configuration.png b/doc/integration/redmine_configuration.png Binary files differdeleted file mode 100644 index 6b145363229..00000000000 --- a/doc/integration/redmine_configuration.png +++ /dev/null diff --git a/doc/integration/redmine_service_template.png b/doc/integration/redmine_service_template.png Binary files differdeleted file mode 100644 index 1159eb5b964..00000000000 --- a/doc/integration/redmine_service_template.png +++ /dev/null diff --git a/doc/project_services/img/redmine_configuration.png b/doc/project_services/img/redmine_configuration.png Binary files differnew file mode 100644 index 00000000000..d14e526ad33 --- /dev/null +++ b/doc/project_services/img/redmine_configuration.png diff --git a/doc/project_services/img/services_templates_redmine_example.png b/doc/project_services/img/services_templates_redmine_example.png Binary files differnew file mode 100644 index 00000000000..384d057fc8e --- /dev/null +++ b/doc/project_services/img/services_templates_redmine_example.png diff --git a/doc/project_services/project_services.md b/doc/project_services/project_services.md index 03937d20728..e3403127723 100644 --- a/doc/project_services/project_services.md +++ b/doc/project_services/project_services.md @@ -1,20 +1,37 @@ # Project Services - -__Project integrations with external services for continuous integration and more.__ + +Project services allow you to integrate GitLab with other applications. Below +is list of the currently supported ones. Click on the service links to see +further configuration instructions and details. Contributions are welcome. ## Services -- Assembla -- [Atlassian Bamboo CI](bamboo.md) An Atlassian product for continuous integration. -- Build box -- Campfire -- Emails on push -- Flowdock -- Gemnasium -- GitLab CI -- [HipChat](hipchat.md) An Atlassian product for private group chat and instant messaging. -- [Irker](irker.md) An IRC gateway to receive messages on repository updates. -- Pivotal Tracker -- Pushover -- Slack -- TeamCity +| Service | Description | +| ------- | ----------- | +| Asana | Asana - Teamwork without email | +| Assembla | Project Management Software (Source Commits Endpoint) | +| [Atlassian Bamboo CI](bamboo.md) | A continuous integration and build server | +| Buildkite | Continuous integration and deployments | +| Builds emails | Email the builds status to a list of recipients | +| Campfire | Simple web-based real-time group chat | +| Custom Issue Tracker | Custom issue tracker | +| Drone CI | Continuous Integration platform built on Docker, written in Go | +| Emails on push | Email the commits and diff of each push to a list of recipients | +| External Wiki | Replaces the link to the internal wiki with a link to an external wiki | +| Flowdock | Flowdock is a collaboration web app for technical teams | +| Gemnasium | Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities | +| [HipChat](hipchat.md) | Private group chat and IM | +| [Irker (IRC gateway)](irker.md) | Send IRC messages, on update, to a list of recipients through an Irker gateway | +| JIRA | Jira issue tracker | +| JetBrains TeamCity CI | A continuous integration and build server | +| PivotalTracker | Project Management Software (Source Commits Endpoint) | +| Pushover | Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop | +| [Redmine](redmine.md) | Redmine issue tracker | +| Slack | A team communication tool for the 21st century | + +## Services Templates + +Services templates is a way to set some predefined values in the Service of +your liking which will then be pre-filled on each project's Service. + +Read more about [Services Templates in this document](services_templates.md). diff --git a/doc/project_services/redmine.md b/doc/project_services/redmine.md new file mode 100644 index 00000000000..b9830ea7c38 --- /dev/null +++ b/doc/project_services/redmine.md @@ -0,0 +1,21 @@ +# Redmine Service + +Go to your project's **Settings > Services > Redmine** and fill in the required +details as described in the table below. + +| Field | Description | +| ----- | ----------- | +| `description` | A name for the issue tracker (to differentiate between instances, for example) | +| `project_url` | The URL to the project in Redmine which is being linked to this GitLab project | +| `issues_url` | The URL to the issue in Redmine project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. | +| `new_issue_url` | This is the URL to create a new issue in Redmine for the project linked to this GitLab project | + +Once you have configured and enabled Redmine: + +- the **Issues** link on the GitLab project pages takes you to the appropriate + Redmine issue index +- clicking **New issue** on the project dashboard creates a new Redmine issue + +As an example, below is a configuration for a project named gitlab-ci. + +![Redmine configuration](img/redmine_configuration.png) diff --git a/doc/project_services/services_templates.md b/doc/project_services/services_templates.md new file mode 100644 index 00000000000..be6d13b6d2b --- /dev/null +++ b/doc/project_services/services_templates.md @@ -0,0 +1,25 @@ +# Services Templates + +A GitLab administrator can add a service template that sets a default for each +project. This makes it much easier to configure individual projects. + +After the template is created, the template details will be pre-filled on a +project's Service page. + +## Enable a Service template + +In GitLab's Admin area, navigate to **Service Templates** and choose the +service template you wish to create. + +For example, in the image below you can see Redmine. + +![Redmine service template](img/services_templates_redmine_example.png) + +--- + +**NOTE:** For each project, you will still need to configure the issue tracking +URLs by replacing `:issues_tracker_id` in the above screenshot with the ID used +by your external issue tracker. Prior to GitLab v7.8, this ID was configured in +the project settings, and GitLab would automatically update the URL configured +in `gitlab.yml`. This behavior is now deprecated and all issue tracker URLs +must be configured directly within the project's **Services** settings. diff --git a/doc/workflow/add-user/add-user.md b/doc/workflow/add-user/add-user.md index 8c9b4f72631..fffa0aba57f 100644 --- a/doc/workflow/add-user/add-user.md +++ b/doc/workflow/add-user/add-user.md @@ -1,25 +1,89 @@ # Project users -You can manage the groups and users and their access levels in all of your projects. You can also personalize the access level you give each user, per project. +You can manage the groups and users and their access levels in all of your +projects. You can also personalize the access level you give each user, +per-project. -Here's how to add or import users to your projects. - -You should have 'master' or 'owner' permissions to add or import a new user +You should have `master` or `owner` permissions to add or import a new user to your project. -To add or import a user, go to your project and click on "Members" on the left side of your screen: +The first step to add or import a user, go to your project and click on +**Members** on the left side of your screen. + +![Members](img/add_user_members_menu.png) + +--- + +## Add a user + +Right next to **People**, start typing the name or username of the user you +want to add. + +![Search for people](img/add_user_search_people.png) + +--- + +Select the user and the [permission level](../../permissions/permissions.md) +that you'd like to give the user. Note that you can select more than one user. + +![Give user permissions](img/add_user_give_permissions.png) + +--- + +Once done, hit **Add users to project** and they will be immediately added to +your project with the permissions you gave them above. + +![List members](img/add_user_list_members.png) + +--- + +From there on, you can either remove an existing user or change their access +level to the project. + +## Import users from another project + +You can import another project's users in your own project by hitting the +**Import members** button on the upper right corner of the **Members** menu. + +In the dropdown menu, you can see only the projects you are Master on. + +![Import members from another project](img/add_user_import_members_from_another_project.png) + +--- + +Select the one you want and hit **Import project members**. A flash message +notifying you that the import was successful will appear, and the new members +are now in the project's members list. Notice that the permissions that they +had on the project you imported from are retained. + +![Members list of new members](img/add_user_imported_members.png) + +--- + +## Invite people using their e-mail address + +If a user you want to give access to doesn't have an account on your GitLab +instance, you can invite them just by typing their e-mail address in the +user search field. + +![Invite user by mail](img/add_user_email_search.png) + +--- -![Members](images/members.png) +As you can imagine, you can mix inviting multiple people and adding existing +GitLab users to the project. -Select "Add members" or "Import members" on the right side of your screen: +![Invite user by mail ready to submit](img/add_user_email_ready.png) -![Add or Import](images/add-members.png) +--- -If you are adding a user, select the user and the [permission level](doc/permissions/permissions.md) that you'd like to -give the user: +Once done, hit **Add users to project** and watch that there is a new member +with the e-mail address we used above. From there on, you can resend the +invitation, change their access level or even delete them. -![Add or Import](images/new-member.png) +![Invite user members list](img/add_user_email_accept.png) -If you are importing a user, follow the steps to select the project where you'd like to import the user from: +--- -![Add or Import](images/select-project.png) +Once the user accepts the invitation, they will be prompted to create a new +GitLab account using the same e-mail address the invitation was sent to. diff --git a/doc/workflow/add-user/images/add-members.png b/doc/workflow/add-user/images/add-members.png Binary files differdeleted file mode 100644 index 2805c5764a5..00000000000 --- a/doc/workflow/add-user/images/add-members.png +++ /dev/null diff --git a/doc/workflow/add-user/images/new-member.png b/doc/workflow/add-user/images/new-member.png Binary files differdeleted file mode 100644 index d500daea56e..00000000000 --- a/doc/workflow/add-user/images/new-member.png +++ /dev/null diff --git a/doc/workflow/add-user/images/select-project.png b/doc/workflow/add-user/images/select-project.png Binary files differdeleted file mode 100644 index dd3844edff8..00000000000 --- a/doc/workflow/add-user/images/select-project.png +++ /dev/null diff --git a/doc/workflow/add-user/img/add_new_user_to_project_settings.png b/doc/workflow/add-user/img/add_new_user_to_project_settings.png Binary files differnew file mode 100644 index 00000000000..3da18cdae53 --- /dev/null +++ b/doc/workflow/add-user/img/add_new_user_to_project_settings.png diff --git a/doc/workflow/add-user/img/add_user_email_accept.png b/doc/workflow/add-user/img/add_user_email_accept.png Binary files differnew file mode 100644 index 00000000000..910affc9659 --- /dev/null +++ b/doc/workflow/add-user/img/add_user_email_accept.png diff --git a/doc/workflow/add-user/img/add_user_email_ready.png b/doc/workflow/add-user/img/add_user_email_ready.png Binary files differnew file mode 100644 index 00000000000..5f02ce89b3e --- /dev/null +++ b/doc/workflow/add-user/img/add_user_email_ready.png diff --git a/doc/workflow/add-user/img/add_user_email_search.png b/doc/workflow/add-user/img/add_user_email_search.png Binary files differnew file mode 100644 index 00000000000..140979fbe13 --- /dev/null +++ b/doc/workflow/add-user/img/add_user_email_search.png diff --git a/doc/workflow/add-user/img/add_user_give_permissions.png b/doc/workflow/add-user/img/add_user_give_permissions.png Binary files differnew file mode 100644 index 00000000000..8ef9156c8d5 --- /dev/null +++ b/doc/workflow/add-user/img/add_user_give_permissions.png diff --git a/doc/workflow/add-user/img/add_user_import_members_from_another_project.png b/doc/workflow/add-user/img/add_user_import_members_from_another_project.png Binary files differnew file mode 100644 index 00000000000..5770d5cf0c4 --- /dev/null +++ b/doc/workflow/add-user/img/add_user_import_members_from_another_project.png diff --git a/doc/workflow/add-user/img/add_user_imported_members.png b/doc/workflow/add-user/img/add_user_imported_members.png Binary files differnew file mode 100644 index 00000000000..dea4b3f40ad --- /dev/null +++ b/doc/workflow/add-user/img/add_user_imported_members.png diff --git a/doc/workflow/add-user/img/add_user_list_members.png b/doc/workflow/add-user/img/add_user_list_members.png Binary files differnew file mode 100644 index 00000000000..7daa6ca7d9e --- /dev/null +++ b/doc/workflow/add-user/img/add_user_list_members.png diff --git a/doc/workflow/add-user/images/members.png b/doc/workflow/add-user/img/add_user_members_menu.png Binary files differindex f1797b95f67..f1797b95f67 100644 --- a/doc/workflow/add-user/images/members.png +++ b/doc/workflow/add-user/img/add_user_members_menu.png diff --git a/doc/workflow/add-user/img/add_user_search_people.png b/doc/workflow/add-user/img/add_user_search_people.png Binary files differnew file mode 100644 index 00000000000..5ac10ce80d4 --- /dev/null +++ b/doc/workflow/add-user/img/add_user_search_people.png diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb index 6e57b16ccb6..2363ad797fa 100644 --- a/features/steps/group/milestones.rb +++ b/features/steps/group/milestones.rb @@ -28,7 +28,7 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps end step 'I should see group milestone with descriptions and expiry date' do - expect(page).to have_content('expires at Aug 20, 2114') + expect(page).to have_content('expires on Aug 20, 2114') end step 'I should see group milestone with all issues and MRs assigned to that milestone' do diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 0781236cf6d..8b1390e3289 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -269,7 +269,7 @@ module API # Remove a forked_from relationship # # Parameters: - # id: (required) - The ID of the project being marked as a fork + # id: (required) - The ID of the project being marked as a fork # Example Request: # DELETE /projects/:id/fork delete ":id/fork" do @@ -278,6 +278,16 @@ module API user_project.forked_project_link.destroy end end + + # Upload a file + # + # Parameters: + # id: (required) - The ID of the project + # file: (required) - The file to be uploaded + post ":id/uploads" do + ::Projects::UploadService.new(user_project, params[:file]).execute + end + # search for projects current_user has access to # # Parameters: diff --git a/lib/api/tags.rb b/lib/api/tags.rb index 47621f443e6..2d8a9e51bb9 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -40,6 +40,27 @@ module API end end + # Delete tag + # + # Parameters: + # id (required) - The ID of a project + # tag_name (required) - The name of the tag + # Example Request: + # DELETE /projects/:id/repository/tags/:tag + delete ":id/repository/tags/:tag_name", requirements: { tag_name: /.*/ } do + authorize_push_project + result = DeleteTagService.new(user_project, current_user). + execute(params[:tag_name]) + + if result[:status] == :success + { + tag_name: params[:tag_name] + } + else + render_api_error!(result[:message], result[:return_code]) + end + end + # Add release notes to tag # # Parameters: diff --git a/lib/gitlab/build_data_builder.rb b/lib/gitlab/build_data_builder.rb index 86bfa0a4378..34e949130da 100644 --- a/lib/gitlab/build_data_builder.rb +++ b/lib/gitlab/build_data_builder.rb @@ -23,6 +23,7 @@ module Gitlab build_started_at: build.started_at, build_finished_at: build.finished_at, build_duration: build.duration, + build_allow_failure: build.allow_failure, # TODO: do we still need it? project_id: project.id, diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index 7a86c09158e..7f938780ab1 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -41,6 +41,9 @@ module Gitlab use_db && ActiveRecord::Base.connection.active? && !ActiveRecord::Migrator.needs_migration? && ActiveRecord::Base.connection.table_exists?('application_settings') + + rescue ActiveRecord::NoDatabaseError + false end end end diff --git a/lib/gitlab/email/receiver.rb b/lib/gitlab/email/receiver.rb index 2b252b32887..2ca21af5bc8 100644 --- a/lib/gitlab/email/receiver.rb +++ b/lib/gitlab/email/receiver.rb @@ -74,7 +74,7 @@ module Gitlab def sent_notification return nil unless reply_key - + SentNotification.for(reply_key) end @@ -82,10 +82,7 @@ module Gitlab attachments = Email::AttachmentUploader.new(message).execute(sent_notification.project) attachments.each do |link| - text = "[#{link[:alt]}](#{link[:url]})" - text.prepend("!") if link[:is_image] - - reply << "\n\n#{text}" + reply << "\n\n#{link[:markdown]}" end reply diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb index 403ebeec474..db580b5e578 100644 --- a/lib/gitlab/fogbugz_import/importer.rb +++ b/lib/gitlab/fogbugz_import/importer.rb @@ -232,9 +232,7 @@ module Gitlab return nil if res.nil? - text = "[#{res['alt']}](#{res['url']})" - text = "!#{text}" if res['is_image'] - text + res[:markdown] end def build_attachment_url(rel_url) diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 68527c3a4f8..efc850eb705 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -240,7 +240,7 @@ describe ApplicationHelper do describe 'time_ago_with_tooltip' do def element(*arguments) Time.zone = 'UTC' - time = Time.zone.parse('2015-07-02 08:00') + time = Time.zone.parse('2015-07-02 08:23') element = helper.time_ago_with_tooltip(time, *arguments) Nokogiri::HTML::DocumentFragment.parse(element).first_element_child @@ -251,15 +251,15 @@ describe ApplicationHelper do end it 'includes the date string' do - expect(element.text).to eq '2015-07-02 08:00:00 UTC' + expect(element.text).to eq '2015-07-02 08:23:00 UTC' end it 'has a datetime attribute' do - expect(element.attr('datetime')).to eq '2015-07-02T08:00:00Z' + expect(element.attr('datetime')).to eq '2015-07-02T08:23:00Z' end it 'has a formatted title attribute' do - expect(element.attr('title')).to eq 'Jul 02, 2015 8:00am' + expect(element.attr('title')).to eq 'Jul 2, 2015 8:23am' end it 'includes a default js-timeago class' do diff --git a/spec/lib/banzai/filter/task_list_filter_spec.rb b/spec/lib/banzai/filter/task_list_filter_spec.rb index f2e3a44478d..569cbc885c7 100644 --- a/spec/lib/banzai/filter/task_list_filter_spec.rb +++ b/spec/lib/banzai/filter/task_list_filter_spec.rb @@ -7,4 +7,10 @@ describe Banzai::Filter::TaskListFilter, lib: true 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/lib/gitlab/build_data_builder_spec.rb b/spec/lib/gitlab/build_data_builder_spec.rb index 839b30f1ff4..38be9448794 100644 --- a/spec/lib/gitlab/build_data_builder_spec.rb +++ b/spec/lib/gitlab/build_data_builder_spec.rb @@ -14,6 +14,7 @@ describe 'Gitlab::BuildDataBuilder' do it { expect(data[:tag]).to eq(build.tag) } it { expect(data[:build_id]).to eq(build.id) } it { expect(data[:build_status]).to eq(build.status) } + it { expect(data[:build_allow_failure]).to eq(false) } it { expect(data[:project_id]).to eq(build.project.id) } it { expect(data[:project_name]).to eq(build.project.name_with_namespace) } end diff --git a/spec/lib/gitlab/email/receiver_spec.rb b/spec/lib/gitlab/email/receiver_spec.rb index b535413bbd4..abe179cd4af 100644 --- a/spec/lib/gitlab/email/receiver_spec.rb +++ b/spec/lib/gitlab/email/receiver_spec.rb @@ -42,7 +42,7 @@ describe Gitlab::Email::Receiver, lib: true do context "when the email was auto generated" do let!(:reply_key) { '636ca428858779856c226bb145ef4fad' } let!(:email_raw) { fixture_file("emails/auto_reply.eml") } - + it "raises an AutoGeneratedEmailError" do expect { receiver.execute }.to raise_error(Gitlab::Email::Receiver::AutoGeneratedEmailError) end @@ -90,7 +90,7 @@ describe Gitlab::Email::Receiver, lib: true do context "when the reply is blank" do let!(:email_raw) { fixture_file("emails/no_content_reply.eml") } - + it "raises an EmptyEmailError" do expect { receiver.execute }.to raise_error(Gitlab::Email::Receiver::EmptyEmailError) end @@ -107,13 +107,16 @@ describe Gitlab::Email::Receiver, lib: true do end context "when everything is fine" do + let(:markdown) { "![image](uploads/image.png)" } + before do allow_any_instance_of(Gitlab::Email::AttachmentUploader).to receive(:execute).and_return( [ { url: "uploads/image.png", is_image: true, - alt: "image" + alt: "image", + markdown: markdown } ] ) @@ -132,7 +135,7 @@ describe Gitlab::Email::Receiver, lib: true do note = noteable.notes.last - expect(note.note).to include("![image](uploads/image.png)") + expect(note.note).to include(markdown) end end end diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb index 2d90b0793cc..7070aa4ac62 100644 --- a/spec/models/hooks/web_hook_spec.rb +++ b/spec/models/hooks/web_hook_spec.rb @@ -77,5 +77,17 @@ describe ProjectHook, models: true do expect(@project_hook.execute(@data, 'push_hooks')).to eq([false, 'SSL error']) end + + it "handles 200 status code" do + WebMock.stub_request(:post, @project_hook.url).to_return(status: 200, body: "Success") + + expect(@project_hook.execute(@data, 'push_hooks')).to eq([true, 'Success']) + end + + it "handles 2xx status codes" do + WebMock.stub_request(:post, @project_hook.url).to_return(status: 201, body: "Success") + + expect(@project_hook.execute(@data, 'push_hooks')).to eq([true, 'Success']) + end end end diff --git a/spec/models/project_services/builds_email_service_spec.rb b/spec/models/project_services/builds_email_service_spec.rb new file mode 100644 index 00000000000..905379a64e3 --- /dev/null +++ b/spec/models/project_services/builds_email_service_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe BuildsEmailService do + let(:build) { create(:ci_build) } + let(:data) { Gitlab::BuildDataBuilder.build(build) } + let(:service) { BuildsEmailService.new } + + describe :execute do + it "sends email" do + service.recipients = 'test@gitlab.com' + data[:build_status] = 'failed' + expect(BuildEmailWorker).to receive(:perform_async) + service.execute(data) + end + + it "does not sends email with failed build and allowed_failure on" do + data[:build_status] = 'failed' + data[:build_allow_failure] = true + expect(BuildEmailWorker).not_to receive(:perform_async) + service.execute(data) + end + end +end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index ab2530859ea..6f4c336b66c 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -353,6 +353,20 @@ describe API::API, api: true do end end + describe "POST /projects/:id/uploads" do + before { project } + + it "uploads the file and returns its info" do + post api("/projects/#{project.id}/uploads", user), file: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png") + + expect(response.status).to be(201) + expect(json_response['alt']).to eq("dk") + expect(json_response['url']).to start_with("/uploads/") + expect(json_response['url']).to end_with("/dk.png") + expect(json_response['is_image']).to eq(true) + end + end + describe 'GET /projects/:id' do before { project } before { project_member } diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index 17f2643fd45..f966e38cd3e 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -65,6 +65,27 @@ describe API::API, api: true do end end + describe 'DELETE /projects/:id/repository/tags/:tag_name' do + let(:tag_name) { project.repository.tag_names.sort.reverse.first } + + before do + allow_any_instance_of(Repository).to receive(:rm_tag).and_return(true) + end + + context 'delete tag' do + it 'should delete an existing tag' do + delete api("/projects/#{project.id}/repository/tags/#{tag_name}", user) + expect(response.status).to eq(200) + expect(json_response['tag_name']).to eq(tag_name) + end + + it 'should raise 404 if the tag does not exist' do + delete api("/projects/#{project.id}/repository/tags/foobar", user) + expect(response.status).to eq(404) + end + end + end + context 'annotated tag' do it 'should create a new annotated tag' do # Identity must be set in .gitconfig to create annotated tag. diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index 2a70c190337..22ba25217f0 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -80,6 +80,7 @@ describe ProjectsController, 'routing' do it 'to #show' do expect(get('/gitlab/gitlabhq')).to route_to('projects#show', namespace_id: 'gitlab', id: 'gitlabhq') + expect(get('/gitlab/gitlabhq.keys')).to route_to('projects#show', namespace_id: 'gitlab', id: 'gitlabhq.keys') end it 'to #update' do diff --git a/spec/services/projects/download_service_spec.rb b/spec/services/projects/download_service_spec.rb index 5ceed5af9a5..f252e2c5902 100644 --- a/spec/services/projects/download_service_spec.rb +++ b/spec/services/projects/download_service_spec.rb @@ -33,12 +33,12 @@ describe Projects::DownloadService, services: true do @link_to_file = download_file(@project, url) end - it { expect(@link_to_file).to have_key('alt') } - it { expect(@link_to_file).to have_key('url') } - it { expect(@link_to_file).to have_key('is_image') } - it { expect(@link_to_file['is_image']).to be true } - it { expect(@link_to_file['url']).to match('rails_sample.jpg') } - it { expect(@link_to_file['alt']).to eq('rails_sample') } + it { expect(@link_to_file).to have_key(:alt) } + it { expect(@link_to_file).to have_key(:url) } + it { expect(@link_to_file).to have_key(:is_image) } + it { expect(@link_to_file[:is_image]).to be true } + it { expect(@link_to_file[:url]).to match('rails_sample.jpg') } + it { expect(@link_to_file[:alt]).to eq('rails_sample') } end context 'a txt file' do @@ -47,12 +47,12 @@ describe Projects::DownloadService, services: true do @link_to_file = download_file(@project, url) end - it { expect(@link_to_file).to have_key('alt') } - it { expect(@link_to_file).to have_key('url') } - it { expect(@link_to_file).to have_key('is_image') } - it { expect(@link_to_file['is_image']).to be false } - it { expect(@link_to_file['url']).to match('doc_sample.txt') } - it { expect(@link_to_file['alt']).to eq('doc_sample.txt') } + it { expect(@link_to_file).to have_key(:alt) } + it { expect(@link_to_file).to have_key(:url) } + it { expect(@link_to_file).to have_key(:is_image) } + it { expect(@link_to_file[:is_image]).to be false } + it { expect(@link_to_file[:url]).to match('doc_sample.txt') } + it { expect(@link_to_file[:alt]).to eq('doc_sample.txt') } end end end |