Commit 2c37ae2f authored by Jeroen Nijhof's avatar Jeroen Nijhof

Merge gitlab.com:gitlab-org/gitlab-ce

parents 1695063d 08213ed4
......@@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.4.0 (unreleased)
- Add housekeeping function to project settings page
- 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)
......@@ -9,6 +10,7 @@ v 8.4.0 (unreleased)
- Implement search inside emoji picker
- Add API support for looking up a user by username (Stan Hu)
- Add project permissions to all project API endpoints (Stan Hu)
- Link to milestone in "Milestone changed" system note
- Only allow group/project members to mention `@all`
- Expose Git's version in the admin area (Trey Davis)
- Add "Frequently used" category to emoji picker
......@@ -23,6 +25,9 @@ v 8.4.0 (unreleased)
- 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 (Kyungchul Shin)
- Ajax filter by message for commits page
v 8.3.3 (unreleased)
- Get "Merge when build succeeds" to work when commits were pushed to MR target branch while builds were running
......@@ -108,7 +113,6 @@ v 8.3.0
- Fix online editor should not remove newlines at the end of the file
- Expose Git's version in the admin area
- Show "New Merge Request" buttons on canonical repos when you have a fork (Josh Frye)
- Add file finder feature in tree view
v 8.2.3
- Fix application settings cache not expiring after changes (Stan Hu)
......
......@@ -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"
......
......@@ -443,6 +443,10 @@ GEM
omniauth (1.2.2)
hashie (>= 1.2, < 4)
rack (~> 1.0)
omniauth-azure-oauth2 (0.0.6)
jwt (~> 1.0)
omniauth (~> 1.0)
omniauth-oauth2 (~> 1.1)
omniauth-bitbucket (0.0.2)
multi_json (~> 1.7)
omniauth (~> 1.1)
......@@ -488,10 +492,6 @@ GEM
activesupport
nokogiri (>= 1.4.4)
omniauth (~> 1.0)
omniauth-azure-oauth2 (0.0.6)
jwt (~> 1.0)
omniauth (~> 1.0)
omniauth-oauth2 (~> 1.1)
opennebula (4.14.2)
json
nokogiri
......@@ -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)
......@@ -920,6 +919,7 @@ DEPENDENCIES
oauth2 (~> 1.0.0)
octokit (~> 3.7.0)
omniauth (~> 1.2.2)
omniauth-azure-oauth2
omniauth-bitbucket (~> 0.0.2)
omniauth-cas3 (~> 1.1.2)
omniauth-facebook (~> 3.0.0)
......@@ -931,7 +931,6 @@ DEPENDENCIES
omniauth-shibboleth (~> 1.2.0)
omniauth-twitter (~> 1.2.0)
omniauth_crowd
omniauth-azure-oauth2
org-ruby (~> 0.9.12)
paranoia (~> 2.0)
pg (~> 0.18.2)
......@@ -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)
......@@ -999,4 +997,4 @@ DEPENDENCIES
wikicloth (= 0.8.1)
BUNDLED WITH
1.10.6
1.11.2
class @CommitsList
@data =
ref: null
limit: 0
offset: 0
@disable = false
@showProgress: ->
$('.loading').show()
@hideProgress: ->
$('.loading').hide()
@timer = null
@init: (ref, limit) ->
$("body").on "click", ".day-commits-table li.commit", (event) ->
......@@ -18,38 +8,32 @@ class @CommitsList
e.stopPropagation()
return false
@data.ref = ref
@data.limit = limit
@data.offset = limit
Pager.init limit, false
@content = $("#commits-list")
@searchField = $("#commits-search")
@initSearch()
this.initLoadMore()
this.showProgress()
@initSearch: ->
@timer = null
@searchField.keyup =>
clearTimeout(@timer)
@timer = setTimeout(@filterResults, 500)
@filterResults: =>
form = $(".commits-search-form")
search = @searchField.val()
commitsUrl = form.attr("action") + '?' + form.serialize()
@content.fadeTo('fast', 0.5)
@getOld: ->
this.showProgress()
$.ajax
type: "GET"
url: location.href
data: @data
complete: this.hideProgress
success: (data) ->
CommitsList.append(data.count, data.html)
url: form.attr("action")
data: form.serialize()
complete: =>
@content.fadeTo('fast', 1.0)
success: (data) =>
@content.html(data.html)
# Change url so if user reload a page - search results are saved
history.replaceState {page: commitsUrl}, document.title, commitsUrl
dataType: "json"
@append: (count, html) ->
$("#commits-list").append(html)
if count > 0
@data.offset += count
else
@disable = true
@initLoadMore: ->
$(document).unbind('scroll')
$(document).endlessScroll
bottomPixels: 400
fireDelay: 1000
fireOnce: true
ceaseFire: =>
@disable
callback: =>
this.getOld()
......@@ -72,6 +72,15 @@
> p:last-child {
margin-bottom: 0;
}
.block-controls {
float: right;
.control {
float: left;
margin-left: 10px;
}
}
}
.cover-block {
......
......@@ -28,10 +28,6 @@
}
}
.commits-feed-holder {
float: right;
}
li.commit {
list-style: none;
......@@ -126,14 +122,14 @@ li.commit {
.divergence-graph {
padding: 12px 12px 0 0;
float: right;
.graph-side {
position: relative;
width: 80px;
height: 22px;
padding: 5px 0 13px;
float: left;
.bar {
position: absolute;
height: 4px;
......@@ -149,7 +145,7 @@ li.commit {
left: 0;
border-radius: 0 3px 3px 0;
}
.count {
padding-top: 6px;
padding-bottom: 0px;
......
......@@ -138,6 +138,7 @@
*/
.event-last-push {
overflow: auto;
width: 100%;
.event-last-push-text {
@include str-truncated(100%);
padding: 5px 0;
......
......@@ -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;
......
......@@ -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;
......
......@@ -8,10 +8,16 @@ class Projects::CommitsController < Projects::ApplicationController
before_action :authorize_download_code!
def show
@repo = @project.repository
@limit, @offset = (params[:limit] || 40).to_i, (params[:offset] || 0).to_i
search = params[:search]
@commits =
if search.present?
@repository.find_commits_by_message(search, @ref, @path, @limit, @offset).compact
else
@repository.commits(@ref, @path, @limit, @offset)
end
@commits = @repo.commits(@ref, @path, @limit, @offset)
@note_counts = project.notes.where(commit_id: @commits.map(&:id)).
group(:commit_id).count
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -27,9 +27,20 @@
# admin_notification_email :string(255)
# shared_runners_enabled :boolean default(TRUE), not null
# max_artifacts_size :integer default(100), not null
# runners_registration_token :string(255)
# require_two_factor_authentication :boolean default(TRUE)
# runners_registration_token :string
# require_two_factor_authentication :boolean default(FALSE)
# two_factor_grace_period :integer default(48)
# metrics_enabled :boolean default(FALSE)
# metrics_host :string default("localhost")
# metrics_username :string
# metrics_password :string
# metrics_pool_size :integer default(16)
# metrics_timeout :integer default(10)
# metrics_method_call_threshold :integer default(10)
# recaptcha_enabled :boolean default(FALSE)
# recaptcha_site_key :string
# recaptcha_private_key :string
# metrics_port :integer default(8089)
#
class ApplicationSetting < ActiveRecord::Base
......
......@@ -29,6 +29,7 @@
# target_url :string(255)
# description :string(255)
# artifacts_file :text
# gl_project_id :integer
#
module Ci
......
......@@ -2,11 +2,12 @@
#
# Table name: ci_runner_projects
#
# id :integer not null, primary key
# runner_id :integer not null
# project_id :integer not null
# created_at :datetime
# updated_at :datetime
# id :integer not null, primary key
# runner_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# gl_project_id :integer
#
module Ci
......
......@@ -2,12 +2,13 @@
#
# Table name: ci_triggers
#
# id :integer not null, primary key
# token :string(255)
# project_id :integer not null
# deleted_at :datetime
# created_at :datetime
# updated_at :datetime
# id :integer not null, primary key
# token :string(255)
# project_id :integer
# deleted_at :datetime
# created_at :datetime
# updated_at :datetime
# gl_project_id :integer
#
module Ci
......
......@@ -3,12 +3,13 @@
# Table name: ci_variables
#
# id :integer not null, primary key
# project_id :integer not null
# project_id :integer
# key :string(255)
# value :text
# encrypted_value :text
# encrypted_value_salt :string(255)
# encrypted_value_iv :string(255)
# gl_project_id :integer
#
module Ci
......
# == Schema Information
#
# project_id integer
# status string
# finished_at datetime
# trace text
# created_at datetime
# updated_at datetime
# started_at datetime
# runner_id integer
# coverage float
# commit_id integer
# commands text
# job_id integer
# name string
# deploy boolean default: false
# options text
# allow_failure boolean default: false, null: false
# stage string
# trigger_request_id integer
# stage_idx integer
# tag boolean
# ref string
# user_id integer
# type string
# target_url string
# description string
# Table name: ci_builds
#
# id :integer not null, primary key
# project_id :integer
# status :string(255)
# finished_at :datetime
# trace :text
# created_at :datetime
# updated_at :datetime
# started_at :datetime
# runner_id :integer
# coverage :float
# commit_id :integer
# commands :text
# job_id :integer
# name :string(255)
# deploy :boolean default(FALSE)
# options :text
# allow_failure :boolean default(FALSE), not null
# stage :string(255)
# trigger_request_id :integer
# stage_idx :integer
# tag :boolean
# ref :string(255)
# user_id :integer
# type :string(255)
# target_url :string(255)
# description :string(255)
# artifacts_file :text
# gl_project_id :integer
#
class CommitStatus < ActiveRecord::Base
......
......@@ -29,6 +29,7 @@
# target_url :string(255)
# description :string(255)
# artifacts_file :text
# gl_project_id :integer
#
class GenericCommitStatus < CommitStatus
......
......@@ -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
......
......@@ -11,7 +11,6 @@
# type :string(255)
# description :string(255) default(""), not null
# avatar :string(255)
# public :boolean default(FALSE)
#
require 'carrierwave/orm/activerecord'
......
......@@ -15,6 +15,7 @@
# tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE)
# build_events :boolean default(FALSE), not null
#
class ProjectHook < WebHook
......
......@@ -15,6 +15,7 @@
# tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE)
# build_events :boolean default(FALSE), not null
#
class ServiceHook < WebHook
......
......@@ -15,6 +15,7 @@
# tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE)
# build_events :boolean default(FALSE), not null
#
class SystemHook < WebHook
......
......@@ -15,6 +15,7 @@
# tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE)
# build_events :boolean default(FALSE), not null
#
class WebHook < ActiveRecord::Base
......@@ -60,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]
......
......@@ -2,28 +2,28 @@
#
# Table name: merge_requests
#
# id :integer not null, primary key
# target_branch :string(255) not null
# source_branch :string(255) not null
# source_project_id :integer not null
# author_id :integer
# assignee_id :integer
# title :string(255)
# created_at :datetime
# updated_at :datetime
# milestone_id :integer
# state :string(255)
# merge_status :string(255)
# target_project_id :integer not null
# iid :integer
# description :text
# position :integer default(0)
# locked_at :datetime
# updated_by_id :integer
# merge_error :string(255)
# merge_params :text (serialized to hash)
# merge_when_build_succeeds :boolean default(false), not null
# merge_user_id :integer
# id :integer not null, primary key
# target_branch :string(255) not null
# source_branch :string(255) not null
# source_project_id :integer not null
# author_id :integer
# assignee_id :integer
# title :string(255)
# created_at :datetime
# updated_at :datetime
# milestone_id :integer
# state :string(255)
# merge_status :string(255)
# target_project_id :integer not null
# iid :integer
# description :text
# position :integer default(0)
# locked_at :datetime
# updated_by_id :integer
# merge_error :string(255)
# merge_params :text
# merge_when_build_succeeds :boolean default(FALSE), not null
# merge_user_id :integer
#
require Rails.root.join("app/models/commit")
......
......@@ -22,6 +22,7 @@ class Milestone < ActiveRecord::Base
include InternalId
include Sortable
include Referable
include StripAttribute
belongs_to :project
......@@ -61,6 +62,27 @@ class Milestone < ActiveRecord::Base
end
end
def self.reference_pattern
nil
end
def self.link_reference_pattern
super("milestones", /(?<milestone>\d+)/)
end
def to_reference(from_project = nil)
escaped_title = self.title.gsub("]", "\\]")
h = Gitlab::Application.routes.url_helpers
url = h.namespace_project_milestone_url(self.project.namespace, self.project, self)
"[#{escaped_title}](#{url})"
end
def reference_link_text(from_project = nil)
self.title
end
def expired?
if due_date
due_date.past?
......@@ -90,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
......
......@@ -11,7 +11,6 @@
# type :string(255)
# description :string(255) default(""), not null
# avatar :string(255)
# public :boolean default(FALSE)
#
class Namespace < ActiveRecord::Base
......
......@@ -29,6 +29,13 @@
# import_source :string(255)
# commit_count :integer default(0)
# import_error :text
# ci_id :integer
# builds_enabled :boolean default(TRUE), not null
# shared_runners_enabled :boolean default(TRUE), not null
# runners_token :string
# build_coverage_regex :string
# build_allow_git_fetch :boolean default(TRUE), not null
# build_timeout :integer default(3600), not null
#
require 'carrierwave/orm/activerecord'
......
......@@ -16,7 +16,9 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
require 'asana'
class AsanaService < Service
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
class AssemblaService < Service
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
class BambooService < CiService
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
require "addressable/uri"
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
class BuildsEmailService < Service
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
class CampfireService < Service
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
# Base class for CI services
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
class CustomIssueTrackerService < IssueTrackerService
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
class DroneCiService < CiService
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
class EmailsOnPushService < Service
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
class ExternalWikiService < Service
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
require "flowdock-git-hook"
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
require "gemnasium/gitlab_service"
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
# TODO(ayufan): The GitLabCiService is deprecated and the type should be removed when the database entries are removed
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
class GitlabIssueTrackerService < IssueTrackerService
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
class HipchatService < Service
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
require 'uri'
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
class IssueTrackerService < Service
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
class JiraService < IssueTrackerService
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
class PivotaltrackerService < Service
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
class PushoverService < Service
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
class RedmineService < IssueTrackerService
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
class SlackService < Service
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
class TeamcityService < CiService
......
......@@ -92,9 +92,12 @@ class Repository
commits
end
def find_commits_by_message(query)
def find_commits_by_message(query, ref = nil, path = nil, limit = 1000, offset = 0)
ref ||= root_ref
# Limited to 1000 commits for now, could be parameterized?
args = %W(#{Gitlab.config.git.bin_path} log --pretty=%H --max-count 1000 --grep=#{query})
args = %W(#{Gitlab.config.git.bin_path} log #{ref} --pretty=%H --skip #{offset} --max-count #{limit} --grep=#{query})
args = args.concat(%W(-- #{path})) if path.present?
git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:chomp)
commits = git_log_results.map { |c| commit(c) }
......@@ -175,7 +178,7 @@ class Repository
def size
cache.fetch(:size) { raw_repository.size }
end
def diverging_commit_counts(branch)
root_ref_hash = raw_repository.rev_parse_target(root_ref).oid
cache.fetch(:"diverging_commit_counts_#{branch.name}") do
......@@ -183,7 +186,7 @@ class Repository
# than SHA-1 hashes
number_commits_behind = commits_between(branch.target, root_ref_hash).size
number_commits_ahead = commits_between(root_ref_hash, branch.target).size
{ behind: number_commits_behind, ahead: number_commits_ahead }
end
end
......@@ -192,7 +195,7 @@ class Repository
%i(size branch_names tag_names commit_count
readme version contribution_guide changelog license)
end
def branch_cache_keys
branches.map do |branch|
:"diverging_commit_counts_#{branch.name}"
......@@ -205,7 +208,7 @@ class Repository
send(key)
end
end
branches.each do |branch|
unless cache.exist?(:"diverging_commit_counts_#{branch.name}")
send(:diverging_commit_counts, branch)
......@@ -227,10 +230,10 @@ class Repository
cache_keys.each do |key|
cache.expire(key)
end
expire_branch_cache
end
def expire_branch_cache
branches.each do |branch|
cache.expire(:"diverging_commit_counts_#{branch.name}")
......@@ -242,7 +245,7 @@ class Repository
cache.expire(key)
send(key)
end
branches.each do |branch|
cache.expire(:"diverging_commit_counts_#{branch.name}")
diverging_commit_counts(branch)
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
# To add new service you should build a class inherited from Service
......
......@@ -2,62 +2,63 @@
#
# Table name: users
#
# id :integer not null, primary key
# email :string(255) default(""), not null
# encrypted_password :string(255) default(""), not null
# reset_password_token :string(255)
# reset_password_sent_at :datetime
# remember_created_at :datetime
# sign_in_count :integer default(0)
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string(255)
# last_sign_in_ip :string(255)
# created_at :datetime
# updated_at :datetime
# name :string(255)
# admin :boolean default(FALSE), not null
# projects_limit :integer default(10)
# skype :string(255) default(""), not null
# linkedin :string(255) default(""), not null
# twitter :string(255) default(""), not null
# authentication_token :string(255)
# theme_id :integer default(1), not null
# bio :string(255)
# failed_attempts :integer default(0)
# locked_at :datetime
# unlock_token :string(255)
# username :string(255)
# can_create_group :boolean default(TRUE), not null
# can_create_team :boolean default(TRUE), not null
# state :string(255)
# color_scheme_id :integer default(1), not null
# notification_level :integer default(1), not null
# password_expires_at :datetime
# created_by_id :integer
# last_credential_check_at :datetime
# avatar :string(255)
# confirmation_token :string(255)
# confirmed_at :datetime
# confirmation_sent_at :datetime
# unconfirmed_email :string(255)
# hide_no_ssh_key :boolean default(FALSE)
# website_url :string(255) default(""), not null
# notification_email :string(255)
# hide_no_password :boolean default(FALSE)
# password_automatically_set :boolean default(FALSE)
# location :string(255)
# encrypted_otp_secret :string(255)
# encrypted_otp_secret_iv :string(255)
# encrypted_otp_secret_salt :string(255)
# otp_required_for_login :boolean default(FALSE), not null
# otp_backup_codes :text
# public_email :string(255) default(""), not null
# dashboard :integer default(0)
# project_view :integer default(0)
# consumed_timestep :integer
# layout :integer default(0)
# hide_project_limit :boolean default(FALSE)
# id :integer not null, primary key
# email :string(255) default(""), not null
# encrypted_password :string(255) default(""), not null
# reset_password_token :string(255)
# reset_password_sent_at :datetime
# remember_created_at :datetime
# sign_in_count :integer default(0)
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string(255)
# last_sign_in_ip :string(255)
# created_at :datetime
# updated_at :datetime
# name :string(255)
# admin :boolean default(FALSE), not null
# projects_limit :integer default(10)
# skype :string(255) default(""), not null
# linkedin :string(255) default(""), not null
# twitter :string(255) default(""), not null
# authentication_token :string(255)
# theme_id :integer default(1), not null
# bio :string(255)
# failed_attempts :integer default(0)
# locked_at :datetime
# username :string(255)
# can_create_group :boolean default(TRUE), not null
# can_create_team :boolean default(TRUE), not null
# state :string(255)
# color_scheme_id :integer default(1), not null
# notification_level :integer default(1), not null
# password_expires_at :datetime
# created_by_id :integer
# last_credential_check_at :datetime
# avatar :string(255)
# confirmation_token :string(255)
# confirmed_at :datetime
# confirmation_sent_at :datetime
# unconfirmed_email :string(255)
# hide_no_ssh_key :boolean default(FALSE)
# website_url :string(255) default(""), not null
# notification_email :string(255)
# hide_no_password :boolean default(FALSE)
# password_automatically_set :boolean default(FALSE)
# location :string(255)
# encrypted_otp_secret :string(255)
# encrypted_otp_secret_iv :string(255)
# encrypted_otp_secret_salt :string(255)
# otp_required_for_login :boolean default(FALSE), not null
# otp_backup_codes :text
# public_email :string(255) default(""), not null
# dashboard :integer default(0)
# project_view :integer default(0)
# consumed_timestep :integer
# layout :integer default(0)
# hide_project_limit :boolean default(FALSE)
# unlock_token :string
# otp_grace_period_started_at :datetime
#
require 'carrierwave/orm/activerecord'
......
......@@ -41,7 +41,7 @@ class SystemNoteService
#
# Returns the created Note object
def self.change_assignee(noteable, project, author, assignee)
body = assignee.nil? ? 'Assignee removed' : "Reassigned to @#{assignee.username}"
body = assignee.nil? ? 'Assignee removed' : "Reassigned to #{assignee.to_reference}"
create_note(noteable: noteable, project: project, author: author, note: body)
end
......@@ -66,7 +66,7 @@ class SystemNoteService
def self.change_label(noteable, project, author, added_labels, removed_labels)
labels_count = added_labels.count + removed_labels.count
references = ->(label) { "~#{label.id}" }
references = ->(label) { label.to_reference(:id) }
added_labels = added_labels.map(&references).join(' ')
removed_labels = removed_labels.map(&references).join(' ')
......@@ -103,7 +103,7 @@ class SystemNoteService
# Returns the created Note object
def self.change_milestone(noteable, project, author, milestone)
body = 'Milestone '
body += milestone.nil? ? 'removed' : "changed to #{milestone.title}"
body += milestone.nil? ? 'removed' : "changed to #{milestone.to_reference(project)}"
create_note(noteable: noteable, project: project, author: author, note: body)
end
......
......@@ -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
......
......@@ -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:
......
......@@ -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:
......
......@@ -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
......
......@@ -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)
......
......@@ -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)
......
......@@ -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)
......
......@@ -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)
......
......@@ -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
......
......@@ -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}
\- - - - -
\
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -10,26 +10,30 @@
.tree-ref-holder
= render 'shared/ref_switcher', destination: 'commits'
.commits-feed-holder.hidden-xs.hidden-sm
.block-controls.hidden-xs.hidden-sm
- if create_mr_button?(@repository.root_ref, @ref)
= link_to create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' do
= icon('plus')
Create Merge Request
.control
= link_to create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' do
= icon('plus')
Create Merge Request
.control
= form_tag(namespace_project_commits_path(@project.namespace, @project, @id), method: :get, class: 'pull-left commits-search-form') do
= search_field_tag :search, params[:search], { placeholder: 'Filter by commit message', id: 'commits-search', class: 'form-control search-text-input', spellcheck: false }
- if current_user && current_user.private_token
= link_to namespace_project_commits_path(@project.namespace, @project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Commits Feed", class: 'prepend-left-10 btn' do
= icon("rss")
.control
= link_to namespace_project_commits_path(@project.namespace, @project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Commits Feed", class: 'btn' do
= icon("rss")
%ul.breadcrumb.repo-breadcrumb
= commits_breadcrumbs
%div{id: dom_id(@project)}
#commits-list= render "commits", project: @project
#commits-list.content_list= render "commits", project: @project
.clear
= spinner
- if @commits.count == @limit
:javascript
CommitsList.init("#{@ref}", #{@limit});
:javascript
CommitsList.init("#{@ref}", #{@limit});
......@@ -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)
......
......@@ -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)
......
......@@ -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
References:
%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
......@@ -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)
......
......@@ -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?
......
# :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'
# 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 issue tracker |
| Slack | A team communication tool for the 21st century |
......@@ -55,3 +55,8 @@ Feature: Project Commits
Scenario: I browse a commit with an image
Given I visit a commit with an image that changed
Then The diff links to both the previous and current image
@javascript
Scenario: I filter commits by message
When I search "submodules" commits
Then I should see only "submodules" commits
......@@ -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
......
......@@ -124,4 +124,13 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
expect(page).to have_content "build: pending"
expect(page).to have_content "1 build"
end
step 'I search "submodules" commits' do
fill_in 'commits-search', with: 'submodules'
end
step 'I should see only "submodules" commits' do
expect(page).to have_content "More submodules"
expect(page).not_to have_content "Change some files"
end
end
......@@ -70,27 +70,31 @@ module Banzai
end
def call
# `#123`
replace_text_nodes_matching(object_class.reference_pattern) do |content|
object_link_filter(content, object_class.reference_pattern)
end
if object_class.reference_pattern
# `#123`
replace_text_nodes_matching(object_class.reference_pattern) do |content|
object_link_filter(content, object_class.reference_pattern)
end
# `[Issue](#123)`, which is turned into
# `<a href="#123">Issue</a>`
replace_link_nodes_with_href(object_class.reference_pattern) do |link, text|
object_link_filter(link, object_class.reference_pattern, link_text: text)
# `[Issue](#123)`, which is turned into
# `<a href="#123">Issue</a>`
replace_link_nodes_with_href(object_class.reference_pattern) do |link, text|
object_link_filter(link, object_class.reference_pattern, link_text: text)
end
end
# `http://gitlab.example.com/namespace/project/issues/123`, which is turned into
# `<a href="http://gitlab.example.com/namespace/project/issues/123">http://gitlab.example.com/namespace/project/issues/123</a>`
replace_link_nodes_with_text(object_class.link_reference_pattern) do |text|
object_link_filter(text, object_class.link_reference_pattern)
end
if object_class.link_reference_pattern
# `http://gitlab.example.com/namespace/project/issues/123`, which is turned into
# `<a href="http://gitlab.example.com/namespace/project/issues/123">http://gitlab.example.com/namespace/project/issues/123</a>`
replace_link_nodes_with_text(object_class.link_reference_pattern) do |text|
object_link_filter(text, object_class.link_reference_pattern)
end
# `[Issue](http://gitlab.example.com/namespace/project/issues/123)`, which is turned into
# `<a href="http://gitlab.example.com/namespace/project/issues/123">Issue</a>`
replace_link_nodes_with_href(object_class.link_reference_pattern) do |link, text|
object_link_filter(link, object_class.link_reference_pattern, link_text: text)
# `[Issue](http://gitlab.example.com/namespace/project/issues/123)`, which is turned into
# `<a href="http://gitlab.example.com/namespace/project/issues/123">Issue</a>`
replace_link_nodes_with_href(object_class.link_reference_pattern) do |link, text|
object_link_filter(link, object_class.link_reference_pattern, link_text: text)
end
end
end
......
require 'banzai'
module Banzai
module Filter
# HTML filter that replaces milestone references with links.
class MilestoneReferenceFilter < AbstractReferenceFilter
def self.object_class
Milestone
end
def find_object(project, id)
project.milestones.find_by(iid: id)
end
def url_for_object(issue, project)
h = Gitlab::Application.routes.url_helpers
h.namespace_project_milestone_url(project.namespace, project, milestone,
only_path: context[:only_path])
end
end
end
end
......@@ -12,13 +12,18 @@ module Banzai
#
# See https://github.com/github/task_list/pull/60
class TaskListFilter < TaskList::Filter
def add_css_class(node, *new_class_names)
def add_css_class_with_fix(node, *new_class_names)
if new_class_names.include?('task-list')
super if node.children.any? { |c| c['class'] == 'task-list-item' }
else
super
# Don't add class to all lists
return
elsif new_class_names.include?('task-list-item')
add_css_class_without_fix(node.parent, 'task-list')
end
add_css_class_without_fix(node, *new_class_names)
end
alias_method_chain :add_css_class, :fix
end
end
end
......@@ -22,6 +22,7 @@ module Banzai
Filter::CommitRangeReferenceFilter,
Filter::CommitReferenceFilter,
Filter::LabelReferenceFilter,
Filter::MilestoneReferenceFilter,
Filter::TaskListFilter
]
......
......@@ -32,8 +32,8 @@ module Gitlab
def transaction_from_env(env)
trans = Transaction.new
trans.add_tag(:request_method, env['REQUEST_METHOD'])
trans.add_tag(:request_uri, env['REQUEST_URI'])
trans.set(:request_uri, env['REQUEST_URI'])
trans.set(:request_method, env['REQUEST_METHOD'])
trans
end
......
......@@ -4,7 +4,7 @@ module Gitlab
class Transaction
THREAD_KEY = :_gitlab_metrics_transaction
attr_reader :uuid, :tags
attr_reader :tags, :values
def self.current
Thread.current[THREAD_KEY]
......@@ -12,7 +12,6 @@ module Gitlab
def initialize
@metrics = []
@uuid = SecureRandom.uuid
@started_at = nil
@finished_at = nil
......@@ -38,7 +37,6 @@ module Gitlab
end
def add_metric(series, values, tags = {})
tags = tags.merge(transaction_id: @uuid)
prefix = sidekiq? ? 'sidekiq_' : 'rails_'
@metrics << Metric.new("#{prefix}#{series}", values, tags)
......@@ -48,6 +46,10 @@ module Gitlab
@values[name] += value
end
def set(name, value)
@values[name] = value
end
def add_tag(key, value)
@tags[key] = value
end
......
......@@ -19,7 +19,7 @@ module Gitlab
super(text, context.merge(project: project))
end
%i(user label merge_request snippet commit commit_range).each do |type|
%i(user label milestone merge_request snippet commit commit_range).each do |type|
define_method("#{type}s") do
@references[type] ||= references(type, reference_context)
end
......
......@@ -2,25 +2,28 @@
#
# Table name: merge_requests
#
# id :integer not null, primary key
# target_branch :string(255) not null
# source_branch :string(255) not null
# source_project_id :integer not null
# author_id :integer
# assignee_id :integer
# title :string(255)
# created_at :datetime
# updated_at :datetime
# milestone_id :integer
# state :string(255)
# merge_status :string(255)
# target_project_id :integer not null
# iid :integer
# description :text
# position :integer default(0)
# locked_at :datetime
# updated_by_id :integer
# merge_error :string(255)
# id :integer not null, primary key
# target_branch :string(255) not null
# source_branch :string(255) not null
# source_project_id :integer not null
# author_id :integer
# assignee_id :integer
# title :string(255)
# created_at :datetime
# updated_at :datetime
# milestone_id :integer
# state :string(255)
# merge_status :string(255)
# target_project_id :integer not null
# iid :integer
# description :text
# position :integer default(0)
# locked_at :datetime
# updated_by_id :integer
# merge_error :string(255)
# merge_params :text
# merge_when_build_succeeds :boolean default(FALSE), not null
# merge_user_id :integer
#
FactoryGirl.define do
......
......@@ -29,6 +29,13 @@
# import_source :string(255)
# commit_count :integer default(0)
# import_error :text
# ci_id :integer
# builds_enabled :boolean default(TRUE), not null
# shared_runners_enabled :boolean default(TRUE), not null
# runners_token :string
# build_coverage_regex :string
# build_allow_git_fetch :boolean default(TRUE), not null
# build_timeout :integer default(3600), not null
#
FactoryGirl.define do
......
......@@ -212,6 +212,7 @@ describe 'GitLab Markdown', feature: true do
expect(doc).to reference_commit_ranges
expect(doc).to reference_commits
expect(doc).to reference_labels
expect(doc).to reference_milestones
end
end
......
......@@ -214,6 +214,13 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e
- Ignored in links: [Link to <%= simple_label.to_reference %>](#label-link)
- Link to label by reference: [Label](<%= label.to_reference %>)
#### MilestoneReferenceFilter
- Milestone: <%= milestone.to_reference %>
- Milestone in another project: <%= xmilestone.to_reference(project) %>
- Ignored in code: `<%= milestone.to_reference %>`
- Link to milestone by URL: [Milestone](<%= urls.namespace_project_milestone_url(milestone.project.namespace, milestone.project, milestone) %>)
### Task Lists
- [ ] Incomplete task 1
......
......@@ -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
......
......@@ -26,10 +26,10 @@ describe 'reopen/close issue', ->
fixture.load('issues_show.html')
@issue = new Issue()
it 'closes an issue', ->
$.ajax = (obj) ->
expect(obj.type).toBe('PUT')
expect(obj.url).toBe('http://gitlab.com/issues/6/close')
obj.success saved: true
spyOn(jQuery, 'ajax').and.callFake (req) ->
expect(req.type).toBe('PUT')
expect(req.url).toBe('http://gitlab.com/issues/6/close')
req.success saved: true
$btnClose = $('a.btn-close')
$btnReopen = $('a.btn-reopen')
......@@ -46,10 +46,10 @@ describe 'reopen/close issue', ->
it 'fails to closes an issue with success:false', ->
$.ajax = (obj) ->
expect(obj.type).toBe('PUT')
expect(obj.url).toBe('http://goesnowhere.nothing/whereami')
obj.success saved: false
spyOn(jQuery, 'ajax').and.callFake (req) ->
expect(req.type).toBe('PUT')
expect(req.url).toBe('http://goesnowhere.nothing/whereami')
req.success saved: false
$btnClose = $('a.btn-close')
$btnReopen = $('a.btn-reopen')
......@@ -69,10 +69,10 @@ describe 'reopen/close issue', ->
it 'fails to closes an issue with HTTP error', ->
$.ajax = (obj) ->
expect(obj.type).toBe('PUT')
expect(obj.url).toBe('http://goesnowhere.nothing/whereami')
obj.error()
spyOn(jQuery, 'ajax').and.callFake (req) ->
expect(req.type).toBe('PUT')
expect(req.url).toBe('http://goesnowhere.nothing/whereami')
req.error()
$btnClose = $('a.btn-close')
$btnReopen = $('a.btn-reopen')
......@@ -91,10 +91,10 @@ describe 'reopen/close issue', ->
expect($('div.flash-alert').text()).toBe('Unable to update this issue at this time.')
it 'reopens an issue', ->
$.ajax = (obj) ->
expect(obj.type).toBe('PUT')
expect(obj.url).toBe('http://gitlab.com/issues/6/reopen')
obj.success saved: true
spyOn(jQuery, 'ajax').and.callFake (req) ->
expect(req.type).toBe('PUT')
expect(req.url).toBe('http://gitlab.com/issues/6/reopen')
req.success saved: true
$btnClose = $('a.btn-close')
$btnReopen = $('a.btn-reopen')
......
require 'spec_helper'
describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
include FilterSpecHelper
let(:project) { create(:project, :public) }
let(:milestone) { create(:milestone, project: project) }
it 'requires project context' do
expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
end
%w(pre code a style).each do |elem|
it "ignores valid references contained inside '#{elem}' element" do
exp = act = "<#{elem}>milestone #{milestone.to_reference}</#{elem}>"
expect(reference_filter(act).to_html).to eq exp
end
end
context 'internal reference' do
# Convert the Markdown link to only the URL, since these tests aren't run through the regular Markdown pipeline.
# Milestone reference behavior in the full Markdown pipeline is tested elsewhere.
let(:reference) { milestone.to_reference.gsub(/\[([^\]]+)\]\(([^)]+)\)/, '\2') }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.
namespace_project_milestone_url(project.namespace, project, milestone)
end
it 'links with adjacent text' do
doc = reference_filter("milestone (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(milestone.title)}<\/a>\.\)/)
end
it 'includes a title attribute' do
doc = reference_filter("milestone #{reference}")
expect(doc.css('a').first.attr('title')).to eq "Milestone: #{milestone.title}"
end
it 'escapes the title attribute' do
milestone.update_attribute(:title, %{"></a>whatever<a title="})
doc = reference_filter("milestone #{reference}")
expect(doc.text).to eq "milestone #{milestone.title}"
end
it 'includes default classes' do
doc = reference_filter("milestone #{reference}")
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-milestone'
end
it 'includes a data-project attribute' do
doc = reference_filter("milestone #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-project')
expect(link.attr('data-project')).to eq project.id.to_s
end
it 'includes a data-milestone attribute' do
doc = reference_filter("See #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-milestone')
expect(link.attr('data-milestone')).to eq milestone.id.to_s
end
it 'adds to the results hash' do
result = reference_pipeline_result("milestone #{reference}")
expect(result[:references][:milestone]).to eq [milestone]
end
end
end
......@@ -40,9 +40,9 @@ describe Gitlab::Metrics::RackMiddleware do
expect(transaction).to be_an_instance_of(Gitlab::Metrics::Transaction)
end
it 'tags the transaction with the request method and URI' do
expect(transaction.tags[:request_method]).to eq('GET')
expect(transaction.tags[:request_uri]).to eq('/foo')
it 'stores the request method and URI in the transaction as values' do
expect(transaction.values[:request_method]).to eq('GET')
expect(transaction.values[:request_uri]).to eq('/foo')
end
end
......
......@@ -30,9 +30,9 @@ describe Gitlab::Metrics::Transaction do
end
describe '#add_metric' do
it 'adds a metric tagged with the transaction UUID' do
it 'adds a metric to the transaction' do
expect(Gitlab::Metrics::Metric).to receive(:new).
with('rails_foo', { number: 10 }, { transaction_id: transaction.uuid })
with('rails_foo', { number: 10 }, {})
transaction.add_metric('foo', number: 10)
end
......@@ -50,6 +50,17 @@ describe Gitlab::Metrics::Transaction do
end
end
describe '#set' do
it 'sets a value' do
transaction.set(:number, 10)
expect(transaction).to receive(:add_metric).
with('transactions', { duration: 0.0, number: 10 }, {})
transaction.track_self
end
end
describe '#add_tag' do
it 'adds a tag' do
transaction.add_tag(:foo, 'bar')
......
......@@ -2,32 +2,45 @@
#
# Table name: application_settings
#
# id :integer not null, primary key
# default_projects_limit :integer
# signup_enabled :boolean
# signin_enabled :boolean
# gravatar_enabled :boolean
# sign_in_text :text
# created_at :datetime
# updated_at :datetime
# home_page_url :string(255)
# default_branch_protection :integer default(2)
# twitter_sharing_enabled :boolean default(TRUE)
# restricted_visibility_levels :text
# version_check_enabled :boolean default(TRUE)
# max_attachment_size :integer default(10), not null
# default_project_visibility :integer
# default_snippet_visibility :integer
# restricted_signup_domains :text
# user_oauth_applications :boolean default(TRUE)
# after_sign_out_path :string(255)
# session_expire_delay :integer default(10080), not null
# import_sources :text
# help_page_text :text
# admin_notification_email :string(255)
# shared_runners_enabled :boolean default(TRUE), not null
# max_artifacts_size :integer default(100), not null
# runners_registration_token :string(255)
# id :integer not null, primary key
# default_projects_limit :integer
# signup_enabled :boolean
# signin_enabled :boolean
# gravatar_enabled :boolean
# sign_in_text :text
# created_at :datetime
# updated_at :datetime
# home_page_url :string(255)
# default_branch_protection :integer default(2)
# twitter_sharing_enabled :boolean default(TRUE)
# restricted_visibility_levels :text
# version_check_enabled :boolean default(TRUE)
# max_attachment_size :integer default(10), not null
# default_project_visibility :integer
# default_snippet_visibility :integer
# restricted_signup_domains :text
# user_oauth_applications :boolean default(TRUE)
# after_sign_out_path :string(255)
# session_expire_delay :integer default(10080), not null
# import_sources :text
# help_page_text :text
# admin_notification_email :string(255)
# shared_runners_enabled :boolean default(TRUE), not null
# max_artifacts_size :integer default(100), not null
# runners_registration_token :string
# require_two_factor_authentication :boolean default(FALSE)
# two_factor_grace_period :integer default(48)
# metrics_enabled :boolean default(FALSE)
# metrics_host :string default("localhost")
# metrics_username :string
# metrics_password :string
# metrics_pool_size :integer default(16)
# metrics_timeout :integer default(10)
# metrics_method_call_threshold :integer default(10)
# recaptcha_enabled :boolean default(FALSE)
# recaptcha_site_key :string
# recaptcha_private_key :string
# metrics_port :integer default(8089)
#
require 'spec_helper'
......
......@@ -13,7 +13,7 @@
# tag :boolean default(FALSE)
# yaml_errors :text
# committed_at :datetime
# project_id :integer
# gl_project_id :integer
#
require 'spec_helper'
......
......@@ -2,11 +2,12 @@
#
# Table name: ci_runner_projects
#
# id :integer not null, primary key
# runner_id :integer not null
# project_id :integer not null
# created_at :datetime
# updated_at :datetime
# id :integer not null, primary key
# runner_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# gl_project_id :integer
#
require 'spec_helper'
......
......@@ -2,12 +2,13 @@
#
# Table name: ci_triggers
#
# id :integer not null, primary key
# token :string(255)
# project_id :integer not null
# deleted_at :datetime
# created_at :datetime
# updated_at :datetime
# id :integer not null, primary key
# token :string(255)
# project_id :integer
# deleted_at :datetime
# created_at :datetime
# updated_at :datetime
# gl_project_id :integer
#
require 'spec_helper'
......
......@@ -3,12 +3,13 @@
# Table name: ci_variables
#
# id :integer not null, primary key
# project_id :integer not null
# project_id :integer
# key :string(255)
# value :text
# encrypted_value :text
# encrypted_value_salt :string(255)
# encrypted_value_iv :string(255)
# gl_project_id :integer
#
require 'spec_helper'
......
......@@ -29,6 +29,7 @@
# target_url :string(255)
# description :string(255)
# artifacts_file :text
# gl_project_id :integer
#
require 'spec_helper'
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
require 'spec_helper'
......
......@@ -29,6 +29,7 @@
# target_url :string(255)
# description :string(255)
# artifacts_file :text
# gl_project_id :integer
#
require 'spec_helper'
......
......@@ -11,7 +11,6 @@
# type :string(255)
# description :string(255) default(""), not null
# avatar :string(255)
# public :boolean default(FALSE)
#
require 'spec_helper'
......
......@@ -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
......@@ -2,25 +2,28 @@
#
# Table name: merge_requests
#
# id :integer not null, primary key
# target_branch :string(255) not null
# source_branch :string(255) not null
# source_project_id :integer not null
# author_id :integer
# assignee_id :integer
# title :string(255)
# created_at :datetime
# updated_at :datetime
# milestone_id :integer
# state :string(255)
# merge_status :string(255)
# target_project_id :integer not null
# iid :integer
# description :text
# position :integer default(0)
# locked_at :datetime
# updated_by_id :integer
# merge_error :string(255)
# id :integer not null, primary key
# target_branch :string(255) not null
# source_branch :string(255) not null
# source_project_id :integer not null
# author_id :integer
# assignee_id :integer
# title :string(255)
# created_at :datetime
# updated_at :datetime
# milestone_id :integer
# state :string(255)
# merge_status :string(255)
# target_project_id :integer not null
# iid :integer
# description :text
# position :integer default(0)
# locked_at :datetime
# updated_by_id :integer
# merge_error :string(255)
# merge_params :text
# merge_when_build_succeeds :boolean default(FALSE), not null
# merge_user_id :integer
#
require 'spec_helper'
......
......@@ -11,7 +11,6 @@
# type :string(255)
# description :string(255) default(""), not null
# avatar :string(255)
# public :boolean default(FALSE)
#
require 'spec_helper'
......
......@@ -29,6 +29,13 @@
# import_source :string(255)
# commit_count :integer default(0)
# import_error :text
# ci_id :integer
# builds_enabled :boolean default(TRUE), not null
# shared_runners_enabled :boolean default(TRUE), not null
# runners_token :string
# build_coverage_regex :string
# build_allow_git_fetch :boolean default(TRUE), not null
# build_timeout :integer default(3600), not null
#
require 'spec_helper'
......
......@@ -16,6 +16,7 @@
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# note_events :boolean default(TRUE), not null
# build_events :boolean default(FALSE), not null
#
require 'spec_helper'
......
......@@ -2,62 +2,63 @@
#
# Table name: users
#
# id :integer not null, primary key
# email :string(255) default(""), not null
# encrypted_password :string(255) default(""), not null
# reset_password_token :string(255)
# reset_password_sent_at :datetime
# remember_created_at :datetime
# sign_in_count :integer default(0)
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string(255)
# last_sign_in_ip :string(255)
# created_at :datetime
# updated_at :datetime
# name :string(255)
# admin :boolean default(FALSE), not null
# projects_limit :integer default(10)
# skype :string(255) default(""), not null
# linkedin :string(255) default(""), not null
# twitter :string(255) default(""), not null
# authentication_token :string(255)
# theme_id :integer default(1), not null
# bio :string(255)
# failed_attempts :integer default(0)
# locked_at :datetime
# unlock_token :string(255)
# username :string(255)
# can_create_group :boolean default(TRUE), not null
# can_create_team :boolean default(TRUE), not null
# state :string(255)
# color_scheme_id :integer default(1), not null
# notification_level :integer default(1), not null
# password_expires_at :datetime
# created_by_id :integer
# last_credential_check_at :datetime
# avatar :string(255)
# confirmation_token :string(255)
# confirmed_at :datetime
# confirmation_sent_at :datetime
# unconfirmed_email :string(255)
# hide_no_ssh_key :boolean default(FALSE)
# website_url :string(255) default(""), not null
# notification_email :string(255)
# hide_no_password :boolean default(FALSE)
# password_automatically_set :boolean default(FALSE)
# location :string(255)
# encrypted_otp_secret :string(255)
# encrypted_otp_secret_iv :string(255)
# encrypted_otp_secret_salt :string(255)
# otp_required_for_login :boolean default(FALSE), not null
# otp_backup_codes :text
# public_email :string(255) default(""), not null
# dashboard :integer default(0)
# project_view :integer default(0)
# consumed_timestep :integer
# layout :integer default(0)
# hide_project_limit :boolean default(FALSE)
# id :integer not null, primary key
# email :string(255) default(""), not null
# encrypted_password :string(255) default(""), not null
# reset_password_token :string(255)
# reset_password_sent_at :datetime
# remember_created_at :datetime
# sign_in_count :integer default(0)
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string(255)
# last_sign_in_ip :string(255)
# created_at :datetime
# updated_at :datetime
# name :string(255)
# admin :boolean default(FALSE), not null
# projects_limit :integer default(10)
# skype :string(255) default(""), not null
# linkedin :string(255) default(""), not null
# twitter :string(255) default(""), not null
# authentication_token :string(255)
# theme_id :integer default(1), not null
# bio :string(255)
# failed_attempts :integer default(0)
# locked_at :datetime
# username :string(255)
# can_create_group :boolean default(TRUE), not null
# can_create_team :boolean default(TRUE), not null
# state :string(255)
# color_scheme_id :integer default(1), not null
# notification_level :integer default(1), not null
# password_expires_at :datetime
# created_by_id :integer
# last_credential_check_at :datetime
# avatar :string(255)
# confirmation_token :string(255)
# confirmed_at :datetime
# confirmation_sent_at :datetime
# unconfirmed_email :string(255)
# hide_no_ssh_key :boolean default(FALSE)
# website_url :string(255) default(""), not null
# notification_email :string(255)
# hide_no_password :boolean default(FALSE)
# password_automatically_set :boolean default(FALSE)
# location :string(255)
# encrypted_otp_secret :string(255)
# encrypted_otp_secret_iv :string(255)
# encrypted_otp_secret_salt :string(255)
# otp_required_for_login :boolean default(FALSE), not null
# otp_backup_codes :text
# public_email :string(255) default(""), not null
# dashboard :integer default(0)
# project_view :integer default(0)
# consumed_timestep :integer
# layout :integer default(0)
# hide_project_limit :boolean default(FALSE)
# unlock_token :string
# otp_grace_period_started_at :datetime
#
require 'spec_helper'
......
......@@ -171,7 +171,7 @@ describe SystemNoteService, services: true do
context 'when milestone added' do
it 'sets the note text' do
expect(subject.note).to eq "Milestone changed to #{milestone.title}"
expect(subject.note).to eq "Milestone changed to #{milestone.to_reference}"
end
end
......
......@@ -59,6 +59,10 @@ class MarkdownFeature
@label ||= create(:label, name: 'awaiting feedback', project: project)
end
def milestone
@milestone ||= create(:milestone, project: project)
end
# Cross-references -----------------------------------------------------------
def xproject
......@@ -93,6 +97,10 @@ class MarkdownFeature
end
end
def xmilestone
@xmilestone ||= create(:milestone, project: xproject)
end
def urls
Gitlab::Application.routes.url_helpers
end
......
......@@ -130,6 +130,15 @@ module MarkdownMatchers
end
end
# MilestoneReferenceFilter
matcher :reference_milestones do
set_default_markdown_messages
match do |actual|
expect(actual).to have_selector('a.gfm.gfm-milestone', count: 3)
end
end
# TaskListFilter
matcher :parse_task_lists do
set_default_markdown_messages
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment