Commit 3518cc75 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'ce-to-ee' into 'master'

Recent changes from CE master

See merge request !367
parents 66509917 ef8d96e5
......@@ -5,10 +5,7 @@ v 7.10.0 (unreleased)
- Set Application controller default URL options to ensure all url_for calls are consistent (Stan Hu)
- Allow HTML tags in Markdown input
- Fix code unfold not working on Compare commits page (Stan Hu)
- Include missing events and fix save functionality in admin service template settings form (Stan Hu)
- Fix "Import projects from" button to show the correct instructions (Stan Hu)
- Fix dots in Wiki slugs causing errors (Stan Hu)
- Fix OAuth2 issue importing a new project from GitHub and GitLab (Stan Hu)
- Update poltergeist to version 1.6.0 to support PhantomJS 2.0 (Zeger-Jan van de Weg)
- Fix cross references when usernames, milestones, or project names contain underscores (Stan Hu)
- Disable reference creation for comments surrounded by code/preformatted blocks (Stan Hu)
......@@ -17,6 +14,7 @@ v 7.10.0 (unreleased)
- extend the commit calendar to show the actual commits made on a date (Hannes Rosenögger)
- Fix a link in the patch update guide
- Add a service to support external wikis (Hannes Rosenögger)
- Omit the "email patches" link and fix plain diff view for merge commits
- List new commits for newly pushed branch in activity view.
- Add sidetiq gem dependency to match EE
- Add changelog, license and contribution guide links to project sidebar.
......@@ -24,6 +22,7 @@ v 7.10.0 (unreleased)
- Fix alignment of navbar toggle button (Cody Mize)
- Fix checkbox rendering for nested task lists
- Identical look of selectboxes in UI
- Upgrade the gitlab_git gem to version 7.1.3
- Move "Import existing repository by URL" option to button.
- Improve error message when save profile has error.
- Passing the name of pushed ref to CI service (requires GitLab CI 7.9+)
......@@ -40,7 +39,6 @@ v 7.10.0 (unreleased)
v 7.9.0 (unreleased)
- Replace commits calendar with faster contribution calendar that includes issues and merge requests
- Add inifinite scroll to user page activity
- Don't show commit comment button when user is not signed in.
- Don't include system notes in issue/MR comment count.
- Don't mark merge request as updated when merge status relative to target branch changes.
- Link note avatar to user.
......@@ -50,13 +48,31 @@ v 7.9.0 (unreleased)
- AJAX selectbox for issue assignee and author filters
- Fix issue with missing options in issue filtering dropdown if selected one
- Prevent holding Control-Enter or Command-Enter from posting comment multiple times.
- Prevent note form from being cleared when submitting failed.
- Improve file icons rendering on tree (Sullivan Sénéchal)
- API: Add pagination to project events
- Get issue links in notification mail to work again.
- Don't show commit comment button when user is not signed in.
- Fix admin user projects lists.
- Don't leak private group existence by redirecting from namespace controller to group controller.
- Ability to skip some items from backup (database, respositories or uploads)
- Fix "Hello @username." references not working by no longer allowing usernames to end in period.
- Archive repositories in background worker.
- Import GitHub, Bitbucket or GitLab.com projects owned by authenticated user into current namespace.
- Fix and improve help rendering (Sullivan Sénéchal)
v 7.9.0
- Send EmailsOnPush email when branch or tag is created or deleted.
- Faster merge request processing for large repository
- Prevent doubling AJAX request with each commit visit via Turbolink
- Prevent unnecessary doubling of js events on import pages and user calendar
v 7.9.2
- Contains no changes
v 7.9.1
- Include missing events and fix save functionality in admin service template settings form (Stan Hu)
- Fix "Import projects from" button to show the correct instructions (Stan Hu)
- Fix OAuth2 issue importing a new project from GitHub and GitLab (Stan Hu)
- Fix for LDAP with commas in DN
- Fix missing events and in admin Slack service template settings form (Stan Hu)
- Don't show commit comment button when user is not signed in.
- Downgrade gemnasium-gitlab-service gem
v 7.9.0
- Add HipChat integration documentation (Stan Hu)
......@@ -138,6 +154,10 @@ v 7.9.0
- Fix invalid Atom feeds when using emoji, horizontal rules, or images (Christian Walther)
- Backup of repositories with tar instead of git bundle (only now are git-annex files included in the backup)
- Add canceled status for CI
- Send EmailsOnPush email when branch or tag is created or deleted.
- Faster merge request processing for large repository
- Prevent doubling AJAX request with each commit visit via Turbolink
- Prevent unnecessary doubling of js events on import pages and user calendar
v 7.8.4
- Fix issue_tracker_id substitution in custom issue trackers
......
......@@ -39,7 +39,7 @@ gem "browser"
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem "gitlab_git", '~> 7.1.2'
gem "gitlab_git", '~> 7.1.3'
# Ruby/Rack Git Smart-HTTP Server Handler
gem 'gitlab-grack', '~> 2.0.0.rc2', require: 'grack'
......@@ -116,13 +116,14 @@ end
gem "state_machine"
# Issue tags
gem "acts-as-taggable-on"
gem 'acts-as-taggable-on', '~> 3.4'
# Background jobs
gem 'slim'
gem 'sinatra', require: nil
gem 'sidekiq', '~> 3.3'
gem 'sidetiq', '0.6.3'
gem 'sidekiq-limit_fetch'
# HTTP requests
gem "httparty"
......
......@@ -33,8 +33,8 @@ GEM
minitest (~> 5.1)
thread_safe (~> 0.1)
tzinfo (~> 1.1)
acts-as-taggable-on (2.4.1)
rails (>= 3, < 5)
acts-as-taggable-on (3.5.0)
activerecord (>= 3.2, < 5)
addressable (2.3.5)
annotate (2.6.0)
activerecord (>= 2.3.0)
......@@ -212,7 +212,7 @@ GEM
mime-types (~> 1.19)
gitlab_emoji (0.1.0)
gemojione (~> 2.0)
gitlab_git (7.1.2)
gitlab_git (7.1.3)
activesupport (~> 4.0)
charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0)
......@@ -547,6 +547,8 @@ GEM
json
redis (>= 3.0.6)
redis-namespace (>= 1.3.1)
sidekiq-limit_fetch (2.4.1)
sidekiq (>= 2.6.5, < 4.0)
sidetiq (0.6.3)
celluloid (>= 0.14.1)
ice_cube (= 0.11.1)
......@@ -662,7 +664,7 @@ PLATFORMS
DEPENDENCIES
RedCloth
ace-rails-ap
acts-as-taggable-on
acts-as-taggable-on (~> 3.4)
addressable
annotate (~> 2.6.0.beta2)
asana (~> 0.0.6)
......@@ -703,7 +705,7 @@ DEPENDENCIES
gitlab-grack (~> 2.0.0.rc2)
gitlab-linguist (~> 3.0.1)
gitlab_emoji (~> 0.1)
gitlab_git (~> 7.1.2)
gitlab_git (~> 7.1.3)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.2.1)
gollum-lib (~> 4.0.2)
......@@ -770,6 +772,7 @@ DEPENDENCIES
settingslogic
shoulda-matchers (~> 2.7.0)
sidekiq (~> 3.3)
sidekiq-limit_fetch
sidetiq (= 0.6.3)
simplecov
sinatra
......
The GitLab Enterprise Edition (EE) license
Copyright (c) 2013-2014 GitLab B.V.
Copyright (c) 2011-2015 GitLab B.V.
This software and associated documentation files (the "Software") can only be
used with a valid GitLab subscription for the correct number of users. You are
......
web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"}
worker: bundle exec sidekiq -q post_receive -q mailer -q system_hook -q project_web_hook -q gitlab_shell -q common -q default
worker: bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q common -q default
......@@ -20,9 +20,9 @@ class @Calendar
position: "top"
legend: [
0
1
4
7
10
20
30
]
legendCellPadding: 3
onClick: (date, count) ->
......
......@@ -104,6 +104,8 @@ class Dispatcher
new GroupsSelect()
when 'admin:emails:show'
new AdminEmailSelect()
when 'admin:users:show'
new ProjectsList()
switch path.first()
when 'admin'
......
......@@ -9,12 +9,8 @@ class @Issue
if $("a.btn-close").length
$("li.task-list-item input:checkbox").prop("disabled", false)
$(".task-list-item input:checkbox").on(
"click"
null
"issue"
updateTaskState
)
$('.task-list-item input:checkbox').off('change')
$('.task-list-item input:checkbox').change('issue', updateTaskState)
$('.issue-details').waitForImages ->
$('.issuable-affix').affix offset:
......
......@@ -81,12 +81,8 @@ class @MergeRequest
this.$('.remove_source_branch_in_progress').hide()
this.$('.remove_source_branch_widget.failed').show()
$(".task-list-item input:checkbox").on(
"click"
null
"merge_request"
updateTaskState
)
$('.task-list-item input:checkbox').off('change')
$('.task-list-item input:checkbox').change('merge_request', updateTaskState)
activateTab: (action) ->
this.$('.merge-request-tabs li').removeClass 'active'
......
......@@ -37,7 +37,8 @@ class @Notes
$(document).on "click", ".js-note-attachment-delete", @removeAttachment
# reset main target form after submit
$(document).on "ajax:complete", ".js-main-target-form", @resetMainTargetForm
$(document).on "ajax:complete", ".js-main-target-form", @reenableTargetFormSubmitButton
$(document).on "ajax:success", ".js-main-target-form", @resetMainTargetForm
# update the file name when an attachment is selected
$(document).on "change", ".js-note-attachment-input", @updateFormAttachment
......@@ -71,6 +72,7 @@ class @Notes
$(document).off "click", ".js-note-delete"
$(document).off "click", ".js-note-attachment-delete"
$(document).off "ajax:complete", ".js-main-target-form"
$(document).off "ajax:success", ".js-main-target-form"
$(document).off "click", ".js-discussion-reply-button"
$(document).off "click", ".js-add-diff-note-button"
$(document).off "visibilitychange"
......@@ -170,6 +172,11 @@ class @Notes
form.find(".js-note-text").data("autosave").reset()
reenableTargetFormSubmitButton: ->
form = $(".js-main-target-form")
form.find(".js-note-text").trigger "input"
###
Shows the main form and does some setup on it.
......
......@@ -3,12 +3,7 @@ $(document).on("click", '.toggle-nav-collapse', (e) ->
collapsed = 'page-sidebar-collapsed'
expanded = 'page-sidebar-expanded'
if $('.page-with-sidebar').hasClass(collapsed)
$('.page-with-sidebar').removeClass(collapsed).addClass(expanded)
$('.toggle-nav-collapse i').removeClass('fa-angle-right').addClass('fa-angle-left')
$.cookie("collapsed_nav", "false", { path: '/' })
else
$('.page-with-sidebar').removeClass(expanded).addClass(collapsed)
$('.toggle-nav-collapse i').removeClass('fa-angle-left').addClass('fa-angle-right')
$.cookie("collapsed_nav", "true", { path: '/' })
$('.page-with-sidebar').toggleClass("#{collapsed} #{expanded}")
$('.toggle-nav-collapse i').toggleClass("fa-angle-right fa-angle-left")
$.cookie("collapsed_nav", $('.page-with-sidebar').hasClass(collapsed), { path: '/' })
)
......@@ -246,7 +246,7 @@ li.note {
.milestone {
&.milestone-closed {
background: #eee;
background: #f9f9f9;
}
.progress {
margin-bottom: 0;
......
......@@ -24,13 +24,6 @@
display: none !important;
}
.project-home-panel {
.star-fork-buttons {
padding-top: 10px;
padding-right: 15px;
}
}
.project-home-links {
display: none;
}
......
......@@ -49,35 +49,20 @@
@extend .clearfix;
margin-bottom: 15px;
.project-home-desc,
.star-fork-buttons {
&.project-home-row-top {
margin-bottom: 15px;
}
.project-home-desc {
font-size: 16px;
line-height: 1.3;
margin-right: 215px;
}
.project-home-desc {
float: left;
color: #666;
}
.star-fork-buttons {
float: right;
min-width: 200px;
font-weight: bold;
.star-buttons, .fork-buttons {
float: right;
margin-left: 20px;
a:hover {
text-decoration: none;
}
.count {
margin-left: 5px;
}
}
}
}
.visibility-level-label {
......@@ -87,6 +72,27 @@
color: inherit;
}
}
.project-repo-buttons {
margin-top: -3px;
position: absolute;
right: 0;
width: 260px;
text-align: right;
.btn {
font-weight: bold;
font-size: 14px;
line-height: 16px;
.count {
padding-left: 10px;
border-left: 1px solid #ccc;
display: inline-block;
margin-left: 10px;
}
}
}
}
.project-home-links {
......
......@@ -3,21 +3,35 @@ class HelpController < ApplicationController
end
def show
@category = params[:category]
@file = params[:file]
format = params[:format] || 'md'
file_path = Rails.root.join('doc', @category, @file + ".#{format}")
@filepath = params[:filepath]
@format = params[:format]
if %w(png jpg jpeg gif).include?(format)
send_file file_path, disposition: 'inline'
elsif File.exists?(file_path)
render 'show'
respond_to do |format|
format.md { render_doc }
format.all { send_file_data }
end
end
def shortcuts
end
private
def render_doc
if File.exists?(Rails.root.join('doc', @filepath + '.md'))
render 'show.html.haml'
else
not_found!
end
end
def shortcuts
def send_file_data
path = Rails.root.join('doc', "#{@filepath}.#{@format}")
if File.exists?(path)
send_file(path, disposition: 'inline')
else
head :not_found
end
end
def ui
......
......@@ -36,8 +36,11 @@ class Import::BitbucketController < Import::BaseController
def create
@repo_id = params[:repo_id] || ""
repo = client.project(@repo_id.gsub("___", "/"))
@target_namespace = params[:new_namespace].presence || repo["owner"]
@project_name = repo["slug"]
repo_owner = repo["owner"]
repo_owner = current_user.username if repo_owner == client.user["user"]["username"]
@target_namespace = params[:new_namespace].presence || repo_owner
namespace = get_or_create_namespace || (render and return)
......
......@@ -31,8 +31,11 @@ class Import::GithubController < Import::BaseController
def create
@repo_id = params[:repo_id].to_i
repo = client.repo(@repo_id)
@target_namespace = params[:new_namespace].presence || repo.owner.login
@project_name = repo.name
repo_owner = repo.owner.login
repo_owner = current_user.username if repo_owner == client.user.login
@target_namespace = params[:new_namespace].presence || repo_owner
namespace = get_or_create_namespace || (render and return)
......
......@@ -28,8 +28,11 @@ class Import::GitlabController < Import::BaseController
def create
@repo_id = params[:repo_id].to_i
repo = client.project(@repo_id)
@target_namespace = params[:new_namespace].presence || repo["namespace"]["path"]
@project_name = repo["name"]
repo_owner = repo["namespace"]["path"]
repo_owner = current_user.username if repo_owner == client.user["username"]
@target_namespace = params[:new_namespace].presence || repo_owner
namespace = get_or_create_namespace || (render and return)
......
......@@ -4,14 +4,22 @@ class NamespacesController < ApplicationController
def show
namespace = Namespace.find_by(path: params[:id])
unless namespace
return render_404
if namespace
if namespace.is_a?(Group)
group = namespace
else
user = namespace.owner
end
end
if namespace.type == "Group"
redirect_to group_path(namespace)
if user
redirect_to user_path(user)
elsif group && can?(current_user, :read_group, group)
redirect_to group_path(group)
elsif current_user.nil?
authenticate_user!
else
redirect_to user_path(namespace.owner)
render_404
end
end
end
......@@ -11,18 +11,18 @@ class Projects::RepositoriesController < Projects::ApplicationController
end
def archive
unless can?(current_user, :download_code, @project)
render_404 and return
begin
file_path = ArchiveRepositoryService.new(@project, params[:ref], params[:format]).execute
rescue
return head :not_found
end
file_path = ArchiveRepositoryService.new.execute(@project, params[:ref], params[:format])
if file_path
# Send file to user
response.headers["Content-Length"] = File.open(file_path).size.to_s
send_file file_path
else
render_404
redirect_to request.fullpath
end
end
end
......@@ -29,6 +29,10 @@ module GitlabRoutingHelper
namespace_project_merge_request_path(entity.project.namespace, entity.project, entity, *args)
end
def milestone_path(entity, *args)
namespace_project_milestone_path(entity.project.namespace, entity.project, entity, *args)
end
def project_url(project, *args)
namespace_project_url(project.namespace, project, *args)
end
......
......@@ -40,7 +40,7 @@ module IconsHelper
def file_type_icon_class(type, mode, name)
if type == 'folder'
icon_class = 'folder'
elsif mode == 0120000
elsif mode == '120000'
icon_class = 'share'
else
# Guess which icon to choose based on file extension.
......
......@@ -13,22 +13,34 @@ module IssuesHelper
OpenStruct.new(id: 0, title: 'None (backlog)', name: 'Unassigned')
end
def url_for_project_issues(project = @project)
def url_for_project_issues(project = @project, options = {})
return '' if project.nil?
project.issues_tracker.project_url
if options[:only_path]
project.issues_tracker.project_path
else
project.issues_tracker.project_url
end
end
def url_for_new_issue(project = @project)
def url_for_new_issue(project = @project, options = {})
return '' if project.nil?
project.issues_tracker.new_issue_url
if options[:only_path]
project.issues_tracker.new_issue_path
else
project.issues_tracker.new_issue_url
end
end
def url_for_issue(issue_iid, project = @project)
def url_for_issue(issue_iid, project = @project, options = {})
return '' if project.nil?
project.issues_tracker.issue_url(issue_iid)
if options[:only_path]
project.issues_tracker.issue_path(issue_iid)
else
project.issues_tracker.issue_url(issue_iid)
end
end
def title_for_issue(issue_iid, project = @project)
......
......@@ -80,17 +80,17 @@ module ProjectsHelper
@project.milestones.active.order("due_date, title ASC")
end
def link_to_toggle_star(title, starred, signed_in)
cls = 'star-btn'
cls << ' disabled' unless signed_in
def link_to_toggle_star(title, starred)
cls = 'star-btn btn btn-sm btn-default'
toggle_text =
if starred
' Unstar'
else
' Star'
end
toggle_html = content_tag('span', class: 'toggle') do
toggle_text = if starred
' Unstar'
else
' Star'
end
icon('star') + toggle_text
end
......@@ -106,23 +106,25 @@ module ProjectsHelper
data: { type: 'json' }
}
path = toggle_star_namespace_project_path(@project.namespace, @project)
content_tag 'span', class: starred ? 'turn-on' : 'turn-off' do
link_to(
toggle_star_namespace_project_path(@project.namespace, @project),
link_opts
) do
link_to(path, link_opts) do
toggle_html + ' ' + count_html
end
end
end
def link_to_toggle_fork
out = icon('code-fork')
out << ' Fork'
out << content_tag(:span, class: 'count') do
html = content_tag('span') do
icon('code-fork') + ' Fork'
end
count_html = content_tag(:span, class: 'count') do
@project.forks_count.to_s
end
html + count_html
end
private
......
......@@ -52,7 +52,7 @@ module Mentionable
if identifier == "all"
users.push(*project.team.members.flatten)
elsif namespace = Namespace.find_by(path: identifier)
if namespace.type == "Group"
if namespace.is_a?(Group)
users.push(*namespace.users)
else
users << namespace.owner
......
......@@ -24,8 +24,8 @@ class Namespace < ActiveRecord::Base
validates :name,
presence: true, uniqueness: true,
length: { within: 0..255 },
format: { with: Gitlab::Regex.name_regex,
message: Gitlab::Regex.name_regex_message }
format: { with: Gitlab::Regex.namespace_name_regex,
message: Gitlab::Regex.namespace_name_regex_message }
validates :description, length: { within: 0..255 }
validates :path,
......@@ -33,8 +33,8 @@ class Namespace < ActiveRecord::Base
presence: true,
length: { within: 1..255 },
exclusion: { in: Gitlab::Blacklist.path },
format: { with: Gitlab::Regex.path_regex,
message: Gitlab::Regex.path_regex_message }
format: { with: Gitlab::Regex.namespace_regex,
message: Gitlab::Regex.namespace_regex_message }
delegate :name, to: :owner, allow_nil: true, prefix: true
......@@ -44,21 +44,36 @@ class Namespace < ActiveRecord::Base
scope :root, -> { where('type IS NULL') }
def self.by_path(path)
where('lower(path) = :value', value: path.downcase).first
end
class << self
def by_path(path)
where('lower(path) = :value', value: path.downcase).first
end
# Case insensetive search for namespace by path or name
def self.find_by_path_or_name(path)
find_by("lower(path) = :path OR lower(name) = :path", path: path.downcase)
end
# Case insensetive search for namespace by path or name
def find_by_path_or_name(path)
find_by("lower(path) = :path OR lower(name) = :path", path: path.downcase)
end
def self.search(query)
where("name LIKE :query OR path LIKE :query", query: "%#{query}%")
end
def search(query)
where("name LIKE :query OR path LIKE :query", query: "%#{query}%")
end
def clean_path(path)
path.gsub!(/@.*\z/, "")
path.gsub!(/\.git\z/, "")
path.gsub!(/\A-/, "")
path.gsub!(/.\z/, "")
path.gsub!(/[^a-zA-Z0-9_\-\.]/, "")
counter = 0
base = path
while Namespace.by_path(path).present?
counter += 1
path = "#{base}#{counter}"
end
def self.global_id
'GLN'
path
end
end
def to_param
......
......@@ -130,12 +130,12 @@ class Project < ActiveRecord::Base
presence: true,
length: { within: 0..255 },
format: { with: Gitlab::Regex.project_name_regex,
message: Gitlab::Regex.project_regex_message }
message: Gitlab::Regex.project_name_regex_message }
validates :path,
presence: true,
length: { within: 0..255 },
format: { with: Gitlab::Regex.path_regex,
message: Gitlab::Regex.path_regex_message }
format: { with: Gitlab::Regex.project_path_regex,
message: Gitlab::Regex.project_path_regex_message }
validates :issues_enabled, :merge_requests_enabled,
:wiki_enabled, inclusion: { in: [true, false] }
validates :issues_tracker_id, length: { maximum: 255 }, allow_blank: true
......
......@@ -20,8 +20,13 @@
class GitlabIssueTrackerService < IssueTrackerService
include Rails.application.routes.url_helpers
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
default_url_options[:host] = Gitlab.config.gitlab.host
default_url_options[:protocol] = Gitlab.config.gitlab.protocol
default_url_options[:port] = Gitlab.config.gitlab.port unless Gitlab.config.gitlab_on_standard_port?
default_url_options[:script_name] = Gitlab.config.gitlab.relative_url_root
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
def default?
true
......@@ -32,20 +37,26 @@ class GitlabIssueTrackerService < IssueTrackerService
end
def project_url
"#{gitlab_url}#{namespace_project_issues_path(project.namespace, project)}"
namespace_project_issues_url(project.namespace, project)
end
def new_issue_url
"#{gitlab_url}#{new_namespace_project_issue_path(namespace_id: project.namespace, project_id: project)}"
new_namespace_project_issue_url(namespace_id: project.namespace, project_id: project)
end
def issue_url(iid)
"#{gitlab_url}#{namespace_project_issue_path(namespace_id: project.namespace, project_id: project, id: iid)}"
namespace_project_issue_url(namespace_id: project.namespace, project_id: project, id: iid)
end
private
def project_path
namespace_project_issues_path(project.namespace, project)
end
def new_issue_path
new_namespace_project_issue_path(namespace_id: project.namespace, project_id: project)
end
def gitlab_url
Gitlab.config.gitlab.relative_url_root.chomp("/") if Gitlab.config.gitlab.relative_url_root
def issue_path(iid)
namespace_project_issue_path(namespace_id: project.namespace, project_id: project, id: iid)
end
end
......@@ -38,6 +38,18 @@ class IssueTrackerService < Service
self.issues_url.gsub(':id', iid.to_s)
end
def project_path
project_url
end
def new_issue_path
new_issue_url
end
def issue_path(iid)
issue_url(iid)
end
def fields
[
{ type: 'text', name: 'description', placeholder: description },
......
......@@ -267,6 +267,9 @@ class Repository
# Remove archives older than 2 hours
def clean_old_archives
repository_downloads_path = Gitlab.config.gitlab.repository_downloads_path
return unless File.directory?(repository_downloads_path)
Gitlab::Popen.popen(%W(find #{repository_downloads_path} -not -path #{repository_downloads_path} -mmin +120 -delete))
end
......
......@@ -33,8 +33,8 @@ class Snippet < ActiveRecord::Base
validates :file_name,
presence: true,
length: { within: 0..255 },
format: { with: Gitlab::Regex.path_regex,
message: Gitlab::Regex.path_regex_message }
format: { with: Gitlab::Regex.file_name_regex,
message: Gitlab::Regex.file_name_regex_message }
validates :content, presence: true
validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values }
......
......@@ -129,8 +129,8 @@ class User < ActiveRecord::Base
presence: true,
uniqueness: { case_sensitive: false },
exclusion: { in: Gitlab::Blacklist.path },
format: { with: Gitlab::Regex.username_regex,
message: Gitlab::Regex.username_regex_message }
format: { with: Gitlab::Regex.namespace_regex,
message: Gitlab::Regex.namespace_regex_message }
validates :notification_level, inclusion: { in: Notification.notification_levels }, presence: true
validate :namespace_uniq, if: ->(user) { user.username_changed? }
......@@ -238,7 +238,7 @@ class User < ActiveRecord::Base
def non_ldap
joins('LEFT JOIN identities ON identities.user_id = users.id').
where('identities.provider IS NULL OR identities.provider NOT LIKE ?', 'ldap%')
where('identities.provider IS NULL OR identities.provider NOT LIKE ?', 'ldap%')
end
def clean_username(username)
......
class ArchiveRepositoryService
def execute(project, ref, format)
storage_path = Gitlab.config.gitlab.repository_downloads_path
attr_reader :project, :ref, :format
unless File.directory?(storage_path)
FileUtils.mkdir_p(storage_path)
def initialize(project, ref, format)
format ||= 'tar.gz'
@project, @ref, @format = project, ref, format.downcase
end
def execute(options = {})
project.repository.clean_old_archives
raise "No archive file path" unless file_path
return file_path if archived?
unless archiving?
RepositoryArchiveWorker.perform_async(project.id, ref, format)
end
format ||= 'tar.gz'
repository = project.repository
repository.clean_old_archives
repository.archive_repo(ref, storage_path, format.downcase)
archived = wait_until_archived(options[:timeout] || 5.0)
file_path if archived
end
private
def storage_path
Gitlab.config.gitlab.repository_downloads_path
end
def file_path
@file_path ||= project.repository.archive_file_path(ref, storage_path, format)
end
def pid_file_path
@pid_file_path ||= project.repository.archive_pid_file_path(ref, storage_path, format)
end
def archived?
File.exist?(file_path)
end
def archiving?
File.exist?(pid_file_path)
end
def wait_until_archived(timeout = 5.0)
return archived? if timeout == 0.0
t1 = Time.now
begin
sleep 0.1
success = archived?
t2 = Time.now
end until success || t2 - t1 >= timeout
success
end
end
......@@ -16,10 +16,10 @@ module Files
file_name = File.basename(path)
file_path = path
unless file_name =~ Gitlab::Regex.path_regex
unless file_name =~ Gitlab::Regex.file_name_regex
return error(
'Your changes could not be committed, because the file name ' +
Gitlab::Regex.path_regex_message
Gitlab::Regex.file_name_regex_message
)
end
......
%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone.milestones.first) }
%h4
= link_to_gfm truncate(milestone.title, length: 100), dashboard_milestone_path(milestone.safe_title, title: milestone.title)
.row
.col-sm-6
= link_to dashboard_milestone_path(milestone.safe_title, title: milestone.title) do
= pluralize milestone.issue_count, 'Issue'
&nbsp;
= link_to dashboard_milestone_path(milestone.safe_title, title: milestone.title) do
= pluralize milestone.merge_requests_count, 'Merge Request'
&nbsp;
%span.light #{milestone.percent_complete}% complete
.col-sm-6
= milestone_progress_bar(milestone)
%div
- milestone.milestones.each do |milestone|
= link_to milestone_path(milestone) do
%span.label.label-gray
= milestone.project.name_with_namespace
......@@ -16,23 +16,5 @@
.nothing-here-block No milestones to show
- else
- @dashboard_milestones.each do |milestone|
%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone.milestones.first) }
%h4
= link_to_gfm truncate(milestone.title, length: 100), dashboard_milestone_path(milestone.safe_title, title: milestone.title)
%div
%div
= link_to dashboard_milestone_path(milestone.safe_title, title: milestone.title) do
= pluralize milestone.issue_count, 'Issue'
&nbsp;
= link_to dashboard_milestone_path(milestone.safe_title, title: milestone.title) do
= pluralize milestone.merge_requests_count, 'Merge Request'
&nbsp;
%span.light #{milestone.percent_complete}% complete
= milestone_progress_bar(milestone)
%div
%br
- milestone.milestones.each do |milestone|
= link_to namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) do
%span.label.label-default
= milestone.project.name_with_namespace
= render 'milestone', milestone: milestone
= paginate @dashboard_milestones, theme: "gitlab"
= form_for @group_member, url: group_group_members_path(@group), html: { class: 'form-horizontal users-group-form' } do |f|
.form-group
= f.label :user_ids, "People", class: 'control-label'
.col-sm-10= users_select_tag(:user_ids, { multiple: true, skip_ldap: @group.ldap_synced? , class: 'input-large' })
.col-sm-10= users_select_tag(:user_ids, multiple: true, skip_ldap: @group.ldap_synced?, class: 'input-large', scope: :all)
.form-group
= f.label :access_level, "Group Access", class: 'control-label'
......
%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone.milestones.first) }
.pull-right
- if can?(current_user, :manage_group, @group)
- if milestone.closed?
= link_to 'Reopen Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen"
- else
= link_to 'Close Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-sm btn-close"
%h4
= link_to_gfm truncate(milestone.title, length: 100), group_milestone_path(@group, milestone.safe_title, title: milestone.title)
.row
.col-sm-6
= link_to group_milestone_path(@group, milestone.safe_title, title: milestone.title) do
= pluralize milestone.issue_count, 'Issue'
&nbsp;
= link_to group_milestone_path(@group, milestone.safe_title, title: milestone.title) do
= pluralize milestone.merge_requests_count, 'Merge Request'
&nbsp;
%span.light #{milestone.percent_complete}% complete
.col-sm-6
= milestone_progress_bar(milestone)
%div
- milestone.milestones.each do |milestone|
= link_to milestone_path(milestone) do
%span.label.label-gray
= milestone.project.name
......@@ -18,29 +18,5 @@
.nothing-here-block No milestones to show
- else
- @group_milestones.each do |milestone|
%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone.milestones.first) }
.pull-right
- if can?(current_user, :manage_group, @group)
- if milestone.closed?
= link_to 'Reopen Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen"
- else
= link_to 'Close Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-sm btn-close"
%h4
= link_to_gfm truncate(milestone.title, length: 100), group_milestone_path(@group, milestone.safe_title, title: milestone.title)
%div
%div
= link_to group_milestone_path(@group, milestone.safe_title, title: milestone.title) do
= pluralize milestone.issue_count, 'Issue'
&nbsp;
= link_to group_milestone_path(@group, milestone.safe_title, title: milestone.title) do
= pluralize milestone.merge_requests_count, 'Merge Request'
&nbsp;
%span.light #{milestone.percent_complete}% complete
= milestone_progress_bar(milestone)
%div
%br
- milestone.milestones.each do |milestone|
= link_to namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) do
%span.label.label-default
= milestone.project.name
= render 'milestone', milestone: milestone
= paginate @group_milestones, theme: "gitlab"
.documentation.wiki
= markdown File.read(Rails.root.join('doc', @category, @file + '.md')).gsub("$your_email", current_user.email)
= markdown File.read(Rails.root.join('doc', @filepath + '.md')).gsub("$your_email", current_user.email)
......@@ -53,7 +53,7 @@
- if project_nav_tab? :issues
= nav_link(controller: :issues) do
= link_to url_for_project_issues, title: 'Issues', class: 'shortcuts-issues' do
= link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues' do
%i.fa.fa-exclamation-circle
%span
Issues
......
%div
= replace_image_links_with_base64(markdown(@note.note), @note.project)
= replace_image_links_with_base64(markdown(@note.note, reference_only_path: false), @note.project)
-if @issue.description
= replace_image_links_with_base64(markdown(@issue.description), @issue.project)
= replace_image_links_with_base64(markdown(@issue.description, reference_only_path: false), @issue.project)
- if @issue.assignee_id.present?
%p
......
......@@ -6,4 +6,4 @@
Assignee: #{@merge_request.author_name} &rarr; #{@merge_request.assignee_name}
-if @merge_request.description
= replace_image_links_with_base64(markdown(@merge_request.description), @merge_request.project)
= replace_image_links_with_base64(markdown(@merge_request.description, reference_only_path: false), @merge_request.project)
......@@ -5,7 +5,7 @@
%ul.dropdown-menu
- if @project.issues_enabled && can?(current_user, :write_issue, @project)
%li
= link_to url_for_new_issue, title: "New Issue" do
= link_to url_for_new_issue(@project, only_path: true), title: "New Issue" do
New issue
- if @project.merge_requests_enabled && can?(current_user, :write_merge_request, @project)
%li
......
......@@ -2,7 +2,7 @@
.project-home-panel{:class => ("empty-project" if empty_repo)}
.project-identicon-holder
= project_icon(@project, alt: '', class: 'avatar project-avatar')
.project-home-row
.project-home-row.project-home-row-top
.project-home-desc
- if @project.description.present?
= escaped_autolink(@project.description)
......@@ -14,31 +14,30 @@
&ndash;
= link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)) do
= readme.name
.star-fork-buttons
.project-repo-buttons
.inline.star.js-toggler-container{class: @show_star ? 'on' : ''}
- if current_user
= link_to_toggle_star('Star this project.', false)
= link_to_toggle_star('Unstar this project.', true)
- else
= link_to new_user_session_path, class: 'btn star-btn has_tooltip', title: 'You must sign in to star a project' do
%span
= icon('star')
Star
%span.count
= @project.star_count
- unless @project.empty_repo?
.fork-buttons
- if current_user && can?(current_user, :fork_project, @project) && @project.namespace != current_user.namespace
- if current_user && can?(current_user, :fork_project, @project) && @project.namespace != current_user.namespace
.inline.fork-buttons.prepend-left-10
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork' do
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-sm btn-default' do
= link_to_toggle_fork
- else
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project" do
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn btn-sm btn-default' do
= link_to_toggle_fork
.star-buttons
%span.star.js-toggler-container{class: @show_star ? 'on' : ''}
- if current_user
= link_to_toggle_star('Star this project.', false, true)
= link_to_toggle_star('Unstar this project.', true, true)
- else
= link_to_toggle_star('You must sign in to star a project.', false, false)
.project-home-row.hidden-xs
- if current_user && !empty_repo
.project-home-dropdown
= render "dropdown"
- unless @project.empty_repo?
- if can? current_user, :download_code, @project
.pull-right.prepend-left-10
= render 'projects/repositories/download_archive', split_button: true
= render "shared/clone_panel"
......@@ -10,7 +10,8 @@
Download as
%span.caret
%ul.dropdown-menu
%li= link_to "Email Patches", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch)
- unless @commit.parents.length > 1
%li= link_to "Email Patches", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch)
%li= link_to "Plain Diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff)
= link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-primary btn-grouped" do
%span Browse Code »
......
%ul.nav.nav-tabs
= nav_link(controller: [:commit, :commits]) do
= link_to 'Commits', namespace_project_commits_path(@project.namespace, @project, @repository.root_ref)
= link_to namespace_project_commits_path(@project.namespace, @project, @repository.root_ref) do
Commits
%span.badge= number_with_precision(@repository.commit_count, precision: 0, delimiter: ',')
= nav_link(controller: :compare) do
= link_to 'Compare', namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: @ref || @repository.root_ref)
......
......@@ -11,16 +11,14 @@
%span.cred (Expired)
%small
= milestone.expires_at
- if milestone.is_empty?
%span.muted Empty
- else
%div
%div
= link_to namespace_project_issues_path(milestone.project.namespace, milestone.project, milestone_id: milestone.id) do
= pluralize milestone.issues.count, 'Issue'
&nbsp;
= link_to namespace_project_merge_requests_path(milestone.project.namespace, milestone.project, milestone_id: milestone.id) do
= pluralize milestone.merge_requests.count, 'Merge Request'
&nbsp;
%span.light #{milestone.percent_complete}% complete
.row
.col-sm-6
= link_to namespace_project_issues_path(milestone.project.namespace, milestone.project, milestone_id: milestone.id) do
= pluralize milestone.issues.count, 'Issue'
&nbsp;
= link_to namespace_project_merge_requests_path(milestone.project.namespace, milestone.project, milestone_id: milestone.id) do
= pluralize milestone.merge_requests.count, 'Merge Request'
&nbsp;
%span.light #{milestone.percent_complete}% complete
.col-sm-6
= milestone_progress_bar(milestone)
......@@ -15,5 +15,5 @@
if(current_url == log_url) {
// Load 10 more commit log for each file in tree
// if we still on the same page
ajaxGet('#{logs_file_namespace_project_ref_path(@project.namespace, @project, @ref, @path || '/', offset: (@offset + @limit))}');
ajaxGet('#{logs_file_namespace_project_ref_path(@project.namespace, @project, @ref, @path || '', offset: (@offset + @limit))}');
}
......@@ -3,10 +3,10 @@
- split_button = split_button || false
- if split_button == true
%span.btn-group{class: btn_class}
= link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn', rel: 'nofollow' do
= link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn btn-sm', rel: 'nofollow' do
%i.fa.fa-download
%span Download zip
%a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' }
%a.btn-sm.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' }
%span.caret
%span.sr-only
Select Archive Format
......
......@@ -68,7 +68,7 @@
= link_to license_url(@project), class: 'btn btn-block' do
View license
.prepend-top-10
.prepend-top-10.append-bottom-10
%p
%span.light Created on
#{@project.created_at.stamp('Aug 22, 2013')}
......@@ -79,8 +79,16 @@
- else
#{link_to @project.owner_name, @project.owner}
- unless @project.empty_repo?
- if can? current_user, :download_code, @project
%hr
.prepend-top-10.append-bottom-10
= render 'projects/repositories/download_archive', split_button: true
.prepend-top-10
- @project.ci_services.each do |ci_service|
- if ci_service.active? && ci_service.respond_to?(:builds_path)
%hr
- if ci_service.respond_to?(:status_img_path)
= link_to ci_service.builds_path, :'data-no-turbolink' => 'data-no-turbolink' do
= image_tag ci_service.status_img_path, alt: "build status"
......@@ -97,4 +105,3 @@
= readme.name
.wiki
= render_readme(readme)
- if @contributed_projects.present?
- if local_assigns.has_key?(:contributed_projects) && contributed_projects.present?
.panel.panel-default.contributed-projects
.panel-heading Projects contributed to
= render 'shared/projects_list',
projects: @contributed_projects.sort_by(&:star_count).reverse,
projects: contributed_projects.sort_by(&:star_count).reverse,
projects_limit: 5, stars: true, avatar: false
- if @projects.present?
- if local_assigns.has_key?(:projects) && projects.present?
.panel.panel-default
.panel-heading Personal projects
= render 'shared/projects_list',
projects: @projects.sort_by(&:star_count).reverse,
projects: projects.sort_by(&:star_count).reverse,
projects_limit: 10, stars: true, avatar: false
......@@ -44,7 +44,7 @@
= spinner
%aside.col-md-4
= render 'profile', user: @user
= render 'projects'
= render 'projects', projects: @projects, contributed_projects: @contributed_projects
:coffeescript
$(".user-calendar").load("#{user_calendar_path}")
class RepositoryArchiveWorker
include Sidekiq::Worker
sidekiq_options queue: :archive_repo
attr_accessor :project, :ref, :format
def perform(project_id, ref, format)
@project = Project.find(project_id)
@ref, @format = ref, format.downcase
repository = project.repository
repository.clean_old_archives
return unless file_path
return if archived? || archiving?
repository.archive_repo(ref, storage_path, format)
end
private
def storage_path
Gitlab.config.gitlab.repository_downloads_path
end
def file_path
@file_path ||= project.repository.archive_file_path(ref, storage_path, format)
end
def pid_file_path
@pid_file_path ||= project.repository.archive_pid_file_path(ref, storage_path, format)
end
def archived?
File.exist?(file_path)
end
def archiving?
File.exist?(pid_file_path)
end
end
......@@ -37,7 +37,7 @@ start_no_deamonize()
start_sidekiq()
{
bundle exec sidekiq -q post_receive -q mailer -q system_hook -q project_web_hook -q gitlab_shell -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1
bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1
}
load_ok()
......
......@@ -66,8 +66,8 @@ production: &base
# If a commit message matches this regular expression, all issues referenced from the matched text will be closed.
# This happens when the commit is pushed or merged into the default branch of a project.
# When not specified the default issue_closing_pattern as specified below will be used.
# Tip: you can test your closing pattern at http://rubular.com
# issue_closing_pattern: '((?:[Cc]los(?:e[sd]|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?)|([A-Z]*-\d*))+)'
# Tip: you can test your closing pattern at http://rubular.com.
# issue_closing_pattern: '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)'
## Default project features settings
default_projects_features:
......@@ -86,35 +86,6 @@ production: &base
# The default is 'tmp/repositories' relative to the root of the Rails app.
# repository_downloads_path: tmp/repositories
## External issues trackers
issues_tracker:
# redmine:
# title: "Redmine"
# ## If not nil, link 'Issues' on project page will be replaced with this
# ## Use placeholders:
# ## :project_id - GitLab project identifier
# ## :issues_tracker_id - Project Name or Id in external issue tracker
# project_url: "http://redmine.sample/projects/:issues_tracker_id"
#
# ## If not nil, links from /#\d/ entities from commit messages will replaced with this
# ## Use placeholders:
# ## :project_id - GitLab project identifier
# ## :issues_tracker_id - Project Name or Id in external issue tracker
# ## :id - Issue id (from commit messages)
# issues_url: "http://redmine.sample/issues/:id"
#
# ## If not nil, links to creating new issues will be replaced with this
# ## Use placeholders:
# ## :project_id - GitLab project identifier
# ## :issues_tracker_id - Project Name or Id in external issue tracker
# new_issue_url: "http://redmine.sample/projects/:issues_tracker_id/issues/new"
#
# jira:
# title: "Atlassian Jira"
# project_url: "http://jira.sample/issues/?jql=project=:issues_tracker_id"
# issues_url: "http://jira.sample/browse/:id"
# new_issue_url: "http://jira.sample/secure/CreateIssue.jspa"
## Gravatar
## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html
gravatar:
......
......@@ -25,3 +25,5 @@ Sidekiq.configure_client do |config|
namespace: 'resque:gitlab'
}
end
Sidekiq::Queue["archive_repo"].limit = 2
# This is a patch to address the issue in https://github.com/mbleigh/acts-as-taggable-on/issues/427 caused by
# https://github.com/rails/rails/commit/31a43ebc107fbd50e7e62567e5208a05909ec76c
# gem 'acts-as-taggable-on' has the fix included https://github.com/mbleigh/acts-as-taggable-on/commit/89bbed3864a9252276fb8dd7d535fce280454b90
# but not in the currently used version of gem ('2.4.1')
# With replacement of 'acts-as-taggable-on' gem this file will become obsolete
module ActsAsTaggableOn::Taggable
module Core
module ClassMethods
def tagged_with(tags, options = {})
tag_list = ActsAsTaggableOn::TagList.from(tags)
empty_result = where("1 = 0")
return empty_result if tag_list.empty?
joins = []
conditions = []
having = []
select_clause = []
context = options.delete(:on)
owned_by = options.delete(:owned_by)
alias_base_name = undecorated_table_name.gsub('.','_')
quote = ActsAsTaggableOn::Tag.using_postgresql? ? '"' : ''
if options.delete(:exclude)
if options.delete(:wild)
tags_conditions = tag_list.map { |t| sanitize_sql(["#{ActsAsTaggableOn::Tag.table_name}.name #{like_operator} ? ESCAPE '!'", "%#{escape_like(t)}%"]) }.join(" OR ")
else
tags_conditions = tag_list.map { |t| sanitize_sql(["#{ActsAsTaggableOn::Tag.table_name}.name #{like_operator} ?", t]) }.join(" OR ")
end
conditions << "#{table_name}.#{primary_key} NOT IN (SELECT #{ActsAsTaggableOn::Tagging.table_name}.taggable_id FROM #{ActsAsTaggableOn::Tagging.table_name} JOIN #{ActsAsTaggableOn::Tag.table_name} ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key} AND (#{tags_conditions}) WHERE #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name, nil)})"
if owned_by
joins << "JOIN #{ActsAsTaggableOn::Tagging.table_name}" +
" ON #{ActsAsTaggableOn::Tagging.table_name}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
" AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name, nil)}" +
" AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_id = #{owned_by.id}" +
" AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_type = #{quote_value(owned_by.class.base_class.to_s, nil)}"
end
elsif options.delete(:any)
# get tags, drop out if nothing returned (we need at least one)
tags =
if options.delete(:wild)
ActsAsTaggableOn::Tag.named_like_any(tag_list)
else
ActsAsTaggableOn::Tag.named_any(tag_list)
end
return empty_result unless tags.length > 0
# setup taggings alias so we can chain, ex: items_locations_taggings_awesome_cool_123
# avoid ambiguous column name
taggings_context = context ? "_#{context}" : ''
taggings_alias = adjust_taggings_alias(
"#{alias_base_name[0..4]}#{taggings_context[0..6]}_taggings_#{sha_prefix(tags.map(&:name).join('_'))}"
)
tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
" ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
" AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}"
tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
# don't need to sanitize sql, map all ids and join with OR logic
conditions << tags.map { |t| "#{taggings_alias}.tag_id = #{t.id}" }.join(" OR ")
select_clause = "DISTINCT #{table_name}.*" unless context and tag_types.one?
if owned_by
tagging_join << " AND " +
sanitize_sql([
"#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
owned_by.id,
owned_by.class.base_class.to_s
])
end
joins << tagging_join
else
tags = ActsAsTaggableOn::Tag.named_any(tag_list)
return empty_result unless tags.length == tag_list.length
tags.each do |tag|
taggings_alias = adjust_taggings_alias("#{alias_base_name[0..11]}_taggings_#{sha_prefix(tag.name)}")
tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
" ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
" AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}" +
" AND #{taggings_alias}.tag_id = #{tag.id}"
tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
if owned_by
tagging_join << " AND " +
sanitize_sql([
"#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
owned_by.id,
owned_by.class.base_class.to_s
])
end
joins << tagging_join
end
end
taggings_alias, tags_alias = adjust_taggings_alias("#{alias_base_name}_taggings_group"), "#{alias_base_name}_tags_group"
if options.delete(:match_all)
joins << "LEFT OUTER JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
" ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
" AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}"
group_columns = ActsAsTaggableOn::Tag.using_postgresql? ? grouped_column_names_for(self) : "#{table_name}.#{primary_key}"
group = group_columns
having = "COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
end
select(select_clause) \
.joins(joins.join(" ")) \
.where(conditions.join(" AND ")) \
.group(group) \
.having(having) \
.order(options[:order]) \
.readonly(false)
end
end
end
end
......@@ -6,3 +6,4 @@
Mime::Type.register_alias "text/plain", :diff
Mime::Type.register_alias "text/plain", :patch
Mime::Type.register_alias 'text/html', :md
......@@ -39,10 +39,9 @@ Gitlab::Application.routes.draw do
# Help
get 'help' => 'help#index'
get 'help/:category/:file' => 'help#show', as: :help_page
get 'help/:category/*file' => 'help#show'
get 'help/shortcuts'
get 'help/ui' => 'help#ui'
get 'help/:filepath' => 'help#show', as: :help_page, constraints: { filepath: /[^\.]+/ }
#
# Global snippets
......
class RemovePeriodsAtEndsOfUsernames < ActiveRecord::Migration
include Gitlab::ShellAdapter
class Namespace < ActiveRecord::Base
class << self
def by_path(path)
where('lower(path) = :value', value: path.downcase).first
end
def clean_path(path)
path = path.dup
path.gsub!(/@.*\z/, "")
path.gsub!(/\.git\z/, "")
path.gsub!(/\A-/, "")
path.gsub!(/.\z/, "")
path.gsub!(/[^a-zA-Z0-9_\-\.]/, "")
counter = 0
base = path
while Namespace.by_path(path).present?
counter += 1
path = "#{base}#{counter}"
end
path
end
end
end
def up
changed_paths = {}
select_all("SELECT id, username FROM users WHERE username LIKE '%.'").each do |user|
username_was = user["username"]
username = Namespace.clean_path(username_was)
changed_paths[username_was] = username
username = quote_string(username)
execute "UPDATE users SET username = '#{username}' WHERE id = #{user["id"]}"
execute "UPDATE namespaces SET path = '#{username}', name = '#{username}' WHERE type IS NULL AND owner_id = #{user["id"]}"
end
select_all("SELECT id, path FROM namespaces WHERE type = 'Group' AND path LIKE '%.'").each do |group|
path_was = group["path"]
path = Namespace.clean_path(path_was)
changed_paths[path_was] = path
path = quote_string(path)
execute "UPDATE namespaces SET path = '#{path}' WHERE id = #{group["id"]}"
end
changed_paths.each do |path_was, path|
if gitlab_shell.mv_namespace(path_was, path)
# If repositories moved successfully we need to remove old satellites
# and send update instructions to users.
# However we cannot allow rollback since we moved namespace dir
# So we basically we mute exceptions in next actions
begin
gitlab_shell.rm_satellites(path_was)
# We cannot send update instructions since models and mailers
# can't safely be used from migrations as they may be written for
# later versions of the database.
# send_update_instructions
rescue
# Returning false does not rollback after_* transaction but gives
# us information about failing some of tasks
false
end
else
# if we cannot move namespace directory we should rollback
# db changes in order to prevent out of sync between db and fs
raise Exception.new('namespace directory cannot be moved')
end
end
end
end
# Issue closing pattern
By default you can close issues from commit messages by saying 'Closes #12' or 'Fixed #101'.
If a commit message matches the regular expression below, all issues referenced from
the matched text will be closed. This happens when the commit is pushed or merged
into the default branch of a project.
If you want to customize the message please do so in [gitlab.yml](https://gitlab.com/gitlab-org/gitlab-ce/blob/73b92f85bcd6c213b845cc997843a969cf0906cf/config/gitlab.yml.example#L73)
When not specified, the default issue_closing_pattern as shown below will be used:
```bash
((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)
```
For example:
```
git commit -m "Awesome commit message (Fix #20, Fixes #21 and Closes #22). This commit is also related to #17 and fixes #18, #19 and #23."
```
will close `#20`, `#21`, `#22`, `#18`, `#19` and `#23`, but `#17` won't be closed
as it does not match the pattern. It also works with multiline commit messages.
Tip: you can test this closing pattern at [http://rubular.com][1]. Use this site
to test your own patterns.
## Change the pattern
For Omnibus installs you can change the default pattern in `/etc/gitlab/gitlab.rb`:
```
issue_closing_pattern: '((?:[Cc]los(?:e[sd]|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)'
```
For manual installs you can customize the pattern in [gitlab.yml][0].
[0]: https://gitlab.com/gitlab-org/gitlab-ce/blob/40c3675372320febf5264061c9bcd63db2dfd13c/config/gitlab.yml.example#L65
[1]: http://rubular.com/r/Xmbexed1OJ
......@@ -17,6 +17,13 @@ sudo gitlab-rake gitlab:backup:create
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
Also you can choose what should be backed up by adding environment variable SKIP. Available options: db,
uploads (attachments), repositories. Use a comma to specify several options at the same time.
```
sudo gitlab-rake gitlab:backup:create SKIP=db,uploads
```
Example output:
```
......
......@@ -11,7 +11,7 @@ RUN apt-get update -q \
# If the Omnibus package version below is outdated please contribute a merge request to update it.
# If you run GitLab Enterprise Edition point it to a location where you have downloaded it.
RUN TMP_FILE=$(mktemp); \
wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.9.0-omnibus.2-1_amd64.deb \
wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.9.1-omnibus.1-1_amd64.deb \
&& dpkg -i $TMP_FILE \
&& rm -f $TMP_FILE
......
......@@ -54,6 +54,14 @@ Feature: Groups
Then I see a new LDAP synchronization listed
And LDAP disabled
@javascript
Scenario: Add user to group
Given gitlab user "Mike"
When I visit group "Owned" members page
And I click link "Add members"
When I select "Mike" as "Reporter"
Then I should see "Mike" in team list as "Reporter"
# Leave
@javascript
......
......@@ -13,7 +13,7 @@ Feature: Project Star
Given public project "Community"
And I visit project "Community" page
When I click on the star toggle button
Then The project has 0 stars
Then I redirected to sign in page
@javascript
Scenario: Signed in users can toggle star
......
......@@ -8,7 +8,7 @@ class Spinach::Features::DashboardHelp < Spinach::FeatureSteps
end
step 'I visit the "Rake Tasks" help page' do
visit help_page_path("raketasks", "maintenance.md")
visit help_page_path('raketasks/maintenance', format: 'md')
end
step 'I should see "Rake Tasks" page markdown rendered' do
......
......@@ -5,6 +5,32 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
include SharedUser
include Select2Helper
step 'gitlab user "Mike"' do
create(:user, name: "Mike")
end
step 'I click link "Add members"' do
find(:css, 'button.btn-new').click
end
step 'I select "Mike" as "Reporter"' do
user = User.find_by(name: "Mike")
within ".users-group-form" do
select2(user.id, from: "#user_ids", multiple: true)
select "Reporter", from: "access_level"
end
click_button "Add users to group"
end
step 'I should see "Mike" in team list as "Reporter"' do
within '.well-list' do
page.should have_content('Mike')
page.should have_content('Reporter')
end
end
step 'I should see group "Owned" projects list' do
Group.find_by(name: "Owned").projects.each do |project|
page.should have_link project.name
......
......@@ -74,7 +74,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I fill the new file name with an illegal name' do
fill_in :file_name, with: '.git'
fill_in :file_name, with: 'Spaces Not Allowed'
end
step 'I fill the commit message' do
......
......@@ -22,12 +22,16 @@ class Spinach::Features::ProjectStar < Spinach::FeatureSteps
# Requires @javascript
step "I click on the star toggle button" do
find(".star .toggle", visible: true).click
find(".star-btn", visible: true).click
end
step 'I redirected to sign in page' do
current_path.should == new_user_session_path
end
protected
def has_n_stars(n)
expect(page).to have_css(".star .count", text: /^#{n}$/, visible: true)
expect(page).to have_css(".star-btn .count", text: n, visible: true)
end
end
require 'mime/types'
require 'uri'
module API
# Projects API
......@@ -101,10 +100,11 @@ module API
# branch (required) - The name of the branch
# Example Request:
# DELETE /projects/:id/repository/branches/:branch
delete ":id/repository/branches/:branch" do
delete ":id/repository/branches/:branch",
requirements: { branch: /.*/ } do
authorize_push_project
result = DeleteBranchService.new(user_project, current_user).
execute(URI.unescape(params[:branch]))
execute(params[:branch])
if result[:status] == :success
{
......
......@@ -88,17 +88,14 @@ module API
present user_project, with: Entities::ProjectWithAccess, user: current_user
end
# Get a single project events
# Get events for a single project
#
# Parameters:
# id (required) - The ID of a project
# Example Request:
# GET /projects/:id/events
get ":id/events" do
limit = (params[:per_page] || 20).to_i
offset = (params[:page] || 0).to_i * limit
events = user_project.events.recent.limit(limit).offset(offset)
events = paginate user_project.events.recent
present events, with: Entities::Event
end
......
......@@ -133,10 +133,11 @@ module API
authorize! :download_code, user_project
begin
file_path = ArchiveRepositoryService.new.execute(
user_project,
params[:sha],
params[:format])
file_path = ArchiveRepositoryService.new(
user_project,
params[:sha],
params[:format]
).execute
rescue
not_found!('File')
end
......@@ -149,7 +150,7 @@ module API
env['api.format'] = :binary
present data
else
not_found!('File')
redirect request.fullpath
end
end
......
module Backup
class Manager
BACKUP_CONTENTS = %w{repositories/ db/ uploads/ backup_information.yml}
def pack
# saving additional informations
s = {}
......@@ -9,6 +7,7 @@ module Backup
s[:backup_created_at] = Time.now
s[:gitlab_version] = Gitlab::VERSION
s[:tar_version] = tar_version
s[:skipped] = ENV["SKIP"]
tar_file = "#{s[:backup_created_at].to_i}_gitlab_backup.tar"
Dir.chdir(Gitlab.config.backup.path) do
......@@ -17,12 +16,12 @@ module Backup
file << s.to_yaml.gsub(/^---\n/,'')
end
FileUtils.chmod_R(0700, %w{db uploads repositories})
FileUtils.chmod(0700, folders_to_backup)
# create archive
$progress.print "Creating backup archive: #{tar_file} ... "
orig_umask = File.umask(0077)
if Kernel.system('tar', '-cf', tar_file, *BACKUP_CONTENTS)
if Kernel.system('tar', '-cf', tar_file, *backup_contents)
$progress.puts "done".green
else
puts "creating archive #{tar_file} failed".red
......@@ -46,6 +45,7 @@ module Backup
connection = ::Fog::Storage.new(connection_settings)
directory = connection.directories.get(remote_directory)
if directory.files.create(key: tar_file, body: File.open(tar_file), public: false)
$progress.puts "done".green
else
......@@ -56,7 +56,10 @@ module Backup
def cleanup
$progress.print "Deleting tmp directories ... "
BACKUP_CONTENTS.each do |dir|
backup_contents.each do |dir|
next unless File.exist?(File.join(Gitlab.config.backup.path, dir))
if FileUtils.rm_rf(File.join(Gitlab.config.backup.path, dir))
$progress.puts "done".green
else
......@@ -73,6 +76,7 @@ module Backup
if keep_time > 0
removed = 0
Dir.chdir(Gitlab.config.backup.path) do
file_list = Dir.glob('*_gitlab_backup.tar')
file_list.map! { |f| $1.to_i if f =~ /(\d+)_gitlab_backup.tar/ }
......@@ -84,6 +88,7 @@ module Backup
end
end
end
$progress.puts "done. (#{removed} removed)".green
else
$progress.puts "skipping".yellow
......@@ -96,6 +101,7 @@ module Backup
# check for existing backups in the backup dir
file_list = Dir.glob("*_gitlab_backup.tar").each.map { |f| f.split(/_/).first.to_i }
puts "no backups found" if file_list.count == 0
if file_list.count > 1 && ENV["BACKUP"].nil?
puts "Found more than one backup, please specify which one you want to restore:"
puts "rake gitlab:backup:restore BACKUP=timestamp_of_backup"
......@@ -110,6 +116,7 @@ module Backup
end
$progress.print "Unpacking backup ... "
unless Kernel.system(*%W(tar -xf #{tar_file}))
puts "unpacking backup failed".red
exit 1
......@@ -117,7 +124,6 @@ module Backup
$progress.puts "done".green
end
settings = YAML.load_file("backup_information.yml")
ENV["VERSION"] = "#{settings[:db_version]}" if settings[:db_version].to_i > 0
# restoring mismatching backups can lead to unexpected problems
......@@ -136,5 +142,29 @@ module Backup
tar_version, _ = Gitlab::Popen.popen(%W(tar --version))
tar_version.force_encoding('locale').split("\n").first
end
def skipped?(item)
settings[:skipped] && settings[:skipped].include?(item)
end
private
def backup_contents
folders_to_backup + ["backup_information.yml"]
end
def folders_to_backup
folders = %w{repositories db uploads}
if ENV["SKIP"]
return folders.reject{ |folder| ENV["SKIP"].include?(folder) }
end
folders
end
def settings
@settings ||= YAML.load_file("backup_information.yml")
end
end
end
......@@ -28,6 +28,10 @@ module Gitlab
client.auth_code.get_token(code, redirect_uri: redirect_uri).token
end
def user
api.get("/api/v3/user").parsed
end
def issues(project_identifier)
lazy_page_iterator(PER_PAGE) do |page|
api.get("/api/v3/projects/#{project_identifier}/issues?per_page=#{PER_PAGE}&page=#{page}").parsed
......
......@@ -32,12 +32,12 @@ module Gitlab
module Markdown
include IssuesHelper
attr_reader :html_options
attr_reader :options, :html_options
# Public: Parse the provided text with GitLab-Flavored Markdown
#
# text - the source text
# project - extra options for the reference links as given to link_to
# project - the project
# html_options - extra options for the reference links as given to link_to
def gfm(text, project = @project, html_options = {})
gfm_with_options(text, {}, project, html_options)
......@@ -46,9 +46,10 @@ module Gitlab
# Public: Parse the provided text with GitLab-Flavored Markdown
#
# text - the source text
# options - parse_tasks: true - render tasks
# - xhtml: true - output XHTML instead of HTML
# project - extra options for the reference links as given to link_to
# options - parse_tasks - render tasks
# - xhtml - output XHTML instead of HTML
# - reference_only_path - Use relative path for reference links
# project - the project
# html_options - extra options for the reference links as given to link_to
def gfm_with_options(text, options = {}, project = @project, html_options = {})
return text if text.nil?
......@@ -58,6 +59,13 @@ module Gitlab
# for gsub calls to work as we need them to.
text = text.dup.to_str
options.reverse_merge!(
parse_tasks: false,
xhtml: false,
reference_only_path: true
)
@options = options
@html_options = html_options
# Extract pre blocks so they are not altered
......@@ -113,12 +121,13 @@ module Gitlab
markdown_pipeline = HTML::Pipeline::Gitlab.new(filters).pipeline
result = markdown_pipeline.call(text, markdown_context)
saveoptions = 0
save_options = 0
if options[:xhtml]
saveoptions |= Nokogiri::XML::Node::SaveOptions::AS_XHTML
save_options |= Nokogiri::XML::Node::SaveOptions::AS_XHTML
end
text = result[:output].to_html(save_with: saveoptions)
text = result[:output].to_html(save_with: save_options)
if options[:parse_tasks]
text = parse_tasks(text)
......@@ -152,7 +161,7 @@ module Gitlab
text
end
NAME_STR = '[a-zA-Z0-9_][a-zA-Z0-9_\-\.]*'
NAME_STR = Gitlab::Regex::NAMESPACE_REGEX_STR
PROJ_STR = "(?<project>#{NAME_STR}/#{NAME_STR})"
REFERENCE_PATTERN = %r{
......@@ -229,33 +238,37 @@ module Gitlab
end
def reference_user(identifier, project = @project, _ = nil)
options = html_options.merge(
link_options = html_options.merge(
class: "gfm gfm-project_member #{html_options[:class]}"
)
if identifier == "all"
link_to("@all", namespace_project_url(project.namespace, project), options)
link_to(
"@all",
namespace_project_url(project.namespace, project, only_path: options[:reference_only_path]),
link_options
)
elsif namespace = Namespace.find_by(path: identifier)
url =
if namespace.type == "Group"
group_url(identifier)
else
user_url(identifier)
if namespace.is_a?(Group)
group_url(identifier, only_path: options[:reference_only_path])
else
user_url(identifier, only_path: options[:reference_only_path])
end
link_to("@#{identifier}", url, options)
link_to("@#{identifier}", url, link_options)
end
end
def reference_label(identifier, project = @project, _ = nil)
if label = project.labels.find_by(id: identifier)
options = html_options.merge(
link_options = html_options.merge(
class: "gfm gfm-label #{html_options[:class]}"
)
link_to(
render_colored_label(label),
namespace_project_issues_path(project.namespace, project, label_name: label.name),
options
link_options
)
end
end
......@@ -263,14 +276,14 @@ module Gitlab
def reference_issue(identifier, project = @project, prefix_text = nil)
if project.default_issues_tracker?
if project.issue_exists? identifier
url = url_for_issue(identifier, project)
url = url_for_issue(identifier, project, only_path: options[:reference_only_path])
title = title_for_issue(identifier, project)
options = html_options.merge(
link_options = html_options.merge(
title: "Issue: #{title}",
class: "gfm gfm-issue #{html_options[:class]}"
)
link_to("#{prefix_text}##{identifier}", url, options)
link_to("#{prefix_text}##{identifier}", url, link_options)
end
else
if project.external_issue_tracker.present?
......@@ -280,44 +293,46 @@ module Gitlab
end
end
def reference_merge_request(identifier, project = @project,
prefix_text = nil)
def reference_merge_request(identifier, project = @project, prefix_text = nil)
if merge_request = project.merge_requests.find_by(iid: identifier)
options = html_options.merge(
link_options = html_options.merge(
title: "Merge Request: #{merge_request.title}",
class: "gfm gfm-merge_request #{html_options[:class]}"
)
url = namespace_project_merge_request_url(project.namespace, project,
merge_request)
link_to("#{prefix_text}!#{identifier}", url, options)
merge_request,
only_path: options[:reference_only_path])
link_to("#{prefix_text}!#{identifier}", url, link_options)
end
end
def reference_snippet(identifier, project = @project, _ = nil)
if snippet = project.snippets.find_by(id: identifier)
options = html_options.merge(
link_options = html_options.merge(
title: "Snippet: #{snippet.title}",
class: "gfm gfm-snippet #{html_options[:class]}"
)
link_to(
"$#{identifier}",
namespace_project_snippet_url(project.namespace, project, snippet),
options
namespace_project_snippet_url(project.namespace, project, snippet,
only_path: options[:reference_only_path]),
link_options
)
end
end
def reference_commit(identifier, project = @project, prefix_text = nil)
if project.valid_repo? && commit = project.repository.commit(identifier)
options = html_options.merge(
link_options = html_options.merge(
title: commit.link_title,
class: "gfm gfm-commit #{html_options[:class]}"
)
prefix_text = "#{prefix_text}@" if prefix_text
link_to(
"#{prefix_text}#{identifier}",
namespace_project_commit_url(project.namespace, project, commit),
options
namespace_project_commit_url( project.namespace, project, commit,
only_path: options[:reference_only_path]),
link_options
)
end
end
......@@ -328,11 +343,11 @@ module Gitlab
inclusive = identifier !~ /\.{3}/
from_id << "^" if inclusive
if project.valid_repo? &&
from = project.repository.commit(from_id) &&
if project.valid_repo? &&
from = project.repository.commit(from_id) &&
to = project.repository.commit(to_id)
options = html_options.merge(
link_options = html_options.merge(
title: "Commits #{from_id} through #{to_id}",
class: "gfm gfm-commit_range #{html_options[:class]}"
)
......@@ -340,22 +355,23 @@ module Gitlab
link_to(
"#{prefix_text}#{identifier}",
namespace_project_compare_url(project.namespace, project, from: from_id, to: to_id),
options
namespace_project_compare_url(project.namespace, project,
from: from_id, to: to_id,
only_path: options[:reference_only_path]),
link_options
)
end
end
def reference_external_issue(identifier, project = @project,
prefix_text = nil)
url = url_for_issue(identifier, project)
def reference_external_issue(identifier, project = @project, prefix_text = nil)
url = url_for_issue(identifier, project, only_path: options[:reference_only_path])
title = project.external_issue_tracker.title
options = html_options.merge(
link_options = html_options.merge(
title: "Issue in #{title}",
class: "gfm gfm-issue #{html_options[:class]}"
)
link_to("#{prefix_text}##{identifier}", url, options)
link_to("#{prefix_text}##{identifier}", url, link_options)
end
# Turn list items that start with "[ ]" into HTML checkbox inputs.
......
......@@ -86,7 +86,7 @@ module Gitlab
def user_attributes
{
name: auth_hash.name,
username: ::User.clean_username(auth_hash.username),
username: ::Namespace.clean_path(auth_hash.username),
email: auth_hash.email,
password: auth_hash.password,
password_confirmation: auth_hash.password,
......
......@@ -2,49 +2,66 @@ module Gitlab
module Regex
extend self
def username_regex
default_regex
NAMESPACE_REGEX_STR = '(?:[a-zA-Z0-9_\.][a-zA-Z0-9_\-\.]*[a-zA-Z0-9_\-]|[a-zA-Z0-9_])'.freeze
def namespace_regex
@namespace_regex ||= /\A#{NAMESPACE_REGEX_STR}\z/.freeze
end
def namespace_regex_message
"can contain only letters, digits, '_', '-' and '.'. " \
"Cannot start with '-' or end in '.'." \
end
def namespace_name_regex
@namespace_name_regex ||= /\A[a-zA-Z0-9_\-\. ]*\z/.freeze
end
def username_regex_message
default_regex_message
def namespace_name_regex_message
"can contain only letters, digits, '_', '-', '.' and space."
end
def project_name_regex
/\A[a-zA-Z0-9_.][a-zA-Z0-9_\-\. ]*\z/
@project_name_regex ||= /\A[a-zA-Z0-9_.][a-zA-Z0-9_\-\. ]*\z/.freeze
end
def project_regex_message
"can contain only letters, digits, '_', '-' and '.' and space. " \
def project_name_regex_message
"can contain only letters, digits, '_', '-', '.' and space. " \
"It must start with letter, digit or '_'."
end
def name_regex
/\A[a-zA-Z0-9_\-\. ]*\z/
def project_path_regex
@project_path_regex ||= /\A[a-zA-Z0-9_.][a-zA-Z0-9_\-\.]*(?<!\.git)\z/.freeze
end
def name_regex_message
"can contain only letters, digits, '_', '-' and '.' and space."
def project_path_regex_message
"can contain only letters, digits, '_', '-' and '.'. " \
"Cannot start with '-' or end in '.git'" \
end
def path_regex
default_regex
def file_name_regex
@file_name_regex ||= /\A[a-zA-Z0-9_\-\.]*\z/.freeze
end
def path_regex_message
default_regex_message
def file_name_regex_message
"can contain only letters, digits, '_', '-' and '.'. "
end
def archive_formats_regex
#|zip|tar| tar.gz | tar.bz2 |
/(zip|tar|tar\.gz|tgz|gz|tar\.bz2|tbz|tbz2|tb2|bz2)/
# |zip|tar| tar.gz | tar.bz2 |
@archive_formats_regex ||= /(zip|tar|tar\.gz|tgz|gz|tar\.bz2|tbz|tbz2|tb2|bz2)/.freeze
end
def git_reference_regex
# Valid git ref regex, see:
# https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html
%r{
@git_reference_regex ||= %r{
(?!
(?# doesn't begins with)
\/| (?# rule #6)
......@@ -60,18 +77,7 @@ module Gitlab
(?# doesn't end with)
(?<!\.lock) (?# rule #1)
(?<![\/.]) (?# rule #6-7)
}x
end
protected
def default_regex_message
"can contain only letters, digits, '_', '-' and '.'. " \
"Cannot start with '-' or end in '.git'" \
end
def default_regex
/\A[a-zA-Z0-9_.][a-zA-Z0-9_\-\.]*(?<!\.git)\z/
}x.freeze
end
end
end
......@@ -27,9 +27,9 @@ namespace :gitlab do
backup = Backup::Manager.new
backup.unpack
Rake::Task["gitlab:backup:db:restore"].invoke
Rake::Task["gitlab:backup:repo:restore"].invoke
Rake::Task["gitlab:backup:uploads:restore"].invoke
Rake::Task["gitlab:backup:db:restore"].invoke unless backup.skipped?("db")
Rake::Task["gitlab:backup:repo:restore"].invoke unless backup.skipped?("repositories")
Rake::Task["gitlab:backup:uploads:restore"].invoke unless backup.skipped?("uploads")
Rake::Task["gitlab:shell:setup"].invoke
backup.cleanup
......@@ -38,8 +38,13 @@ namespace :gitlab do
namespace :repo do
task create: :environment do
$progress.puts "Dumping repositories ...".blue
Backup::Repository.new.dump
$progress.puts "done".green
if ENV["SKIP"] && ENV["SKIP"].include?("repositories")
$progress.puts "[SKIPPED]".cyan
else
Backup::Repository.new.dump
$progress.puts "done".green
end
end
task restore: :environment do
......@@ -52,8 +57,13 @@ namespace :gitlab do
namespace :db do
task create: :environment do
$progress.puts "Dumping database ... ".blue
Backup::Database.new.dump
$progress.puts "done".green
if ENV["SKIP"] && ENV["SKIP"].include?("db")
$progress.puts "[SKIPPED]".cyan
else
Backup::Database.new.dump
$progress.puts "done".green
end
end
task restore: :environment do
......@@ -66,8 +76,13 @@ namespace :gitlab do
namespace :uploads do
task create: :environment do
$progress.puts "Dumping uploads ... ".blue
Backup::Uploads.new.dump
$progress.puts "done".green
if ENV["SKIP"] && ENV["SKIP"].include?("uploads")
$progress.puts "[SKIPPED]".cyan
else
Backup::Uploads.new.dump
$progress.puts "done".green
end
end
task restore: :environment do
......
......@@ -2,6 +2,7 @@ namespace :gitlab do
desc "GITLAB | Run all tests"
task :test do
cmds = [
%W(rake brakeman),
%W(rake rubocop),
%W(rake spinach),
%W(rake spec),
......
......@@ -55,24 +55,109 @@ describe Import::BitbucketController do
end
describe "POST create" do
before do
@repo = {
slug: 'vim',
owner: "john"
let(:bitbucket_username) { user.username }
let(:bitbucket_user) {
{
user: {
username: bitbucket_username
}
}.with_indifferent_access
end
}
it "takes already existing namespace" do
namespace = create(:namespace, name: "john", owner: user)
expect(Gitlab::BitbucketImport::KeyAdder).
to receive(:new).with(@repo, user).
and_return(double(execute: true))
expect(Gitlab::BitbucketImport::ProjectCreator).
to receive(:new).with(@repo, namespace, user).
let(:bitbucket_repo) {
{
slug: "vim",
owner: bitbucket_username
}.with_indifferent_access
}
before do
allow(Gitlab::BitbucketImport::KeyAdder).
to receive(:new).with(bitbucket_repo, user).
and_return(double(execute: true))
controller.stub_chain(:client, :project).and_return(@repo)
post :create, format: :js
controller.stub_chain(:client, :user).and_return(bitbucket_user)
controller.stub_chain(:client, :project).and_return(bitbucket_repo)
end
context "when the repository owner is the Bitbucket user" do
context "when the Bitbucket user and GitLab user's usernames match" do
it "takes the current user's namespace" do
expect(Gitlab::BitbucketImport::ProjectCreator).
to receive(:new).with(bitbucket_repo, user.namespace, user).
and_return(double(execute: true))
post :create, format: :js
end
end
context "when the Bitbucket user and GitLab user's usernames don't match" do
let(:bitbucket_username) { "someone_else" }
it "takes the current user's namespace" do
expect(Gitlab::BitbucketImport::ProjectCreator).
to receive(:new).with(bitbucket_repo, user.namespace, user).
and_return(double(execute: true))
post :create, format: :js
end
end
end
context "when the repository owner is not the Bitbucket user" do
let(:other_username) { "someone_else" }
before do
bitbucket_repo["owner"] = other_username
end
context "when a namespace with the Bitbucket user's username already exists" do
let!(:existing_namespace) { create(:namespace, name: other_username, owner: user) }
context "when the namespace is owned by the GitLab user" do
it "takes the existing namespace" do
expect(Gitlab::BitbucketImport::ProjectCreator).
to receive(:new).with(bitbucket_repo, existing_namespace, user).
and_return(double(execute: true))
post :create, format: :js
end
end
context "when the namespace is not owned by the GitLab user" do
before do
existing_namespace.owner = create(:user)
existing_namespace.save
end
it "doesn't create a project" do
expect(Gitlab::BitbucketImport::ProjectCreator).
not_to receive(:new)
post :create, format: :js
end
end
end
context "when a namespace with the Bitbucket user's username doesn't exist" do
it "creates the namespace" do
expect(Gitlab::BitbucketImport::ProjectCreator).
to receive(:new).and_return(double(execute: true))
post :create, format: :js
expect(Namespace.where(name: other_username).first).not_to be_nil
end
it "takes the new namespace" do
expect(Gitlab::BitbucketImport::ProjectCreator).
to receive(:new).with(bitbucket_repo, an_instance_of(Group), user).
and_return(double(execute: true))
post :create, format: :js
end
end
end
end
end
......@@ -57,18 +57,98 @@ describe Import::GithubController do
end
describe "POST create" do
let(:github_username) { user.username }
let(:github_user) {
OpenStruct.new(login: github_username)
}
let(:github_repo) {
OpenStruct.new(name: 'vim', full_name: "#{github_username}/vim", owner: OpenStruct.new(login: github_username))
}
before do
@repo = OpenStruct.new(login: 'vim', full_name: 'asd/vim', owner: OpenStruct.new(login: "john"))
controller.stub_chain(:client, :user).and_return(github_user)
controller.stub_chain(:client, :repo).and_return(github_repo)
end
context "when the repository owner is the GitHub user" do
context "when the GitHub user and GitLab user's usernames match" do
it "takes the current user's namespace" do
expect(Gitlab::GithubImport::ProjectCreator).
to receive(:new).with(github_repo, user.namespace, user).
and_return(double(execute: true))
post :create, format: :js
end
end
context "when the GitHub user and GitLab user's usernames don't match" do
let(:github_username) { "someone_else" }
it "takes the current user's namespace" do
expect(Gitlab::GithubImport::ProjectCreator).
to receive(:new).with(github_repo, user.namespace, user).
and_return(double(execute: true))
post :create, format: :js
end
end
end
it "takes already existing namespace" do
namespace = create(:namespace, name: "john", owner: user)
expect(Gitlab::GithubImport::ProjectCreator).
to receive(:new).with(@repo, namespace, user).
and_return(double(execute: true))
controller.stub_chain(:client, :repo).and_return(@repo)
context "when the repository owner is not the GitHub user" do
let(:other_username) { "someone_else" }
before do
github_repo.owner = OpenStruct.new(login: other_username)
end
context "when a namespace with the GitHub user's username already exists" do
let!(:existing_namespace) { create(:namespace, name: other_username, owner: user) }
context "when the namespace is owned by the GitLab user" do
it "takes the existing namespace" do
expect(Gitlab::GithubImport::ProjectCreator).
to receive(:new).with(github_repo, existing_namespace, user).
and_return(double(execute: true))
post :create, format: :js
end
end
context "when the namespace is not owned by the GitLab user" do
before do
existing_namespace.owner = create(:user)
existing_namespace.save
end
it "doesn't create a project" do
expect(Gitlab::GithubImport::ProjectCreator).
not_to receive(:new)
post :create, format: :js
end
end
end
context "when a namespace with the GitHub user's username doesn't exist" do
it "creates the namespace" do
expect(Gitlab::GithubImport::ProjectCreator).
to receive(:new).and_return(double(execute: true))
post :create, format: :js
expect(Namespace.where(name: other_username).first).not_to be_nil
end
it "takes the new namespace" do
expect(Gitlab::GithubImport::ProjectCreator).
to receive(:new).with(github_repo, an_instance_of(Group), user).
and_return(double(execute: true))
post :create, format: :js
post :create, format: :js
end
end
end
end
end
......@@ -48,23 +48,105 @@ describe Import::GitlabController do
end
describe "POST create" do
before do
@repo = {
let(:gitlab_username) { user.username }
let(:gitlab_user) {
{
username: gitlab_username
}.with_indifferent_access
}
let(:gitlab_repo) {
{
path: 'vim',
path_with_namespace: 'asd/vim',
owner: {name: "john"},
namespace: {path: "john"}
path_with_namespace: "#{gitlab_username}/vim",
owner: { name: gitlab_username },
namespace: { path: gitlab_username }
}.with_indifferent_access
}
before do
controller.stub_chain(:client, :user).and_return(gitlab_user)
controller.stub_chain(:client, :project).and_return(gitlab_repo)
end
context "when the repository owner is the GitLab.com user" do
context "when the GitLab.com user and GitLab server user's usernames match" do
it "takes the current user's namespace" do
expect(Gitlab::GitlabImport::ProjectCreator).
to receive(:new).with(gitlab_repo, user.namespace, user).
and_return(double(execute: true))
post :create, format: :js
end
end
context "when the GitLab.com user and GitLab server user's usernames don't match" do
let(:gitlab_username) { "someone_else" }
it "takes the current user's namespace" do
expect(Gitlab::GitlabImport::ProjectCreator).
to receive(:new).with(gitlab_repo, user.namespace, user).
and_return(double(execute: true))
post :create, format: :js
end
end
end
it "takes already existing namespace" do
namespace = create(:namespace, name: "john", owner: user)
expect(Gitlab::GitlabImport::ProjectCreator).
to receive(:new).with(@repo, namespace, user).
and_return(double(execute: true))
controller.stub_chain(:client, :project).and_return(@repo)
context "when the repository owner is not the GitLab.com user" do
let(:other_username) { "someone_else" }
before do
gitlab_repo["namespace"]["path"] = other_username
end
context "when a namespace with the GitLab.com user's username already exists" do
let!(:existing_namespace) { create(:namespace, name: other_username, owner: user) }
context "when the namespace is owned by the GitLab server user" do
it "takes the existing namespace" do
expect(Gitlab::GitlabImport::ProjectCreator).
to receive(:new).with(gitlab_repo, existing_namespace, user).
and_return(double(execute: true))
post :create, format: :js
end
end
context "when the namespace is not owned by the GitLab server user" do
before do
existing_namespace.owner = create(:user)
existing_namespace.save
end
it "doesn't create a project" do
expect(Gitlab::GitlabImport::ProjectCreator).
not_to receive(:new)
post :create, format: :js
end
end
end
context "when a namespace with the GitLab.com user's username doesn't exist" do
it "creates the namespace" do
expect(Gitlab::GitlabImport::ProjectCreator).
to receive(:new).and_return(double(execute: true))
post :create, format: :js
expect(Namespace.where(name: other_username).first).not_to be_nil
end
it "takes the new namespace" do
expect(Gitlab::GitlabImport::ProjectCreator).
to receive(:new).with(gitlab_repo, an_instance_of(Group), user).
and_return(double(execute: true))
post :create, format: :js
post :create, format: :js
end
end
end
end
end
require 'spec_helper'
describe NamespacesController do
let!(:user) { create(:user, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
describe "GET show" do
context "when the namespace belongs to a user" do
let!(:other_user) { create(:user) }
it "redirects to the user's page" do
get :show, id: other_user.username
expect(response).to redirect_to(user_path(other_user))
end
end
context "when the namespace belongs to a group" do
let!(:group) { create(:group) }
let!(:project) { create(:project, namespace: group) }
context "when the group has public projects" do
before do
project.update_attribute(:visibility_level, Project::PUBLIC)
end
context "when not signed in" do
it "redirects to the group's page" do
get :show, id: group.path
expect(response).to redirect_to(group_path(group))
end
end
context "when signed in" do
before do
sign_in(user)
end
it "redirects to the group's page" do
get :show, id: group.path
expect(response).to redirect_to(group_path(group))
end
end
end
context "when the project doesn't have public projects" do
context "when not signed in" do
it "redirects to the sign in page" do
get :show, id: group.path
expect(response).to redirect_to(new_user_session_path)
end
end
context "when signed in" do
before do
sign_in(user)
end
context "when the user has access to the project" do
before do
project.team << [user, :master]
end
context "when the user is blocked" do
before do
user.block
project.team << [user, :master]
end
it "redirects to the sign in page" do
get :show, id: group.path
expect(response).to redirect_to(new_user_session_path)
end
end
context "when the user isn't blocked" do
it "redirects to the group's page" do
get :show, id: group.path
expect(response).to redirect_to(group_path(group))
end
end
end
context "when the user doesn't have access to the project" do
it "responds with status 404" do
get :show, id: group.path
expect(response.status).to eq(404)
end
end
end
end
end
context "when the namespace doesn't exist" do
context "when signed in" do
before do
sign_in(user)
end
it "responds with status 404" do
get :show, id: "doesntexist"
expect(response.status).to eq(404)
end
end
context "when not signed in" do
it "redirects to the sign in page" do
get :show, id: "doesntexist"
expect(response).to redirect_to(new_user_session_path)
end
end
end
end
end
require "spec_helper"
describe Projects::RepositoriesController do
let(:project) { create(:project) }
let(:user) { create(:user) }
describe "GET archive" do
before do
sign_in(user)
project.team << [user, :developer]
allow(ArchiveRepositoryService).to receive(:new).and_return(service)
end
let(:service) { ArchiveRepositoryService.new(project, "master", "zip") }
it "executes ArchiveRepositoryService" do
expect(ArchiveRepositoryService).to receive(:new).with(project, "master", "zip")
expect(service).to receive(:execute)
get :archive, namespace_id: project.namespace.path, project_id: project.path, ref: "master", format: "zip"
end
context "when the service raises an error" do
before do
allow(service).to receive(:execute).and_raise("Archive failed")
end
it "renders Not Found" do
get :archive, namespace_id: project.namespace.path, project_id: project.path, ref: "master", format: "zip"
expect(response.status).to eq(404)
end
end
context "when the service doesn't return a path" do
before do
allow(service).to receive(:execute).and_return(nil)
end
it "reloads the page" do
get :archive, namespace_id: project.namespace.path, project_id: project.path, ref: "master", format: "zip"
expect(response).to redirect_to(archive_namespace_project_repository_path(project.namespace, project, ref: "master", format: "zip"))
end
end
context "when the service returns a path" do
let(:path) { Rails.root.join("spec/fixtures/dk.png").to_s }
before do
allow(service).to receive(:execute).and_return(path)
end
it "sends the file" do
get :archive, namespace_id: project.namespace.path, project_id: project.path, ref: "master", format: "zip"
expect(response.body).to eq(File.binread(path))
end
end
end
end
......@@ -6,7 +6,7 @@ describe 'Help Pages', feature: true do
login_as :user
end
it 'replace the variable $your_email with the email of the user' do
visit help_page_path(category: 'ssh', file: 'README.md')
visit help_page_path(filepath: 'ssh/README', format: 'md')
expect(page).to have_content("ssh-keygen -t rsa -C \"#{@user.email}\"")
end
end
......
......@@ -700,7 +700,7 @@ describe GitlabMarkdownHelper do
end
it "should leave ref-like href of 'manual' links untouched" do
expect(markdown("why not [inspect !#{merge_request.iid}](http://example.tld/#!#{merge_request.iid})")).to eq("<p>why not <a href=\"http://example.tld/#!#{merge_request.iid}\">inspect </a><a class=\"gfm gfm-merge_request \" href=\"#{namespace_project_merge_request_url(project.namespace, project, merge_request)}\" title=\"Merge Request: #{merge_request.title}\">!#{merge_request.iid}</a><a href=\"http://example.tld/#!#{merge_request.iid}\"></a></p>\n")
expect(markdown("why not [inspect !#{merge_request.iid}](http://example.tld/#!#{merge_request.iid})")).to eq("<p>why not <a href=\"http://example.tld/#!#{merge_request.iid}\">inspect </a><a class=\"gfm gfm-merge_request \" href=\"#{namespace_project_merge_request_path(project.namespace, project, merge_request)}\" title=\"Merge Request: #{merge_request.title}\">!#{merge_request.iid}</a><a href=\"http://example.tld/#!#{merge_request.iid}\"></a></p>\n")
end
it "should leave ref-like src of images untouched" do
......
......@@ -7,7 +7,7 @@ describe IconsHelper do
end
it 'returns share class' do
expect(file_type_icon_class('file', 0120000, 'link')).to eq 'share'
expect(file_type_icon_class('file', '120000', 'link')).to eq 'share'
end
it 'returns file-pdf-o class with .pdf' do
......
require 'spec_helper'
describe Gitlab::Regex do
describe 'path regex' do
it { expect('gitlab-ce').to match(Gitlab::Regex.path_regex) }
it { expect('gitlab_git').to match(Gitlab::Regex.path_regex) }
it { expect('_underscore.js').to match(Gitlab::Regex.path_regex) }
it { expect('100px.com').to match(Gitlab::Regex.path_regex) }
it { expect('?gitlab').not_to match(Gitlab::Regex.path_regex) }
it { expect('git lab').not_to match(Gitlab::Regex.path_regex) }
it { expect('gitlab.git').not_to match(Gitlab::Regex.path_regex) }
describe 'project path regex' do
it { expect('gitlab-ce').to match(Gitlab::Regex.project_path_regex) }
it { expect('gitlab_git').to match(Gitlab::Regex.project_path_regex) }
it { expect('_underscore.js').to match(Gitlab::Regex.project_path_regex) }
it { expect('100px.com').to match(Gitlab::Regex.project_path_regex) }
it { expect('?gitlab').not_to match(Gitlab::Regex.project_path_regex) }
it { expect('git lab').not_to match(Gitlab::Regex.project_path_regex) }
it { expect('gitlab.git').not_to match(Gitlab::Regex.project_path_regex) }
end
describe 'project name regex' do
......
......@@ -33,8 +33,6 @@ describe Namespace do
it { is_expected.to respond_to(:to_param) }
end
it { expect(Namespace.global_id).to eq('GLN') }
describe :to_param do
it { expect(namespace.to_param).to eq(namespace.path) }
end
......@@ -85,4 +83,14 @@ describe Namespace do
it { expect(Namespace.find_by_path_or_name('WOW')).to eq(@namespace) }
it { expect(Namespace.find_by_path_or_name('unknown')).to eq(nil) }
end
describe ".clean_path" do
let!(:user) { create(:user, username: "johngitlab-etc") }
let!(:namespace) { create(:namespace, path: "JohnGitLab-etc1") }
it "cleans the path and makes sure it's available" do
expect(Namespace.clean_path("-john+gitlab-ETC%.git@gmail.com")).to eq("johngitlab-ETC2")
end
end
end
......@@ -31,6 +31,7 @@ describe GitlabIssueTrackerService do
context 'with absolute urls' do
before do
GitlabIssueTrackerService.default_url_options[:script_name] = "/gitlab/root"
@service = project.create_gitlab_issue_tracker_service(active: true)
end
......@@ -39,15 +40,15 @@ describe GitlabIssueTrackerService do
end
it 'should give the correct path' do
expect(@service.project_url).to eq("/#{project.path_with_namespace}/issues")
expect(@service.new_issue_url).to eq("/#{project.path_with_namespace}/issues/new")
expect(@service.issue_url(432)).to eq("/#{project.path_with_namespace}/issues/432")
expect(@service.project_url).to eq("http://localhost/gitlab/root/#{project.path_with_namespace}/issues")
expect(@service.new_issue_url).to eq("http://localhost/gitlab/root/#{project.path_with_namespace}/issues/new")
expect(@service.issue_url(432)).to eq("http://localhost/gitlab/root/#{project.path_with_namespace}/issues/432")
end
end
context 'with enabled relative urls' do
context 'with relative urls' do
before do
Settings.gitlab.stub(:relative_url_root).and_return("/gitlab/root")
GitlabIssueTrackerService.default_url_options[:script_name] = "/gitlab/root"
@service = project.create_gitlab_issue_tracker_service(active: true)
end
......@@ -56,9 +57,9 @@ describe GitlabIssueTrackerService do
end
it 'should give the correct path' do
expect(@service.project_url).to eq("/gitlab/root/#{project.path_with_namespace}/issues")
expect(@service.new_issue_url).to eq("/gitlab/root/#{project.path_with_namespace}/issues/new")
expect(@service.issue_url(432)).to eq("/gitlab/root/#{project.path_with_namespace}/issues/432")
expect(@service.project_path).to eq("/gitlab/root/#{project.path_with_namespace}/issues")
expect(@service.new_issue_path).to eq("/gitlab/root/#{project.path_with_namespace}/issues/new")
expect(@service.issue_path(432)).to eq("/gitlab/root/#{project.path_with_namespace}/issues/432")
end
end
end
......
......@@ -320,16 +320,6 @@ describe User do
end
end
describe ".clean_username" do
let!(:user) { create(:user, username: "johngitlab-etc") }
let!(:namespace) { create(:namespace, path: "JohnGitLab-etc1") }
it "cleans a username and makes sure it's available" do
expect(User.clean_username("-john+gitlab-ETC%.git@gmail.com")).to eq("johngitlab-ETC2")
end
end
describe 'all_ssh_keys' do
it { is_expected.to have_many(:keys).dependent(:destroy) }
......
......@@ -247,12 +247,12 @@ describe API::API, api: true do
expect(json_response['message']['name']).to eq([
'can\'t be blank',
'is too short (minimum is 0 characters)',
Gitlab::Regex.project_regex_message
Gitlab::Regex.project_name_regex_message
])
expect(json_response['message']['path']).to eq([
'can\'t be blank',
'is too short (minimum is 0 characters)',
Gitlab::Regex.send(:default_regex_message)
Gitlab::Regex.send(:project_path_regex_message)
])
end
......
......@@ -152,7 +152,7 @@ describe API::API, api: true do
expect(json_response['message']['projects_limit']).
to eq(['must be greater than or equal to 0'])
expect(json_response['message']['username']).
to eq([Gitlab::Regex.send(:default_regex_message)])
to eq([Gitlab::Regex.send(:namespace_regex_message)])
end
it "shouldn't available for non admin users" do
......@@ -278,7 +278,7 @@ describe API::API, api: true do
expect(json_response['message']['projects_limit']).
to eq(['must be greater than or equal to 0'])
expect(json_response['message']['username']).
to eq([Gitlab::Regex.send(:default_regex_message)])
to eq([Gitlab::Regex.send(:namespace_regex_message)])
end
context "with existing user" do
......
......@@ -73,41 +73,41 @@ end
# help_markdown GET /help/markdown(.:format) help#markdown
# help_ssh GET /help/ssh(.:format) help#ssh
# help_raketasks GET /help/raketasks(.:format) help#raketasks
describe HelpController, "routing" do
it "to #index" do
expect(get("/help")).to route_to('help#index')
describe HelpController, 'routing' do
it 'to #index' do
expect(get('/help')).to route_to('help#index')
end
it "to #permissions" do
expect(get("/help/permissions/permissions")).to route_to('help#show', category: "permissions", file: "permissions")
it 'to #permissions' do
expect(get('/help/permissions/permissions')).to route_to('help#show', filepath: 'permissions/permissions')
end
it "to #workflow" do
expect(get("/help/workflow/README")).to route_to('help#show', category: "workflow", file: "README")
it 'to #workflow' do
expect(get('/help/workflow/README')).to route_to('help#show', filepath: 'workflow/README')
end
it "to #api" do
expect(get("/help/api/README")).to route_to('help#show', category: "api", file: "README")
it 'to #api' do
expect(get('/help/api/README')).to route_to('help#show', filepath: 'api/README')
end
it "to #web_hooks" do
expect(get("/help/web_hooks/web_hooks")).to route_to('help#show', category: "web_hooks", file: "web_hooks")
it 'to #web_hooks' do
expect(get('/help/web_hooks/web_hooks')).to route_to('help#show', filepath: 'web_hooks/web_hooks')
end
it "to #system_hooks" do
expect(get("/help/system_hooks/system_hooks")).to route_to('help#show', category: "system_hooks", file: "system_hooks")
it 'to #system_hooks' do
expect(get('/help/system_hooks/system_hooks')).to route_to('help#show', filepath: 'system_hooks/system_hooks')
end
it "to #markdown" do
expect(get("/help/markdown/markdown")).to route_to('help#show',category: "markdown", file: "markdown")
it 'to #markdown' do
expect(get('/help/markdown/markdown')).to route_to('help#show',filepath: 'markdown/markdown')
end
it "to #ssh" do
expect(get("/help/ssh/README")).to route_to('help#show', category: "ssh", file: "README")
it 'to #ssh' do
expect(get('/help/ssh/README')).to route_to('help#show', filepath: 'ssh/README')
end
it "to #raketasks" do
expect(get("/help/raketasks/README")).to route_to('help#show', category: "raketasks", file: "README")
it 'to #raketasks' do
expect(get('/help/raketasks/README')).to route_to('help#show', filepath: 'raketasks/README')
end
end
......
require 'spec_helper'
describe ArchiveRepositoryService do
let(:project) { create(:project) }
subject { ArchiveRepositoryService.new(project, "master", "zip") }
describe "#execute" do
it "cleans old archives" do
expect(project.repository).to receive(:clean_old_archives)
subject.execute(timeout: 0.0)
end
context "when the repository doesn't have an archive file path" do
before do
allow(project.repository).to receive(:archive_file_path).and_return(nil)
end
it "raises an error" do
expect {
subject.execute(timeout: 0.0)
}.to raise_error
end
end
context "when the repository has an archive file path" do
let(:file_path) { "/archive.zip" }
let(:pid_file_path) { "/archive.zip.pid" }
before do
allow(project.repository).to receive(:archive_file_path).and_return(file_path)
allow(project.repository).to receive(:archive_pid_file_path).and_return(pid_file_path)
end
context "when the archive file already exists" do
before do
allow(File).to receive(:exist?).with(file_path).and_return(true)
end
it "returns the file path" do
expect(subject.execute(timeout: 0.0)).to eq(file_path)
end
end
context "when the archive file doesn't exist yet" do
before do
allow(File).to receive(:exist?).with(file_path).and_return(false)
allow(File).to receive(:exist?).with(pid_file_path).and_return(true)
end
context "when the archive pid file doesn't exist yet" do
before do
allow(File).to receive(:exist?).with(pid_file_path).and_return(false)
end
it "queues the RepositoryArchiveWorker" do
expect(RepositoryArchiveWorker).to receive(:perform_async)
subject.execute(timeout: 0.0)
end
end
context "when the archive pid file already exists" do
it "doesn't queue the RepositoryArchiveWorker" do
expect(RepositoryArchiveWorker).not_to receive(:perform_async)
subject.execute(timeout: 0.0)
end
end
context "when the archive file exists after a little while" do
before do
Thread.new do
sleep 0.1
allow(File).to receive(:exist?).with(file_path).and_return(true)
end
end
it "returns the file path" do
expect(subject.execute(timeout: 0.2)).to eq(file_path)
end
end
context "when the archive file doesn't exist after the timeout" do
it "returns nil" do
expect(subject.execute(timeout: 0.0)).to eq(nil)
end
end
end
end
end
end
......@@ -44,3 +44,5 @@ RSpec.configure do |config|
TestEnv.init
end
end
ActiveRecord::Migration.maintain_test_schema!
......@@ -85,7 +85,7 @@ module TestEnv
end
# We must copy bare repositories because we will push to them.
system(*%W(git clone -q --bare #{factory_repo_path} #{factory_repo_path_bare}))
system(git_env, *%W(git clone -q --bare #{factory_repo_path} #{factory_repo_path_bare}))
end
def copy_repo(project)
......@@ -113,4 +113,10 @@ module TestEnv
def factory_repo_name
'gitlab-test'
end
# Prevent developer git configurations from being persisted to test
# repositories
def git_env
{'GIT_TEMPLATE_DIR' => ''}
end
end
This diff is collapsed.
This diff is collapsed.
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