Commit 00241a84 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'ce-changes' into 'master'

CE changes into EE
parents 750f63ba e64343d6
stage:
before:
- cp config/gitlab.teatro.yml config/gitlab.yml
- mkdir /apps/gitlab-satellites
- mkdir /apps/repositories
database:
- RAILS_ENV=development force=yes bundle exec rake db:create gitlab:setup
\ No newline at end of file
v 7.1.0
- Remove observers
- Improve MR discussions
- Filter by description on Issues#index page
- Fix bug with namespace select when create new project page
- Show README link after description for non-master members
- Add @all mention for comments
- Dont show reply button if user is not signed in
- Expose more information for issues with webhook
v 7.0.0
- The CPU no longer overheats when you hold down the spacebar
- Improve edit file UI
......
# Contribute to GitLab
This guide details how contribute to GitLab.
If you want to know how the GitLab team handles contributions have a look at [the GitLab contributing process](PROCESS.md).
Thank you for your interest in contributing to GitLab.
This guide details how contribute to GitLab in a way that is efficient for everyone.
If you have read this guide and want to know how the GitLab core-team operates please see [the GitLab contributing process](PROCESS.md).
## Contributor license agreement
......@@ -75,9 +75,9 @@ If you can, please submit a merge request with the fix or improvements including
The **official merge window** is in the beginning of the month from the 1st to the 7th day of the month. The best time to submit a MR and get feedback fast. Before this time the GitLab B.V. team is still dealing with work that is created by the monthly release such as assisting subscribers with upgrade issues, the release of Enterprise Edition and the upgrade of GitLab Cloud. After the 7th it is already getting closer to the release date of the next version. This means there is less time to fix the issues created by merging large new features.
Please keep the change in a single MR **as small as possible**. If you want to contribute a large feature think very hard what the minimum viable change is. Can you split functionality? Can you only submit the backend/API code? Can you start with a very simple UI? The smaller a MR is the more likely it is it will be merged, after that you can send more MR's to enhance it.
Please keep the change in a single MR **as small as possible**. If you want to contribute a large feature think very hard what the minimum viable change is. Can you split functionality? Can you only submit the backend/API code? Can you start with a very simple UI? Can you do part of the refactor? The increased reviewability of small MR's that leads to higher code quality is more important to us than having a mimimal commit log. The smaller a MR is the more likely it is it will be merged (quickly), after that you can send more MR's to enhance it.
For examples of feedback on merge requests please look at already [closed merge requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed). Please ensure that your merge request meets the following contribution acceptance criteria.
For examples of feedback on merge requests please look at already [closed merge requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed). If you would like quick feedback on your merge request feel free to mention one of the Merge Marshalls of [the core-team](https://about.gitlab.com/core-team/). Please ensure that your merge request meets the following contribution acceptance criteria.
**Please format your merge request description as follows:**
......@@ -97,7 +97,8 @@ For examples of feedback on merge requests please look at already [closed merge
1. Keeps the GitLab code base clean and well structured
1. Contains functionality we think other users will benefit from too
1. Doesn't add configuration options since they complicate future changes
1. Contains a single commit (please use `git rebase -i` to squash commits)
1. Initially contains a single commit (please use `git rebase -i` to squash commits)
1. Changes after submitting the merge request should be in separate commits (no squashing)
1. It conforms to the following style guides
## Style guides
......@@ -113,4 +114,4 @@ For examples of feedback on merge requests please look at already [closed merge
1. [Shell commands](doc/development/shell_commands.md) created by GitLab contributors to enhance security
1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
This is also the style used by linting tools such as [Rubocop](https://github.com/bbatsov/rubocop), PullReview[https://www.pullreview.com/] and [Hound CI](https://houndci.com).
This is also the style used by linting tools such as [Rubocop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com).
......@@ -11,7 +11,6 @@ end
gem "rails", "~> 4.1.0"
gem "protected_attributes"
gem 'rails-observers'
# Make links from text
gem 'rails_autolink', '~> 1.1'
......
......@@ -365,8 +365,6 @@ GEM
bundler (>= 1.3.0, < 2.0)
railties (= 4.1.1)
sprockets-rails (~> 2.0)
rails-observers (0.1.2)
activemodel (~> 4.0)
rails_autolink (1.1.6)
rails (> 3.1)
rails_best_practices (1.14.4)
......@@ -645,7 +643,6 @@ DEPENDENCIES
rack-cors
rack-mini-profiler
rails (~> 4.1.0)
rails-observers
rails_autolink (~> 1.1)
rails_best_practices
raphael-rails (~> 2.1.2)
......
7.0.0-ee
7.1.0.pre-ee
......@@ -143,6 +143,14 @@ $ ->
$(@).next('table').show()
$(@).remove()
# Show/hide comments on diff
$("body").on "click", ".js-toggle-diff-comments", (e) ->
$(@).find('i').
toggleClass('icon-chevron-down').
toggleClass('icon-chevron-up')
$(@).closest(".diff-file").find(".notes_holder").toggle()
e.preventDefault()
(($) ->
# Disable an element and add the 'disabled' Bootstrap class
$.fn.extend disable: ->
......
......@@ -21,7 +21,6 @@ $(document).ready ->
$(".div-dropzone").append divSpinner
$(".div-dropzone-spinner").append iconSpinner
dropzone = $(".div-dropzone").dropzone(
url: project_image_path_upload
dictDefaultMessage: ""
......@@ -77,6 +76,107 @@ $(document).ready ->
return
)
child = $(dropzone[0]).children("textarea")
formatLink = (str) ->
"![" + str.alt + "](" + str.url + ")"
handlePaste = (e) ->
e.preventDefault()
my_event = e.originalEvent
if my_event.clipboardData and my_event.clipboardData.items
processItem(my_event)
processItem = (e) ->
image = isImage(e)
if image
filename = getFilename(e) or "image.png"
text = "{{" + filename + "}}"
pasteText(text)
uploadFile image.getAsFile(), filename
else
text = e.clipboardData.getData("text/plain")
pasteText(text)
isImage = (data) ->
i = 0
while i < data.clipboardData.items.length
item = data.clipboardData.items[i]
if item.type.indexOf("image") isnt -1
return item
i++
return false
pasteText = (text) ->
caretStart = $(child)[0].selectionStart
caretEnd = $(child)[0].selectionEnd
textEnd = $(child).val().length
beforeSelection = $(child).val().substring 0, caretStart
afterSelection = $(child).val().substring caretEnd, textEnd
$(child).val beforeSelection + text + afterSelection
$(".markdown-area").trigger "input"
getFilename = (e) ->
if window.clipboardData and window.clipboardData.getData
value = window.clipboardData.getData("Text")
else if e.clipboardData and e.clipboardData.getData
value = e.clipboardData.getData("text/plain")
value = value.split("\r")
value.first()
uploadFile = (item, filename) ->
formData = new FormData()
formData.append "markdown_img", item, filename
$.ajax
url: project_image_path_upload
type: "POST"
data: formData
dataType: "json"
processData: false
contentType: false
headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
beforeSend: ->
showSpinner()
closeAlertMessage()
success: (e, textStatus, response) ->
insertToTextArea(filename, formatLink(response.responseJSON.link))
error: (response) ->
showError(response.responseJSON.message)
complete: ->
closeSpinner()
insertToTextArea = (filename, url) ->
$(child).val (index, val) ->
val.replace("{{" + filename + "}}", url + "\n")
appendToTextArea = (url) ->
$(child).val (index, val) ->
val + url + "\n"
showSpinner = (e) ->
$(".div-dropzone-spinner").css "opacity", 0.7
closeSpinner = ->
$(".div-dropzone-spinner").css "opacity", 0
showError = (message) ->
checkIfMsgExists = $(".error-alert").children().length
if checkIfMsgExists is 0
$(".error-alert").append divAlert
$(".div-dropzone-alert").append btnAlert + message
closeAlertMessage = ->
$(".div-dropzone-alert").alert "close"
$(".markdown-selector").click (e) ->
e.preventDefault()
$(".div-dropzone").click()
......
......@@ -109,10 +109,10 @@ class MergeRequest
type: 'GET'
url: this.$('.merge-request-tabs .diffs-tab a').attr('href')
beforeSend: =>
this.$('.status').addClass 'loading'
this.$('.mr-loading-status .loading').show()
complete: =>
@diffs_loaded = true
this.$('.status').removeClass 'loading'
this.$('.mr-loading-status .loading').hide()
success: (data) =>
this.$(".diffs").html(data.html)
dataType: 'json'
......
......@@ -142,8 +142,15 @@ class Notes
# remove the note (will be added again below)
row.next().find(".note").remove()
# append new note to all matching discussions
$(".notes[rel='" + note.discussion_id + "']").append note.html
# Add note to 'Changes' page discussions
$(".notes[rel='" + note.discussion_id + "']").append note.html
# Init discussion on 'Discussion' page if it is merge request page
if $('body').attr('data-page').indexOf('projects:merge_request') == 0
$('ul.main-notes-list').append(note.discussion_with_diff_html)
else
# append new note to all matching discussions
$(".notes[rel='" + note.discussion_id + "']").append note.html
# cleanup after successfully creating a diff/discussion note
@removeDiscussionNoteForm(form)
......
......@@ -8,8 +8,6 @@
*= require select2
*= require highlightjs.min
*= require_self
*= require nprogress
*= require nprogress-bootstrap
*= require dropzone/basic
*/
......@@ -20,6 +18,12 @@
*/
@import 'gl_bootstrap';
/**
* NProgress load bar css
*/
@import 'nprogress';
@import 'nprogress-bootstrap';
/**
* Font icons
*
......
......@@ -16,6 +16,7 @@
.append-bottom-15 { margin-bottom:15px }
.append-bottom-20 { margin-bottom:20px }
.inline { display: inline-block }
.center { text-align: center }
.underlined-link { text-decoration: underline; }
.hint { font-style: italic; color: #999; }
......@@ -55,7 +56,7 @@ pre {
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
background: $bg_style_color;
background: $bg_primary;
color: #FFF
}
......@@ -256,13 +257,6 @@ li.note {
}
}
h1.http_status_code {
font-size: 56px;
line-height: 100px;
font-weight: normal;
color: #456;
}
.control-group {
.controls {
span {
......
......@@ -76,30 +76,10 @@ label {
}
}
.commit-message-container {
background-color: $body-bg;
position: relative;
font-family: $monospace_font;
$left: 12px;
.max-width-marker {
width: 72ch;
color: rgba(0, 0, 0, 0.0);
font-family: inherit;
left: $left;
height: 100%;
border-right: 1px solid mix($input-border, white);
position: absolute;
z-index: 1;
}
> textarea {
background-color: rgba(0, 0, 0, 0.0);
font-family: inherit;
padding-left: $left;
position: relative;
z-index: 2;
}
}
.fieldset-form fieldset {
margin-bottom: 20px;
}
.form-control {
@include box-shadow(none);
}
......@@ -41,8 +41,8 @@
}
.ui-state-active {
border: 1px solid $bg_style_color;
background: $bg_style_color;
border: 1px solid $bg_primary;
background: $bg_primary;
color: #FFF;
}
......
......@@ -42,7 +42,7 @@
.select2-results {
max-height: 350px;
.select2-highlighted {
background: $bg_style_color;
background: $bg_primary;
}
}
}
......
......@@ -9,7 +9,7 @@
$font-size-base: 13px !default;
$nav-pills-active-link-hover-bg: $bg_primary;
$pagination-active-bg: $bg_primary;
$list-group-active-bg: $bg_style_color;
$list-group-active-bg: $bg_primary;
// Core variables and mixins
@import "bootstrap/variables";
......
......@@ -2,7 +2,6 @@
* General Colors
*/
$style_color: #474D57;
$bg_style_color: #2299BB;
$hover: #D9EDF7;
/*
......@@ -40,3 +39,8 @@ $border_warning: #EB9532;
*/
$added: #63c363;
$deleted: #f77;
/**
*
*/
$nprogress-color: #3498db;
......@@ -213,3 +213,27 @@ li.commit {
padding: 4px 12px;
}
}
.commit-message-container {
background-color: $body-bg;
position: relative;
font-family: $monospace_font;
$left: 12px;
.max-width-marker {
width: 72ch;
color: rgba(0, 0, 0, 0.0);
font-family: inherit;
left: $left;
height: 100%;
border-right: 1px solid mix($input-border, white);
position: absolute;
z-index: 1;
}
> textarea {
background-color: rgba(0, 0, 0, 0.0);
font-family: inherit;
padding-left: $left;
position: relative;
z-index: 2;
}
}
.error-page {
max-width: 400px;
margin: 0 auto;
h1, h2, h3 {
text-align: center;
}
h1 {
font-size: 56px;
line-height: 100px;
font-weight: 300;
}
}
......@@ -156,12 +156,13 @@
.filter_icon {
a {
text-align:center;
background: #EEE;
background: $bg_primary;
margin-bottom: 10px;
float: left;
padding: 9px 6px;
font-size: 18px;
width: 40px;
color: #FFF;
@include border-radius(3px);
}
......
......@@ -6,14 +6,12 @@ header {
&.navbar-gitlab {
margin-bottom: 0;
min-height: 40px;
border: none;
.navbar-inner {
background: #F1F1F1;
border-bottom: 1px solid #DDD;
filter: none;
.nav > li > a {
color: $style_color;
font-size: 14px;
line-height: 32px;
padding: 6px 10px;
......@@ -248,8 +246,6 @@ header {
float: left;
height: 46px;
width: 2px;
background: white;
border-left: 1px solid #DDD;
margin-left: 10px;
margin-right: 10px;
}
......
......@@ -3,7 +3,7 @@
margin: 20px 0;
margin-top: 0;
padding-top: 4px;
border-bottom: 1px solid #E1E1E1;
border-bottom: 1px solid #E9E9E9;
ul {
padding: 0;
......
......@@ -43,38 +43,14 @@ ul.notes {
}
.discussion {
padding: 8px 0;
padding: 10px 0;
overflow: hidden;
display: block;
position:relative;
border-bottom: 1px solid #EEE;
.discussion-body {
margin-left: 50px;
.diff-file,
.discussion-hidden,
.notes {
background-color: #F9F9F9;
}
.diff-file .notes {
/* reset */
background: inherit;
border: none;
@include box-shadow(none);
}
.discussion-hidden .note {
@extend .cgray;
padding: 8px;
text-align: center;
}
.notes .note {
border-color: #ddd;
padding: 8px;
}
.reply-btn {
margin-top: 8px;
}
}
}
......@@ -137,10 +113,6 @@ ul.notes {
vertical-align: top;
}
}
.reply-btn {
margin: 5px;
}
}
/**
......@@ -376,3 +348,17 @@ ul.notes {
margin-top: 5px;
margin-bottom: 5px;
}
.discussion-body,
.diff-file {
.notes .note {
border-color: #ddd;
padding: 10px 15px;
}
.discussion-reply-holder {
background: #f9f9f9;
padding: 10px 15px;
border-top: 1px solid #DDD;
}
}
......@@ -56,10 +56,10 @@
text-align: center;
.prev {
@extend .thumbnail;
height: 30px;
width: 175px;
height: 80px;
width: 160px;
margin-bottom: 10px;
@include border-radius(4px);
&.classic {
background: #31363e;
......@@ -92,10 +92,13 @@
text-align: center;
.prev {
@extend .thumbnail;
height: 151px;
width: 220px;
width: 160px;
margin-bottom: 10px;
img {
max-width: 100%;
@include border-radius(4px);
}
}
}
}
......
......@@ -4,11 +4,21 @@
*
*/
.ui_basic {
.separator {
background: #F9F9F9;
border-left: 1px solid #DDD;
header {
&.navbar-gitlab {
.navbar-inner {
background: #F1F1F1;
border-bottom: 1px solid #DDD;
.nav > li > a {
color: $style_color;
}
.separator {
background: #F9F9F9;
border-left: 1px solid #DDD;
}
}
}
}
.main-nav {
background: #FFF;
}
......
......@@ -100,6 +100,16 @@ class Admin::UsersController < Admin::ApplicationController
end
end
def remove_email
email = user.emails.find(params[:email_id])
email.destroy
respond_to do |format|
format.html { redirect_to :back, notice: "Successfully removed email." }
format.js { render nothing: true }
end
end
protected
def user
......
......@@ -48,7 +48,7 @@ class ApplicationController < ActionController::Base
flash[:alert] = "Your account is blocked. Retry when an admin has unblocked it."
new_user_session_path
else
super
@return_to || root_path
end
end
......
......@@ -33,6 +33,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
end
end
def omniauth_error
@provider = params[:provider]
@error = params[:error]
render 'errors/omniauth_error', layout: "errors", status: 422
end
private
def handle_omniauth
......@@ -47,14 +53,19 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# Create user if does not exist
# and allow_single_sign_on is true
if Gitlab.config.omniauth['allow_single_sign_on']
@user ||= Gitlab::OAuth::User.create(oauth)
if Gitlab.config.omniauth['allow_single_sign_on'] && !@user
@user, errors = Gitlab::OAuth::User.create(oauth)
end
if @user
if @user && !errors
sign_in_and_redirect(@user)
else
flash[:notice] = "There's no such user!"
if errors
error_message = errors.map{ |attribute, message| "#{attribute} #{message}" }.join(", ")
redirect_to omniauth_error_path(oauth['provider'], error: error_message) and return
else
flash[:notice] = "There's no such user!"
end
redirect_to new_user_session_path
end
end
......
......@@ -13,6 +13,8 @@ class Projects::CommitsController < Projects::ApplicationController
@limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
@commits = @repo.commits(@ref, @path, @limit, @offset)
@note_counts = Note.where(commit_id: @commits.map(&:id)).
group(:commit_id).count
respond_to do |format|
format.html # index.html.erb
......
......@@ -20,7 +20,7 @@ class Projects::IssuesController < Projects::ApplicationController
terms = params['issue_search']
@issues = issues_filtered
@issues = @issues.where("title LIKE ?", "%#{terms}%") if terms.present?
@issues = @issues.where("title LIKE ? OR description LIKE ?", "%#{terms}%", "%#{terms}%") if terms.present?
@issues = @issues.page(params[:page]).per(20)
assignee_id, milestone_id = params[:assignee_id], params[:milestone_id]
......
......@@ -32,6 +32,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def show
@note_counts = Note.where(commit_id: @merge_request.commits.map(&:id)).
group(:commit_id).count
respond_to do |format|
format.html
format.diff { render text: @merge_request.to_diff(current_user) }
......@@ -86,6 +88,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@compare_failed = true
end
@note_counts = Note.where(commit_id: @commits.map(&:id)).
group(:commit_id).count
@diffs = compare_action.diffs
@merge_request.title = @merge_request.source_branch.titleize.humanize
@merge_request.description = @merge_request.target_project.merge_requests_template
......
......@@ -21,7 +21,7 @@ class Projects::NotesController < Projects::ApplicationController
end
def create
@note = Notes::CreateService.new(project, current_user, params).execute
@note = Notes::CreateService.new(project, current_user, params[:note]).execute
respond_to do |format|
format.json { render_note_json(@note) }
......@@ -85,12 +85,24 @@ class Projects::NotesController < Projects::ApplicationController
)
end
def note_to_discussion_with_diff_html(note)
return unless note.for_diff_line?
render_to_string(
"projects/notes/_discussion",
layout: false,
formats: [:html],
locals: { discussion_notes: [note] }
)
end
def render_note_json(note)
render json: {
id: note.id,
discussion_id: note.discussion_id,
html: note_to_html(note),
discussion_html: note_to_discussion_html(note)
discussion_html: note_to_discussion_html(note),
discussion_with_diff_html: note_to_discussion_with_diff_html(note)
}
end
......
......@@ -37,7 +37,7 @@ class Projects::RefsController < Projects::ApplicationController
0
end
@limit = 10
@limit = 25
@path = params[:path]
......
......@@ -98,8 +98,7 @@ class ProjectsController < ApplicationController
def destroy
return access_denied! unless can?(current_user, :remove_project, project)
project.team.truncate
project.destroy
::Projects::DestroyService.new(@project, current_user, {}).execute
respond_to do |format|
format.html { redirect_to root_path }
......@@ -125,18 +124,12 @@ class ProjectsController < ApplicationController
def autocomplete_sources
note_type = params['type']
note_id = params['type_id']
participating = if note_type && note_id
participants_in(note_type, note_id)
else
[]
end
team_members = sorted(@project.team.members)
participants = team_members + participating
participants = ::Projects::ParticipantsService.new(@project).execute(note_type, note_id)
@suggestions = {
emojis: Emoji.names.map { |e| { name: e, path: view_context.image_url("emoji/#{e}.png") } },
issues: @project.issues.select([:iid, :title, :description]),
mergerequests: @project.merge_requests.select([:iid, :title, :description]),
members: participants.uniq
members: participants
}
respond_to do |format|
......@@ -192,25 +185,4 @@ class ProjectsController < ApplicationController
def user_layout
current_user ? "projects" : "public_projects"
end
def participants_in(type, id)
users = case type
when "Issue"
issue = @project.issues.find_by_iid(id)
issue ? issue.participants : []
when "MergeRequest"
merge_request = @project.merge_requests.find_by_iid(id)
merge_request ? merge_request.participants : []
when "Commit"
author_ids = Note.for_commit_id(id).pluck(:author_id).uniq
User.where(id: author_ids)
else
[]
end
sorted(users)
end
def sorted(users)
users.uniq.to_a.compact.sort_by(&:username).map { |user| { username: user.username, name: user.name } }
end
end
class UsersSessionsController < Devise::SessionsController
def create
@return_to = params[:return_to]
super
end
end
......@@ -45,15 +45,15 @@ module EventsHelper
def event_feed_title(event)
if event.issue?
"#{event.author_name} #{event.action_name} issue ##{event.target_id}: #{event.issue_title} at #{event.project_name}"
"#{event.author_name} #{event.action_name} issue ##{event.target_iid}: #{event.issue_title} at #{event.project_name}"
elsif event.merge_request?
"#{event.author_name} #{event.action_name} MR ##{event.target_id}: #{event.merge_request_title} at #{event.project_name}"
"#{event.author_name} #{event.action_name} MR ##{event.target_iid}: #{event.merge_request_title} at #{event.project_name}"
elsif event.push?
"#{event.author_name} #{event.push_action_name} #{event.ref_type} #{event.ref_name} at #{event.project_name}"
elsif event.membership_changed?
"#{event.author_name} #{event.action_name} #{event.project_name}"
elsif event.note?
"#{event.author_name} commented on #{event.note_target_type} ##{truncate event.note_target_id} at #{event.project_name}"
"#{event.author_name} commented on #{event.note_target_type} ##{truncate event.note_target_iid} at #{event.project_name}"
else
""
end
......
......@@ -63,10 +63,14 @@ module GitlabMarkdownHelper
paths = extract_paths(text)
paths.uniq.each do |file_path|
new_path = rebuild_path(file_path)
# Finds quoted path so we don't replace other mentions of the string
# eg. "doc/api" will be replaced and "/home/doc/api/text" won't
text.gsub!("\"#{file_path}\"", "\"/#{new_path}\"")
# If project does not have repository
# its nothing to rebuild
if @repository.exists? && !@repository.empty?
new_path = rebuild_path(file_path)
# Finds quoted path so we don't replace other mentions of the string
# eg. "doc/api" will be replaced and "/home/doc/api/text" won't
text.gsub!("\"#{file_path}\"", "\"/#{new_path}\"")
end
end
text
......@@ -91,7 +95,12 @@ module GitlabMarkdownHelper
end
def link_to_ignore?(link)
ignored_protocols.map{ |protocol| link.include?(protocol) }.any?
if link =~ /\#\w+/
# ignore anchors like <a href="#my-header">
true
else
ignored_protocols.map{ |protocol| link.include?(protocol) }.any?
end
end
def ignored_protocols
......@@ -169,7 +178,7 @@ module GitlabMarkdownHelper
def current_sha
if @commit
@commit.id
else
elsif @repository && !@repository.empty?
@repository.head_commit.sha
end
end
......
......@@ -15,12 +15,6 @@ module NotesHelper
end
end
def link_to_merge_request_diff_line_note(note)
if note.for_merge_request_diff_line? and note.diff
link_to "#{note.diff_file_name}:L#{note.diff_new_line}", diffs_project_merge_request_path(note.project, note.noteable, anchor: note.line_code)
end
end
def note_timestamp(note)
# Shows the created at time and the updated at time if different
ts = "#{time_ago_with_tooltip(note.created_at, 'bottom', 'note_created_ago')}"
......@@ -61,4 +55,23 @@ module NotesHelper
link_to "", "javascript:;", class: "add-diff-note js-add-diff-note-button",
data: data, title: "Add a comment to this line"
end
def link_to_reply_diff(note)
return unless current_user
data = {
noteable_type: note.noteable_type,
noteable_id: note.noteable_id,
commit_id: note.commit_id,
line_code: note.line_code,
discussion_id: note.discussion_id
}
link_to "javascript:;", class: "btn reply-btn js-discussion-reply-button",
data: data, title: "Add a reply" do
link_text = ""
link_text < content_tag(:i, nil, class: 'icon-comment')
link_text << "Reply"
end
end
end
......@@ -49,12 +49,16 @@ module Mentionable
matches = mentionable_text.scan(/@[a-zA-Z][a-zA-Z0-9_\-\.]*/)
matches.each do |match|
identifier = match.delete "@"
if has_project
id = project.team.members.find_by(username: identifier).try(:id)
if identifier == "all"
users += project.team.members.flatten
else
id = User.where(username: identifier).pluck(:id).first
if has_project
id = project.team.members.find_by(username: identifier).try(:id)
else
id = User.find_by(username: identifier).try(:id)
end
users << User.find(id) unless id.blank?
end
users << User.find(id) unless id.blank?
end
users.uniq
end
......
......@@ -41,6 +41,9 @@ class Event < ActiveRecord::Base
# For Hash only
serialize :data
# Callbacks
after_create :reset_project_activity
# Scopes
scope :recent, -> { order("created_at DESC") }
scope :code_push, -> { where(action: PUSHED) }
......@@ -303,4 +306,10 @@ class Event < ActiveRecord::Base
target.respond_to? :title
end
end
def reset_project_activity
if project
project.update_column(:last_activity_at, self.created_at)
end
end
end
......@@ -57,6 +57,7 @@ class Note < ActiveRecord::Base
serialize :st_diff
before_create :set_diff, if: ->(n) { n.line_code.present? }
after_update :set_references
class << self
def create_status_change_note(noteable, project, author, status, source)
......@@ -178,10 +179,28 @@ class Note < ActiveRecord::Base
@diff ||= Gitlab::Git::Diff.new(st_diff) if st_diff.respond_to?(:map)
end
# Check if such line of code exists in merge request diff
# If exists - its active discussion
# If not - its outdated diff
def active?
# TODO: determine if discussion is outdated
# according to recent MR diff or not
true
return true unless self.diff
noteable.diffs.each do |mr_diff|
next unless mr_diff.new_path == self.diff.new_path
Gitlab::DiffParser.new(mr_diff.diff.lines.to_a, mr_diff.new_path).
each do |full_line, type, line_code, line_new, line_old|
if full_line == diff_line
return true
end
end
end
false
end
def outdated?
!active?
end
def diff_file_index
......@@ -314,4 +333,8 @@ class Note < ActiveRecord::Base
order('id DESC').limit(100).
update_all(updated_at: Time.now)
end
def set_references
notice_added_references(project, author)
end
end
......@@ -128,6 +128,7 @@ class Repository
Rails.cache.delete(cache_key(:commit_count))
Rails.cache.delete(cache_key(:graph_log))
Rails.cache.delete(cache_key(:readme))
Rails.cache.delete(cache_key(:version))
Rails.cache.delete(cache_key(:contribution_guide))
end
......@@ -156,12 +157,24 @@ class Repository
Gitlab::Git::Blob.find(self, sha, path)
end
def blob_by_oid(oid)
Gitlab::Git::Blob.raw(self, oid)
end
def readme
Rails.cache.fetch(cache_key(:readme)) do
tree(:head).readme
end
end
def version
Rails.cache.fetch(cache_key(:version)) do
tree(:head).blobs.find do |file|
file.name.downcase == 'version'
end
end
end
def contribution_guide
Rails.cache.fetch(cache_key(:contribution_guide)) do
tree(:head).contribution_guide
......
......@@ -132,6 +132,10 @@ class User < ActiveRecord::Base
before_validation :sanitize_attrs
before_save :ensure_authentication_token
after_save :ensure_namespace_correct
after_create :post_create_hook
after_destroy :post_destroy_hook
alias_attribute :private_token, :authentication_token
......@@ -369,7 +373,7 @@ class User < ActiveRecord::Base
end
def several_namespaces?
owned_groups.any?
owned_groups.any? || masters_groups.any?
end
def namespace_id
......@@ -491,4 +495,36 @@ class User < ActiveRecord::Base
GravatarService.new.execute(email, size)
end
end
def ensure_namespace_correct
# Ensure user has namespace
self.create_namespace!(path: self.username, name: self.username) unless self.namespace
if self.username_changed?
self.namespace.update_attributes(path: self.username, name: self.username)
end
end
def post_create_hook
log_info("User \"#{self.name}\" (#{self.email}) was created")
notification_service.new_user(self)
system_hook_service.execute_hooks_for(self, :create)
end
def post_destroy_hook
log_info("User \"#{self.name}\" (#{self.email}) was removed")
system_hook_service.execute_hooks_for(self, :destroy)
end
def notification_service
NotificationService.new
end
def log_info message
Gitlab::AppLogger.info message
end
def system_hook_service
SystemHooksService.new
end
end
......@@ -33,6 +33,9 @@ class UsersGroup < ActiveRecord::Base
scope :with_group, ->(group) { where(group_id: group.id) }
scope :with_user, ->(user) { where(user_id: user.id) }
after_create :notify_create
after_update :notify_update
validates :group_access, inclusion: { in: UsersGroup.group_access_roles.values }, presence: true
validates :user_id, presence: true
validates :group_id, presence: true
......@@ -43,4 +46,18 @@ class UsersGroup < ActiveRecord::Base
def access_field
group_access
end
def notify_create
notification_service.new_group_member(self)
end
def notify_update
if group_access_changed?
notification_service.update_group_member(self)
end
end
def notification_service
NotificationService.new
end
end
......@@ -37,6 +37,10 @@ class UsersProject < ActiveRecord::Base
scope :in_projects, ->(projects) { where(project_id: projects.map { |p| p.id }) }
scope :with_user, ->(user) { where(user_id: user.id) }
after_create :post_create_hook
after_update :post_update_hook
after_destroy :post_destroy_hook
class << self
# Add users to project teams with passed access option
......@@ -114,4 +118,37 @@ class UsersProject < ActiveRecord::Base
def owner?
project.owner == user
end
def post_create_hook
Event.create(
project_id: self.project.id,
action: Event::JOINED,
author_id: self.user.id
)
notification_service.new_team_member(self)
system_hook_service.execute_hooks_for(self, :create)
end
def post_update_hook
notification_service.update_team_member(self) if self.project_access_changed?
end
def post_destroy_hook
Event.create(
project_id: self.project.id,
action: Event::LEFT,
author_id: self.user.id
)
system_hook_service.execute_hooks_for(self, :destroy)
end
def notification_service
NotificationService.new
end
def system_hook_service
SystemHooksService.new
end
end
class BaseObserver < ActiveRecord::Observer
def notification
NotificationService.new
end
def event_service
EventCreateService.new
end
def log_info message
Gitlab::AppLogger.info message
end
end
class NoteObserver < BaseObserver
def after_create(note)
notification.new_note(note)
# Skip system notes, like status changes and cross-references.
unless note.system
event_service.leave_note(note, note.author)
# Create a cross-reference note if this Note contains GFM that names an
# issue, merge request, or commit.
note.references.each do |mentioned|
Note.create_cross_reference_note(mentioned, note.noteable, note.author, note.project)
end
end
end
def after_update(note)
note.notice_added_references(note.project, note.author)
end
end
class ProjectActivityCacheObserver < BaseObserver
observe :event
def after_create(event)
event.project.update_column(:last_activity_at, event.created_at) if event.project
end
end
class ProjectObserver < BaseObserver
def after_create(project)
log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"")
end
def after_update(project)
project.send_move_instructions if project.namespace_id_changed?
project.rename_repo if project.path_changed?
end
def before_destroy(project)
project.repository.expire_cache unless project.empty_repo?
end
def after_destroy(project)
GitlabShellWorker.perform_async(
:remove_repository,
project.path_with_namespace
)
GitlabShellWorker.perform_async(
:remove_repository,
project.path_with_namespace + ".wiki"
)
project.satellite.destroy
log_info("Project \"#{project.name}\" was removed")
end
end
class SystemHookObserver < BaseObserver
observe :user, :project, :users_project
def after_create(model)
system_hook_service.execute_hooks_for(model, :create)
end
def after_destroy(model)
system_hook_service.execute_hooks_for(model, :destroy)
end
private
def system_hook_service
SystemHooksService.new
end
end
class UserObserver < BaseObserver
def after_create(user)
log_info("User \"#{user.name}\" (#{user.email}) was created")
notification.new_user(user)
end
def after_destroy user
log_info("User \"#{user.name}\" (#{user.email}) was removed")
end
def after_save user
# Ensure user has namespace
user.create_namespace!(path: user.username, name: user.username) unless user.namespace
if user.username_changed?
user.namespace.update_attributes(path: user.username, name: user.username)
end
end
end
class UsersGroupObserver < BaseObserver
def after_create(membership)
notification.new_group_member(membership)
end
def after_update(membership)
notification.update_group_member(membership) if membership.group_access_changed?
end
end
class UsersProjectObserver < BaseObserver
def after_create(users_project)
Event.create(
project_id: users_project.project.id,
action: Event::JOINED,
author_id: users_project.user.id
)
notification.new_team_member(users_project)
end
def after_update(users_project)
notification.update_team_member(users_project) if users_project.project_access_changed?
end
def after_destroy(users_project)
Event.create(
project_id: users_project.project.id,
action: Event::LEFT,
author_id: users_project.user.id
)
end
end
......@@ -28,4 +28,8 @@ class BaseService
def log_info message
Gitlab::AppLogger.info message
end
def system_hook_service
SystemHooksService.new
end
end
......@@ -7,8 +7,11 @@ module Issues
Note.create_assignee_change_note(issue, issue.project, current_user, issue.assignee)
end
def execute_hooks(issue)
issue.project.execute_hooks(issue.to_hook_data, :issue_hooks)
def execute_hooks(issue, action = 'open')
issue_data = issue.to_hook_data
issue_url = Gitlab::UrlBuilder.new(:issue).build(issue.id)
issue_data[:object_attributes].merge!(url: issue_url, action: action)
issue.project.execute_hooks(issue_data, :issue_hooks)
end
def create_milestone_note(issue)
......
......@@ -5,7 +5,7 @@ module Issues
notification_service.close_issue(issue, current_user)
event_service.close_issue(issue, current_user)
create_note(issue, commit)
execute_hooks(issue)
execute_hooks(issue, 'close')
end
issue
......
......@@ -8,7 +8,7 @@ module Issues
notification_service.new_issue(issue, current_user)
event_service.open_issue(issue, current_user)
issue.create_cross_references!(issue.project, current_user)
execute_hooks(issue)
execute_hooks(issue, 'open')
end
issue
......
......@@ -4,7 +4,7 @@ module Issues
if issue.reopen
event_service.reopen_issue(issue, current_user)
create_note(issue)
execute_hooks(issue)
execute_hooks(issue, 'reopen')
end
issue
......
......@@ -23,7 +23,7 @@ module Issues
end
issue.notice_added_references(issue.project, current_user)
execute_hooks(issue)
execute_hooks(issue, 'update')
end
issue
......
module Notes
class CreateService < BaseService
def execute
note = project.notes.new(params[:note])
note = project.notes.new(params)
note.author = current_user
note.system = false
note.save
if note.save
notification_service.new_note(note)
# Skip system notes, like status changes and cross-references.
unless note.system
event_service.leave_note(note, note.author)
# Create a cross-reference note if this Note contains GFM that names an
# issue, merge request, or commit.
note.references.each do |mentioned|
Note.create_cross_reference_note(mentioned, note.noteable, note.author, note.project)
end
end
end
note
end
end
......
......@@ -51,6 +51,9 @@ module Projects
@project.creator = current_user
if @project.save
log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"")
system_hook_service.execute_hooks_for(@project, :create)
unless @project.group
@project.users_projects.create(
project_access: UsersProject::MASTER,
......
module Projects
class DestroyService < BaseService
def execute
return false unless can?(current_user, :remove_project, project)
project.team.truncate
project.repository.expire_cache unless project.empty_repo?
if project.destroy
GitlabShellWorker.perform_async(
:remove_repository,
project.path_with_namespace
)
GitlabShellWorker.perform_async(
:remove_repository,
project.path_with_namespace + ".wiki"
)
project.satellite.destroy
log_info("Project \"#{project.name}\" was removed")
system_hook_service.execute_hooks_for(project, :destroy)
true
end
end
end
end
module Projects
class ParticipantsService < BaseService
def initialize(project)
@project = project
end
def execute(note_type, note_id)
participating = if note_type && note_id
participants_in(note_type, note_id)
else
[]
end
team_members = sorted(@project.team.members)
participants = all_members + team_members + participating
participants.uniq
end
def participants_in(type, id)
users = case type
when "Issue"
issue = @project.issues.find_by_iid(id)
issue ? issue.participants : []
when "MergeRequest"
merge_request = @project.merge_requests.find_by_iid(id)
merge_request ? merge_request.participants : []
when "Commit"
author_ids = Note.for_commit_id(id).pluck(:author_id).uniq
User.where(id: author_ids)
else
[]
end
sorted(users)
end
def sorted(users)
users.uniq.to_a.compact.sort_by(&:username).map { |user| { username: user.username, name: user.name } }
end
def all_members
[{ username: "all", name: "Project and Group Members" }]
end
end
end
......@@ -13,7 +13,15 @@ module Projects
project.change_head(new_branch)
end
project.update_attributes(params[:project], as: role)
if project.update_attributes(params[:project], as: role)
if project.previous_changes.include?('namespace_id')
project.send_move_instructions
end
if project.previous_changes.include?('path')
project.rename_repo
end
end
end
end
end
......@@ -28,6 +28,7 @@ module Search
projects: [],
merge_requests: [],
issues: [],
notes: [],
total_results: 0,
}
end
......
......@@ -18,8 +18,9 @@ module Search
result[:total_results] = blobs.total_count
else
result[:merge_requests] = project.merge_requests.search(query).order('updated_at DESC').limit(20)
result[:issues] = project.issues.search(query).order('updated_at DESC').limit(20)
result[:total_results] = %w(issues merge_requests).sum { |items| result[items.to_sym].size }
result[:issues] = project.issues.where("title like :query OR description like :query ", query: "%#{query}%").order('updated_at DESC').limit(20)
result[:notes] = Note.where(noteable_type: 'issue').where(project_id: project.id).where("note like :query", query: "%#{query}%").order('updated_at DESC').limit(20)
result[:total_results] = %w(issues merge_requests notes).sum { |items| result[items.to_sym].size }
end
result
......@@ -30,6 +31,7 @@ module Search
merge_requests: [],
issues: [],
blobs: [],
notes: [],
total_results: 0,
}
end
......
......@@ -44,6 +44,8 @@
%li
%span.light Secondary email:
%strong= email.email
= link_to remove_email_admin_user_path(@user, email), data: { confirm: "Are you sure you want to remove #{email.email}?" }, method: :delete, class: "btn-tiny btn btn-remove pull-right", title: 'Remove secondary email', id: "remove_email_#{email.id}" do
%i.icon-remove
%li
%span.light Can create groups:
......
- if @has_authorized_projects
.dashboard.row
.activities.col-md-8
%section.activities.col-md-8
= render 'activities'
.side.col-md-4.left.responsive-side
%aside.side.col-md-4.left.responsive-side
= render 'sidebar'
.fixed.sidebar-expand-button.hidden-lg.hidden-md
......
......@@ -7,8 +7,8 @@
= f.check_box :remember_me
%span Remember me
%div
= hidden_field_tag 'return_to', params[:return_to]
= f.submit "Sign in", class: "btn-create btn"
.pull-right
= link_to "Forgot your password?", new_password_path(resource_name), class: "btn"
%h1.http_status_code 403
%h3.page-title Access Denied
%h1 403
%h3 Access Denied
%hr
%p You are not allowed to access this page.
%p Read more about project permissions #{link_to "here", help_page_path("permissions", "permissions"), class: "vlink"}
%h1.http_status_code 500
%h3.page-title Encoding Error
%h1 500
%h3 Encoding Error
%hr
%p Page can't be loaded because of an encoding error.
%h1.http_status_code 404
%h3.page-title Git Resource Not found
%h1 404
%h3 Git Resource Not found
%hr
%p
Application can't get access to some branch or commit in your repository. It
......
%h1.http_status_code 404
%h3.page-title The resource you were looking for doesn't exist.
%h1 404
%h3 The resource you were looking for doesn't exist.
%hr
%p You may have mistyped the address or the page may have moved.
%h1 422
%h3 Sign-in using #{@provider} auth failed
%hr
%p Sign-in failed because #{@error}.
%p There are couple of steps you can take:
%ul
%li Try logging in using your email
%li Try logging in using your username
%li If you have forgotten your password, try recovering it using #{ link_to "Password recovery", new_password_path(resource_name) }
%p If none of the options work, try contacting the GitLab administrator.
= form_tag group_filter_path(entity), method: 'get' do
%fieldset
%ul.nav.nav-pills.nav-stacked
%li{class: ("active" if !params[:status])}
= link_to group_filter_path(entity, status: nil) do
Open
%li{class: ("active" if params[:status] == 'closed')}
= link_to group_filter_path(entity, status: 'closed') do
Closed
%li{class: ("active" if params[:status] == 'all')}
= link_to group_filter_path(entity, status: 'all') do
All
%fieldset
%legend Projects:
%ul.nav.nav-pills.nav-stacked
- @projects.each do |project|
- unless entities_per_project(project, entity).zero?
%li{class: ("active" if params[:project_id] == project.id.to_s)}
= link_to group_filter_path(entity, project_id: project.id) do
= project.name_with_namespace
%small.pull-right= entities_per_project(project, entity)
- if @projects.blank?
.nothing-here-block This group has no projects yet
%fieldset
%hr
= link_to "Reset", group_filter_path(entity), class: 'btn pull-right'
......@@ -6,12 +6,12 @@
= f.label :name, class: 'control-label' do
Group name
.col-sm-10
= f.text_field :name, placeholder: "Ex. OpenSource", class: "form-control"
= f.text_field :name, placeholder: "Ex. OpenSource", class: "form-control", tabindex: 1, autofocus: true
.form-group.group-description-holder
= f.label :description, "Details", class: 'control-label'
.col-sm-10
= f.text_area :description, maxlength: 250, class: "form-control js-gfm-input", rows: 4
= f.text_area :description, maxlength: 250, class: "form-control js-gfm-input", rows: 4, tabindex: 2
.form-group.group-description-holder
= f.label :avatar, "Group avatar", class: 'control-label'
......@@ -35,6 +35,4 @@
%li Existing projects may be moved into a group
.form-actions
= f.submit 'Create group', class: "btn btn-create"
= f.submit 'Create group', class: "btn btn-create", tabindex: 3
.dashboard
.activities.col-md-8.hidden-sm.hidden-xs
%section.activities.col-md-8.hidden-sm.hidden-xs
- if current_user
= render "events/event_last_push", event: @last_push
= link_to dashboard_path, class: 'btn btn-tiny' do
......@@ -16,7 +16,7 @@
- else
.nothing-here-block Project activity will be displayed here
= spinner
.side.col-md-4
%aside.side.col-md-4
.light-well.append-bottom-20
= image_tag group_icon(@group.path), class: "avatar s90"
.clearfix.light
......
%head
%meta{charset: "utf-8"}
-# Go repository retrieval support
-# Need to be the fist thing in the head
-# Since Go is using an XML parser to process HTML5
-# https://github.com/gitlabhq/gitlabhq/pull/5958#issuecomment-45397555
- if controller_name == 'projects' && action_name == 'show'
%meta{name: "go-import", content: "#{@project.web_url_without_protocol} git #{@project.web_url}.git"}
%meta{content: "GitLab Community Edition", name: "description"}
%title
= "#{title} | " if defined?(title)
GitLab
......@@ -24,8 +33,3 @@
= auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, format: :atom, private_token: current_user.private_token), title: "Recent commits to #{@project.name}:#{@ref}")
- if current_controller?(:issues)
= auto_discovery_link_tag(:atom, project_issues_url(@project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
-# Go repository retrieval support.
- if controller_name == 'projects' && action_name == 'show'
%meta{name: "go-import", content: "#{@project.web_url_without_protocol} git #{@project.web_url}.git"}
......@@ -13,10 +13,10 @@
%i.icon-reorder
.pull-right.hidden-xs
= link_to "Sign in", new_session_path(:user), class: 'btn btn-sign-in btn-new'
= link_to "Sign in", new_session_path(:user, return_to: request.fullpath), class: 'btn btn-sign-in btn-new'
.navbar-collapse.collapse
%ul.nav.navbar-nav
%li.visible-xs
= link_to "Sign in", new_session_path(:user)
= link_to "Sign in", new_session_path(:user, return_to: request.fullpath)
.search
= form_tag search_path, method: :get, class: 'navbar-form pull-left' do |f|
= text_field_tag "search", nil, placeholder: search_placeholder, class: "search-input"
= search_field_tag "search", nil, placeholder: search_placeholder, class: "search-input"
= hidden_field_tag :group_id, @group.try(:id)
- if @project && @project.persisted?
= hidden_field_tag :project_id, @project.id
......
......@@ -4,7 +4,6 @@
%body{class: "#{app_theme} application"}
= render "layouts/head_panel", title: "" if current_user
= render "layouts/flash"
.container
.content
%center.padded.prepend-top-20
= yield
.container.navless-container
.error-page
= yield
%ul
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do
= link_to admin_root_path, title: "Stats" do
%i.icon-home
Overview
= nav_link(controller: :projects) do
= link_to "Projects", admin_projects_path
= nav_link(controller: :groups) do
= link_to "Groups", admin_groups_path
= nav_link(controller: :users) do
= link_to "Users", admin_users_path
= nav_link(controller: :groups) do
= link_to "Groups", admin_groups_path
= nav_link(controller: :logs) do
= link_to "Logs", admin_logs_path
= nav_link(controller: :broadcast_messages) do
......
%ul
= nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
= link_to root_path, title: "Home" do
%i.icon-home
Activity
= nav_link(path: 'dashboard#projects') do
= link_to projects_dashboard_path do
Projects
......
%ul
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
= link_to group_path(@group), title: "Home" do
%i.icon-home
Activity
= nav_link(path: 'groups#issues') do
= link_to issues_group_path(@group) do
Issues
......
%ul
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path, title: "Profile" do
%i.icon-home
Profile
= nav_link(controller: :accounts) do
= link_to "Account", profile_account_path
= nav_link(controller: :emails) do
......
%ul
= nav_link(path: 'projects#show', html_options: {class: "home"}) do
= link_to project_path(@project), title: "Project" do
%i.icon-home
Activity
- if project_nav_tab? :files
= nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
......
......@@ -5,7 +5,7 @@
= render 'form'
:javascript
$('#key_key').on('keyup', function(){
$('#key_key').on('focusout', function(){
var title = $('#key_title'),
val = $('#key_key').val(),
key_mail = val.match(/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+|\.[a-zA-Z0-9._-]+)/gi);
......@@ -13,4 +13,4 @@
if( key_mail && key_mail.length > 0 && title.val() == '' ){
$('#key_title').val( key_mail );
}
});
});
\ No newline at end of file
......@@ -68,12 +68,14 @@
%p.light
- if @user.avatar?
You can change your avatar here
%br
or remove the current avatar to revert to #{link_to "gravatar.com", "http://gravatar.com"}
- if Gitlab.config.gravatar.enabled
%br
or remove the current avatar to revert to #{link_to "gravatar.com", "http://gravatar.com"}
- else
You can upload an avatar here
%br
or change it at #{link_to "gravatar.com", "http://gravatar.com"}
- if Gitlab.config.gravatar.enabled
%br
or change it at #{link_to "gravatar.com", "http://gravatar.com"}
%hr
%a.choose-btn.btn.btn-small.js-choose-user-avatar-button
%i.icon-paper-clip
......
......@@ -21,6 +21,11 @@
- if can?(current_user, :admin_project, @project)
&ndash;
%strong= link_to 'Edit', edit_project_path
- elsif !@project.empty_repo? && @repository.readme
- readme = @repository.readme
&ndash;
= link_to project_blob_path(@project, tree_join(@repository.root_ref, readme.name)) do
= readme.name
- unless empty_repo
.col-md-5
......
......@@ -20,7 +20,7 @@
= render blob_commit, project: @project
%div#tree-content-holder.tree-content-holder
.file-holder
%article.file-holder
.file-title.clearfix
%i.icon-file
%span.file_name
......
.file-content.blob_file.blob-no-preview
%center
.center
= link_to project_raw_path(@project, @id) do
%h1.light
%i.icon-download-alt
......
......@@ -9,11 +9,15 @@
= link_to "Browse Code »", project_tree_path(project, commit), class: "pull-right"
.notes_count
- notes = project.notes.for_commit_id(commit.id)
- if notes.any?
- if @note_counts
- note_count = @note_counts.fetch(commit.id, 0)
- else
- notes = project.notes.for_commit_id(commit.id)
- note_count = notes.count
- if note_count > 0
%span.label.label-gray
%i.icon-comment
= notes.count
%i.icon-comment= note_count
- if commit.description?
.commit-row-description.js-toggle-content
......
......@@ -45,7 +45,7 @@
- file = project.repository.blob_at(@commit.id, diff.new_path)
- file = project.repository.blob_at(@commit.parent_id, diff.old_path) unless file
- next unless file
.diff-file.js-toggle-container{id: "diff-#{i}"}
.diff-file{id: "diff-#{i}"}
.diff-header{id: "file-path-#{hexdigest(diff.new_path || diff.old_path)}"}
- if diff.deleted_file
%span= diff.old_path
......@@ -61,7 +61,7 @@
%span.file-mode= "#{diff.a_mode}#{diff.b_mode}"
.diff-btn-group
= link_to "#", class: "js-toggle-button btn btn-small" do
= link_to "#", class: "js-toggle-diff-comments btn btn-small" do
%i.icon-chevron-down
Diff comments
&nbsp;
......
......@@ -33,7 +33,7 @@
- else
.light-well
%center
.center
%h4
There isn't anything to compare.
%p.slead
......
......@@ -190,7 +190,7 @@
.nothing-here-block Only project owner can remove a project
.save-project-loader.hide
%center
.center
%h2
%i.icon-spinner.icon-spin
Saving project.
......
......@@ -18,7 +18,7 @@
.file-content.code
%pre.js-edit-mode-pane#editor= @blob.data
.js-edit-mode-pane#preview.hide
%center
.center
%h2
%i.icon-spinner.icon-spin
......
.loading-graph
%center
.center
%h3.page-title
%i.icon-spinner.icon-spin
Building repository graph.
......
- if @project.import_in_progress?
.save-project-loader
%center
.center
%h2
%i.icon-spinner.icon-spin
Import in progress.
......@@ -11,7 +11,7 @@
- elsif @project.import_failed?
.save-project-loader
%center
.center
%h2
Import failed. Retry?
%hr
......
......@@ -39,7 +39,7 @@
%p We can't compare selected branches. It may be because of huge diff or satellite timeout. Please try again or select different branches.
- else
.light-well
%center
.center
%h4
There isn't anything to merge.
%p.slead
......
......@@ -31,7 +31,8 @@
= render "projects/merge_requests/show/diffs"
.notes.tab-content.voting_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" }
= render "projects/notes/notes_with_form"
.status
.mr-loading-status
= spinner
:javascript
var merge_request;
......
......@@ -72,7 +72,7 @@
Create a group
.save-project-loader.hide
%center
.center
%h2
%i.icon-spinner.icon-spin
Creating project &amp; repository.
......
- note = notes.first # example note
-# Check if line want not changed since comment was left
- if !defined?(line) || line == note.diff_line
%tr.notes_holder.js-toggle-content
%tr.notes_holder
%td.notes_line{ colspan: 2 }
%span.btn.disabled
%i.icon-comment
......@@ -9,5 +9,5 @@
%td.notes_content
%ul.notes{ rel: note.discussion_id }
= render notes
= render "projects/notes/discussion_reply_button", note: note
.discussion-reply-holder
= link_to_reply_diff(note)
......@@ -2,7 +2,7 @@
- note2 = notes2.first # example note
-# Check if line want not changed since comment was left
/- if !defined?(line) || line == note.diff_line
%tr.notes_holder.js-toggle-content
%tr.notes_holder
- if note1
%td.notes_line
%span.btn.disabled
......@@ -12,7 +12,8 @@
%ul.notes{ rel: note1.discussion_id }
= render notes1
= render "projects/notes/discussion_reply_button", note: note1
.discussion-reply-holder
= link_to_reply_diff(note1)
- else
%td= ""
%td= ""
......@@ -26,7 +27,8 @@
%ul.notes{ rel: note2.discussion_id }
= render notes2
= render "projects/notes/discussion_reply_button", note: note2
.discussion-reply-holder
= link_to_reply_diff(note2)
- else
%td= ""
%td= ""
......
- note = discussion_notes.first
.discussion.js-toggle-container{ class: note.discussion_id }
.discussion-header
.discussion-actions
= link_to "#", class: "js-toggle-button" do
%i.icon-chevron-up
Show/hide discussion
= image_tag avatar_icon(note.author_email), class: "avatar s32"
%div
= link_to_member(@project, note.author, avatar: false)
- if note.for_merge_request?
- if note.diff
started a discussion on this merge request diff
= link_to_merge_request_diff_line_note(note)
- else
started
%strong
%i.icon-remove
outdated
discussion on this merge request diff
- elsif note.for_commit?
started a discussion on commit
#{link_to note.noteable.short_id, project_commit_path(note.project, note.noteable)}
= link_to_commit_diff_line_note(note) if note.for_diff_line?
- else
%cite.cgray started a discussion
%div
- last_note = discussion_notes.last
last updated by
= link_to_member(@project, last_note.author, avatar: false)
%span.discussion-last-update
#{time_ago_with_tooltip(last_note.updated_at, 'bottom', 'discussion_updated_ago')}
.discussion-body.js-toggle-content
- if note.for_diff_line?
- if note.active?
= render "projects/notes/discussion_diff", discussion_notes: discussion_notes, note: note
- else
= link_to 'show outdated discussion', '#', class: 'js-show-outdated-discussion'
%div.hide.outdated-discussion
.notes{ rel: discussion_notes.first.discussion_id }
= render discussion_notes
- else
.notes{ rel: discussion_notes.first.discussion_id }
= render discussion_notes
= render "projects/notes/discussion_reply_button", note: discussion_notes.first
- if note.for_merge_request?
- if note.outdated?
= render "projects/notes/discussions/outdated", discussion_notes: discussion_notes
- else
= render "projects/notes/discussions/active", discussion_notes: discussion_notes
- else
= render "projects/notes/discussions/commit", discussion_notes: discussion_notes
= link_to "javascript:;",
class: "btn reply-btn js-discussion-reply-button",
data: { noteable_type: note.noteable_type,
noteable_id: note.noteable_id,
commit_id: note.commit_id,
line_code: note.line_code,
discussion_id: note.discussion_id },
title: "Add a reply" do
%i.icon-comment
Reply
......@@ -17,8 +17,8 @@
= f.text_area :note, size: 255, class: 'note_text js-note-text js-gfm-input markdown-area'
.light.clearfix
.pull-left Comments are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}
.pull-right Attach images (JPG, PNG, GIF) by dragging &amp; dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
.pull-left Comments are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"),{ target: '_blank', tabindex: -1 }}
.pull-right Attach images (JPG, PNG, GIF) by dragging &amp; dropping or #{link_to "selecting them", '#', class: 'markdown-selector', tabindex: -1 }.
.note-preview-holder.hide
.js-note-preview
......
- note = discussion_notes.first
.discussion.js-toggle-container{ class: note.discussion_id }
.discussion-header
.discussion-actions
= link_to "#", class: "js-toggle-button" do
%i.icon-chevron-up
Show/hide discussion
= image_tag avatar_icon(note.author_email), class: "avatar s32"
%div
= link_to_member(@project, note.author, avatar: false)
started a discussion
= link_to diffs_project_merge_request_path(note.project, note.noteable, anchor: note.line_code) do
%strong on the diff
.last-update.hide.js-toggle-content
- last_note = discussion_notes.last
last updated by
= link_to_member(@project, last_note.author, avatar: false)
%span.discussion-last-update
#{time_ago_with_tooltip(last_note.updated_at, 'bottom', 'discussion_updated_ago')}
.discussion-body.js-toggle-content
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
- note = discussion_notes.first
.discussion.js-toggle-container{ class: note.discussion_id }
.discussion-header
.discussion-actions
= link_to "#", class: "js-toggle-button" do
%i.icon-chevron-up
Show/hide discussion
= image_tag avatar_icon(note.author_email), class: "avatar s32"
%div
= link_to_member(@project, note.author, avatar: false)
started a discussion on commit
= link_to(note.noteable.short_id, project_commit_path(note.project, note.noteable), class: 'monospace')
.last-update.hide.js-toggle-content
- last_note = discussion_notes.last
last updated by
= link_to_member(@project, last_note.author, avatar: false)
%span.discussion-last-update
#{time_ago_with_tooltip(last_note.updated_at, 'bottom', 'discussion_updated_ago')}
.discussion-body.js-toggle-content
- if note.for_diff_line?
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
- else
.panel.panel-default
.notes{ rel: discussion_notes.first.discussion_id }
= render discussion_notes
.discussion-reply-holder
= link_to_reply_diff(discussion_notes.first)
- note = discussion_notes.first
.discussion.js-toggle-container{ class: note.discussion_id }
.discussion-header
.discussion-actions
= link_to "#", class: "js-toggle-button" do
%i.icon-chevron-down
Show/hide discussion
= image_tag avatar_icon(note.author_email), class: "avatar s32"
%div
= link_to_member(@project, note.author, avatar: false)
started a discussion on the
%strong outdated diff
%div
- last_note = discussion_notes.last
last updated by
= link_to_member(@project, last_note.author, avatar: false)
%span.discussion-last-update
#{time_ago_with_tooltip(last_note.updated_at, 'bottom', 'discussion_updated_ago')}
.discussion-body.js-toggle-content.hide
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
= render "home_panel"
.row
.col-md-9
%section.col-md-9
= render "events/event_last_push", event: @last_push
= render 'shared/event_filter'
.content_list
= spinner
.col-md-3.project-side.hidden-sm.hidden-xs
%aside.col-md-3.project-side.hidden-sm.hidden-xs
.clearfix
- if @project.archived?
.alert.alert-warning
......@@ -44,6 +44,12 @@
= link_to project_blob_path(@project, tree_join(@repository.root_ref, readme.name)), class: 'btn btn-block' do
= readme.name
- if @repository.version
- version = @repository.version
= link_to project_blob_path(@project, tree_join(@repository.root_ref, version.name)), class: 'btn btn-block' do
Version:
= @repository.blob_by_oid(version.id).data
.prepend-top-10
%p
%span.light Created on
......
......@@ -5,4 +5,4 @@
= link_to blob_item.name, project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name))
%td.tree_time_ago.cgray
= render 'spinner'
%td.hidden-xs.tree_commit{ colspan: 2 }
%td.hidden-xs.tree_commit
.readme-holder#README
%article.readme-holder#README
%h4.readme-file-title
%i.icon-file
= readme.name
......
......@@ -5,4 +5,4 @@
= link_to tree_item.name, project_tree_path(@project, tree_join(@id || @commit.id, tree_item.name))
%td.tree_time_ago.cgray
= render 'spinner'
%td.hidden-xs.tree_commit{ colspan: 2 }
%td.hidden-xs.tree_commit
......@@ -15,3 +15,4 @@
%ul.bordered-list
= render partial: "search/results/merge_request", collection: @search_results[:merge_requests]
= render partial: "search/results/issue", collection: @search_results[:issues]
= render partial: "search/results/note", collection: @search_results[:notes]
%li
note on issue:
= link_to [note.project, note.noteable] do
%span ##{note.noteable.iid}
%strong.term
= truncate note.noteable.title, length: 50
%span.light (#{note.project.name_with_namespace})
- if note.noteable.closed?
%span.label Closed
......@@ -14,8 +14,9 @@ class RepositoryImportWorker
project.import_finish
project.save
project.satellite.create unless project.satellite.exists?
project.update_repository_size
else
project.import_fail
end
end
end
end
\ No newline at end of file
......@@ -18,15 +18,6 @@ module Gitlab
# :all can be used as a placeholder for all plugins not explicitly named.
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# Activate observers that should always be running.
config.active_record.observers = :project_activity_cache_observer,
:note_observer,
:project_observer,
:system_hook_observer,
:user_observer,
:users_group_observer,
:users_project_observer
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
......
production: &base
gitlab:
host: localhost
port: 80
https: false
user: root
email_from: example@example.com
support_email: support@example.com
default_projects_features:
issues: true
merge_requests: true
wiki: true
wall: false
snippets: false
visibility_level: "private" # can be "private" | "internal" | "public"
issues_tracker:
gravatar:
enabled: true # Use user avatar image from Gravatar.com (default: true)
ldap:
enabled: false
host: '_your_ldap_server'
port: 636
uid: 'sAMAccountName'
method: 'ssl' # "tls" or "ssl" or "plain"
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
password: '_the_password_of_the_bind_user'
allow_username_or_email_login: true
base: ''
user_filter: ''
omniauth:
enabled: false
satellites:
# Relative paths are relative to Rails.root (default: tmp/repo_satellites/)
path: /apps/gitlab-satellites/
backup:
path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/)
gitlab_shell:
path: /apps/gitlab-shell/
# REPOS_PATH MUST NOT BE A SYMLINK!!!
repos_path: /apps/repositories/
hooks_path: /apps/gitlab-shell/hooks/
upload_pack: true
receive_pack: true
git:
bin_path: /usr/bin/git
max_size: 5242880 # 5.megabytes
timeout: 10
extra:
development:
<<: *base
test:
<<: *base
gravatar:
enabled: true
gitlab:
host: localhost
port: 80
issues_tracker:
redmine:
title: "Redmine"
project_url: "http://redmine/projects/:issues_tracker_id"
issues_url: "http://redmine/:project_id/:issues_tracker_id/:id"
new_issue_url: "http://redmine/projects/:issues_tracker_id/issues/new"
staging:
<<: *base
......@@ -68,6 +68,7 @@ Gitlab::Application.routes.draw do
put :team_update
put :block
put :unblock
delete 'remove/:email_id', action: 'remove_email', as: 'remove_email'
end
end
......@@ -171,8 +172,11 @@ Gitlab::Application.routes.draw do
resources :projects, constraints: { id: /[^\/]+/ }, only: [:new, :create]
devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations , passwords: :passwords}
devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations , passwords: :passwords, sessions: :users_sessions }
devise_scope :user do
get "/users/auth/:provider/omniauth_error" => "omniauth_callbacks#omniauth_error", as: :omniauth_error
end
#
# Project Area
#
......
......@@ -84,7 +84,7 @@ GET /users
]
```
You can search for a users by email or username with: `/users?search=John`
You can search for users by email or username with: `/users?search=John`
Also see `def search query` in `app/models/user.rb`.
......
......@@ -95,8 +95,8 @@ Remove the old Ruby 1.8 if present
Download Ruby and compile it:
mkdir /tmp/ruby && cd /tmp/ruby
curl --progress ftp://ftp.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p481.tar.gz | tar xz
cd ruby-2.0.0-p481
curl --progress ftp://ftp.ruby-lang.org/pub/ruby/2.1/ruby-2.1.2.tar.gz | tar xz
cd ruby-2.1.2
./configure --disable-install-rdoc
make
sudo make install
......@@ -243,7 +243,7 @@ GitLab Shell is an ssh access and repository management software developed speci
# By default, the gitlab-shell config is generated from your main gitlab config.
#
# Note: When using GitLab with HTTPS please change the following:
# - Provide paths to the certificates under `ca_file` and `ca_path options.
# - Provide paths to the certificates under `ca_file` and `ca_path` options.
# - The `gitlab_url` option must point to the https endpoint of GitLab.
# - In case you are using self signed certificate set `self_signed_cert` to `true`.
# See #using-https for all necessary details.
......
......@@ -2,9 +2,7 @@
## Operating Systems
GitLab is developed for the Linux operating system. For the installations options and instructions please see [the installation section of the readme](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/README.md#installation).
### Supported Linux distributions
### Supported Unix distributions
- Ubuntu
- Debian
......@@ -13,36 +11,29 @@ GitLab is developed for the Linux operating system. For the installations option
- Scientific Linux
- Oracle Linux
### Unsupported Linux distributions
For the installations options please see [the installation page on the GitLab website](https://about.gitlab.com/installation/).
### Unsupported Unix distributions
- OS X
- Arch Linux
- Fedora
- Gentoo
- FreeBSD
But on the above unsupported distributions is still possible to install GitLab yourself with the [manual installation guide](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md).
### Unsupported Unix operating systems
There is nothing that prevents GitLab from running on other Unix operating systems.
On the above unsupported distributions is still possible to install GitLab yourself.
Please see the [manual installation guide](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md) and the [unofficial installation guides](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Unofficial-Installation-Guides) on the public wiki for more information.
This means you may get it to work on systems running FreeBSD or OS X.
If you want to do this, please be aware it could be a lot of work.
Please consider using a virtual machine to run GitLab.
### Other operating systems such as Windows
### Non Unix operating systems such as Windows
GitLab is developed for Unix operating systems.
GitLab does **not** run on Windows and we have no plans of supporting it in the near future.
Please consider using a virtual machine to run GitLab.
## Ruby versions
GitLab requires Ruby (MRI) 2.0 or 2.1
You will have to use the standard MRI implementation of Ruby.
We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/)) but GitLab needs several Gems that have native extensions.
## Hardware requirements
......@@ -59,7 +50,11 @@ We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/)) but GitLab
### Memory
- 512MB is the absolute minimum but we do not recommend this amount of memory, you'll need to configure a minimum swap of 256MB, you're memory will only allow you to run one slow unicorn worker, things will case only git ssh access to work because the git http access requires two running workers (one to receive the user request and one for the authorization check),
- 512MB is the absolute minimum but we do not recommend this amount of memory.
You will either need to configure a minimum swap of 256MB and this will only allow you to run one slow unicorn worker.
One unicorn worker will cause only git ssh access to work because the git http access requires two running workers.
It requires one worker to receive the user request and one worker for the authorization check.
Or if you use SSD you can configure 2GB of swap to use two Unicorn workers and have slow operation with http access.
- 1GB supports up to 100 users (with individual repositories under 250MB, otherwise git memory usage necessitates configuring swap space)
- **2GB** is the **recommended** memory size and supports up to 500 users
- 4GB supports up to 2,000 users
......@@ -80,7 +75,15 @@ If you have enough RAM memory and a recent CPU the speed of GitLab is mainly lim
## Database
If you want to run the database separately, the **recommended** database size is **1 MB per user**
If you want to run the database separately, the **recommended** database size is **1 MB per user**.
## Redis and Sidekiq
Redis stores all user sessions and the background task queue.
The storage requirements for Redis are minimal, about 25kB per user.
Sidekiq processes the background jobs with a multithreaded process.
This process starts with the entire Rails stack (200MB+) but it can grow over time due to memory leaks.
On a very active server (10.000 active users) the Sidekiq process can use 1GB+ of memory.
## Supported webbrowsers
......
......@@ -16,6 +16,7 @@ If a user is a GitLab administrator they receive all permissions.
| Pull project code | | ✓ | ✓ | ✓ | ✓ |
| Download project | | ✓ | ✓ | ✓ | ✓ |
| Create code snippets | | ✓ | ✓ | ✓ | ✓ |
| Create new milestones | | | ✓ | ✓ | ✓ |
| Create new merge request | | | ✓ | ✓ | ✓ |
| Create new branches | | | ✓ | ✓ | ✓ |
| Push to non-protected branches | | | ✓ | ✓ | ✓ |
......
......@@ -7,6 +7,10 @@ Creates a backup archive of the database and all repositories. This archive will
The filename will be `[TIMESTAMP]_gitlab_backup.tar`. This timestamp can be used to restore an specific backup.
```
# omnibus-gitlab
sudo gitlab-rake gitlab:backup:create
# installation from source or cookbook
bundle exec rake gitlab:backup:create RAILS_ENV=production
```
......@@ -42,6 +46,10 @@ Deleting old backups... [SKIPPING]
## Restore a previously created backup
```
# omnibus-gitlab
sudo gitlab-rake gitlab:backup:restore
# installation from source or cookbook
bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
......@@ -84,6 +92,8 @@ Deleting tmp directories...[DONE]
## Configure cron to make daily backups
For omnibus-gitlab, see https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md#scheduling-a-backup .
```
cd /home/git/gitlab
sudo -u git -H editor config/gitlab.yml # Enable keep_time in the backup section to automatically delete old backups
......
......@@ -5,11 +5,19 @@
Remove namespaces(dirs) from `/home/git/repositories` if they don't exist in GitLab database.
```
# omnibus-gitlab
sudo gitlab-rake gitlab:cleanup:dirs
# installation from source or cookbook
bundle exec rake gitlab:cleanup:dirs RAILS_ENV=production
```
Remove repositories (global only for now) from `/home/git/repositories` if they don't exist in GitLab database.
```
# omnibus-gitlab
sudo gitlab-rake gitlab:cleanup:repos
# installation from source or cookbook
bundle exec rake gitlab:cleanup:repos RAILS_ENV=production
```
......@@ -15,6 +15,10 @@ How to use:
2. run the command below
```
# omnibus-gitlab
sudo gitlab-rake gitlab:import:repos
# installation from source or cookbook
bundle exec rake gitlab:import:repos RAILS_ENV=production
```
......
......@@ -5,6 +5,10 @@
This command gathers information about your GitLab installation and the System it runs on. These may be useful when asking for help or reporting issues.
```
# omnibus-gitlab
sudo gitlab-rake gitlab:env:info
# installation from source or cookbook
bundle exec rake gitlab:env:info RAILS_ENV=production
```
......@@ -52,6 +56,10 @@ It will check that each component was setup according to the installation guide
You may also have a look at our [Trouble Shooting Guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide).
```
# omnibus-gitlab
sudo gitlab-rake gitlab:check
# installation from source or cookbook
bundle exec rake gitlab:check RAILS_ENV=production
```
......
......@@ -3,6 +3,10 @@
## Add user as a developer to all projects
```bash
# omnibus-gitlab
sudo gitlab-rake gitlab:import:user_to_projects[username@domain.tld]
# installation from source or cookbook
bundle exec rake gitlab:import:user_to_projects[username@domain.tld]
```
......@@ -13,12 +17,20 @@ Notes:
- admin users are added as masters
```bash
# omnibus-gitlab
sudo gitlab-rake gitlab:import:all_users_to_all_projects
# installation from source or cookbook
bundle exec rake gitlab:import:all_users_to_all_projects
```
## Add user as a developer to all groups
```bash
# omnibus-gitlab
sudo gitlab-rake gitlab:import:user_to_groups[username@domain.tld]
# installation from source or cookbook
bundle exec rake gitlab:import:user_to_groups[username@domain.tld]
```
......@@ -29,5 +41,9 @@ Notes:
- admin users are added as owners so they can add additional users to the group
```bash
# omnibus-gitlab
sudo gitlab-rake gitlab:import:all_users_to_all_groups
# installation from source or cookbook
bundle exec rake gitlab:import:all_users_to_all_groups
```
......@@ -2,26 +2,44 @@
## Add a web hook for **ALL** projects:
# omnibus-gitlab
sudo gitlab-rake gitlab:web_hook:add URL="http://example.com/hook"
# source installations or cookbook
RAILS_ENV=production bundle exec rake gitlab:web_hook:add URL="http://example.com/hook"
## Add a web hook for projects in a given **NAMESPACE**:
# omnibus-gitlab
sudo gitlab-rake gitlab:web_hook:add URL="http://example.com/hook" NAMESPACE=acme
# source installations or cookbook
RAILS_ENV=production bundle exec rake gitlab:web_hook:add URL="http://example.com/hook" NAMESPACE=acme
## Remove a web hook from **ALL** projects using:
# omnibus-gitlab
sudo gitlab-rake gitlab:web_hook:rm URL="http://example.com/hook"
# source installations or cookbook
RAILS_ENV=production bundle exec rake gitlab:web_hook:rm URL="http://example.com/hook"
## Remove a web hook from projects in a given **NAMESPACE**:
# omnibus-gitlab
sudo gitlab-rake gitlab:web_hook:rm URL="http://example.com/hook" NAMESPACE=acme
# source installations or cookbook
RAILS_ENV=production bundle exec rake gitlab:web_hook:rm URL="http://example.com/hook" NAMESPACE=acme
## List **ALL** web hooks:
# omnibus-gitlab
sudo gitlab-rake gitlab:web_hook:list
# source installations or cookbook
RAILS_ENV=production bundle exec rake gitlab:web_hook:list
## List the web hooks from projects in a given **NAMESPACE**:
# omnibus-gitlab
sudo gitlab-rake gitlab:web_hook:list NAMESPACE=/
# source installations or cookbook
RAILS_ENV=production bundle exec rake gitlab:web_hook:list NAMESPACE=/
> Note: `/` is the global namespace.
......@@ -120,6 +120,17 @@ Merge the RC1 code into GitLab.com. Once the build is green, deploy in the morni
It is important to do this as soon as possible, so we can catch any errors before we release the full version.
### **9. Create a regressions issue**
On [the GitLab CE issue tracker on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues/) create an issue titled "GitLab X.X regressions" add the following text:
This is a meta issue to discuss possible regressions in this monthly release and any patch versions.
Please do not raise issues directly in this issue but link to issues that might warrant a patch release.
The decision to create a patch release or not is with the release manager who is assigned to this issue.
The release manager will comment here about the plans for patch releases.
Assign the issue to the release manager and /cc all the core-team members active on the issue tracker. If there are any known bugs in the release add them immediately.
# **21st - Preparation **
### **1. Prepare the blog post**
......@@ -219,17 +230,6 @@ Include a link to the blog post and keep it short.
Proposed email for CE: "We have released a new version of GitLab Community Edition and its packages. See our blog post(<link>) for more information."
### **10. Create a regressions issue**
On [the GitLab CE issue tracker on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues/) create an issue titled "GitLab X.X regressions" add the following text:
This is a meta issue to discuss possible regressions in this monthly release and any patch versions.
Please do not raise issues directly in this issue but link to issues that might warrant a patch release.
The decision to create a patch release or not is with the release manager who is assigned to this issue.
The release manager will comment here about the plans for patch releases.
Assign the issue to the release manager and /cc all the core-team members active on the issue tracker. If there are any known bugs in the release add them immediately.
# **23rd - Optional Patch Release**
# **24th - Update GitLab.com**
......
......@@ -13,7 +13,39 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
sudo service gitlab stop
```
### 2. Get latest code
### 2. Update Ruby
If you are still using Ruby 1.9.3 or below, you will need to update Ruby.
You can check which version you are running with `ruby -v`.
If you are you running Ruby 2.0.x, you do not need to upgrade ruby, but can consider doing so for performance reasons.
If you are running Ruby 2.1.1 consider upgrading to 2.1.2, because of the high memory usage of Ruby 2.1.1.
Install, update dependencies:
```bash
sudo apt-get install build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl
```
Download and compile Ruby:
```bash
mkdir /tmp/ruby && cd /tmp/ruby
curl --progress ftp://ftp.ruby-lang.org/pub/ruby/2.1/ruby-2.1.2.tar.gz | tar xz
cd ruby-2.1.2
./configure --disable-install-rdoc
make
sudo make install
```
Install Bundler:
```bash
sudo gem install bundler --no-ri --no-rdoc
```
### 3. Get latest code
```bash
cd /home/git/gitlab
......@@ -34,7 +66,7 @@ For GitLab Enterprise Edition:
sudo -u git -H git checkout 7-0-stable-ee
```
### 3. Update gitlab-shell (and its config)
### 4. Update gitlab-shell (and its config)
```bash
cd /home/git/gitlab-shell
......@@ -42,7 +74,7 @@ sudo -u git -H git fetch
sudo -u git -H git checkout v1.9.6
```
### 4. Install libs, migrations, etc.
### 5. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
......@@ -58,9 +90,13 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
# Clean up assets and cache
sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
# Update init.d script
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
sudo chmod +x /etc/init.d/gitlab
```
### 5. Update config files
### 6. Update config files
#### New configuration options for gitlab.yml
......@@ -70,12 +106,12 @@ There are new configuration options available for gitlab.yml. View them with the
git diff 6-9-stable:config/gitlab.yml.example 7-0-stable:config/gitlab.yml.example
```
### 6. Start application
### 7. Start application
sudo service gitlab start
sudo service nginx restart
### 7. Check application status
### 8. Check application status
Check if GitLab and its environment are configured correctly:
......
- [The individual upgrade guides](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update)
- [Upgrader](upgrader.md)
- [Ruby](ruby.md)
- [Patch versions](patch_versions.md)
- [MySQL to PostgreSQL](mysql_to_postgresql.md)
# Updating Ruby from source
This guide explains how to update Ruby in case you installed it from source according to the [instructions](../install/installation.md#2-ruby).
## 1. Look for Ruby versions
This guide will only update `/usr/local/bin/ruby`. You can see which Ruby binaries are installed on your system by running:
```bash
ls -l $(which -a ruby)
```
## 2. Stop GitLab
```bash
sudo service gitlab stop
```
## 3. Install or update dependencies
Here we are assuming you are using Debian/Ubuntu.
```bash
sudo apt-get install build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl
```
## 4. Download, compile and install Ruby
Find the latest stable version of Ruby 1.9 or 2.0 at <https://www.ruby-lang.org/en/downloads/>. We recommend at least 2.0.0-p353, which is patched against [CVE-2013-4164](https://www.ruby-lang.org/en/news/2013/11/22/heap-overflow-in-floating-point-parsing-cve-2013-4164/).
```bash
cd /tmp
curl --progress http://cache.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p353.tar.gz | tar xz
cd ruby-2.0.0-p353
./configure --disable-install-rdoc
make
sudo make install # overwrite the existing Ruby in /usr/local/bin
sudo gem install bundler
```
### 5. Reinstall GitLab gem bundle
Just to be sure we will reinstall the gems used by GitLab. Note that the `bundle install` command [depends on your choice of database](../install/installation.md#install-gems).
```bash
cd /home/git/gitlab
sudo -u git -H rm -rf vendor/bundle # remove existing Gem bundle
sudo -u git -H bundle install --deployment --without development test mysql aws # Assuming PostgreSQL
```
## 6. Start GitLab
We are now ready to restart GitLab.
```bash
sudo service gitlab start
```
## Done
- [Workflow](workflow.md)
- [Project Features](project_features.md)
- [Authorization for merge requests](authorization_for_merge_requests.md)
- [Groups](groups.md)
# GitLab Groups
GitLab groups allow you to group projects into directories and give users to several projects at once.
When you create a new project in GitLab, the default namespace for the project is the personal namespace associated with your GitLab user.
In this document we will see how to create groups, put projects in groups and manage who can access the projects in a group.
## Creating groups
You can create a group by going to the 'Groups' tab of the GitLab dashboard and clicking the 'New group' button.
![Click the 'New group' button in the 'Groups' tab](groups/new_group_button.png)
Next, enter the name (required) and the optional description and group avatar.
![Fill in the name for your new group](groups/new_group_form.png)
When your group has been created you are presented with the group dashboard feed, which will be empty.
![Group dashboard](groups/group_dashboard.png)
You can use the 'New project' button to add a project to the new group.
## Transfering an existing project into a group
You can transfer an existing project into a group you own from the project settings page.
First scroll down to the 'Dangerous settings' and click 'Show them to me'.
Now you can pick any of the groups you manage as the new namespace for the group.
![Transfer a project to a new namespace](groups/transfer_project.png)
GitLab administrators can use the admin interface to move any project to any namespace if needed.
## Adding users to a group
One of the benefits of putting multiple projects in one group is that you can give a user to access to all projects in the group with one action.
Suppose we have a group with two projects.
![Group with two projects](groups/group_with_two_projects.png)
On the 'Group Members' page we can now add a new user Barry to the group.
![Add user Barry to the group](groups/add_member_to_group.png)
Now because Barry is a 'Developer' member of the 'Open Source' group, he automatically gets 'Developer' access to all projects in the 'Open Source' group.
![Barry has 'Developer' access to GitLab CI](groups/project_members_via_group.png)
If necessary, you can increase the access level of an individual user for a specific project, by adding them as a Member to the project.
![Barry effectively has 'Master' access to GitLab CI now](groups/override_access_level.png)
## Managing group memberships via LDAP
In GitLab Enterprise Edition it is possible to manage GitLab group memberships using LDAP groups.
See [the GitLab Enterprise Edition documentation](http://doc.gitlab.com/ee/integration/ldap.html) for more information.
## Allowing only admins to create groups
By default, any GitLab user can create new groups.
This ability can be disabled for individual users from the admin panel.
It is also possible to configure GitLab so that new users default to not being able to create groups:
```
# For omnibus-gitlab, put the following in /etc/gitlab/gitlab.rb
gitlab_rails['gitlab_default_can_create_group'] = false
# For installations from source, uncomment the 'default_can_create_group'
# line in /home/git/gitlab/config/gitlab.yml
```
......@@ -21,3 +21,11 @@ Feature: Admin Users
And click edit on my user
When I submit modified user
Then I see user attributes changed
@javascript
Scenario: Remove users secondary email
Given I visit admin users page
And I view the user with secondary email
And I see the secondary email
When I click remove secondary email
Then I should not see secondary email anymore
......@@ -31,3 +31,8 @@ Feature: Project Feature
And I fill in merge request template
And I save project
Then I should see project with merge request template saved
Scenario: I should see project readme and version
When I visit project "Shop" page
Then I should see project "Shop" README link
And I should see project "Shop" version
......@@ -24,3 +24,10 @@ Feature: Project Redirects
Given I sign in as a user
When I visit project "Enterprise" page
Then page status code should be 404
Scenario: I visit a public project without signing in
When I visit project "Community" page
And I should see project "Community" home page
And I click on "Sign In"
And Authenticate
Then I should be redirected to "Community" page
......@@ -4,7 +4,7 @@ class AdminActiveTab < Spinach::FeatureSteps
include SharedActiveTab
Then 'the active main tab should be Home' do
ensure_active_main_tab('Home')
ensure_active_main_tab('Overview')
end
Then 'the active main tab should be Projects' do
......
......@@ -44,4 +44,23 @@ class AdminUsers < Spinach::FeatureSteps
step 'click edit on my user' do
find("#edit_user_#{current_user.id}").click
end
step 'I view the user with secondary email' do
@user_with_secondary_email = User.last
@user_with_secondary_email.emails.new(email: "secondary@example.com")
@user_with_secondary_email.save
visit "/admin/users/#{@user_with_secondary_email.username}"
end
step 'I see the secondary email' do
page.should have_content "Secondary email: #{@user_with_secondary_email.emails.last.email}"
end
step 'I click remove secondary email' do
find("#remove_email_#{@user_with_secondary_email.emails.last.id}").click
end
step 'I should not see secondary email anymore' do
page.should_not have_content "Secondary email:"
end
end
......@@ -4,7 +4,7 @@ class DashboardActiveTab < Spinach::FeatureSteps
include SharedActiveTab
Then 'the active main tab should be Home' do
ensure_active_main_tab('Home')
ensure_active_main_tab('Activity')
end
Then 'the active main tab should be Issues' do
......
......@@ -4,7 +4,7 @@ class ProfileActiveTab < Spinach::FeatureSteps
include SharedActiveTab
Then 'the active main tab should be Home' do
ensure_active_main_tab('Home')
ensure_active_main_tab('Profile')
end
Then 'the active main tab should be Account' do
......
......@@ -7,7 +7,7 @@ class ProjectActiveTab < Spinach::FeatureSteps
# Main Tabs
Then 'the active main tab should be Home' do
ensure_active_main_tab('Home')
ensure_active_main_tab('Activity')
end
Then 'the active main tab should be Settings' do
......
......@@ -131,21 +131,21 @@ class ProjectMergeRequests < Spinach::FeatureSteps
end
step 'I should see a discussion has started on line 185' do
page.should have_content "#{current_user.name} started a discussion on this merge request diff"
page.should have_content "app/assets/stylesheets/tree.scss:L185"
page.should have_content "#{current_user.name} started a discussion"
page.should have_content "app/assets/stylesheets/tree.scss"
page.should have_content "Line is wrong"
end
step 'I should see a discussion has started on commit b1e6a9dbf1:L185' do
page.should have_content "#{current_user.name} started a discussion on commit"
page.should have_content "app/assets/stylesheets/tree.scss:L185"
page.should have_content "app/assets/stylesheets/tree.scss"
page.should have_content "Line is wrong"
end
step 'I should see a discussion has started on commit b1e6a9dbf1' do
page.should have_content "#{current_user.name} started a discussion on commit"
page.should have_content "One comment to rule them all"
page.should have_content "app/assets/stylesheets/tree.scss:L185"
page.should have_content "app/assets/stylesheets/tree.scss"
end
step 'merge request is mergeable' do
......@@ -267,9 +267,7 @@ class ProjectMergeRequests < Spinach::FeatureSteps
click_button "Add Comment"
end
within ".note-text" do
page.should have_content message
end
page.should have_content message
end
def init_diff_note_first_file
......
......@@ -34,4 +34,16 @@ class ProjectFeature < Spinach::FeatureSteps
step 'I should see project with merge request template saved' do
find_field('project_merge_requests_template').value.should == 'This merge request should contain the following.'
end
step 'I should see project "Shop" README link' do
within '.project-side' do
page.should have_content "README.md"
end
end
step 'I should see project "Shop" version' do
within '.project-side' do
page.should have_content "Version: 2.2.0"
end
end
end
......@@ -31,5 +31,27 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps
project = Project.find_by(name: 'Community')
visit project_path(project) + 'DoesNotExist'
end
step 'I click on "Sign In"' do
within '.pull-right' do
click_link "Sign in"
end
end
step 'Authenticate' do
admin = create(:admin)
project = Project.find_by(name: 'Community')
find(:xpath, "//input[@id='return_to']").set "/#{project.path_with_namespace}"
fill_in "user_login", with: admin.email
fill_in "user_password", with: admin.password
click_button "Sign in"
Thread.current[:current_user] = admin
end
step 'I should be redirected to "Community" page' do
project = Project.find_by(name: 'Community')
page.current_path.should == "/#{project.path_with_namespace}"
page.status_code.should == 200
end
end
......@@ -2,11 +2,7 @@ module SharedActiveTab
include Spinach::DSL
def ensure_active_main_tab(content)
if content == "Home"
page.find('.main-nav li.active').should have_css('i.icon-home')
else
page.find('.main-nav li.active').should have_content(content)
end
page.find('.main-nav li.active').should have_content(content)
end
def ensure_active_sub_tab(content)
......
......@@ -2,7 +2,7 @@ module SharedDiffNote
include Spinach::DSL
Given 'I cancel the diff comment' do
within(".diff-file") do
within(diff_file_selector) do
find(".js-close-discussion-note-form").click
end
end
......@@ -13,14 +13,14 @@ module SharedDiffNote
end
Given 'I haven\'t written any diff comment text' do
within(".diff-file") do
within(diff_file_selector) do
fill_in "note[note]", with: ""
end
end
Given 'I leave a diff comment like "Typo, please fix"' do
find('a[data-line-code="586fb7c4e1add2d4d24e27566ed7064680098646_29_14"]').click
within(".diff-file form[rel$='586fb7c4e1add2d4d24e27566ed7064680098646_29_14']") do
within("#{diff_file_selector} form[rel$='586fb7c4e1add2d4d24e27566ed7064680098646_29_14']") do
fill_in "note[note]", with: "Typo, please fix"
find(".js-comment-button").trigger("click")
sleep 0.05
......@@ -29,7 +29,7 @@ module SharedDiffNote
Given 'I preview a diff comment text like "Should fix it :smile:"' do
find('a[data-line-code="586fb7c4e1add2d4d24e27566ed7064680098646_29_14"]').click
within(".diff-file form[rel$='586fb7c4e1add2d4d24e27566ed7064680098646_29_14']") do
within("#{diff_file_selector} form[rel$='586fb7c4e1add2d4d24e27566ed7064680098646_29_14']") do
fill_in "note[note]", with: "Should fix it :smile:"
find(".js-note-preview-button").trigger("click")
end
......@@ -38,7 +38,7 @@ module SharedDiffNote
Given 'I preview another diff comment text like "DRY this up"' do
find('a[data-line-code="586fb7c4e1add2d4d24e27566ed7064680098646_57_41"]').click
within(".diff-file form[rel$='586fb7c4e1add2d4d24e27566ed7064680098646_57_41']") do
within("#{diff_file_selector} form[rel$='586fb7c4e1add2d4d24e27566ed7064680098646_57_41']") do
fill_in "note[note]", with: "DRY this up"
find(".js-note-preview-button").trigger("click")
end
......@@ -53,63 +53,61 @@ module SharedDiffNote
end
Given 'I write a diff comment like ":-1: I don\'t like this"' do
within(".diff-file") do
within(diff_file_selector) do
fill_in "note[note]", with: ":-1: I don\'t like this"
end
end
Given 'I submit the diff comment' do
within(".diff-file") do
within(diff_file_selector) do
click_button("Add Comment")
end
end
Then 'I should not see the diff comment form' do
within(".diff-file") do
within(diff_file_selector) do
page.should_not have_css("form.new_note")
end
end
Then 'I should not see the diff comment preview button' do
within(".diff-file") do
within(diff_file_selector) do
page.should have_css(".js-note-preview-button", visible: false)
end
end
Then 'I should not see the diff comment text field' do
within(".diff-file") do
within(diff_file_selector) do
page.should have_css(".js-note-text", visible: false)
end
end
Then 'I should only see one diff form' do
within(".diff-file") do
within(diff_file_selector) do
page.should have_css("form.new_note", count: 1)
end
end
Then 'I should see a diff comment form with ":-1: I don\'t like this"' do
within(".diff-file") do
within(diff_file_selector) do
page.should have_field("note[note]", with: ":-1: I don\'t like this")
end
end
Then 'I should see a diff comment saying "Typo, please fix"' do
within(".diff-file .note") do
within("#{diff_file_selector} .note") do
page.should have_content("Typo, please fix")
end
end
Then 'I should see a discussion reply button' do
within(".diff-file") do
within(diff_file_selector) do
page.should have_link("Reply")
end
end
Then 'I should see a temporary diff comment form' do
within(".diff-file") do
within(diff_file_selector) do
page.should have_css(".js-temp-notes-holder form.new_note")
end
end
......@@ -119,40 +117,44 @@ module SharedDiffNote
end
Then 'I should see an empty diff comment form' do
within(".diff-file") do
within(diff_file_selector) do
page.should have_field("note[note]", with: "")
end
end
Then 'I should see the cancel comment button' do
within(".diff-file form") do
within("#{diff_file_selector} form") do
page.should have_css(".js-close-discussion-note-form", text: "Cancel")
end
end
Then 'I should see the diff comment preview' do
within(".diff-file form") do
within("#{diff_file_selector} form") do
page.should have_css(".js-note-preview", visible: false)
end
end
Then 'I should see the diff comment edit button' do
within(".diff-file") do
within(diff_file_selector) do
page.should have_css(".js-note-write-button", visible: true)
end
end
Then 'I should see the diff comment preview button' do
within(".diff-file") do
within(diff_file_selector) do
page.should have_css(".js-note-preview-button", visible: true)
end
end
Then 'I should see two separate previews' do
within(".diff-file") do
within(diff_file_selector) do
page.should have_css(".js-note-preview", visible: true, count: 2)
page.should have_content("Should fix it")
page.should have_content("DRY this up")
end
end
def diff_file_selector
".diff-file"
end
end
......@@ -50,12 +50,15 @@ module API
post ":id/#{noteables_str}/:#{noteable_id_str}/notes" do
required_attributes! [:body]
@noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"])
@note = @noteable.notes.new(note: params[:body])
@note.author = current_user
@note.project = user_project
opts = {
note: params[:body],
noteable_type: noteables_str.classify,
noteable_id: params[noteable_id_str]
}
@note = ::Notes::CreateService.new(user_project, current_user, opts).execute
if @note.save
if @note.valid?
present @note, with: Entities::Note
else
not_found!
......
......@@ -169,10 +169,13 @@ module Gitlab
end
def reference_user(identifier, project = @project)
if user = User.find_by(username: identifier)
options = html_options.merge(
options = html_options.merge(
class: "gfm gfm-team_member #{html_options[:class]}"
)
if identifier == "all"
link_to("@all", project_url(project), options)
elsif user = User.find_by(username: identifier)
link_to("@#{identifier}", user_url(identifier), options)
end
end
......
......@@ -44,7 +44,13 @@ module Gitlab
user.username = email_username.gsub("'", "")
end
user.save!
begin
user.save!
rescue ActiveRecord::RecordInvalid => e
log.info "(OAuth) Email #{e.record.errors[:email]}. Username #{e.record.errors[:username]}"
return nil, e.record.errors
end
log.info "(OAuth) Creating user #{email} from login with extern_uid => #{uid}"
if Gitlab.config.omniauth['block_auto_created_users'] && !ldap?
......
module Gitlab
class UrlBuilder
include Rails.application.routes.url_helpers
def initialize(type)
@type = type
end
def build(id)
case @type
when :issue
issue_url(id)
end
end
private
def issue_url(id)
issue = Issue.find(id)
project_issue_url(id: issue.iid,
project_id: issue.project,
host: Settings.gitlab['url'])
end
end
end
namespace :cache do
desc "GITLAB | Clear redis cache"
task :clear => :environment do
Rails.cache.clear
# Hack into Rails.cache until https://github.com/redis-store/redis-store/pull/225
# is accepted (I hope) and we can update the redis-store gem.
redis_store = Rails.cache.instance_variable_get(:@data)
redis_store.keys.each_slice(1000) do |key_slice|
redis_store.del(*key_slice)
end
end
end
......@@ -47,20 +47,16 @@ describe "Admin::Users", feature: true do
it "should call send mail" do
Notify.should_receive(:new_user_email)
User.observers.enable :user_observer do
click_button "Create user"
end
click_button "Create user"
end
it "should send valid email to user with email & password" do
User.observers.enable :user_observer do
click_button "Create user"
user = User.last
email = ActionMailer::Base.deliveries.last
email.subject.should have_content("Account was created")
email.text_part.body.should have_content(user.email)
email.text_part.body.should have_content('password')
end
click_button "Create user"
user = User.last
email = ActionMailer::Base.deliveries.last
email.subject.should have_content("Account was created")
email.text_part.body.should have_content(user.email)
email.text_part.body.should have_content('password')
end
end
......
require 'spec_helper'
describe "Profile account page", feature: true do
before(:each) { enable_observers }
after(:each) {disable_observers}
let(:user) { create(:user) }
before do
......
require 'spec_helper'
describe "Projects", feature: true do
before(:each) { enable_observers }
after(:each) {disable_observers}
before { login_as :user }
describe "DELETE /projects/:id" do
......
......@@ -2,7 +2,6 @@ require 'spec_helper'
describe "Search", feature: true do
before do
ActiveRecord::Base.observers.enable(:user_observer)
login_as :user
@project = create(:project, namespace: @user.namespace)
@project.team << [@user, :reporter]
......
......@@ -5,6 +5,7 @@ describe GitlabMarkdownHelper do
include IssuesHelper
let!(:project) { create(:project) }
let(:empty_project) { create(:empty_project) }
let(:user) { create(:user, username: 'gfm') }
let(:commit) { project.repository.commit }
......@@ -506,6 +507,19 @@ describe GitlabMarkdownHelper do
end
end
describe "markdwon for empty repository" do
before do
@project = empty_project
@repository = empty_project.repository
end
it "should not touch relative urls" do
actual = "[GitLab API doc][GitLab readme]\n [GitLab readme]: doc/api/README.md\n"
expected = "<p><a href=\"doc/api/README.md\">GitLab API doc</a></p>\n"
markdown(actual).should match(expected)
end
end
describe "#render_wiki_content" do
before do
@wiki = double('WikiPage')
......
require 'spec_helper'
describe Gitlab::UrlBuilder do
describe 'When asking for an issue' do
it 'returns the issue url' do
issue = create(:issue)
url = Gitlab::UrlBuilder.new(:issue).build(issue.id)
expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.to_param}/issues/#{issue.iid}"
end
end
end
......@@ -65,26 +65,4 @@ describe Event do
it { @event.branch_name.should == "master" }
it { @event.author.should == @user }
end
describe 'Team events' do
let(:user_project) { double.as_null_object }
let(:observer) { UsersProjectObserver.instance }
before {
Event.should_receive :create
observer.stub(notification: double.as_null_object)
}
describe "Joined project team" do
it "should create event" do
observer.after_create user_project
end
end
describe "Left project team" do
it "should create event" do
observer.after_destroy user_project
end
end
end
end
......@@ -27,9 +27,6 @@
require 'spec_helper'
describe Project do
before { enable_observers }
after { disable_observers }
describe "Associations" do
it { should belong_to(:group) }
it { should belong_to(:namespace) }
......
......@@ -19,21 +19,20 @@ require "spec_helper"
describe SystemHook do
describe "execute" do
before(:each) { ActiveRecord::Base.observers.enable(:all) }
before(:each) do
@system_hook = create(:system_hook)
WebMock.stub_request(:post, @system_hook.url)
end
it "project_create hook" do
project = create(:project)
Projects::CreateService.new(create(:user), name: 'empty').execute
WebMock.should have_requested(:post, @system_hook.url).with(body: /project_create/).once
end
it "project_destroy hook" do
project = create(:project)
project.destroy
user = create(:user)
project = create(:empty_project, namespace: user.namespace)
Projects::DestroyService.new(project, user, {}).execute
WebMock.should have_requested(:post, @system_hook.url).with(body: /project_destroy/).once
end
......
......@@ -145,7 +145,6 @@ describe User do
describe 'projects' do
before do
ActiveRecord::Base.observers.enable(:user_observer)
@user = create :user
@project = create :project, namespace: @user.namespace
@project_2 = create :project, group: create(:group) # Grant MASTER access to the user
......@@ -168,7 +167,6 @@ describe User do
describe 'groups' do
before do
ActiveRecord::Base.observers.enable(:user_observer)
@user = create :user
@group = create :group
@group.add_owner(@user)
......@@ -181,7 +179,6 @@ describe User do
describe 'group multiple owners' do
before do
ActiveRecord::Base.observers.enable(:user_observer)
@user = create :user
@user2 = create :user
@group = create :group
......@@ -195,7 +192,6 @@ describe User do
describe 'namespaced' do
before do
ActiveRecord::Base.observers.enable(:user_observer)
@user = create :user
@project = create :project, namespace: @user.namespace
end
......@@ -339,7 +335,7 @@ describe User do
user.all_ssh_keys.should include(key.key)
end
end
describe :avatar_type do
let(:user) { create(:user) }
......
......@@ -37,4 +37,32 @@ describe UsersGroup do
it { should respond_to(:user_name) }
it { should respond_to(:user_email) }
end
context 'notification' do
describe "#after_create" do
it "should send email to user" do
membership = build(:users_group)
membership.stub(notification_service: double('NotificationService').as_null_object)
membership.should_receive(:notification_service)
membership.save
end
end
describe "#after_update" do
before do
@membership = create :users_group
@membership.stub(notification_service: double('NotificationService').as_null_object)
end
it "should send email to user" do
@membership.should_receive(:notification_service)
@membership.update_attribute(:group_access, UsersGroup::MASTER)
end
it "does not send an email when the access level has not changed" do
@membership.should_not_receive(:notification_service)
@membership.update_attribute(:group_access, UsersGroup::OWNER)
end
end
end
end
require 'spec_helper'
describe NoteObserver do
subject { NoteObserver.instance }
before { subject.stub(notification: double('NotificationService').as_null_object) }
let(:team_without_author) { (1..2).map { |n| double :user, id: n } }
let(:note) { double(:note).as_null_object }
describe '#after_create' do
it 'is called after a note is created' do
subject.should_receive :after_create
Note.observers.enable :note_observer do
create(:note)
end
end
it 'sends out notifications' do
subject.should_receive(:notification)
subject.after_create(note)
end
it 'creates cross-reference notes as appropriate' do
@p = create(:project)
@referenced = create(:issue, project: @p)
@referencer = create(:issue, project: @p)
@author = create(:user)
Note.should_receive(:create_cross_reference_note).with(@referenced, @referencer, @author, @p)
Note.observers.enable :note_observer do
create(:note, project: @p, author: @author, noteable: @referencer,
note: "Duplicate of ##{@referenced.iid}")
end
end
it "doesn't cross-reference system notes" do
Note.should_receive(:create_cross_reference_note).once
Note.observers.enable :note_observer do
Note.create_cross_reference_note(create(:issue), create(:issue))
end
end
end
describe '#after_update' do
it 'checks for new cross-references' do
note.should_receive(:notice_added_references)
subject.after_update(note)
end
end
end
require 'spec_helper'
describe UserObserver do
before(:each) { enable_observers }
after(:each) {disable_observers}
subject { UserObserver.instance }
before { subject.stub(notification: double('NotificationService').as_null_object) }
it 'calls #after_create when new users are created' do
new_user = build(:user)
subject.should_receive(:after_create).with(new_user)
new_user.save
end
context 'when a new user is created' do
it 'sends an email' do
subject.should_receive(:notification)
create(:user)
end
it 'trigger logger' do
user = double(:user, id: 42, password: 'P@ssword!', name: 'John', email: 'u@mail.local', extern_uid?: false)
Gitlab::AppLogger.should_receive(:info)
create(:user)
end
end
end
require 'spec_helper'
describe UsersGroupObserver do
before(:each) { enable_observers }
after(:each) { disable_observers }
subject { UsersGroupObserver.instance }
before { subject.stub(notification: double('NotificationService').as_null_object) }
describe "#after_create" do
it "should send email to user" do
subject.should_receive(:notification)
create(:users_group)
end
end
describe "#after_update" do
before do
@membership = create :users_group
end
it "should send email to user" do
subject.should_receive(:notification)
@membership.update_attribute(:group_access, UsersGroup::MASTER)
end
it "does not send an email when the access level has not changed" do
subject.should_not_receive(:notification)
@membership.update_attribute(:group_access, UsersGroup::OWNER)
end
end
end
require 'spec_helper'
describe UsersProjectObserver do
before(:each) { enable_observers }
after(:each) { disable_observers }
let(:user) { create(:user) }
let(:project) { create(:project) }
subject { UsersProjectObserver.instance }
before { subject.stub(notification: double('NotificationService').as_null_object) }
describe "#after_update" do
before do
@users_project = create :users_project
end
it "should called when UsersProject updated" do
subject.should_receive(:after_update)
@users_project.update_attribute(:project_access, UsersProject::MASTER)
end
it "should send email to user" do
subject.should_receive(:notification)
@users_project.update_attribute(:project_access, UsersProject::OWNER)
end
it "should not called after UsersProject destroyed" do
subject.should_not_receive(:after_update)
@users_project.destroy
end
end
describe "#after_destroy" do
before do
@users_project = create :users_project
end
it "should called when UsersProject destroyed" do
subject.should_receive(:after_destroy)
@users_project.destroy
end
it "should create new event" do
Event.should_receive(:create)
@users_project.destroy
end
end
describe "#after_create" do
it "should send email to user" do
subject.should_receive(:notification)
Event.stub(create: true)
create(:users_project)
end
it "should create new event" do
Event.should_receive(:create)
create(:users_project)
end
end
end
......@@ -3,8 +3,6 @@ require 'mime/types'
describe API::API, api: true do
include ApiHelpers
before(:each) { enable_observers }
after(:each) {disable_observers}
let(:user) { create(:user) }
let(:user2) { create(:user) }
......
......@@ -3,9 +3,6 @@ require 'mime/types'
describe API::API, api: true do
include ApiHelpers
before(:each) { enable_observers }
after(:each) {disable_observers}
let(:user) { create(:user) }
let(:user2) { create(:user) }
let!(:project) { create(:project, creator_id: user.id) }
......
......@@ -2,9 +2,6 @@ require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
let(:user) { create(:user) }
let!(:project) { create(:project, namespace: user.namespace ) }
before { project.team << [user, :developer] }
......
......@@ -2,9 +2,6 @@ require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
let(:user) { create(:user) }
let(:key) { create(:key, user: user) }
let(:project) { create(:project) }
......
......@@ -2,9 +2,6 @@ require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
let(:user) { create(:user) }
let!(:project) { create(:project, namespace: user.namespace ) }
let!(:issue) { create(:issue, author: user, assignee: user, project: project) }
......
......@@ -2,8 +2,6 @@ require "spec_helper"
describe API::API, api: true do
include ApiHelpers
before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
let(:user) { create(:user) }
let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) }
let!(:merge_request) { create(:merge_request, :simple, author: user, assignee: user, source_project: project, target_project: project, title: "Test") }
......
......@@ -2,9 +2,6 @@ require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
before(:each) { enable_observers }
after(:each) {disable_observers}
let(:user) { create(:user) }
let!(:project) { create(:project, namespace: user.namespace ) }
let!(:milestone) { create(:milestone, project: project) }
......@@ -92,9 +89,6 @@ describe API::API, api: true do
end
describe "PUT /projects/:id/milestones/:milestone_id to test observer on close" do
before { enable_observers }
after { disable_observers }
it "should create an activity event when an milestone is closed" do
Event.should_receive(:create)
......
......@@ -2,9 +2,6 @@ require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
let(:admin) { create(:admin) }
let!(:group1) { create(:group) }
let!(:group2) { create(:group) }
......
......@@ -2,9 +2,6 @@ require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
let(:user) { create(:user) }
let!(:project) { create(:project, namespace: user.namespace ) }
let!(:issue) { create(:issue, project: project, author: user) }
......@@ -128,14 +125,10 @@ describe API::API, api: true do
end
describe "POST /projects/:id/noteable/:noteable_id/notes to test observer on create" do
before { enable_observers }
after { disable_observers }
it "should create an activity event when an issue note is created" do
Event.should_receive(:create)
post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!'
end
end
end
......@@ -2,9 +2,6 @@ require 'spec_helper'
describe API::API, 'ProjectHooks', api: true do
include ApiHelpers
before(:each) { enable_observers }
after(:each) { disable_observers }
let(:user) { create(:user) }
let(:user3) { create(:user) }
let!(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
......
......@@ -2,9 +2,6 @@ require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
before(:each) { enable_observers }
after(:each) { disable_observers }
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:user3) { create(:user) }
......
......@@ -2,9 +2,6 @@ require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
before(:each) { enable_observers }
after(:each) { disable_observers }
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:user3) { create(:user) }
......
......@@ -3,9 +3,6 @@ require 'mime/types'
describe API::API, api: true do
include ApiHelpers
before(:each) { enable_observers }
after(:each) {disable_observers}
let(:user) { create(:user) }
let(:user2) { create(:user) }
let!(:project) { create(:project, creator_id: user.id) }
......
......@@ -2,9 +2,6 @@ require "spec_helper"
describe API::API, api: true do
include ApiHelpers
before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
let(:user) { create(:user) }
let(:project) {create(:project, creator_id: user.id, namespace: user.namespace) }
......
require 'spec_helper'
describe Issues::BulkUpdateService do
before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
let(:issue) {
create(:issue, project: @project)
}
}
before do
@user = create :user
......@@ -23,7 +20,7 @@ describe Issues::BulkUpdateService do
@issues = 5.times.collect do
create(:issue, project: @project)
end
@params = {
@params = {
update: {
status: 'closed',
issues_ids: @issues.map(&:id)
......@@ -48,7 +45,7 @@ describe Issues::BulkUpdateService do
@issues = 5.times.collect do
create(:closed_issue, project: @project)
end
@params = {
@params = {
update: {
status: 'reopen',
issues_ids: @issues.map(&:id)
......@@ -71,7 +68,7 @@ describe Issues::BulkUpdateService do
before do
@new_assignee = create :user
@params = {
@params = {
update: {
issues_ids: [issue.id],
assignee_id: @new_assignee.id
......@@ -93,7 +90,7 @@ describe Issues::BulkUpdateService do
before do
@milestone = create :milestone
@params = {
@params = {
update: {
issues_ids: [issue.id],
milestone_id: @milestone.id
......
require 'spec_helper'
describe Notes::CreateService do
let(:project) { create(:empty_project) }
let(:issue) { create(:issue, project: project) }
let(:user) { create(:user) }
describe :execute do
context "valid params" do
before do
project.team << [user, :master]
opts = {
note: 'Awesome comment',
description: 'please fix',
noteable_type: 'Issue',
noteable_id: issue.id
}
@note = Notes::CreateService.new(project, user, opts).execute
end
it { @note.should be_valid }
it { @note.note.should == 'Awesome comment' }
end
end
end
......@@ -95,6 +95,49 @@ describe NotificationService do
end
end
context 'issue note mention' do
let(:issue) { create(:issue, assignee: create(:user)) }
let(:mentioned_issue) { create(:issue, assignee: issue.assignee) }
let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@all mentioned') }
before do
build_team(note.project)
end
describe :new_note do
it do
# Notify all team members
note.project.team.members.each do |member|
# User with disabled notification should not be notified
next if member.id == @u_disabled.id
should_email(member.id)
end
should_email(note.noteable.author_id)
should_email(note.noteable.assignee_id)
should_not_email(note.author_id)
should_not_email(@u_disabled.id)
should_not_email(@u_not_mentioned.id)
notification.new_note(note)
end
it 'filters out "mentioned in" notes' do
mentioned_note = Note.create_cross_reference_note(mentioned_issue, issue, issue.author, issue.project)
Notify.should_not_receive(:note_issue_email)
notification.new_note(mentioned_note)
end
end
def should_email(user_id)
Notify.should_receive(:note_issue_email).with(user_id, note.id)
end
def should_not_email(user_id)
Notify.should_not_receive(:note_issue_email).with(user_id, note.id)
end
end
context 'commit note' do
let(:note) { create(:note_on_commit) }
......@@ -312,6 +355,7 @@ describe NotificationService do
@u_disabled = create(:user, notification_level: Notification::N_DISABLED)
@u_mentioned = create(:user, username: 'mention', notification_level: Notification::N_PARTICIPATING)
@u_committer = create(:user, username: 'committer')
@u_not_mentioned = create(:user, username: 'regular', notification_level: Notification::N_PARTICIPATING)
project.team << [@u_watcher, :master]
project.team << [@u_participating, :master]
......
require 'spec_helper'
describe Projects::CreateService do
before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
describe :create_by_user do
before do
@user = create :user
......@@ -66,11 +63,8 @@ describe Projects::CreateService do
@settings.stub(:merge_requests) { true }
@settings.stub(:wiki) { true }
@settings.stub(:snippets) { true }
stub_const("Settings", Class.new)
@restrictions = double("restrictions")
@restrictions.stub(:restricted_visibility_levels) { [] }
Settings.stub_chain(:gitlab).and_return(@restrictions)
Settings.stub_chain(:gitlab, :default_projects_features).and_return(@settings)
Gitlab.config.gitlab.stub(restricted_visibility_levels: [])
Gitlab.config.gitlab.stub(:default_projects_features).and_return(@settings)
end
context 'should be public when setting is public' do
......@@ -109,11 +103,9 @@ describe Projects::CreateService do
@settings.stub(:wiki) { true }
@settings.stub(:snippets) { true }
@settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }
stub_const("Settings", Class.new)
@restrictions = double("restrictions")
@restrictions.stub(:restricted_visibility_levels) { [ Gitlab::VisibilityLevel::PUBLIC ] }
Settings.stub_chain(:gitlab).and_return(@restrictions)
Settings.stub_chain(:gitlab, :default_projects_features).and_return(@settings)
@restrictions = [ Gitlab::VisibilityLevel::PUBLIC ]
Gitlab.config.gitlab.stub(restricted_visibility_levels: @restrictions)
Gitlab.config.gitlab.stub(:default_projects_features).and_return(@settings)
end
context 'should be private when option is public' do
......@@ -158,4 +150,3 @@ describe Projects::CreateService do
Projects::CreateService.new(user, opts).execute
end
end
require 'spec_helper'
describe Projects::ImageService do
before(:each) { enable_observers }
after(:each) { disable_observers }
describe 'Image service' do
before do
@user = create :user
......
require 'spec_helper'
describe Projects::TransferService do
before(:each) { enable_observers }
after(:each) {disable_observers}
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:group2) { create(:group) }
......
require 'spec_helper'
describe Projects::UpdateService do
before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
describe :update_by_user do
before do
@user = create :user
......
require 'spec_helper'
describe 'Search::GlobalService' do
let(:user) { create(:user, namespace: found_namespace) }
let(:public_user) { create(:user, namespace: public_namespace) }
let(:internal_user) { create(:user, namespace: internal_namespace) }
let(:user) { create(:user) }
let(:public_user) { create(:user) }
let(:internal_user) { create(:user) }
let(:found_namespace) { create(:namespace, name: 'searchable namespace', path:'another_thing') }
let(:unfound_namespace) { create(:namespace, name: 'unfound namespace', path: 'yet_something_else') }
let(:internal_namespace) { create(:namespace, name: 'searchable internal namespace', path: 'something_internal') }
let(:public_namespace) { create(:namespace, name: 'searchable public namespace', path: 'something_public') }
let!(:found_project) { create(:empty_project, :private, name: 'searchable_project') }
let!(:unfound_project) { create(:empty_project, :private, name: 'unfound_project') }
let!(:internal_project) { create(:empty_project, :internal, name: 'searchable_internal_project') }
let!(:public_project) { create(:empty_project, :public, name: 'searchable_public_project') }
let!(:found_project) { create(:project, :private, name: 'searchable_project', creator_id: user.id, namespace: found_namespace) }
let!(:unfound_project) { create(:project, :private, name: 'unfound_project', creator_id: user.id, namespace: unfound_namespace) }
let!(:internal_project) { create(:project, :internal, name: 'searchable_internal_project', creator_id: internal_user.id, namespace: internal_namespace) }
let!(:public_project) { create(:project, :public, name: 'searchable_public_project', creator_id: public_user.id, namespace: public_namespace) }
before do
found_project.team << [user, :master]
end
describe '#execute' do
context 'unauthenticated' do
......@@ -38,7 +37,7 @@ describe 'Search::GlobalService' do
end
it 'namespace name should be searchable' do
context = Search::GlobalService.new(user, search: "searchable namespace")
context = Search::GlobalService.new(user, search: found_project.namespace.path)
results = context.execute
results[:projects].should match_array [found_project]
end
......
......@@ -43,7 +43,7 @@ RSpec.configure do |config|
# instead of true.
config.before(:suite) do
TestEnv.init(observers: false, init_repos: true, repos: false)
TestEnv.init(init_repos: true, repos: false)
end
config.before(:each) do
TestEnv.setup_stubs
......
......@@ -3,9 +3,7 @@ module LoginHelpers
#
# role - User role (e.g., :admin, :user)
def login_as(role)
ActiveRecord::Base.observers.enable(:user_observer) do
@user = create(role)
end
@user = create(role)
login_with(@user)
end
......
......@@ -17,14 +17,6 @@ module TestEnv
def init(opts = {})
RSpec::Mocks::setup(self)
# Disable observers to improve test speed
#
# You can enable it in whole test case where needed by next string:
#
# before(:each) { enable_observers }
#
disable_observers if opts[:observers] == false
# Disable mailer for spinach tests
disable_mailer if opts[:mailer] == false
setup_stubs
......@@ -33,14 +25,6 @@ module TestEnv
setup_test_repos(opts) if opts[:repos] == true
end
def enable_observers
ActiveRecord::Base.observers.enable(:all)
end
def disable_observers
ActiveRecord::Base.observers.disable(:all)
end
def disable_mailer
NotificationService.any_instance.stub(mailer: double.as_null_object)
end
......@@ -89,10 +73,6 @@ module TestEnv
Repository.any_instance.stub(
size: 12.45
)
BaseObserver.any_instance.stub(
current_user: double("current_user", id: 1)
)
end
def clear_repo_dir(namespace, name)
......
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